Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send raw transaction fix #3037

Merged
merged 5 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have 2 flags there with same value?
SkipTypedWrapping = 32
and
ForTreeRoot = 32

yes they are the same, but intention differs...

Not sure if good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm. IMO the intention is the same. We're skipping byte array rlp for typed objects.

Copy link
Member

@LukaszRozmej LukaszRozmej May 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe name it:
ForTreeRoot
ForRpc/FromRpc ?

// 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