diff --git a/src/Nethermind/Ethereum.Test.Base/AuthorizationListJson.cs b/src/Nethermind/Ethereum.Test.Base/AuthorizationListJson.cs index a2b8b6cfcbc..728fe1bd52b 100644 --- a/src/Nethermind/Ethereum.Test.Base/AuthorizationListJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/AuthorizationListJson.cs @@ -2,15 +2,16 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; +using Nethermind.Int256; namespace Ethereum.Test.Base; public class AuthorizationListJson { - public ulong ChainId { get; set; } + public UInt256 ChainId { get; set; } public Address Address { get; set; } - public ulong Nonce { get; set; } + public UInt256 Nonce { get; set; } public ulong V { get; set; } - public byte[] R { get; set; } - public byte[] S { get; set; } + public string R { get; set; } + public string S { get; set; } public Address Signer { get; set; } } diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 7d41065cd52..e1bfb68d9e4 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -162,13 +162,51 @@ public static Transaction Convert(PostStateJson postStateJson, TransactionJson t { transaction.AuthorizationList = transactionJson.AuthorizationList - .Select(i => new AuthorizationTuple( - i.ChainId, - i.Address, - i.Nonce, - i.V, - i.R, - i.S)).ToArray(); + .Select(i => + { + if (i.ChainId > ulong.MaxValue) + { + i.ChainId = 0; + transaction.SenderAddress = Address.Zero; + } + if (i.Nonce > ulong.MaxValue) + { + i.Nonce = 0; + transaction.SenderAddress = Address.Zero; + } + UInt256 s = UInt256.Zero; + if (i.S.Length > 66) + { + i.S = "0x0"; + transaction.SenderAddress = Address.Zero; + } + else + { + s = UInt256.Parse(i.S); + } + UInt256 r = UInt256.Zero; + if (i.R.Length > 66) + { + i.R = "0x0"; + transaction.SenderAddress = Address.Zero; + } + else + { + r = UInt256.Parse(i.R); + } + if (i.V > byte.MaxValue) + { + i.V = 0; + transaction.SenderAddress = Address.Zero; + } + return new AuthorizationTuple( + i.ChainId.u0, + i.Address, + i.Nonce.u0, + (byte)i.V, + r, + s); + }).ToArray(); if (transaction.AuthorizationList.Any()) { transaction.Type = TxType.SetCode; diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs index f52adce9ba7..96972893b1b 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Numerics; using FluentAssertions; +using Microsoft.AspNetCore.Mvc.Routing; using Nethermind.Consensus.Messages; using Nethermind.Consensus.Validators; using Nethermind.Core; @@ -528,7 +529,7 @@ public void IsWellFormed_CreateTxInSetCode_ReturnsFalse() { TransactionBuilder txBuilder = Build.A.Transaction .WithType(TxType.SetCode) - .WithAuthorizationCode(new AuthorizationTuple(0, TestItem.AddressA, 0, 0, [], [])) + .WithAuthorizationCode(new AuthorizationTuple(0, TestItem.AddressA, 0, 0, 0, 0)) .WithMaxFeePerGas(100000) .WithGasLimit(1000000) .WithChainId(TestBlockchainIds.ChainId) @@ -552,7 +553,7 @@ public void IsWellFormed_AuthorizationListTxInPragueSpec_ReturnsTrue() TransactionBuilder txBuilder = Build.A.Transaction .WithType(TxType.SetCode) .WithTo(TestItem.AddressA) - .WithAuthorizationCode(new AuthorizationTuple(0, TestItem.AddressA, 0, 0, [], [])) + .WithAuthorizationCode(new AuthorizationTuple(0, TestItem.AddressA, 0, 0, 0, 0)) .WithMaxFeePerGas(100000) .WithGasLimit(1000000) .WithChainId(TestBlockchainIds.ChainId) @@ -599,30 +600,6 @@ public void IsWellFormed_NullAuthorizationList_ReturnsFalse() Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).AsBool, Is.False); } - private static object[] BadSignatures = - { - new object[] { 1ul, (UInt256)1, Secp256K1Curve.HalfNPlusOne, false}, - new object[] { 1ul, UInt256.Zero, Secp256K1Curve.HalfN, true }, - new object[] { 0ul, UInt256.Zero, UInt256.Zero, true }, - }; - [TestCaseSource(nameof(BadSignatures))] - public void IsWellFormed_AuthorizationTupleHasBadSignature_ReturnsFalse(ulong yParity, UInt256 r, UInt256 s, bool expected) - { - TransactionBuilder txBuilder = Build.A.Transaction - .WithType(TxType.SetCode) - .WithTo(TestItem.AddressA) - .WithAuthorizationCode(new AuthorizationTuple(0, Address.Zero, 0, new Signature(r, s, yParity + Signature.VOffset))) - .WithMaxFeePerGas(100000) - .WithGasLimit(1000000) - .WithChainId(TestBlockchainIds.ChainId) - .SignedAndResolved(); - - Transaction tx = txBuilder.TestObject; - TxValidator txValidator = new(TestBlockchainIds.ChainId); - - Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).AsBool, Is.EqualTo(expected)); - } - private static IEnumerable NonSetCodeTypes() => Enum.GetValues().Where(t => t != TxType.SetCode && t != TxType.DepositTx); diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index 070ab579360..b5db9fd9eef 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -328,15 +328,6 @@ public ValidationResult IsWellFormed(Transaction transaction, IReleaseSpec relea transaction.AuthorizationList switch { null or { Length: 0 } => TxErrorMessages.MissingAuthorizationList, - var authorizationList when authorizationList.Any(a => !ValidateAuthoritySignature(a.AuthoritySignature)) => - TxErrorMessages.InvalidAuthoritySignature, _ => ValidationResult.Success }; - - private bool ValidateAuthoritySignature(Signature signature) - { - UInt256 sValue = new(signature.SAsSpan, isBigEndian: true); - - return sValue < Secp256K1Curve.HalfNPlusOne && signature.RecoveryId is 0 or 1; - } } diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs index 0d243601940..b6181919823 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs @@ -55,6 +55,87 @@ public void DecodeValueDecoderContext_CodeAddressIsNull_ThrowsRlpException() , Throws.TypeOf()); } + public static IEnumerable WrongSizeFieldsEncodedCases() + { + yield return TupleRlpStream( + //Wrong chain size + Enumerable.Range(0, 9).Select(i => (byte)0xFF).ToArray(), + Address.Zero.Bytes, + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 1).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray() + ); + + yield return TupleRlpStream( + //Wrong address size + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 19).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 1).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray() + ); + + yield return TupleRlpStream( + //Wrong address size + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 21).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 1).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray() + ); + + yield return TupleRlpStream( + //Wrong nonce size + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Address.Zero.Bytes, + Enumerable.Range(0, 9).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 1).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray() + ); + + yield return TupleRlpStream( + //Wrong yParity size + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Address.Zero.Bytes, + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 2).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray() + ); + + yield return TupleRlpStream( + //Wrong R size + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Address.Zero.Bytes, + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 1).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 33).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray() + ); + + yield return TupleRlpStream( + //Wrong S size + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Address.Zero.Bytes, + Enumerable.Range(0, 8).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 1).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 32).Select(i => (byte)0xFF).ToArray(), + Enumerable.Range(0, 33).Select(i => (byte)0xFF).ToArray() + ); + } + + [TestCaseSource(nameof(WrongSizeFieldsEncodedCases))] + public void Encode_TupleHasFieldsOutsideBoundaries_ThrowsRlpException(RlpStream badEncoding) + { + AuthorizationTupleDecoder sut = new(); + + Assert.That(() => sut.Decode(badEncoding, RlpBehaviors.None), Throws.InstanceOf()); + } + private static RlpStream TupleRlpStreamWithNull() { Address? codeAddress = null; @@ -77,4 +158,25 @@ private static RlpStream TupleRlpStreamWithNull() stream.Position = 0; return stream; } + + private static RlpStream TupleRlpStream(byte[] chainId, byte[] address, byte[] nonce, byte[] yParity, byte[] r, byte[] s) + { + int length = + +Rlp.LengthOf(chainId) + + Rlp.LengthOf(address) + + Rlp.LengthOf(nonce) + + Rlp.LengthOf(yParity) + + Rlp.LengthOf(r) + + Rlp.LengthOf(s); + RlpStream stream = new RlpStream(Rlp.LengthOfSequence(length)); + stream.StartSequence(length); + stream.Encode(chainId); + stream.Encode(address); + stream.Encode(nonce); + stream.Encode(yParity); + stream.Encode(r); + stream.Encode(s); + stream.Position = 0; + return stream; + } } diff --git a/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs b/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs index 69fd3b53768..55c9a2c9bbf 100644 --- a/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs +++ b/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Crypto; +using Nethermind.Int256; using System; namespace Nethermind.Core; @@ -16,10 +17,10 @@ public AuthorizationTuple( ulong chainId, Address codeAddress, ulong nonce, - ulong yParity, - byte[] r, - byte[] s, - Address? authority = null) : this(chainId, codeAddress, nonce, new Signature(r, s, yParity + Signature.VOffset), authority) + byte yParity, + UInt256 r, + UInt256 s, + Address? authority = null) : this(chainId, codeAddress, nonce, new Signature(r, s, (ulong)yParity + Signature.VOffset), authority) { } public ulong ChainId { get; } = chainId; diff --git a/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs b/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs index d1c068694da..6d98c21351a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs @@ -125,9 +125,9 @@ [new AuthorizationTuple( TestContext.CurrentContext.Random.NextULong(), new Address(TestContext.CurrentContext.Random.NextBytes(20)), TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)) + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))) ], GasCostOf.NewAccount); yield return ( @@ -135,16 +135,16 @@ [new AuthorizationTuple( TestContext.CurrentContext.Random.NextULong(), new Address(TestContext.CurrentContext.Random.NextBytes(20)), TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)), + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))), new AuthorizationTuple( TestContext.CurrentContext.Random.NextULong(), new Address(TestContext.CurrentContext.Random.NextBytes(20)), TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)) + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))) ], GasCostOf.NewAccount * 2); yield return ( @@ -152,23 +152,23 @@ [new AuthorizationTuple( TestContext.CurrentContext.Random.NextULong(), new Address(TestContext.CurrentContext.Random.NextBytes(20)), TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)), + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))), new AuthorizationTuple( TestContext.CurrentContext.Random.NextULong(), new Address(TestContext.CurrentContext.Random.NextBytes(20)), TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)), + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))), new AuthorizationTuple( TestContext.CurrentContext.Random.NextULong(), new Address(TestContext.CurrentContext.Random.NextBytes(20)), TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)) + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))) ], GasCostOf.NewAccount * 3); } @@ -192,9 +192,9 @@ public void Calculate_TxHasAuthorizationListBeforePrague_ThrowsInvalidDataExcept 0, TestItem.AddressF, 0, - TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextBytes(10), - TestContext.CurrentContext.Random.NextBytes(10)) + TestContext.CurrentContext.Random.NextByte(), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10)), + new UInt256(TestContext.CurrentContext.Random.NextBytes(10))) ) .TestObject; diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs index 4fe6ab74f20..d627b8190cd 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs @@ -20,6 +20,7 @@ using System; using System.Linq; using Nethermind.Int256; +using Nethermind.Consensus; namespace Nethermind.Evm.Test; @@ -158,18 +159,21 @@ public void Execute_SenderAndSignerIsTheSameOrNotWithCodeThatSavesCallerAddress_ Assert.That(cellValue.ToArray(), Is.EqualTo(sender.Address.Bytes)); } - public static IEnumerable DifferentCommitValues() + + public static IEnumerable DifferentAuthorityTupleValues() { //Base case - yield return new object[] { 1ul, 0ul, TestItem.AddressA.Bytes }; + yield return new object[] { 1ul, 0ul, true }; //Wrong nonce - yield return new object[] { 1ul, 1ul, new[] { (byte)0x0 } }; + yield return new object[] { 1ul, 1ul, false }; //Wrong chain id - yield return new object[] { 2ul, 0ul, new[] { (byte)0x0 } }; + yield return new object[] { 2ul, 0ul, false }; + //Nonce is too high + yield return new object[] { 2ul, ulong.MaxValue, false }; } - [TestCaseSource(nameof(DifferentCommitValues))] - public void Execute_CommitMessageHasDifferentData_ExpectedAddressIsSavedInStorageSlot(ulong chainId, ulong nonce, byte[] expectedStorageValue) + [TestCaseSource(nameof(DifferentAuthorityTupleValues))] + public void Execute_AuthorityTupleHasDifferentData_EOACodeIsEmptyOrAsExpected(ulong chainId, ulong nonce, bool expectDelegation) { PrivateKey sender = TestItem.PrivateKeyA; PrivateKey signer = TestItem.PrivateKeyB; @@ -197,8 +201,36 @@ public void Execute_CommitMessageHasDifferentData_ExpectedAddressIsSavedInStorag _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); - var actual = _stateProvider.Get(new StorageCell(signer.Address, 0)).ToArray(); - Assert.That(actual, Is.EqualTo(expectedStorageValue)); + byte[] actual = _stateProvider.GetCode(signer.Address); + Assert.That(Eip7702Constants.IsDelegatedCode(actual), Is.EqualTo(expectDelegation)); + } + + [TestCase(ulong.MaxValue, false)] + [TestCase(ulong.MaxValue - 1, true)] + public void Execute_AuthorityNonceHasMaxValueOrBelow_MaxValueNonceIsNotAllowed(ulong nonce, bool expectDelegation) + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + _stateProvider.CreateAccount(signer.Address, 0, nonce); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(100_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign(signer, 0, codeSource, nonce)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + byte[] actual = _stateProvider.GetCode(signer.Address); + Assert.That(Eip7702Constants.IsDelegatedCode(actual), Is.EqualTo(expectDelegation)); } [TestCase(0)] @@ -235,6 +267,35 @@ public void Execute_TxHasDifferentAmountOfAuthorizedCode_UsedGasIsExpected(int c Assert.That(tracer.GasSpent, Is.EqualTo(GasCostOf.Transaction + GasCostOf.NewAccount * count)); } + public void Execute_TxHasDifferentAmount() + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(100_000) + .WithAuthorizationCode(Enumerable.Range(0, 2) + .Select(i => _ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + TestItem.AddressC, + 0)).ToArray()) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(100000000).TestObject; + + CallOutputTracer tracer = new(); + + _transactionProcessor.Execute(tx, block.Header, tracer); + + } + private static IEnumerable EvmExecutionErrorCases() { byte[] runOutOfGasCode = Prepare.EvmCode @@ -506,6 +567,84 @@ public void Execute_DelegatedCodeUsesEXTOPCODES_StoresExpectedValue(byte[] code, Assert.That(_stateProvider.Get(new StorageCell(signer.Address, 0)).ToArray(), Is.EquivalentTo(expectedValue)); } + public static IEnumerable EXTCODEHASHAccountSetup() + { + yield return new object[] { + (IWorldState state, Address account) => + { + //Account does not exists + }, + true }; + yield return new object[] { + (IWorldState state, Address account) => + { + //Account is empty + state.CreateAccount(account, 0); + }, + true}; + yield return new object[] { + (IWorldState state, Address account) => + { + //Account has balance + state.CreateAccount(account, 1); + }, + false}; + yield return new object[] { + (IWorldState state, Address account) => + { + //Account has nonce + state.CreateAccount(account, 0, 1); + }, + false}; + + yield return new object[] { + (IWorldState state, Address account) => + { + //Account has code + state.CreateAccount(account, 0); + state.InsertCode(account, Prepare.EvmCode.RETURN().Done, Prague.Instance); + }, + false}; + } + [TestCaseSource(nameof(EXTCODEHASHAccountSetup))] + public void Execute_CodeSavesEXTCODEHASHWithDifferentAccountSetup_SavesZeroIfAccountDoesNotExistsOrIsEmpty(Action setupAccount, bool expectZero) + { + PrivateKey signer = TestItem.PrivateKeyA; + PrivateKey sender = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + Address target = TestItem.AddressD; + + setupAccount(_stateProvider, target); + + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + byte[] code = Prepare.EvmCode + .PushData(signer.Address) + .Op(Instruction.EXTCODEHASH) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + + DeployCode(codeSource, code); + DeployCode(signer.Address, [.. Eip7702Constants.DelegationHeader, .. target.Bytes]); + + _stateProvider.Commit(Prague.Instance, true); + + Transaction tx = Build.A.Transaction + .WithTo(codeSource) + .WithGasLimit(100_000) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + TransactionResult result = _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + Assert.That(new UInt256(_stateProvider.Get(new StorageCell(codeSource, 0))), expectZero ? Is.EqualTo((UInt256)0) : Is.Not.EqualTo((UInt256)0)); + } + public static IEnumerable CountsAsAccessedCases() { EthereumEcdsa ethereumEcdsa = new(BlockchainIds.GenericNonRealNetwork); @@ -605,6 +744,58 @@ public void Execute_AuthorityAccountExistsOrNot_NonceIsIncrementedByOne(bool acc Assert.That(_stateProvider.GetNonce(authority.Address), Is.EqualTo((UInt256)1)); } + + [Test] + public void Execute_SetNormalDelegationAndThenSetDelegationWithZeroAddress_AccountCodeIsReset() + { + PrivateKey authority = TestItem.PrivateKeyA; + PrivateKey sender = TestItem.PrivateKeyB; + + _stateProvider.CreateAccount(authority.Address, 0); + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + AuthorizationTuple[] tuples = + { + _ethereumEcdsa.Sign(authority, 1, sender.Address, 0), + }; + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressB) + .WithGasLimit(100_000) + .WithAuthorizationCode(tuples) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue - 1) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + _stateProvider.CommitTree(block.Number); + + byte[] actual = _stateProvider.GetCode(authority.Address); + Assert.That(Eip7702Constants.IsDelegatedCode(actual), Is.True); + + tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithNonce(1) + .WithTo(TestItem.AddressB) + .WithGasLimit(100_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign(authority, 1, Address.Zero, 1)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + actual = _stateProvider.GetCode(authority.Address); + + Assert.That(actual, Is.EqualTo(Array.Empty())); + Assert.That(_stateProvider.HasCode(authority.Address), Is.False); + } + private void DeployCode(Address codeSource, byte[] code) { _stateProvider.CreateAccountIfNotExists(codeSource, 0); diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index fe631b3fb10..939352a20ac 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -133,6 +133,11 @@ public void InsertCode(IWorldState state, ReadOnlyMemory code, Address cod public void SetDelegation(IWorldState state, Address codeSource, Address authority, IReleaseSpec spec) { + if (codeSource == Address.Zero) + { + state.InsertCode(authority, Keccak.OfAnEmptyString, Array.Empty(), spec); + return; + } byte[] authorizedBuffer = new byte[Eip7702Constants.DelegationHeader.Length + Address.Size]; Eip7702Constants.DelegationHeader.CopyTo(authorizedBuffer); codeSource.Bytes.CopyTo(authorizedBuffer, Eip7702Constants.DelegationHeader.Length); diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index e67770b53e8..07ca52e64b0 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -62,6 +62,6 @@ public static class GasCostOf public const long AccessStorageListEntry = 1900; // eip-2930 public const long TLoad = WarmStateRead; // eip-1153 public const long TStore = WarmStateRead; // eip-1153 - public const long PerAuthBaseCost = 2500; // eip-7702 + public const long PerAuthBaseCost = 12500; // eip-7702 } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 35e4087a3c9..3b32dc52f14 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -255,17 +255,30 @@ bool IsValidForExecution( ISet
accessedAddresses, [NotNullWhen(false)] out string? error) { - if (authorizationTuple.Authority is null) + UInt256 s = new(authorizationTuple.AuthoritySignature.SAsSpan, isBigEndian: true); + if (authorizationTuple.Authority is null + || s > Secp256K1Curve.HalfN + //V minus the offset can only be 1 or 0 since eip-155 does not apply to Setcode signatures + || (authorizationTuple.AuthoritySignature.V - Signature.VOffset > 1)) { error = "Bad signature."; return false; } - if (authorizationTuple.ChainId != 0 && SpecProvider.ChainId != authorizationTuple.ChainId) + + if ((authorizationTuple.ChainId != 0 + && SpecProvider.ChainId != authorizationTuple.ChainId) + ) { error = $"Chain id ({authorizationTuple.ChainId}) does not match."; return false; } + if (authorizationTuple.Nonce == ulong.MaxValue) + { + error = $"Nonce ({authorizationTuple.Nonce}) must be less than 2**64 - 1."; + return false; + } + accessedAddresses.Add(authorizationTuple.Authority); if (WorldState.HasCode(authorizationTuple.Authority) && !_codeInfoRepository.TryGetDelegation(WorldState, authorizationTuple.Authority, out _)) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index b5ca0aa4a7d..b9fa6506d3c 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1924,7 +1924,9 @@ private CallResult ExecuteCode GetContentLengthWithoutSig(tuple.ChainId, tuple.CodeAddress, tuple.Nonce) - + Rlp.LengthOf(tuple.AuthoritySignature.RecoveryId) + + Rlp.LengthOf(tuple.AuthoritySignature.V - Signature.VOffset) + Rlp.LengthOf(new UInt256(tuple.AuthoritySignature.R.AsSpan(), true)) + Rlp.LengthOf(new UInt256(tuple.AuthoritySignature.S.AsSpan(), true)); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index b0d7efdd6de..46645bbf1e4 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -1560,6 +1560,30 @@ internal byte[][] DecodeByteArrays() return result; } + public byte DecodeByte() + { + byte byteValue = PeekByte(); + if (byteValue < 128) + { + SkipBytes(1); + return byteValue; + } + + if (byteValue == 128) + { + SkipBytes(1); + return 0; + } + + if (byteValue == 129) + { + SkipBytes(1); + return ReadByte(); + } + + throw new RlpException($"Unexpected value while decoding byte {byteValue}"); + } + public T[] DecodeArray(IRlpValueDecoder? decoder = null, bool checkPositions = true, T defaultElement = default) {