Skip to content

Commit

Permalink
Send raw transaction fix (#3037)
Browse files Browse the repository at this point in the history
* init fix

* add tests

* add send raw transaction tests

* Trace raw tx (berlin)

* add commment
  • Loading branch information
MarekM25 authored May 4, 2021
1 parent ad120a8 commit cff76f4
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 28 deletions.
82 changes: 75 additions & 7 deletions src/Nethermind/Nethermind.Core.Test/Encoding/TxDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void CalculateHash_and_tx_hash_after_decoding_return_the_same_value((stri
TestContext.Out.WriteLine($"Testing {testCase.Hash}");
RlpStream incomingTxRlp = Bytes.FromHexString(testCase.IncomingRlpHex).AsRlpStream();
Transaction decoded = _txDecoder.Decode(incomingTxRlp);
Rlp encodedForTreeRoot = _txDecoder.Encode(decoded, RlpBehaviors.ForTreeRoot);
Rlp encodedForTreeRoot = _txDecoder.Encode(decoded, RlpBehaviors.SkipTypedWrapping);

decoded.CalculateHash().Should().Be(decoded.Hash);
decoded.Hash.Should().Be(Keccak.Compute(encodedForTreeRoot.Bytes));
Expand All @@ -98,7 +98,7 @@ public void Hash_calculation_do_not_change_after_roundtrip((string IncomingRlpHe
TestContext.Out.WriteLine($"Testing {testCase.Hash}");
RlpStream incomingTxRlp = Bytes.FromHexString(testCase.IncomingRlpHex).AsRlpStream();
Transaction decoded = _txDecoder.Decode(incomingTxRlp);
Rlp encodedForTreeRoot = _txDecoder.Encode(decoded, RlpBehaviors.ForTreeRoot);
Rlp encodedForTreeRoot = _txDecoder.Encode(decoded, RlpBehaviors.SkipTypedWrapping);
decoded.Hash.Should().Be(Keccak.Compute(encodedForTreeRoot.Bytes));
}

Expand All @@ -108,21 +108,34 @@ public void Hash_calculation_do_not_change_after_roundtrip2((string IncomingRlpH
TestContext.Out.WriteLine($"Testing {testCase.Hash}");
RlpStream incomingTxRlp = Bytes.FromHexString(testCase.IncomingRlpHex).AsRlpStream();
Transaction decoded = _txDecoder.Decode(incomingTxRlp);
Rlp encodedForTreeRoot = _txDecoder.Encode(decoded, RlpBehaviors.ForTreeRoot);
Rlp encodedForTreeRoot = _txDecoder.Encode(decoded, RlpBehaviors.SkipTypedWrapping);
decoded.Hash.Should().Be(Keccak.Compute(encodedForTreeRoot.Bytes));
}

[TestCaseSource(nameof(YoloV3TestCases))]
public void ValueDecoderContext_return_the_same_transaction_as_rlp_stream((string IncomingRlpHex, Keccak Hash) testCase)
public void ValueDecoderContext_return_the_same_transaction_as_rlp_stream_with_wrapping((string IncomingRlpHex, Keccak Hash) testCase)
{
ValueDecoderContext_return_the_same_transaction_as_rlp_stream(testCase, false);
}

[TestCaseSource(nameof(SkipTypedWrappingTestCases))]
public void ValueDecoderContext_return_the_same_transaction_as_rlp_stream_without_additional_wrapping((string IncomingRlpHex, Keccak Hash) testCase)
{
ValueDecoderContext_return_the_same_transaction_as_rlp_stream(testCase, true);
}

private void ValueDecoderContext_return_the_same_transaction_as_rlp_stream(
(string IncomingRlpHex, Keccak Hash) testCase, bool wrapping)
{
TestContext.Out.WriteLine($"Testing {testCase.Hash}");
RlpStream incomingTxRlp = Bytes.FromHexString(testCase.IncomingRlpHex).AsRlpStream();
Span<byte> spanIncomingTxRlp = Bytes.FromHexString(testCase.IncomingRlpHex).AsSpan();
Rlp.ValueDecoderContext decoderContext = new Rlp.ValueDecoderContext(spanIncomingTxRlp);
Transaction decodedByValueDecoderContext = _txDecoder.Decode(ref decoderContext);
Transaction decoded = _txDecoder.Decode(incomingTxRlp);
Transaction decodedByValueDecoderContext = _txDecoder.Decode(ref decoderContext, wrapping ? RlpBehaviors.SkipTypedWrapping : RlpBehaviors.None);
Transaction decoded = _txDecoder.Decode(incomingTxRlp, wrapping ? RlpBehaviors.SkipTypedWrapping : RlpBehaviors.None);
Rlp encoded = _txDecoder.Encode(decoded!);
Rlp encodedWithDecodedByValueDecoderContext = _txDecoder.Encode(decodedByValueDecoderContext!);
decoded!.Hash.Should().Be(testCase.Hash);
decoded!.Hash.Should().Be(decodedByValueDecoderContext!.Hash);
Assert.AreEqual(encoded.Bytes, encodedWithDecodedByValueDecoderContext.Bytes);
}
Expand All @@ -131,11 +144,66 @@ public void ValueDecoderContext_return_the_same_transaction_as_rlp_stream((strin
[TestCaseSource(nameof(TestCaseSource))]
public void Rlp_encode_should_return_the_same_as_rlp_stream_encoding((Transaction Tx, string Description) testCase)
{
Rlp rlpStreamResult = _txDecoder.Encode(testCase.Tx, RlpBehaviors.ForTreeRoot);
Rlp rlpStreamResult = _txDecoder.Encode(testCase.Tx, RlpBehaviors.SkipTypedWrapping);
Rlp rlpResult = Rlp.Encode(testCase.Tx, false, true, testCase.Tx.ChainId ?? 0);
Assert.AreEqual(rlpResult.Bytes, rlpStreamResult.Bytes);
}

public static IEnumerable<(string, Keccak)> SkipTypedWrappingTestCases()
{
yield return
(
"01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9",
new Keccak("0x212a85be428a85d00fb5335b013bc8d3cf7511ffdd8938de768f4ca8bf1caf50")
);
yield return
(
"01f8c786796f6c6f763301843b9aca00826a40948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f85bf859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000080a01feaff3227c4fe4954fe5297898027d71eb9ae2291e2b967f00b2f5ccd0597baa053bfeb53c31024700b8d3b226eb60766178b17f215c3a5b5bd7fa2c45db86fb8",
new Keccak("0x8204f9c9043f170ab4c061c60e690a79f3bdb88d4af69c69d67248b25fb6a4a7")
);
yield return
(
"01f8c786796f6c6f763302843b9aca00826a40948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f85bf859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000001a05dd874c3cbf30fa22f2612c4dda995e53dda6f3aad335760bccd0fe3ae65dadda056208b02dac8246ecbf4624c8b49302e4869781e630ebba356e13d532166ba5d",
new Keccak("0x2c876e955d2b656d858cdad0400920fba877b807c70b36ca18b67db96865f6a0")
);

yield return
(
"01f8c786796f6c6f763303843b9aca00826a40948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f85bf859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000080a072515bdc69de9eb8e5067ffcd069ec84745ea629cfc60b854edccdd6a9d1d80fa063bb9c012fdb80aabdb915bea8e3c99b574e88cf37daea4dcc535627b48b56f0",
new Keccak("0x449556a7ee5a1e708a0afcc5110507c6a45e894a3ad6d7e21c50bbe521626229")
);
yield return
(
"01f9017e86796f6c6f763304843b9aca00829ab0948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f90111f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000133700000000000000000000000f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000133700000000000000000000000f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000080a09e41e382c76d2913521d7191ecced4a1a16fe0f3e5e22d83d50dd58adbe409e1a07c0e036eff80f9ca192ac26d533fc49c280d90c8b62e90c1a1457b50e51e6144",
new Keccak("0x70c0d568f790ee752c9f86073f7687e286b919add9e1d768dc6c5f1812a364f7")
);
yield return
(
"f86905843b9aca00825208948a8eafb1cf62bfbeb1741769dae1a9dd47996192018086f2ded8deec8aa04135bba08382dae6a1d5ec4b557f2460e1d63fb6f93773a7a951ce38a28a31ada03d36a791688f311252df622a48a9acfb0500fd3584a8305ee004d895c0257400",
new Keccak("0x593dd0e1bf113b762674470741817c4d823c73fb7377da4f6073c7885585ae92")
);
yield return
(
"01f8a587796f6c6f76337880843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000001a0e0bfceab9cadc44aa4a2c7f985f773586e2976d19cc54620e95f7d5e24bfeb6aa03225ae7b7ef42716ee5cae1b0f7f05d9777b1d4882d387e4da88cae65095bef8",
new Keccak("0x5cb00d928abf074cb81fc5e54dd49ef541afa7fc014b8a53fb8c29f3ecb5cadb")
);
yield return
(
"01f8c887796f6c6f76337801843b9aca00826a40948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f85bf859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000001a0cb68184d42bd56b82abc9fc7cde0190308205264d6af4224d40fb504eee00d5aa036a483e4fd876a9a7817dee78f12decc534d124a55ae2c89c65e6c45452eb2ce",
new Keccak("0xd570bbb09f5bd9abf8fdc6ec7e036612bcb6b02b25f51ef0e1544f2a539ca3ac")
);
yield return
(
"01f8c887796f6c6f76337803843b9aca00826a40948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f85bf859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000080a094f3a6bbf5039b1a794e5be7628809bdf757c4ff59e5399dec74c61137074f80a049baf92bb5fb2d6c6bf8287fcd75eaea80ad38d1b8d29ce242c4ac51e1067d52",
new Keccak("0x0a956694228afe4577bd94fcf8a3aa8544bbadcecfe0d66ccad8ec7ae56c025f")
);
yield return
(
"01f85b821e8e8204d7847735940083030d408080853a60005500c080a0f43e70c79190701347517e283ef63753f6143a5225cbb500b14d98eadfb7616ba070893923d8a1fc97499f426524f9e82f8e0322dfac7c3d7e8a9eee515f0bcdc4",
new Keccak("0x64450bbd000900379235ca8cad7c6f04288b9a9044967e1e1d63c0bc352624e0")
);
}

public static IEnumerable<(string, Keccak)> YoloV3TestCases()
{
yield return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace Nethermind.JsonRpc.Test.Modules
{
[Parallelizable(ParallelScope.All)]
[TestFixture]
public class EthModuleTests
public class EthRpcModuleTests
{
[TestCase("earliest", "0x3635c9adc5dea00000")]
[TestCase("latest", "0x3635c9adc5de9f09e5")]
Expand Down Expand Up @@ -703,6 +703,21 @@ public async Task Send_transaction_with_signature_will_not_try_to_sign()
Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}", serialized);
}

[TestCase("01f85b821e8e8204d7847735940083030d408080853a60005500c080a0f43e70c79190701347517e283ef63753f6143a5225cbb500b14d98eadfb7616ba070893923d8a1fc97499f426524f9e82f8e0322dfac7c3d7e8a9eee515f0bcdc4")]
public async Task Send_raw_transaction_will_send_transaction(string rawTransaction)
{
using Context ctx = await Context.Create();
ITxSender txSender = Substitute.For<ITxSender>();
IBlockchainBridge bridge = Substitute.For<IBlockchainBridge>();
txSender.SendTransaction(Arg.Any<Transaction>(), TxHandlingOptions.PersistentBroadcast).Returns(TestItem.KeccakA);

ctx._test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockchainBridge(bridge).WithTxSender(txSender).Build();
string serialized = ctx._test.TestEthRpc("eth_sendRawTransaction", rawTransaction);

await txSender.Received().SendTransaction(Arg.Any<Transaction>(), TxHandlingOptions.PersistentBroadcast);
Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}", serialized);
}

[Test]
public async Task Send_transaction_without_signature_will_not_set_nonce_when_zero_and_not_null()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,13 @@ public void Can_trace_raw_parity_style()
ResultWrapper<ParityTxTraceFromReplay> result = traceRpcModule.trace_rawTransaction(Bytes.FromHexString("f889808609184e72a00082271094000000000000000000000000000000000000000080a47f74657374320000000000000000000000000000000000000000000000000000006000571ca08a8bbf888cfa37bbf0bb965423625641fc956967b81d12e23709cead01446075a01ce999b56a8a88504be365442ea61239198e23d1fce7d00fcfc5cd3b44b7215f"), new[] {"trace"});
Assert.NotNull(result.Data);
}

[Test]
public void Can_trace_raw_parity_style_berlin_tx()
{
TraceRpcModule traceRpcModule = new TraceRpcModule(NullReceiptStorage.Instance, _tracer, _blockTree, _jsonRpcConfig);
ResultWrapper<ParityTxTraceFromReplay> result = traceRpcModule.trace_rawTransaction(Bytes.FromHexString("01f85b821e8e8204d7847735940083030d408080853a60005500c080a0f43e70c79190701347517e283ef63753f6143a5225cbb500b14d98eadfb7616ba070893923d8a1fc97499f426524f9e82f8e0322dfac7c3d7e8a9eee515f0bcdc4"), new[] {"trace"});
Assert.NotNull(result.Data);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ public async Task<ResultWrapper<Keccak>> eth_sendRawTransaction(byte[] transacti
{
try
{
Transaction tx = Rlp.Decode<Transaction>(transaction, RlpBehaviors.AllowUnsigned);
Transaction tx = Rlp.Decode<Transaction>(transaction, RlpBehaviors.AllowUnsigned | RlpBehaviors.SkipTypedWrapping);
return await SendTx(tx);
}
catch (RlpException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public ResultWrapper<ParityTxTraceFromReplay[]> trace_callMany((TransactionForRp

public ResultWrapper<ParityTxTraceFromReplay> trace_rawTransaction(byte[] data, string[] traceTypes)
{
Transaction tx = _txDecoder.Decode(new RlpStream(data));
Transaction tx = _txDecoder.Decode(new RlpStream(data), RlpBehaviors.SkipTypedWrapping);
return TraceTx(tx, traceTypes, BlockParameter.Latest);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public int GetLength(TxReceipt item, RlpBehaviors rlpBehaviors)
(int Total, int Logs) length = GetContentLength(item, rlpBehaviors);
int receiptPayloadLength = Rlp.GetSequenceRlpLength(length.Total);

bool isForTxRoot = (rlpBehaviors & RlpBehaviors.ForTreeRoot) == RlpBehaviors.ForTreeRoot;
bool isForTxRoot = (rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping;
int result = item.TxType != TxType.Legacy
? isForTxRoot
? (1 + receiptPayloadLength)
Expand Down Expand Up @@ -173,7 +173,7 @@ public void Encode(RlpStream rlpStream, TxReceipt item, RlpBehaviors rlpBehavior

if (item.TxType != TxType.Legacy)
{
if ((rlpBehaviors & RlpBehaviors.ForTreeRoot) == RlpBehaviors.None)
if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.None)
{
rlpStream.StartByteArray(sequenceLength + 1, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public void Encode(RlpStream rlpStream, TxReceipt? item, RlpBehaviors rlpBehavio

if (item.TxType != TxType.Legacy)
{
if ((rlpBehaviors & RlpBehaviors.ForTreeRoot) == RlpBehaviors.None)
if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.None)
{
rlpStream.StartByteArray(sequenceLength + 1, false);
}
Expand Down Expand Up @@ -332,7 +332,7 @@ public int GetLength(TxReceipt item, RlpBehaviors rlpBehaviors)
(int Total, int Logs) length = GetContentLength(item, rlpBehaviors);
int receiptPayloadLength = Rlp.GetSequenceRlpLength(length.Total);

bool isForTxRoot = (rlpBehaviors & RlpBehaviors.ForTreeRoot) == RlpBehaviors.ForTreeRoot;
bool isForTxRoot = (rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping;
int result = item.TxType != TxType.Legacy
? isForTxRoot
? (1 + receiptPayloadLength)
Expand Down
7 changes: 5 additions & 2 deletions src/Nethermind/Nethermind.Serialization.Rlp/RlpBehaviors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public enum RlpBehaviors
Storage = 4,
Eip658Receipts = 8,
AllowUnsigned = 16,
ForTreeRoot = 32,
All = AllowExtraData | ForSealing | Storage | Eip658Receipts | AllowUnsigned | ForTreeRoot
SkipTypedWrapping = 32, // introduced after typed transactions. In the network (devp2p) transaction has additional wrapping
// when we're calculating tx hash or sending raw transaction we should skip this wrapping
// with additional wrapping for typed transactions we're decoding Uint8Array([TransactionType, TransactionPayload]
// without wrapping we're decoding (TransactionType || TransactionPayload)
All = AllowExtraData | ForSealing | Storage | Eip658Receipts | AllowUnsigned | SkipTypedWrapping
}
}
Loading

0 comments on commit cff76f4

Please sign in to comment.