diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/ReceiptsForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/ReceiptsForRpcTests.cs index 2a0ebbd40dc9..2a9adf07fe06 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/ReceiptsForRpcTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Data/ReceiptsForRpcTests.cs @@ -132,6 +132,56 @@ public void Error_field_is_not_deserialized() Assert.That(receiptForRpc!.ToReceipt().Error, Is.Null); } + [Test] + public void Post_byzantium_receipt_serializes_status_without_root() + { + TxReceipt receipt = new() + { + Bloom = Bloom.Empty, + Index = 0, + Recipient = TestItem.AddressA, + Sender = TestItem.AddressB, + BlockHash = TestItem.KeccakA, + BlockNumber = 1, + GasUsed = 1000, + TxHash = Keccak.OfAnEmptyString, + StatusCode = 1, + GasUsedTotal = 1000, + Logs = [] + }; + + using JsonDocument document = JsonDocument.Parse(SerializeReceipt(receipt)); + JsonElement root = document.RootElement; + + Assert.That(root.TryGetProperty("root", out _), Is.False); + Assert.That(root.GetProperty("status").GetString(), Is.EqualTo("0x1")); + } + + [Test] + public void Pre_byzantium_receipt_serializes_root_without_status() + { + TxReceipt receipt = new() + { + Bloom = Bloom.Empty, + Index = 0, + Recipient = TestItem.AddressA, + Sender = TestItem.AddressB, + BlockHash = TestItem.KeccakA, + BlockNumber = 1, + GasUsed = 1000, + TxHash = Keccak.OfAnEmptyString, + PostTransactionState = TestItem.KeccakB, + GasUsedTotal = 1000, + Logs = [] + }; + + using JsonDocument document = JsonDocument.Parse(SerializeReceipt(receipt)); + JsonElement root = document.RootElement; + + Assert.That(root.TryGetProperty("status", out _), Is.False); + Assert.That(root.GetProperty("root").GetString(), Is.EqualTo(TestItem.KeccakB.ToString())); + } + private static TxReceipt CreateDiagnosticReceipt() => new() { diff --git a/src/Nethermind/Nethermind.JsonRpc/Converters/TxReceiptConverter.cs b/src/Nethermind/Nethermind.JsonRpc/Converters/TxReceiptConverter.cs index 205c0ecb59a7..dc337e993f49 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Converters/TxReceiptConverter.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Converters/TxReceiptConverter.cs @@ -30,10 +30,19 @@ public override void Write(Utf8JsonWriter writer, TxReceipt value, JsonSerialize writer.WritePropertyName("type"); JsonSerializer.Serialize(writer, receipt.Type, options); } - writer.WritePropertyName("root"); - ByteArrayConverter.Convert(writer, (receipt.Root ?? Keccak.Zero).Bytes); - writer.WritePropertyName("status"); - JsonSerializer.Serialize(writer, receipt.Status, options); + // Pre-Byzantium receipts carry a post-state root; from Byzantium onwards they carry a status + // code. The two are mutually exclusive (EIP-658), matching the receipt schema in + // ethereum/execution-apis and go-ethereum's marshalReceipt. + if (receipt.Root is not null) + { + writer.WritePropertyName("root"); + ByteArrayConverter.Convert(writer, receipt.Root.Bytes); + } + else + { + writer.WritePropertyName("status"); + JsonSerializer.Serialize(writer, receipt.Status, options); + } writer.WritePropertyName("cumulativeGasUsed"); JsonSerializer.Serialize(writer, receipt.CumulativeGasUsed, options);