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]); }
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)); }
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"); }
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)); }
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); }
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))); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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"); } }
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)); }
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); } } }
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); } } }