示例#1
0
        private static Transaction[] GenerateTxsForRefunds(IGrouping <Address, DepositDetails> depositGroup, DevKeyStoreWallet wallet)
        {
            Console.WriteLine();
            Console.Write($"Provide nonce for {depositGroup.Key}: ");
            int nonce = int.Parse(Console.ReadLine());

            Console.Write($"Provide address to send the refund to: ");
            string  hexAddress = Console.ReadLine();
            Address refundTo   = new Address(hexAddress);

            SecureString securedPassword = ConsoleUtils.ReadSecret("Provide password: "******"Password has been accepted.");
                Console.WriteLine();
                Console.WriteLine($"Great, will generate refund transactions for deposits of {depositGroup.Key} starting with nonce {nonce}. ETH/DAI will be sent to {refundTo}.");
                List <Transaction> transactions = new List <Transaction>(depositGroup.Count());
                foreach (DepositDetails depositDetails in depositGroup)
                {
                    Deposit     deposit     = depositDetails.Deposit;
                    RefundClaim refundClaim = new RefundClaim(deposit.Id, depositDetails.DataAsset.Id, deposit.Units,
                                                              deposit.Value, deposit.ExpiryTime, depositDetails.Pepper, depositDetails.DataAsset.Provider.Address, refundTo);
                    UInt256 gasPrice = 20.GWei();

                    AbiEncoder  abiEncoder  = new AbiEncoder();
                    byte[]      txData      = abiEncoder.Encode(AbiEncodingStyle.IncludeSignature, ContractData.ClaimRefundSig, depositDetails.DataAsset.Id, refundClaim.Units, refundClaim.Value, refundClaim.ExpiryTime, refundClaim.Pepper, refundClaim.Provider, depositDetails.Consumer);
                    Transaction transaction = new Transaction();
                    transaction.Value         = 0;
                    transaction.Data          = txData;
                    transaction.To            = new Address("0xb1AD03b75bD9E5AB89968D7a37d99F9dd220796D");
                    transaction.SenderAddress = depositDetails.Consumer;
                    transaction.GasLimit      = 100000;
                    transaction.GasPrice      = gasPrice;
                    transaction.Nonce         = (UInt256)nonce++;
                    wallet.Sign(transaction, ChainId.Mainnet);

                    EthereumEcdsa ecdsa            = new EthereumEcdsa(ChainId.Mainnet, LimboLogs.Instance);
                    Address       recoveredAddress = ecdsa.RecoverAddress(transaction);
                    if (recoveredAddress != transaction.SenderAddress)
                    {
                        Console.WriteLine("Signature failure");
                        return(new Transaction[0]);
                    }

                    transactions.Add(transaction);
                }

                return(transactions.ToArray());
            }

            Console.WriteLine("Incorrect password.");
            return(new Transaction[0]);
        }
示例#2
0
        public void Sign_and_recover()
        {
            EthereumEcdsa ethereumEcdsa = new EthereumEcdsa(ChainId.Olympic, LimboLogs.Instance);

            Keccak     message    = Keccak.Compute("Test message");
            PrivateKey privateKey = Build.A.PrivateKey.TestObject;
            Signature  signature  = ethereumEcdsa.Sign(privateKey, message);

            Assert.AreEqual(privateKey.Address, ethereumEcdsa.RecoverAddress(signature, message));
        }
示例#3
0
        public void Test_eip155_for_the_first_ropsten_transaction()
        {
            Transaction tx = Rlp.Decode <Transaction>(new Rlp(Bytes.FromHexString("0xf85f808082520894353535353535353535353535353535353535353580801ca08d24b906be2d91a0bf2168862726991cc408cddf94cb087b392ce992573be891a077964b4e55a5c8ec7b85087d619c641c06def33ab052331337ca9efcd6b82aef")));

            Assert.AreEqual(new Keccak("0x5fd225549ed5c587c843e04578bdd4240fc0d7ab61f8e9faa37e84ec8dc8766d"), tx.Hash, "hash");
            EthereumEcdsa ecdsa = new EthereumEcdsa(ChainId.Ropsten, LimboLogs.Instance);
            Address       from  = ecdsa.RecoverAddress(tx);

            Assert.AreEqual(new Address("0x874b54a8bd152966d63f706bae1ffeb0411921e5"), from, "from");
        }
示例#4
0
        public void Sign_and_recover()
        {
            EthereumEcdsa ethereumEcdsa = new EthereumEcdsa(OlympicSpecProvider.Instance, NullLogManager.Instance);

            Keccak     message    = Keccak.Compute("Test message");
            PrivateKey privateKey = Build.A.PrivateKey.TestObject;
            Signature  signature  = ethereumEcdsa.Sign(privateKey, message);

            Assert.AreEqual(privateKey.Address, ethereumEcdsa.RecoverAddress(signature, message));
        }
示例#5
0
        public void Signature_test_olympic(uint blockNumber)
        {
            EthereumEcdsa ecdsa = new EthereumEcdsa(OlympicSpecProvider.Instance, LimboLogs.Instance);
            PrivateKey    key   = Build.A.PrivateKey.TestObject;
            Transaction   tx    = Build.A.Transaction.TestObject;

            ecdsa.Sign(key, tx, blockNumber);
            Address address = ecdsa.RecoverAddress(tx, blockNumber);

            Assert.AreEqual(key.Address, address);
        }
示例#6
0
        private bool TxMatchesAddresses(Transaction tx, bool validateChainId)
        {
            Address?senderAddress = tx.SenderAddress;

            if (senderAddress == null && tx.Signature != null)
            {
                senderAddress = _ecdsa.RecoverAddress(tx, !validateChainId);
            }
            return((_fromAddresses == null || _fromAddresses.Contains(senderAddress)) &&
                   (_toAddresses == null || _toAddresses.Contains(tx.To)));
        }
示例#7
0
        public void Signature_test_olympic(bool isEip155Enabled)
        {
            EthereumEcdsa ecdsa = new EthereumEcdsa(ChainId.Mainnet, LimboLogs.Instance);
            PrivateKey    key   = Build.A.PrivateKey.TestObject;
            Transaction   tx    = Build.A.Transaction.TestObject;

            ecdsa.Sign(key, tx, isEip155Enabled);
            Address address = ecdsa.RecoverAddress(tx);

            Assert.AreEqual(key.Address, address);
        }
示例#8
0
        public void Sign_goerli()
        {
            EthereumEcdsa ecdsa = new EthereumEcdsa(ChainId.Goerli, LimboLogs.Instance);
            PrivateKey    key   = Build.A.PrivateKey.TestObject;
            Transaction   tx    = Build.A.Transaction.TestObject;

            ecdsa.Sign(key, tx, true);
            Address address = ecdsa.RecoverAddress(tx);

            Assert.AreEqual(key.Address, address);
        }
示例#9
0
        public void Signature_test_ropsten(bool eip155)
        {
            EthereumEcdsa ecdsa = new EthereumEcdsa(ChainId.Ropsten, LimboLogs.Instance);
            PrivateKey    key   = Build.A.PrivateKey.TestObject;
            Transaction   tx    = Build.A.Transaction.TestObject;

            ecdsa.Sign(key, tx, eip155);
            Address address = ecdsa.RecoverAddress(tx);

            Assert.AreEqual(key.Address, address);
        }
示例#10
0
        public void Recover_kovan([Values(false, true)] bool eip155)
        {
            EthereumEcdsa singEcdsa = new EthereumEcdsa(ChainId.Mainnet, LimboLogs.Instance);
            PrivateKey    key       = Build.A.PrivateKey.TestObject;
            Transaction   tx        = Build.A.Transaction.TestObject;

            singEcdsa.Sign(key, tx, eip155);

            EthereumEcdsa recoverEcdsa = new EthereumEcdsa(ChainId.Kovan, LimboLogs.Instance);
            Address       address      = recoverEcdsa.RecoverAddress(tx, true);

            Assert.AreEqual(key.Address, address);
        }
示例#11
0
        public async Task seal_can_recover_address()
        {
            _auRaStepCalculator.CurrentStep.Returns(11);
            _validSealerStrategy.IsValidSealer(Arg.Any <IList <Address> >(), _address, 11).Returns(true);
            var block = Build.A.Block.WithHeader(Build.A.BlockHeader.WithBeneficiary(_address).WithAura(11, null).TestObject).TestObject;

            block = await _auRaSealer.SealBlock(block, CancellationToken.None);

            var ecdsa     = new EthereumEcdsa(ChainId.Morden, LimboLogs.Instance);
            var signature = new Signature(block.Header.AuRaSignature);

            signature.V += Signature.VOffset;
            var recoveredAddress = ecdsa.RecoverAddress(signature, block.Header.CalculateHash(RlpBehaviors.ForSealing));

            recoveredAddress.Should().Be(_address);
        }
示例#12
0
        public async Task seal_can_recover_address()
        {
            _auRaStepCalculator.CurrentStep.Returns(11);
            _auRaValidator.IsValidSealer(_address, 11).Returns(true);
            var block = Build.A.Block.WithHeader(Build.A.BlockHeader.WithBeneficiary(_address).WithAura(11, null).TestObject).TestObject;

            block = await _auRaSealer.SealBlock(block, CancellationToken.None);

            var ecdsa     = new EthereumEcdsa(new MordenSpecProvider(), NullLogManager.Instance);
            var signature = new Signature(block.Header.AuRaSignature);

            signature.V += Signature.VOffset;
            var recoveredAddress = ecdsa.RecoverAddress(signature, BlockHeader.CalculateHash(block.Header, RlpBehaviors.ForSealing));

            recoveredAddress.Should().Be(_address);
        }
示例#13
0
        public (byte[], bool) Run(byte[] inputData)
        {
            Metrics.EcRecoverPrecompile++;

            inputData ??= Array.Empty <byte>();
            Span <byte> inputDataSpan = stackalloc byte[128];

            inputData.AsSpan(0, Math.Min(128, inputData.Length))
            .CopyTo(inputDataSpan.Slice(0, Math.Min(128, inputData.Length)));

            Keccak      hash   = new Keccak(inputDataSpan.Slice(0, 32).ToArray());
            Span <byte> vBytes = inputDataSpan.Slice(32, 32);
            Span <byte> r      = inputDataSpan.Slice(64, 32);
            Span <byte> s      = inputDataSpan.Slice(96, 32);

            // TEST: CALLCODEEcrecoverV_prefixedf0_d0g0v0
            // TEST: CALLCODEEcrecoverV_prefixedf0_d1g0v0
            if (!Bytes.AreEqual(_zero31, vBytes.Slice(0, 31)))
            {
                return(Array.Empty <byte>(), true);
            }

            byte v = vBytes[31];

            if (v != 27 && v != 28)
            {
                return(Array.Empty <byte>(), true);
            }

            Signature signature = new Signature(r, s, v);
            Address   recovered = _ecdsa.RecoverAddress(signature, hash);

            if (recovered == null)
            {
                return(Array.Empty <byte>(), true);
            }

            byte[] result = recovered.Bytes;
            if (result.Length != 32)
            {
                result = result.PadLeft(32);
            }

            // TODO: change recovery code to return bytes
            return(result, true);
        }
示例#14
0
        public void Can_sign_on_networks_with_chain_id(DevWalletType walletType, int chainId)
        {
            EthereumEcdsa ecdsa  = new EthereumEcdsa(chainId, LimboLogs.Instance);
            IWallet       wallet = SetupWallet(walletType);

            for (int i = 1; i <= (walletType == DevWalletType.Memory ? 10 : 3); i++)
            {
                Address     signerAddress = wallet.GetAccounts()[0];
                Transaction tx            = new Transaction();
                tx.SenderAddress = signerAddress;

                wallet.Sign(tx, chainId);
                Address recovered = ecdsa.RecoverAddress(tx);
                Assert.AreEqual(signerAddress, recovered, $"{i}");
                Assert.AreEqual(chainId, tx.Signature.ChainId, "chainId");
            }
        }
示例#15
0
        public void Can_sign_on_networks_with_chain_id(DevWalletType walletType)
        {
            const int     networkId = 40000;
            EthereumEcdsa ecdsa     = new EthereumEcdsa(new SingleReleaseSpecProvider(Latest.Release, networkId), NullLogManager.Instance);
            IWallet       wallet    = SetupWallet(walletType);

            for (int i = 1; i <= (walletType == DevWalletType.Memory ? 10 : 3); i++)
            {
                Address     signerAddress = wallet.GetAccounts()[0];
                Transaction tx            = new Transaction();
                tx.SenderAddress = signerAddress;

                wallet.Sign(tx, networkId);
                Address recovered = ecdsa.RecoverAddress(tx, networkId);
                Assert.AreEqual(signerAddress, recovered, $"{i}");
                Assert.AreEqual(networkId, tx.Signature.ChainId, "chainId");
            }
        }
        protected override Keccak Handle(byte[] transaction, IWeb3EthApi api)
        {
            PublicEntry publicEntry;

            try
            {
                Transaction   tx    = Rlp.Decode <Transaction>(transaction);
                EthereumEcdsa ecdsa = new EthereumEcdsa(MainnetSpecProvider.Instance, LimboLogs.Instance);
                tx.SenderAddress = ecdsa.RecoverAddress(tx, MainnetSpecProvider.IstanbulBlockNumber);
                tx.Timestamp     = (UInt256)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
                publicEntry      = new PublicEntry
                {
                    Data            = (tx.Data ?? tx.Init).ToByteString(),
                    GasLimit        = (ulong)tx.GasLimit,
                    GasPrice        = tx.GasPrice.ToUint256ByteString(),
                    Nonce           = (ulong)tx.Nonce,
                    SenderAddress   = tx.SenderAddress.Bytes.ToByteString(),
                    ReceiverAddress = tx.To?.Bytes.ToByteString() ?? ByteString.Empty,
                    Amount          = tx.Value.ToUint256ByteString(),
                    Signature       = new Protocol.Cryptography.Signature
                    {
                        RawBytes = ByteString.CopyFrom((byte)1)
                    }
                };
            }
            catch
            {
                try
                {
                    TransactionBroadcast transactionBroadcast = TransactionBroadcast.Parser.ParseFrom(transaction);
                    publicEntry = transactionBroadcast.PublicEntry;
                }
                catch (Exception)
                {
                    throw new InvalidDataException($"Transaction data could not be deserialized into a {nameof(PublicEntry)}");
                }
            }

            return(api.SendTransaction(publicEntry));
        }
示例#17
0
        public (byte[], bool) Run(byte[] inputData)
        {
            Metrics.EcRecoverPrecompile++;

            inputData = (inputData ?? Bytes.Empty).PadRight(128);

            Keccak hash = new Keccak(inputData.Slice(0, 32));

            byte[] vBytes = inputData.Slice(32, 32);
            byte[] r      = inputData.Slice(64, 32);
            byte[] s      = inputData.Slice(96, 32);

            // TEST: CALLCODEEcrecoverV_prefixedf0_d0g0v0
            // TEST: CALLCODEEcrecoverV_prefixedf0_d1g0v0
            for (int i = 0; i < 31; i++)
            {
                if (vBytes[i] != 0)
                {
                    return(Bytes.Empty, true);
                }
            }

            byte v = vBytes[31];

            if (v != 27 && v != 28)
            {
                return(Bytes.Empty, true);
            }

            Signature signature = new Signature(r, s, v);
            Address   recovered = _ecdsa.RecoverAddress(signature, hash);

            if (recovered == null)
            {
                return(Bytes.Empty, true);
            }

            return(recovered.Bytes.PadLeft(32), true);  // TODO: change recovery code to return bytes?
        }
        private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer,
                             ExecutionOptions executionOptions)
        {
            bool eip658NotEnabled = !_specProvider.GetSpec(block.Number).IsEip658Enabled;

            // restore is CallAndRestore - previous call, we will restore state after the execution
            bool restore      = (executionOptions & ExecutionOptions.Restore) == ExecutionOptions.Restore;
            bool noValidation = (executionOptions & ExecutionOptions.NoValidation) == ExecutionOptions.NoValidation;
            // commit - is for standard execute, we will commit thee state after execution
            bool commit = (executionOptions & ExecutionOptions.Commit) == ExecutionOptions.Commit || eip658NotEnabled;
            //!commit - is for build up during block production, we won't commit state after each transaction to support rollbacks
            //we commit only after all block is constructed
            bool notSystemTransaction = !transaction.IsSystem();
            bool deleteCallerAccount  = false;

            IReleaseSpec spec = _specProvider.GetSpec(block.Number);

            if (!notSystemTransaction)
            {
                spec = new SystemTransactionReleaseSpec(spec);
            }

            UInt256 value = transaction.Value;

            if (!transaction.TryCalculatePremiumPerGas(block.BaseFeePerGas, out UInt256 premiumPerGas) && !noValidation)
            {
                TraceLogInvalidTx(transaction, "MINER_PREMIUM_IS_NEGATIVE");
                QuickFail(transaction, block, txTracer, eip658NotEnabled, "miner premium is negative");
                return;
            }

            UInt256 effectiveGasPrice =
                transaction.CalculateEffectiveGasPrice(spec.IsEip1559Enabled, block.BaseFeePerGas);

            long gasLimit = transaction.GasLimit;

            byte[] machineCode = transaction.IsContractCreation ? transaction.Data : null;
            byte[] data        = transaction.IsMessageCall ? transaction.Data : Array.Empty <byte>();

            Address?caller = transaction.SenderAddress;

            if (_logger.IsTrace)
            {
                _logger.Trace($"Executing tx {transaction.Hash}");
            }

            if (caller is null)
            {
                TraceLogInvalidTx(transaction, "SENDER_NOT_SPECIFIED");
                QuickFail(transaction, block, txTracer, eip658NotEnabled, "sender not specified");
                return;
            }

            if (!noValidation && _stateProvider.IsInvalidContractSender(spec, caller))
            {
                TraceLogInvalidTx(transaction, "SENDER_IS_CONTRACT");
                QuickFail(transaction, block, txTracer, eip658NotEnabled, "sender has deployed code");
                return;
            }

            long intrinsicGas = IntrinsicGasCalculator.Calculate(transaction, spec);

            if (_logger.IsTrace)
            {
                _logger.Trace($"Intrinsic gas calculated for {transaction.Hash}: " + intrinsicGas);
            }

            if (notSystemTransaction)
            {
                if (gasLimit < intrinsicGas)
                {
                    TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
                    QuickFail(transaction, block, txTracer, eip658NotEnabled, "gas limit below intrinsic gas");
                    return;
                }

                if (!noValidation && gasLimit > block.GasLimit - block.GasUsed)
                {
                    TraceLogInvalidTx(transaction,
                                      $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}");
                    QuickFail(transaction, block, txTracer, eip658NotEnabled, "block gas limit exceeded");
                    return;
                }
            }

            if (!_stateProvider.AccountExists(caller))
            {
                // hacky fix for the potential recovery issue
                if (transaction.Signature != null)
                {
                    transaction.SenderAddress = _ecdsa.RecoverAddress(transaction, !spec.ValidateChainId);
                }

                if (caller != transaction.SenderAddress)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn(
                            $"TX recovery issue fixed - tx was coming with sender {caller} and the now it recovers to {transaction.SenderAddress}");
                    }
                    caller = transaction.SenderAddress;
                }
                else
                {
                    TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {caller}");
                    if (!commit || noValidation || effectiveGasPrice == UInt256.Zero)
                    {
                        deleteCallerAccount = !commit || restore;
                        _stateProvider.CreateAccount(caller, UInt256.Zero);
                    }
                }

                if (caller is null)
                {
                    throw new InvalidDataException(
                              $"Failed to recover sender address on tx {transaction.Hash} when previously recovered sender account did not exist.");
                }
            }

            UInt256 senderReservedGasPayment = noValidation ? UInt256.Zero : (ulong)gasLimit * effectiveGasPrice;

            if (notSystemTransaction)
            {
                UInt256 senderBalance = _stateProvider.GetBalance(caller);
                if (!noValidation && ((ulong)intrinsicGas * effectiveGasPrice + value > senderBalance ||
                                      senderReservedGasPayment + value > senderBalance))
                {
                    TraceLogInvalidTx(transaction,
                                      $"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}");
                    QuickFail(transaction, block, txTracer, eip658NotEnabled, "insufficient sender balance");
                    return;
                }

                if (!noValidation && spec.IsEip1559Enabled && !transaction.IsFree() &&
                    senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value)
                {
                    TraceLogInvalidTx(transaction,
                                      $"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}");
                    QuickFail(transaction, block, txTracer, eip658NotEnabled,
                              "insufficient MaxFeePerGas for sender balance");
                    return;
                }

                if (transaction.Nonce != _stateProvider.GetNonce(caller))
                {
                    TraceLogInvalidTx(transaction,
                                      $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(caller)})");
                    QuickFail(transaction, block, txTracer, eip658NotEnabled, "wrong transaction nonce");
                    return;
                }

                _stateProvider.IncrementNonce(caller);
            }

            _stateProvider.SubtractFromBalance(caller, senderReservedGasPayment, spec);
            if (commit)
            {
                _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : NullTxTracer.Instance);
            }

            long unspentGas = gasLimit - intrinsicGas;
            long spentGas   = gasLimit;

            Snapshot snapshot = _worldState.TakeSnapshot();

            _stateProvider.SubtractFromBalance(caller, value, spec);
            byte statusCode = StatusCode.Failure;
            TransactionSubstate substate = null;

            Address?recipientOrNull = null;

            try
            {
                Address?recipient =
                    transaction.GetRecipient(transaction.IsContractCreation ? _stateProvider.GetNonce(caller) : 0);
                if (transaction.IsContractCreation)
                {
                    // if transaction is a contract creation then recipient address is the contract deployment address
                    Address contractAddress = recipient;
                    PrepareAccountForContractDeployment(contractAddress !, spec);
                }

                if (recipient == null)
                {
                    // this transaction is not a contract creation so it should have the recipient known and not null
                    throw new InvalidDataException("Recipient has not been resolved properly before tx execution");
                }

                recipientOrNull = recipient;

                ExecutionEnvironment env = new();
                env.TxExecutionContext = new TxExecutionContext(block, caller, effectiveGasPrice);
                env.Value            = value;
                env.TransferValue    = value;
                env.Caller           = caller;
                env.CodeSource       = recipient;
                env.ExecutingAccount = recipient;
                env.InputData        = data ?? Array.Empty <byte>();
                env.CodeInfo         = machineCode == null
                    ? _virtualMachine.GetCachedCodeInfo(_worldState, recipient, spec)
                    : new CodeInfo(machineCode);

                ExecutionType executionType =
                    transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call;
                using (EvmState state =
                           new(unspentGas, env, executionType, true, snapshot, false))
                {
                    if (spec.UseTxAccessLists)
                    {
                        state.WarmUp(transaction.AccessList); // eip-2930
                    }

                    if (spec.UseHotAndColdStorage)
                    {
                        state.WarmUp(caller);    // eip-2929
                        state.WarmUp(recipient); // eip-2929
                    }

                    substate   = _virtualMachine.Run(state, _worldState, txTracer);
                    unspentGas = state.GasAvailable;

                    if (txTracer.IsTracingAccess)
                    {
                        txTracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells);
                    }
                }

                if (substate.ShouldRevert || substate.IsError)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace("Restoring state from before transaction");
                    }
                    _worldState.Restore(snapshot);
                }
                else
                {
                    // tks: there is similar code fo contract creation from init and from CREATE
                    // this may lead to inconsistencies (however it is tested extensively in blockchain tests)
                    if (transaction.IsContractCreation)
                    {
                        long codeDepositGasCost = CodeDepositHandler.CalculateCost(substate.Output.Length, spec);
                        if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate)
                        {
                            throw new OutOfGasException();
                        }

                        if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output))
                        {
                            throw new InvalidCodeException();
                        }

                        if (unspentGas >= codeDepositGasCost)
                        {
                            Keccak codeHash = _stateProvider.UpdateCode(substate.Output);
                            _stateProvider.UpdateCodeHash(recipient, codeHash, spec);
                            unspentGas -= codeDepositGasCost;
                        }
                    }

                    foreach (Address toBeDestroyed in substate.DestroyList)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Destroying account {toBeDestroyed}");
                        }
                        _storageProvider.ClearStorage(toBeDestroyed);
                        _stateProvider.DeleteAccount(toBeDestroyed);
                        if (txTracer.IsTracingRefunds)
                        {
                            txTracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled));
                        }
                    }

                    statusCode = StatusCode.Success;
                }

                spentGas = Refund(gasLimit, unspentGas, substate, caller, effectiveGasPrice, spec);
            }
            catch (Exception ex) when(
                ex is EvmException || ex is OverflowException) // TODO: OverflowException? still needed? hope not
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"EVM EXCEPTION: {ex.GetType().Name}");
                }
                _worldState.Restore(snapshot);
            }

            if (_logger.IsTrace)
            {
                _logger.Trace("Gas spent: " + spentGas);
            }

            Address gasBeneficiary             = block.GasBeneficiary;
            bool    gasBeneficiaryNotDestroyed = substate?.DestroyList.Contains(gasBeneficiary) != true;

            if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed)
            {
                if (notSystemTransaction)
                {
                    UInt256 fees = (ulong)spentGas * premiumPerGas;
                    if (_stateProvider.AccountExists(gasBeneficiary))
                    {
                        _stateProvider.AddToBalance(gasBeneficiary, fees, spec);
                    }
                    else
                    {
                        _stateProvider.CreateAccount(gasBeneficiary, fees);
                    }

                    if (!transaction.IsFree() && spec.IsEip1559Enabled && spec.Eip1559FeeCollector is not null)
                    {
                        UInt256 burntFees = (ulong)spentGas * block.BaseFeePerGas;
                        if (!burntFees.IsZero)
                        {
                            if (_stateProvider.AccountExists(spec.Eip1559FeeCollector))
                            {
                                _stateProvider.AddToBalance(spec.Eip1559FeeCollector, burntFees, spec);
                            }
                            else
                            {
                                _stateProvider.CreateAccount(spec.Eip1559FeeCollector, burntFees);
                            }
                        }
                    }
                }
            }

            if (restore)
            {
                _storageProvider.Reset();
                _stateProvider.Reset();
                if (deleteCallerAccount)
                {
                    _stateProvider.DeleteAccount(caller);
                }
                else
                {
                    _stateProvider.AddToBalance(caller, senderReservedGasPayment, spec);
                    if (notSystemTransaction)
                    {
                        _stateProvider.DecrementNonce(caller);
                    }

                    _stateProvider.Commit(spec);
                }
            }
            else if (commit)
            {
                _storageProvider.Commit(txTracer.IsTracingState ? txTracer : NullStorageTracer.Instance);
                _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : NullStateTracer.Instance);
            }

            if (!noValidation && notSystemTransaction)
            {
                block.GasUsed += spentGas;
            }

            if (txTracer.IsTracingReceipt)
            {
                Keccak stateRoot = null;
                if (eip658NotEnabled)
                {
                    _stateProvider.RecalculateStateRoot();
                    stateRoot = _stateProvider.StateRoot;
                }

                if (statusCode == StatusCode.Failure)
                {
                    txTracer.MarkAsFailed(recipientOrNull, spentGas,
                                          (substate?.ShouldRevert ?? false) ? substate.Output.ToArray() : Array.Empty <byte>(),
                                          substate?.Error, stateRoot);
                }
                else
                {
                    txTracer.MarkAsSuccess(recipientOrNull, spentGas, substate.Output.ToArray(),
                                           substate.Logs.Any() ? substate.Logs.ToArray() : Array.Empty <LogEntry>(), stateRoot);
                }
            }
        }
示例#19
0
        private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer, bool isCall)
        {
            bool notSystemTransaction = !transaction.IsSystem();
            bool wasSenderAccountCreatedInsideACall = false;

            IReleaseSpec spec = _specProvider.GetSpec(block.Number);

            if (!notSystemTransaction)
            {
                spec = new SystemTransactionReleaseSpec(spec);
            }

            Address recipient = transaction.To;
            UInt256 value     = transaction.Value;
            UInt256 gasPrice  = transaction.GasPrice;
            long    gasLimit  = transaction.GasLimit;

            byte[] machineCode = transaction.Init;
            byte[] data        = transaction.Data ?? Array.Empty <byte>();

            Address sender = transaction.SenderAddress;

            if (_logger.IsTrace)
            {
                _logger.Trace($"Executing tx {transaction.Hash}");
            }

            if (sender == null)
            {
                TraceLogInvalidTx(transaction, "SENDER_NOT_SPECIFIED");
                QuickFail(transaction, block, txTracer, "sender not specified");
                return;
            }

            long intrinsicGas = _intrinsicGasCalculator.Calculate(transaction, spec);

            if (_logger.IsTrace)
            {
                _logger.Trace($"Intrinsic gas calculated for {transaction.Hash}: " + intrinsicGas);
            }

            if (notSystemTransaction)
            {
                if (gasLimit < intrinsicGas)
                {
                    TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
                    QuickFail(transaction, block, txTracer, "gas limit below intrinsic gas");
                    return;
                }

                if (!isCall && gasLimit > block.GasLimit - block.GasUsed)
                {
                    TraceLogInvalidTx(transaction,
                                      $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}");
                    QuickFail(transaction, block, txTracer, "block gas limit exceeded");
                    return;
                }
            }

            if (!_stateProvider.AccountExists(sender))
            {
                // hacky fix for the potential recovery issue
                if (transaction.Signature != null)
                {
                    transaction.SenderAddress = _ecdsa.RecoverAddress(transaction, !spec.ValidateChainId);
                }

                if (sender != transaction.SenderAddress)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"TX recovery issue fixed - tx was coming with sender {sender} and the now it recovers to {transaction.SenderAddress}");
                    }
                    sender = transaction.SenderAddress;
                }
                else
                {
                    TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}");
                    if (isCall || gasPrice == UInt256.Zero)
                    {
                        wasSenderAccountCreatedInsideACall = isCall;
                        _stateProvider.CreateAccount(sender, UInt256.Zero);
                    }
                }
            }

            if (notSystemTransaction)
            {
                UInt256 senderBalance = _stateProvider.GetBalance(sender);
                if (!isCall && (ulong)intrinsicGas * gasPrice + value > senderBalance)
                {
                    TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({sender})_BALANCE = {senderBalance}");
                    QuickFail(transaction, block, txTracer, "insufficient sender balance");
                    return;
                }

                if (transaction.Nonce != _stateProvider.GetNonce(sender))
                {
                    TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(sender)})");
                    QuickFail(transaction, block, txTracer, "wrong transaction nonce");
                    return;
                }

                _stateProvider.IncrementNonce(sender);
            }

            UInt256 senderReservedGasPayment = isCall ? UInt256.Zero : (ulong)gasLimit * gasPrice;

            _stateProvider.SubtractFromBalance(sender, senderReservedGasPayment, spec);
            _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null);

            long unspentGas = gasLimit - intrinsicGas;
            long spentGas   = gasLimit;

            int stateSnapshot   = _stateProvider.TakeSnapshot();
            int storageSnapshot = _storageProvider.TakeSnapshot();

            _stateProvider.SubtractFromBalance(sender, value, spec);
            byte statusCode = StatusCode.Failure;
            TransactionSubstate substate = null;

            try
            {
                if (transaction.IsContractCreation)
                {
                    recipient = transaction.IsSystem()
                        ? transaction.SenderAddress
                        : ContractAddress.From(sender, _stateProvider.GetNonce(sender) - 1);

                    if (_stateProvider.AccountExists(recipient))
                    {
                        if (_virtualMachine.GetCachedCodeInfo(recipient, spec).MachineCode.Length != 0 || _stateProvider.GetNonce(recipient) != 0)
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Trace($"Contract collision at {recipient}"); // the account already owns the contract with the code
                            }

                            throw new TransactionCollisionException();
                        }

                        _stateProvider.UpdateStorageRoot(recipient, Keccak.EmptyTreeHash);
                    }
                }

                ExecutionEnvironment env = new ExecutionEnvironment();
                env.Value            = value;
                env.TransferValue    = value;
                env.Sender           = sender;
                env.CodeSource       = recipient;
                env.ExecutingAccount = recipient;
                env.CurrentBlock     = block;
                env.GasPrice         = gasPrice;
                env.InputData        = data ?? Array.Empty <byte>();
                env.CodeInfo         = machineCode == null?_virtualMachine.GetCachedCodeInfo(recipient, spec) : new CodeInfo(machineCode);

                env.Originator = sender;

                ExecutionType executionType = transaction.IsContractCreation ? ExecutionType.Create : ExecutionType.Call;
                using (EvmState state = new EvmState(unspentGas, env, executionType, true, false))
                {
                    substate   = _virtualMachine.Run(state, txTracer);
                    unspentGas = state.GasAvailable;
                }

                if (substate.ShouldRevert || substate.IsError)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace("Restoring state from before transaction");
                    }
                    _stateProvider.Restore(stateSnapshot);
                    _storageProvider.Restore(storageSnapshot);
                }
                else
                {
                    // tks: there is similar code fo contract creation from init and from CREATE
                    // this may lead to inconsistencies (however it is tested extensively in blockchain tests)
                    if (transaction.IsContractCreation)
                    {
                        long codeDepositGasCost = CodeDepositHandler.CalculateCost(substate.Output.Length, spec);
                        if (unspentGas < codeDepositGasCost && spec.IsEip2Enabled)
                        {
                            throw new OutOfGasException();
                        }

                        if (unspentGas >= codeDepositGasCost)
                        {
                            Keccak codeHash = _stateProvider.UpdateCode(substate.Output);
                            _stateProvider.UpdateCodeHash(recipient, codeHash, spec);
                            unspentGas -= codeDepositGasCost;
                        }
                    }

                    foreach (Address toBeDestroyed in substate.DestroyList)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Destroying account {toBeDestroyed}");
                        }
                        _storageProvider.ClearStorage(toBeDestroyed);
                        _stateProvider.DeleteAccount(toBeDestroyed);
                        if (txTracer.IsTracingRefunds)
                        {
                            txTracer.ReportRefund(RefundOf.Destroy);
                        }
                    }

                    statusCode = StatusCode.Success;
                }

                spentGas = Refund(gasLimit, unspentGas, substate, sender, gasPrice, spec);
            }
            catch (Exception ex) when(ex is EvmException || ex is OverflowException)  // TODO: OverflowException? still needed? hope not
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"EVM EXCEPTION: {ex.GetType().Name}");
                }
                _stateProvider.Restore(stateSnapshot);
                _storageProvider.Restore(storageSnapshot);
            }

            if (_logger.IsTrace)
            {
                _logger.Trace("Gas spent: " + spentGas);
            }

            Address gasBeneficiary = block.GasBeneficiary;

            if (statusCode == StatusCode.Failure || !(substate?.DestroyList.Contains(gasBeneficiary) ?? false))
            {
                if (notSystemTransaction)
                {
                    if (!_stateProvider.AccountExists(gasBeneficiary))
                    {
                        _stateProvider.CreateAccount(gasBeneficiary, (ulong)spentGas * gasPrice);
                    }
                    else
                    {
                        _stateProvider.AddToBalance(gasBeneficiary, (ulong)spentGas * gasPrice, spec);
                    }
                }
            }

            if (!isCall)
            {
                _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null);
                _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null);
            }
            else
            {
                _storageProvider.Reset();
                _stateProvider.Reset();

                if (wasSenderAccountCreatedInsideACall)
                {
                    _stateProvider.DeleteAccount(sender);
                }
                else
                {
                    _stateProvider.AddToBalance(sender, senderReservedGasPayment, spec);
                    if (notSystemTransaction)
                    {
                        _stateProvider.DecrementNonce(sender);
                    }
                }

                _stateProvider.Commit(spec);
            }

            if (!isCall && notSystemTransaction)
            {
                block.GasUsed += spentGas;
            }

            if (txTracer.IsTracingReceipt)
            {
                Keccak stateRoot     = null;
                bool   eip658Enabled = _specProvider.GetSpec(block.Number).IsEip658Enabled;
                if (!eip658Enabled)
                {
                    _stateProvider.RecalculateStateRoot();
                    stateRoot = _stateProvider.StateRoot;
                }

                if (statusCode == StatusCode.Failure)
                {
                    txTracer.MarkAsFailed(recipient, spentGas, (substate?.ShouldRevert ?? false) ? substate.Output : Array.Empty <byte>(), substate?.Error, stateRoot);
                }
                else
                {
                    txTracer.MarkAsSuccess(recipient, spentGas, substate.Output, substate.Logs.Any() ? substate.Logs.ToArray() : Array.Empty <LogEntry>(), stateRoot);
                }
            }
        }