diff --git a/contracts/deployment_scripts/testnet/layer2/001_whitelist_tokens.ts b/contracts/deployment_scripts/testnet/layer2/001_whitelist_tokens.ts index 371bea2a10..c05bec6a2e 100644 --- a/contracts/deployment_scripts/testnet/layer2/001_whitelist_tokens.ts +++ b/contracts/deployment_scripts/testnet/layer2/001_whitelist_tokens.ts @@ -97,12 +97,12 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { setTimeout(fail, 30_000) const messageBusContract = (await hre.ethers.getContractAt('MessageBus', '0x526c84529b2b8c11f57d93d3f5537aca3aecef9b')); const gasLimit = await messageBusContract.estimateGas.verifyMessageFinalized(messages[1], { - maxFeePerGas: 2, + maxFeePerGas: 1000000001, }) try { while (await messageBusContract.callStatic.verifyMessageFinalized(messages[1], { - maxFeePerGas: 2, - gasLimit: gasLimit.mul(2), + maxFeePerGas: 1000000001, + gasLimit: gasLimit.add(gasLimit.div(2)), from: l2Accounts.deployer }) != true) { console.log(`Messages not stored on L2 yet, retrying...`); diff --git a/go/common/types.go b/go/common/types.go index d3afa6def0..8fe0ec7e09 100644 --- a/go/common/types.go +++ b/go/common/types.go @@ -44,6 +44,12 @@ type ( L2Receipt = types.Receipt L2Receipts = types.Receipts + L2PricedTransaction struct { + Tx *L2Tx + PublishingCost *big.Int + } + L2PricedTransactions []L2PricedTransaction + CrossChainMessage = MessageBus.StructsCrossChainMessage CrossChainMessages = []CrossChainMessage ValueTransferEvent struct { @@ -72,6 +78,14 @@ type ( EncodedBatchRequest []byte ) +func (txs L2PricedTransactions) ToTransactions() types.Transactions { + ret := make(types.Transactions, 0) + for _, tx := range txs { + ret = append(ret, tx.Tx) + } + return ret +} + const ( L2GenesisHeight = uint64(0) L1GenesisHeight = uint64(0) diff --git a/go/config/enclave_cli_flags.go b/go/config/enclave_cli_flags.go index 586a9e7c2a..f896085737 100644 --- a/go/config/enclave_cli_flags.go +++ b/go/config/enclave_cli_flags.go @@ -1,6 +1,7 @@ package config import ( + "github.com/ethereum/go-ethereum/params" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/flag" ) @@ -53,16 +54,16 @@ var EnclaveFlags = map[string]*flag.TenFlag{ SequencerIDFlag: flag.NewStringFlag(SequencerIDFlag, "", "The 20 bytes of the address of the sequencer for this network"), MaxBatchSizeFlag: flag.NewUint64Flag(MaxBatchSizeFlag, 1024*25, "The maximum size a batch is allowed to reach uncompressed"), MaxRollupSizeFlag: flag.NewUint64Flag(MaxRollupSizeFlag, 1024*64, "The maximum size a rollup is allowed to reach"), - L2BaseFeeFlag: flag.NewUint64Flag(L2BaseFeeFlag, 1, ""), + L2BaseFeeFlag: flag.NewUint64Flag(L2BaseFeeFlag, params.InitialBaseFee, ""), L2CoinbaseFlag: flag.NewStringFlag(L2CoinbaseFlag, "0xd6C9230053f45F873Cb66D8A02439380a37A4fbF", ""), - GasBatchExecutionLimit: flag.NewUint64Flag(GasBatchExecutionLimit, 30_000_000, "Max gas that can be executed in a single batch"), + GasBatchExecutionLimit: flag.NewUint64Flag(GasBatchExecutionLimit, 3_000_000_000, "Max gas that can be executed in a single batch"), ObscuroGenesisFlag: flag.NewStringFlag(ObscuroGenesisFlag, "", "The json string with the obscuro genesis"), L1ChainIDFlag: flag.NewInt64Flag(L1ChainIDFlag, 1337, "An integer representing the unique chain id of the Ethereum chain used as an L1 (default 1337)"), ObscuroChainIDFlag: flag.NewInt64Flag(ObscuroChainIDFlag, 443, "An integer representing the unique chain id of the Obscuro chain (default 443)"), UseInMemoryDBFlag: flag.NewBoolFlag(UseInMemoryDBFlag, true, "Whether the enclave will use an in-memory DB rather than persist data"), ProfilerEnabledFlag: flag.NewBoolFlag(ProfilerEnabledFlag, false, "Runs a profiler instance (Defaults to false)"), DebugNamespaceEnabledFlag: flag.NewBoolFlag(DebugNamespaceEnabledFlag, false, "Whether the debug namespace is enabled"), - GasLocalExecutionCapFlag: flag.NewUint64Flag(GasLocalExecutionCapFlag, 40_000_000, "Max gas usage when executing local transactions"), + GasLocalExecutionCapFlag: flag.NewUint64Flag(GasLocalExecutionCapFlag, 4_000_000_000, "Max gas usage when executing local transactions"), } // enclaveRestrictedFlags are the flags that the enclave can receive ONLY over the Ego signed enclave.json diff --git a/go/enclave/components/batch_executor.go b/go/enclave/components/batch_executor.go index ec38798e44..3a4be57738 100644 --- a/go/enclave/components/batch_executor.go +++ b/go/enclave/components/batch_executor.go @@ -67,12 +67,12 @@ func NewBatchExecutor( } } -// payL1Fees - this function modifies the state db according to the transactions contained within the batch context -// in order to subtract gas fees from the balance. It returns a list of the transactions that have prepaid for their L1 -// publishing costs. -func (executor *batchExecutor) payL1Fees(stateDB *state.StateDB, context *BatchExecutionContext) (common.L2Transactions, common.L2Transactions) { - transactions := make(common.L2Transactions, 0) - freeTransactions := make(common.L2Transactions, 0) +// filterTransactionsWithSufficientFunds - this function estimates hte l1 fees for the transaction in a given batch execution context. It does so by taking the price of the +// pinned L1 block and using it as the cost per gas for the estimated gas of the calldata encoding of a transaction. It filters out any transactions that cannot afford to pay for their L1 +// publishing cost. +func (executor *batchExecutor) filterTransactionsWithSufficientFunds(stateDB *state.StateDB, context *BatchExecutionContext) (common.L2PricedTransactions, common.L2PricedTransactions) { + transactions := make(common.L2PricedTransactions, 0) + freeTransactions := make(common.L2PricedTransactions, 0) block, _ := executor.storage.FetchBlock(context.BlockPtr) for _, tx := range context.Transactions { @@ -97,43 +97,25 @@ func (executor *batchExecutor) payL1Fees(stateDB *state.StateDB, context *BatchE isFreeTransaction = isFreeTransaction && tx.GasPrice().Cmp(gethcommon.Big0) == 0 if isFreeTransaction { - freeTransactions = append(freeTransactions, tx) + freeTransactions = append(freeTransactions, common.L2PricedTransaction{ + Tx: tx, + PublishingCost: big.NewInt(0), + }) continue } if accBalance.Cmp(cost) == -1 { executor.logger.Info(fmt.Sprintf("insufficient account balance for tx - want: %d have: %d", cost, accBalance), log.TxKey, tx.Hash(), "addr", sender.Hex()) continue } - stateDB.SubBalance(*sender, cost) - stateDB.AddBalance(context.Creator, cost) - // todo - add refund logic. - transactions = append(transactions, tx) + transactions = append(transactions, common.L2PricedTransaction{ + Tx: tx, + PublishingCost: big.NewInt(0).Set(cost), + }) } return transactions, freeTransactions } -func (executor *batchExecutor) refundL1Fees(stateDB *state.StateDB, context *BatchExecutionContext, transactions []*common.L2Tx) { - block, _ := executor.storage.FetchBlock(context.BlockPtr) - for _, tx := range transactions { - cost, err := executor.gasOracle.EstimateL1StorageGasCost(tx, block) - if err != nil { - executor.logger.Warn("Unable to get gas cost for tx", log.TxKey, tx.Hash(), log.ErrKey, err) - continue - } - - sender, err := core.GetAuthenticatedSender(context.ChainConfig.ChainID.Int64(), tx) - if err != nil { - // todo @siliev - is this critical? Potential desync spot - executor.logger.Warn("Unable to extract sender for tx", log.TxKey, tx.Hash()) - continue - } - - stateDB.AddBalance(*sender, cost) - stateDB.SubBalance(context.Creator, cost) - } -} - func (executor *batchExecutor) ComputeBatch(context *BatchExecutionContext, failForEmptyBatch bool) (*ComputedBatch, error) { //nolint:gocognit defer core.LogMethodDuration(executor.logger, measure.NewStopwatch(), "Batch context processed") @@ -183,23 +165,29 @@ func (executor *batchExecutor) ComputeBatch(context *BatchExecutionContext, fail crossChainTransactions := executor.crossChainProcessors.Local.CreateSyntheticTransactions(messages, stateDB) executor.crossChainProcessors.Local.ExecuteValueTransfers(transfers, stateDB) - transactionsToProcess, freeTransactions := executor.payL1Fees(stateDB, context) + transactionsToProcess, freeTransactions := executor.filterTransactionsWithSufficientFunds(stateDB, context) + + xchainTxs := make(common.L2PricedTransactions, 0) + for _, xTx := range crossChainTransactions { + xchainTxs = append(xchainTxs, common.L2PricedTransaction{ + Tx: xTx, + PublishingCost: big.NewInt(0), + }) + } - crossChainTransactions = append(crossChainTransactions, freeTransactions...) + syntheticTransactions := append(xchainTxs, freeTransactions...) successfulTxs, excludedTxs, txReceipts, err := executor.processTransactions(batch, 0, transactionsToProcess, stateDB, context.ChainConfig, false) if err != nil { return nil, fmt.Errorf("could not process transactions. Cause: %w", err) } - executor.refundL1Fees(stateDB, context, excludedTxs) - - ccSuccessfulTxs, _, ccReceipts, err := executor.processTransactions(batch, len(successfulTxs), crossChainTransactions, stateDB, context.ChainConfig, true) + ccSuccessfulTxs, _, ccReceipts, err := executor.processTransactions(batch, len(successfulTxs), syntheticTransactions, stateDB, context.ChainConfig, true) if err != nil { return nil, err } - if err = executor.verifyInboundCrossChainTransactions(crossChainTransactions, ccSuccessfulTxs, ccReceipts); err != nil { + if err = executor.verifyInboundCrossChainTransactions(syntheticTransactions, ccSuccessfulTxs, ccReceipts); err != nil { return nil, fmt.Errorf("batch computation failed due to cross chain messages. Cause: %w", err) } @@ -220,7 +208,7 @@ func (executor *batchExecutor) ComputeBatch(context *BatchExecutionContext, fail // we need to copy the batch to reset the internal hash cache copyBatch := *batch copyBatch.Header.Root = stateDB.IntermediateRoot(false) - copyBatch.Transactions = append(successfulTxs, freeTransactions...) + copyBatch.Transactions = append(successfulTxs, freeTransactions.ToTransactions()...) copyBatch.ResetHash() if err = executor.populateOutboundCrossChainData(©Batch, block, txReceipts); err != nil { @@ -387,8 +375,8 @@ func (executor *batchExecutor) populateHeader(batch *core.Batch, receipts types. } } -func (executor *batchExecutor) verifyInboundCrossChainTransactions(transactions types.Transactions, executedTxs types.Transactions, receipts types.Receipts) error { - if transactions.Len() != executedTxs.Len() { +func (executor *batchExecutor) verifyInboundCrossChainTransactions(transactions common.L2PricedTransactions, executedTxs types.Transactions, receipts types.Receipts) error { + if len(transactions) != executedTxs.Len() { return fmt.Errorf("some synthetic transactions have not been executed") } @@ -404,7 +392,7 @@ func (executor *batchExecutor) verifyInboundCrossChainTransactions(transactions func (executor *batchExecutor) processTransactions( batch *core.Batch, tCount int, - txs []*common.L2Tx, + txs common.L2PricedTransactions, stateDB *state.StateDB, cc *params.ChainConfig, noBaseFee bool, @@ -424,18 +412,18 @@ func (executor *batchExecutor) processTransactions( executor.logger, ) for _, tx := range txs { - result, f := txResults[tx.Hash()] + result, f := txResults[tx.Tx.Hash()] if !f { return nil, nil, nil, fmt.Errorf("there should be an entry for each transaction") } rec, foundReceipt := result.(*types.Receipt) if foundReceipt { - executedTransactions = append(executedTransactions, tx) + executedTransactions = append(executedTransactions, tx.Tx) txReceipts = append(txReceipts, rec) } else { // Exclude all errors - excludedTransactions = append(excludedTransactions, tx) - executor.logger.Info("Excluding transaction from batch", log.TxKey, tx.Hash(), log.BatchHashKey, batch.Hash(), "cause", result) + excludedTransactions = append(excludedTransactions, tx.Tx) + executor.logger.Info("Excluding transaction from batch", log.TxKey, tx.Tx.Hash(), log.BatchHashKey, batch.Hash(), "cause", result) } } sort.Sort(sortByTxIndex(txReceipts)) diff --git a/go/enclave/crosschain/message_bus_manager.go b/go/enclave/crosschain/message_bus_manager.go index 1731a10515..1a70bd7465 100644 --- a/go/enclave/crosschain/message_bus_manager.go +++ b/go/enclave/crosschain/message_bus_manager.go @@ -86,7 +86,7 @@ func (m *MessageBusManager) GenerateMessageBusDeployTx() (*common.L2Tx, error) { tx := &types.LegacyTx{ Nonce: 0, // The first transaction of the owner identity should always be deploying the contract Value: gethcommon.Big0, - Gas: 5_000_000, // It's quite the expensive contract. + Gas: 500_000_000, // It's quite the expensive contract. GasPrice: gethcommon.Big0, // Synthetic transactions are on the house. Or the house. Data: gethcommon.FromHex(MessageBus.MessageBusMetaData.Bin), To: nil, // Geth requires nil instead of gethcommon.Address{} which equates to zero address in order to return receipt. diff --git a/go/enclave/enclave.go b/go/enclave/enclave.go index 21c5d79cc7..47621b0526 100644 --- a/go/enclave/enclave.go +++ b/go/enclave/enclave.go @@ -77,9 +77,10 @@ type enclaveImpl struct { crossChainProcessors *crosschain.Processors sharedSecretProcessor *components.SharedSecretProcessor - chain l2chain.ObscuroChain - service nodetype.NodeType - registry components.BatchRegistry + chain l2chain.ObscuroChain + service nodetype.NodeType + registry components.BatchRegistry + gasOracle gas.Oracle mgmtContractLib mgmtcontractlib.MgmtContractLib attestationProvider components.AttestationProvider // interface for producing attestation reports and verifying them @@ -268,9 +269,10 @@ func NewEnclave( debugger: debug, stopControl: stopcontrol.New(), - chain: chain, - registry: registry, - service: service, + chain: chain, + registry: registry, + service: service, + gasOracle: gasOracle, mainMutex: sync.Mutex{}, } @@ -678,6 +680,20 @@ func (e *enclaveImpl) GetTransactionCount(encryptedParams common.EncryptedParams address := gethcommon.HexToAddress(addressStr) + seqNo := e.registry.HeadBatchSeq().Uint64() + if len(paramList) == 3 { + tag, err := gethencoding.ExtractBlockNumber(paramList[2]) + if err != nil { + return responses.AsPlaintextError(fmt.Errorf("unexpected tag parameter. Cause: %w", err)), nil + } + + b, err := e.registry.GetBatchAtHeight(*tag) + if err != nil { + return responses.AsPlaintextError(fmt.Errorf("cant retrieve batch for tag. Cause: %w", err)), nil + } + seqNo = b.SeqNo().Uint64() + } + // extract, create and validate the VK encryption handler vkHandler, err := createVKHandler(&address, paramList[0], e.config.ObscuroChainID) if err != nil { @@ -685,18 +701,15 @@ func (e *enclaveImpl) GetTransactionCount(encryptedParams common.EncryptedParams } var nonce uint64 - headBatch := e.registry.HeadBatchSeq() - if headBatch != nil { - l2Head, err := e.storage.FetchBatchBySeqNo(headBatch.Uint64()) - if err == nil { - // todo - we should return an error when head state is not available, but for current test situations with race - // conditions we allow it to return zero while head state is uninitialized - s, err := e.storage.CreateStateDB(l2Head.Hash()) - if err != nil { - return nil, responses.ToInternalError(err) - } - nonce = s.GetNonce(address) + l2Head, err := e.storage.FetchBatchBySeqNo(seqNo) + if err == nil { + // todo - we should return an error when head state is not available, but for current test situations with race + // conditions we allow it to return zero while head state is uninitialized + s, err := e.storage.CreateStateDB(l2Head.Hash()) + if err != nil { + return nil, responses.ToInternalError(err) } + nonce = s.GetNonce(address) } encoded := hexutil.EncodeUint64(nonce) @@ -1025,6 +1038,32 @@ func (e *enclaveImpl) EstimateGas(encryptedParams common.EncryptedParamsEstimate return responses.AsEncryptedError(err, vkHandler), nil } + block, err := e.blockResolver.FetchHeadBlock() + if err != nil { + return responses.AsPlaintextError(fmt.Errorf("internal server error")), err + } + + // The message is ran through the l1 publishing cost estimation for the current + // known head block. + l1Cost, err := e.gasOracle.EstimateL1CostForMsg(callMsg, block) + if err != nil { + return responses.AsPlaintextError(fmt.Errorf("internal server error")), err + } + + batch, err := e.storage.FetchHeadBatch() + if err != nil { + return responses.AsPlaintextError(fmt.Errorf("internal server error")), err + } + + // We divide the total estimated l1 cost by the l2 fee per gas in order to convert + // the expected cost into l2 gas based on current pricing. + // todo @siliev - add overhead when the base fee becomes dynamic. + publishingGas := big.NewInt(0).Div(l1Cost, batch.Header.BaseFee) + + // The one additional gas captures the modulo leftover in some edge cases + // where BaseFee is bigger than the l1cost. + publishingGas = big.NewInt(0).Add(publishingGas, gethcommon.Big1) + executionGasEstimate, err := e.DoEstimateGas(callMsg, blockNumber, e.config.GasLocalExecutionCapFlag) if err != nil { err = fmt.Errorf("unable to estimate transaction - %w", err) @@ -1042,7 +1081,9 @@ func (e *enclaveImpl) EstimateGas(encryptedParams common.EncryptedParamsEstimate return responses.AsEncryptedError(err, vkHandler), nil } - return responses.AsEncryptedResponse(&executionGasEstimate, vkHandler), nil + totalGasEstimate := hexutil.Uint64(publishingGas.Uint64() + uint64(executionGasEstimate)) + + return responses.AsEncryptedResponse(&totalGasEstimate, vkHandler), nil } func (e *enclaveImpl) GetLogs(encryptedParams common.EncryptedParamsGetLogs) (*responses.Logs, common.SystemError) { //nolint diff --git a/go/enclave/evm/evm_facade.go b/go/enclave/evm/evm_facade.go index 31560d705c..d67a4879d8 100644 --- a/go/enclave/evm/evm_facade.go +++ b/go/enclave/evm/evm_facade.go @@ -5,6 +5,10 @@ import ( "fmt" "math/big" + // unsafe package imported in order to link to a private function in go-ethereum. + // This allows us to customize the message generated from a signed transaction and inject custom gas logic. + _ "unsafe" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/state" @@ -30,7 +34,7 @@ import ( // header - the header of the rollup where this transaction will be included // fromTxIndex - for the receipts and events, the evm needs to know for each transaction the order in which it was executed in the block. func ExecuteTransactions( - txs []*common.L2Tx, + txs common.L2PricedTransactions, s *state.StateDB, header *common.BatchHeader, storage storage.Storage, @@ -68,24 +72,27 @@ func ExecuteTransactions( header.Number.Uint64(), ) if err != nil { - result[t.Hash()] = err - logger.Info("Failed to execute tx:", log.TxKey, t.Hash(), log.CtrErrKey, err) + result[t.Tx.Hash()] = err + logger.Info("Failed to execute tx:", log.TxKey, t.Tx.Hash(), log.CtrErrKey, err) continue } - result[t.Hash()] = r + result[t.Tx.Hash()] = r logReceipt(r, logger) } s.Finalise(true) return result } +//go:linkname applyTransaction github.com/ethereum/go-ethereum/core.applyTransaction +func applyTransaction(msg *gethcore.Message, config *params.ChainConfig, gp *gethcore.GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash gethcommon.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) + func executeTransaction( s *state.StateDB, cc *params.ChainConfig, chain *ObscuroChainContext, gp *gethcore.GasPool, header *types.Header, - t *common.L2Tx, + t common.L2PricedTransaction, usedGas *uint64, vmCfg vm.Config, tCount int, @@ -93,26 +100,70 @@ func executeTransaction( batchHeight uint64, ) (*types.Receipt, error) { rules := cc.Rules(big.NewInt(0), true, 0) - from, err := types.Sender(types.LatestSigner(cc), t) + from, err := types.Sender(types.LatestSigner(cc), t.Tx) if err != nil { return nil, err } - s.Prepare(rules, from, gethcommon.Address{}, t.To(), nil, nil) + s.Prepare(rules, from, gethcommon.Address{}, t.Tx.To(), nil, nil) snap := s.Snapshot() - s.SetTxContext(t.Hash(), tCount) + s.SetTxContext(t.Tx.Hash(), tCount) before := header.MixDigest // calculate a random value per transaction header.MixDigest = crypto.CalculateTxRnd(before.Bytes(), tCount) - receipt, err := gethcore.ApplyTransaction(cc, chain, nil, gp, s, header, t, usedGas, vmCfg) - // adjust the receipt to point to the right batch hash - if receipt != nil { - receipt.Logs = s.GetLogs(t.Hash(), batchHeight, batchHash) - receipt.BlockHash = batchHash - receipt.BlockNumber = big.NewInt(int64(batchHeight)) - for _, l := range receipt.Logs { - l.BlockHash = batchHash + applyTx := func( + config *params.ChainConfig, + bc gethcore.ChainContext, + author *gethcommon.Address, + gp *gethcore.GasPool, + statedb *state.StateDB, + header *types.Header, + tx common.L2PricedTransaction, + usedGas *uint64, + cfg vm.Config, + ) (*types.Receipt, error) { + msg, err := gethcore.TransactionToMessage(tx.Tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee) + if err != nil { + return nil, err + } + l1cost := tx.PublishingCost + l1Gas := big.NewInt(0) + hasL1Cost := l1cost.Cmp(big.NewInt(0)) != 0 + + // If a transaction has to be published on the l1, it will have an l1 cost + if hasL1Cost { + l1Gas.Div(l1cost, header.BaseFee) // TotalCost/CostPerGas = Gas + l1Gas.Add(l1Gas, big.NewInt(1)) // Cover from leftover from the division + + // The gas limit of the transaction (evm message) should always be higher than the gas overhead + // used to cover the l1 cost + if msg.GasLimit < l1Gas.Uint64() { + return nil, fmt.Errorf("gas limit set by user is too low to pay for execution and l1 fees. Want at least: %d have: %d", l1Gas, msg.GasLimit) + } + + // Remove the gas overhead for l1 publishing from the gas limit in order to define + // the actual gas limit for execution + msg.GasLimit -= l1Gas.Uint64() + + // Remove the l1 cost from the sender + // and pay it to the coinbase of the batch + statedb.SubBalance(msg.From, l1cost) + statedb.AddBalance(header.Coinbase, l1cost) + } + + // Create a new context to be used in the EVM environment + blockContext := gethcore.NewEVMBlockContext(header, bc, author) + vmenv := vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.Tx.BlobHashes()}, statedb, config, cfg) + receipt, err := applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx.Tx, usedGas, vmenv) + if err != nil { + // If the transaction has l1 cost, then revert the funds exchange + // as it will not be published on error (no receipt condition) + if hasL1Cost { + statedb.SubBalance(header.Coinbase, l1cost) + statedb.AddBalance(msg.From, l1cost) + } + return receipt, err } // Do not increase the balance of zero address as it is the contract deployment address. @@ -122,7 +173,22 @@ func executeTransaction( executionGasCost := big.NewInt(0).Mul(gasUsed, header.BaseFee) // As the baseFee is burned, we add it back to the coinbase. // Geth should automatically add the tips. - s.AddBalance(header.Coinbase, executionGasCost) + statedb.AddBalance(header.Coinbase, executionGasCost) + } + receipt.GasUsed += l1Gas.Uint64() + + return receipt, err + } + + receipt, err := applyTx(cc, chain, nil, gp, s, header, t, usedGas, vmCfg) + + // adjust the receipt to point to the right batch hash + if receipt != nil { + receipt.Logs = s.GetLogs(t.Tx.Hash(), batchHeight, batchHash) + receipt.BlockHash = batchHash + receipt.BlockNumber = big.NewInt(int64(batchHeight)) + for _, l := range receipt.Logs { + l.BlockHash = batchHash } } diff --git a/go/enclave/gas/oracle.go b/go/enclave/gas/oracle.go index 31ff3ae3fa..8ab2cc8d24 100644 --- a/go/enclave/gas/oracle.go +++ b/go/enclave/gas/oracle.go @@ -4,7 +4,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/ten-protocol/go-ten/go/common/gethapi" ) // Oracle - the interface for the future precompiled gas oracle contract @@ -12,6 +14,7 @@ import ( type Oracle interface { ProcessL1Block(block *types.Block) EstimateL1StorageGasCost(tx *types.Transaction, block *types.Block) (*big.Int, error) + EstimateL1CostForMsg(args *gethapi.TransactionArgs, block *types.Block) (*big.Int, error) } type oracle struct { @@ -49,3 +52,18 @@ func (o *oracle) EstimateL1StorageGasCost(tx *types.Transaction, block *types.Bl l1Gas := CalculateL1GasUsed(encodedTx, big.NewInt(0)) return big.NewInt(0).Mul(l1Gas, block.BaseFee()), nil } + +func (o *oracle) EstimateL1CostForMsg(args *gethapi.TransactionArgs, block *types.Block) (*big.Int, error) { + encoded := make([]byte, 0) + if args.Data != nil { + encoded = append(encoded, *args.Data...) + } + + // We get the non zero gas cost per byte of calldata, and multiply it by the fixed bytes + // of a transaction. Then we take the data of a transaction and calculate the l1 gas used for it. + // Both are added together and multiplied by the base fee to give us the final cost for the message. + nonZeroGas := big.NewInt(int64(params.TxDataNonZeroGasEIP2028)) + overhead := big.NewInt(0).Mul(big.NewInt(150), nonZeroGas) + l1Gas := CalculateL1GasUsed(encoded, overhead) + return big.NewInt(0).Mul(l1Gas, block.BaseFee()), nil +} diff --git a/go/enclave/l2chain/l2_chain.go b/go/enclave/l2chain/l2_chain.go index 7f56e0fde6..b7b68cf356 100644 --- a/go/enclave/l2chain/l2_chain.go +++ b/go/enclave/l2chain/l2_chain.go @@ -133,7 +133,7 @@ func (oc *obscuroChain) ObsCallAtBlock(apiArgs *gethapi.TransactionArgs, blockNu return nil, fmt.Errorf("unable to fetch head state batch. Cause: %w", err) } - callMsg, err := apiArgs.ToMessage(batch.Header.GasLimit, batch.Header.BaseFee) + callMsg, err := apiArgs.ToMessage(batch.Header.GasLimit-1, batch.Header.BaseFee) if err != nil { return nil, fmt.Errorf("unable to convert TransactionArgs to Message - %w", err) } diff --git a/go/host/rpc/clientapi/client_api_eth.go b/go/host/rpc/clientapi/client_api_eth.go index 49bdfe03bf..9c15154e07 100644 --- a/go/host/rpc/clientapi/client_api_eth.go +++ b/go/host/rpc/clientapi/client_api_eth.go @@ -7,6 +7,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/host" @@ -73,7 +74,7 @@ func (api *EthereumAPI) GasPrice(context.Context) (*hexutil.Big, error) { } if header.BaseFee == nil || header.BaseFee.Cmp(gethcommon.Big0) == 0 { - return (*hexutil.Big)(big.NewInt(1)), nil + return (*hexutil.Big)(big.NewInt(params.InitialBaseFee)), nil } return (*hexutil.Big)(big.NewInt(0).Set(header.BaseFee)), nil @@ -184,15 +185,31 @@ func (api *EthereumAPI) GetStorageAt(_ context.Context, encryptedParams common.E // FeeHistory is a placeholder for an RPC method required by MetaMask/Remix. // rpc.DecimalOrHex -> []byte -func (api *EthereumAPI) FeeHistory(context.Context, []byte, rpc.BlockNumber, []float64) (*FeeHistoryResult, error) { +func (api *EthereumAPI) FeeHistory(context.Context, string, rpc.BlockNumber, []float64) (*FeeHistoryResult, error) { // todo (#1621) - return a non-dummy fee history + header, err := api.host.DB().GetHeadBatchHeader() + if err != nil { + api.logger.Error("Unable to retrieve header for fee history.", log.ErrKey, err) + return nil, fmt.Errorf("unable to retrieve fee history") + } + + batches := make([]*common.BatchHeader, 0) + batches = append(batches, header) - return &FeeHistoryResult{ - OldestBlock: (*hexutil.Big)(big.NewInt(0)), + feeHist := &FeeHistoryResult{ + OldestBlock: (*hexutil.Big)(header.Number), Reward: [][]*hexutil.Big{}, BaseFee: []*hexutil.Big{}, GasUsedRatio: []float64{}, - }, nil + } + + for _, header := range batches { + // 0.9 - This number represents how full the block is. As we dont have a dynamic base fee, we tell whomever is requesting that + // we expect the baseFee to increase, rather than decrease in order to avoid underpriced transactions. + feeHist.GasUsedRatio = append(feeHist.GasUsedRatio, 0.9) + feeHist.BaseFee = append(feeHist.BaseFee, (*hexutil.Big)(header.BaseFee)) + } + return feeHist, nil } // FeeHistoryResult is the structure returned by Geth `eth_feeHistory` API. diff --git a/go/obsclient/authclient.go b/go/obsclient/authclient.go index e1326903b2..2db0151f61 100644 --- a/go/obsclient/authclient.go +++ b/go/obsclient/authclient.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/params" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/viewingkey" "github.com/ten-protocol/go-ten/go/responses" @@ -60,7 +61,9 @@ func DialWithAuth(rpcurl string, wal wallet.Wallet, logger gethlog.Logger) (*Aut if err != nil { return nil, err } - return NewAuthObsClient(encClient), nil + + authClient := NewAuthObsClient(encClient) + return authClient, nil } // TransactionByHash returns transaction (if found), isPending (always false currently as we don't search the mempool), error @@ -74,6 +77,16 @@ func (ac *AuthObsClient) TransactionByHash(ctx context.Context, hash gethcommon. return &tx, false, nil } +func (ac *AuthObsClient) GasPrice(ctx context.Context) (*big.Int, error) { + var result responses.GasPriceType + err := ac.rpcClient.CallContext(ctx, &result, rpc.GasPrice) + if err != nil { + return nil, err + } + + return result.ToInt(), nil +} + func (ac *AuthObsClient) TransactionReceipt(ctx context.Context, txHash gethcommon.Hash) (*types.Receipt, error) { var result responses.ReceiptType err := ac.rpcClient.CallContext(ctx, &result, rpc.GetTransactionReceipt, txHash) @@ -165,7 +178,6 @@ func (ac *AuthObsClient) EstimateGas(ctx context.Context, msg *ethereum.CallMsg) func (ac *AuthObsClient) EstimateGasAndGasPrice(txData types.TxData) types.TxData { unEstimatedTx := types.NewTx(txData) - gasPrice := gethcommon.Big1 // constant gas price atm gasLimit, err := ac.EstimateGas(context.Background(), ðereum.CallMsg{ From: ac.Address(), @@ -177,6 +189,14 @@ func (ac *AuthObsClient) EstimateGasAndGasPrice(txData types.TxData) types.TxDat gasLimit = unEstimatedTx.Gas() } + gasPrice, err := ac.GasPrice(context.Background()) + if err != nil { + // params.InitialBaseFee should be the new standard gas price. + // If the gas price is too low, then the gas required to be put in a transaction + // becomes astronomical. + gasPrice = big.NewInt(params.InitialBaseFee) + } + return &types.LegacyTx{ Nonce: unEstimatedTx.Nonce(), GasPrice: gasPrice, diff --git a/go/responses/types.go b/go/responses/types.go index 954f31165b..690e0ac5f4 100644 --- a/go/responses/types.go +++ b/go/responses/types.go @@ -43,12 +43,13 @@ type ( // Data Types type ( - BalanceType = hexutil.Big - CallType = string - ReceiptType = types.Receipt - RawTxType = common.Hash - TxType = types.Transaction - NonceType = string - GasType = hexutil.Uint64 - LogsType = []*types.Log + BalanceType = hexutil.Big + CallType = string + ReceiptType = types.Receipt + RawTxType = common.Hash + TxType = types.Transaction + NonceType = string + GasType = hexutil.Uint64 + LogsType = []*types.Log + GasPriceType = hexutil.Big ) diff --git a/go/rpc/client.go b/go/rpc/client.go index b9db9052a1..81bea5c0bb 100644 --- a/go/rpc/client.go +++ b/go/rpc/client.go @@ -22,6 +22,7 @@ const ( EstimateGas = "eth_estimateGas" GetLogs = "eth_getLogs" GetStorageAt = "eth_getStorageAt" + GasPrice = "eth_gasPrice" Health = "obscuro_health" Config = "obscuro_config" diff --git a/integration/common/constants.go b/integration/common/constants.go index 80d68b5446..ef510672c5 100644 --- a/integration/common/constants.go +++ b/integration/common/constants.go @@ -4,6 +4,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/log" "github.com/ten-protocol/go-ten/go/config" @@ -75,15 +76,19 @@ func DefaultEnclaveConfig() *config.EnclaveConfig { EdgelessDBHost: "", SqliteDBPath: "", ProfilerEnabled: false, - MinGasPrice: big.NewInt(1), + MinGasPrice: big.NewInt(params.InitialBaseFee), SequencerID: gethcommon.BytesToAddress([]byte("")), ObscuroGenesis: "", DebugNamespaceEnabled: false, MaxBatchSize: 1024 * 25, MaxRollupSize: 1024 * 64, GasPaymentAddress: gethcommon.HexToAddress("0xd6C9230053f45F873Cb66D8A02439380a37A4fbF"), - BaseFee: new(big.Int).SetUint64(1), - GasBatchExecutionLimit: 30_000_000, - GasLocalExecutionCapFlag: 40_000_000, + BaseFee: new(big.Int).SetUint64(params.InitialBaseFee), + + // Due to hiding L1 costs in the gas quantity, the gas limit needs to be huge + // Arbitrum with the same approach has gas limit of 1,125,899,906,842,624, + // whilst the usage is small. Should be ok since execution is paid for anyway. + GasLocalExecutionCapFlag: 300_000_000_000, + GasBatchExecutionLimit: 300_000_000_000, } } diff --git a/integration/common/utils.go b/integration/common/utils.go index aabfea5804..1b703d9382 100644 --- a/integration/common/utils.go +++ b/integration/common/utils.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/ethclient" "github.com/ten-protocol/go-ten/go/common/errutil" @@ -96,13 +97,15 @@ func PrefundWallets(ctx context.Context, faucetWallet wallet.Wallet, faucetClien txHashes := make([]gethcommon.Hash, len(wallets)) for idx, w := range wallets { destAddr := w.Address() - tx := &types.LegacyTx{ + txData := &types.LegacyTx{ Nonce: startingNonce + uint64(idx), Value: alloc, Gas: uint64(100_000), GasPrice: gethcommon.Big1, To: &destAddr, } + + tx := faucetClient.EstimateGasAndGasPrice(txData) //nolint: contextcheck signedTx, err := faucetWallet.SignTransaction(tx) if err != nil { panic(err) @@ -110,7 +113,9 @@ func PrefundWallets(ctx context.Context, faucetWallet wallet.Wallet, faucetClien err = faucetClient.SendTransaction(ctx, signedTx) if err != nil { - panic(fmt.Sprintf("could not transfer from faucet. Cause: %s", err)) + var txJSON []byte + txJSON, _ = signedTx.MarshalJSON() + panic(fmt.Sprintf("could not transfer from faucet for tx %s. Cause: %s", string(txJSON[:]), err)) } txHashes[idx] = signedTx.Hash() @@ -124,7 +129,7 @@ func PrefundWallets(ctx context.Context, faucetWallet wallet.Wallet, faucetClien defer wg.Done() err := AwaitReceipt(ctx, faucetClient, txHash, timeout) if err != nil { - panic(fmt.Sprintf("faucet transfer transaction unsuccessful. Cause: %s", err)) + panic(fmt.Sprintf("faucet transfer transaction %s unsuccessful. Cause: %s", txHash, err)) } }(txHash) } @@ -141,7 +146,7 @@ func InteractWithSmartContract(client *ethclient.Client, wallet wallet.Wallet, c Nonce: wallet.GetNonceAndIncrement(), To: &contractAddress, Gas: uint64(1_000_000), - GasPrice: gethcommon.Big1, + GasPrice: big.NewInt(params.InitialBaseFee), Data: contractInteractionData, } signedTx, err := wallet.SignTransaction(&interactionTx) diff --git a/integration/eth2network/eth2_configs.go b/integration/eth2network/eth2_configs.go index 582095f855..8921fb97b4 100644 --- a/integration/eth2network/eth2_configs.go +++ b/integration/eth2network/eth2_configs.go @@ -46,7 +46,7 @@ BELLATRIX_FORK_VERSION: 0x20000089 TERMINAL_TOTAL_DIFFICULTY: 10 # Capella -CAPELLA_FORK_EPOCH: 90000000000000000 +CAPELLA_FORK_EPOCH: 90000000000000000 CAPELLA_FORK_VERSION: 0x20000089 # Time parameters diff --git a/integration/obscurogateway/tengateway_test.go b/integration/obscurogateway/tengateway_test.go index 073f5ae927..bee7661d9c 100644 --- a/integration/obscurogateway/tengateway_test.go +++ b/integration/obscurogateway/tengateway_test.go @@ -15,6 +15,7 @@ import ( wecommon "github.com/ten-protocol/go-ten/tools/walletextension/common" gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" @@ -159,6 +160,9 @@ func testMultipleAccountsSubscription(t *testing.T, httpURL, wsURL string, w wal Data: gethcommon.FromHex(eventsContractBytecode), } + err = getFeeAndGas(user0.HTTPClient, w, deployTx) + require.NoError(t, err) + signedTx, err := w.SignTransaction(deployTx) require.NoError(t, err) @@ -279,6 +283,9 @@ func testSubscriptionTopics(t *testing.T, httpURL, wsURL string, w wallet.Wallet Data: gethcommon.FromHex(eventsContractBytecode), } + err = getFeeAndGas(user0.HTTPClient, w, deployTx) + require.NoError(t, err) + signedTx, err := w.SignTransaction(deployTx) require.NoError(t, err) @@ -418,6 +425,9 @@ func testErrorsRevertedArePassed(t *testing.T, httpURL, wsURL string, w wallet.W Data: gethcommon.FromHex(errorsContractBytecode), } + err = getFeeAndGas(ethStdClient, w, deployTx) + require.NoError(t, err) + signedTx, err := w.SignTransaction(deployTx) require.NoError(t, err) @@ -478,11 +488,13 @@ func testUnsubscribe(t *testing.T, httpURL, wsURL string, w wallet.Wallet) { // deploy events contract deployTx := &types.LegacyTx{ Nonce: w.GetNonceAndIncrement(), - Gas: uint64(1_000_000), + Gas: uint64(10_000_000), GasPrice: gethcommon.Big1, Data: gethcommon.FromHex(eventsContractBytecode), } + require.NoError(t, getFeeAndGas(user.HTTPClient, w, deployTx)) + signedTx, err := w.SignTransaction(deployTx) require.NoError(t, err) @@ -533,6 +545,8 @@ func testClosingConnectionWhileSubscribed(t *testing.T, httpURL, wsURL string, w Data: gethcommon.FromHex(eventsContractBytecode), } + require.NoError(t, getFeeAndGas(user.HTTPClient, w, deployTx)) + signedTx, err := w.SignTransaction(deployTx) require.NoError(t, err) @@ -645,6 +659,30 @@ func waitServerIsReady(serverAddr string) error { return fmt.Errorf("timed out before server was ready") } +func getFeeAndGas(client *ethclient.Client, wallet wallet.Wallet, legacyTx *types.LegacyTx) error { + tx := types.NewTx(legacyTx) + + history, err := client.FeeHistory(context.Background(), 1, nil, []float64{}) + if err != nil || len(history.BaseFee) == 0 { + return err + } + + estimate, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: wallet.Address(), + To: tx.To(), + Value: tx.Value(), + Data: tx.Data(), + }) + if err != nil { + return err + } + + legacyTx.Gas = estimate + legacyTx.GasPrice = history.BaseFee[0] // big.NewInt(gethparams.InitialBaseFee) + + return nil +} + func transferETHToAddress(client *ethclient.Client, wallet wallet.Wallet, toAddress gethcommon.Address, amount int64) (*types.Receipt, error) { //nolint:unparam transferTx1 := types.LegacyTx{ Nonce: wallet.GetNonceAndIncrement(), @@ -654,6 +692,12 @@ func transferETHToAddress(client *ethclient.Client, wallet wallet.Wallet, toAddr GasPrice: gethcommon.Big1, Data: nil, } + + err := getFeeAndGas(client, wallet, &transferTx1) + if err != nil { + return nil, err + } + signedTx, err := wallet.SignTransaction(&transferTx1) if err != nil { return nil, err diff --git a/integration/simulation/output_stats.go b/integration/simulation/output_stats.go index 0d886d60e3..fb810592d6 100644 --- a/integration/simulation/output_stats.go +++ b/integration/simulation/output_stats.go @@ -133,6 +133,7 @@ func (o *OutputStats) String() string { "totalWithdrawnAmount: %d\n"+ "rollupWithMoreRecentProof: %d\n"+ "nrTransferTransactions: %d\n"+ + "nrNativeTransferTransactions: %d\n"+ "nrBlockParsedERC20Deposits: %d\n"+ "gasBridgeCount: %d\n", o.simulation.Stats.NrMiners, @@ -151,6 +152,7 @@ func (o *OutputStats) String() string { o.simulation.Stats.TotalWithdrawalRequestedAmount, o.simulation.Stats.RollupWithMoreRecentProofCount, o.simulation.Stats.NrTransferTransactions, + o.simulation.Stats.NrNativeTransferTransactions, o.canonicalERC20DepositCount, len(o.simulation.TxInjector.TxTracker.GasBridgeTransactions), ) diff --git a/integration/simulation/simulation.go b/integration/simulation/simulation.go index f39cf86520..1e24750bab 100644 --- a/integration/simulation/simulation.go +++ b/integration/simulation/simulation.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/filters" + gethparams "github.com/ethereum/go-ethereum/params" "github.com/ten-protocol/go-ten/contracts/generated/MessageBus" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/errutil" @@ -23,6 +24,7 @@ import ( "github.com/ten-protocol/go-ten/integration/erc20contract" "github.com/ten-protocol/go-ten/integration/ethereummock" "github.com/ten-protocol/go-ten/integration/simulation/network" + "github.com/ten-protocol/go-ten/integration/simulation/params" "github.com/ten-protocol/go-ten/integration/simulation/stats" @@ -30,10 +32,6 @@ import ( testcommon "github.com/ten-protocol/go-ten/integration/common" ) -const ( - allocObsWallets = 750_000_000_000_000 // The amount the faucet allocates to each Obscuro wallet. -) - var initialBalance = common.ValueInWei(big.NewInt(5000)) // Simulation represents all the data required to inject transactions on a network @@ -58,7 +56,7 @@ func (s *Simulation) Start() { // Arbitrary sleep to wait for RPC clients to get up and running // and for all l2 nodes to receive the genesis l2 batch - time.Sleep(2 * time.Second) + time.Sleep(5 * time.Second) s.bridgeFundingToObscuro() s.trackLogs() // Create log subscriptions, to validate that they're working correctly later. @@ -208,9 +206,14 @@ func (s *Simulation) trackLogs() { // Prefunds the L2 wallets with `allocObsWallets` each. func (s *Simulation) prefundObscuroAccounts() { faucetWallet := s.Params.Wallets.L2FaucetWallet - faucetClient := s.RPCHandles.ObscuroWalletRndClient(faucetWallet) + faucetClient := s.RPCHandles.ObscuroWalletClient(faucetWallet.Address(), 0) // get sequencer, else errors on submission get swallowed nonce := NextNonce(s.ctx, s.RPCHandles, faucetWallet) - testcommon.PrefundWallets(s.ctx, faucetWallet, faucetClient, nonce, s.Params.Wallets.AllObsWallets(), big.NewInt(allocObsWallets), s.Params.ReceiptTimeout) + + // Give 100 ether per account - ether is 1e18 so best convert it by code + // as a lot of the hardcodes were giving way too little and choking the gas payments + allocObsWallets := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethparams.Ether)) + + testcommon.PrefundWallets(s.ctx, faucetWallet, faucetClient, nonce, s.Params.Wallets.AllObsWallets(), allocObsWallets, s.Params.ReceiptTimeout) } // This deploys an ERC20 contract on Obscuro, which is used for token arithmetic. @@ -226,7 +229,7 @@ func (s *Simulation) deployObscuroERC20s() { // 0x526c84529b2b8c11f57d93d3f5537aca3aecef9b - this is the address of the L2 contract which is currently hardcoded. contractBytes := erc20contract.L2BytecodeWithDefaultSupply(string(token), gethcommon.HexToAddress("0x526c84529b2b8c11f57d93d3f5537aca3aecef9b")) - deployContractTx := types.DynamicFeeTx{ + deployContractTxData := types.DynamicFeeTx{ Nonce: NextNonce(s.ctx, s.RPCHandles, owner), Gas: 5_000_000, GasFeeCap: gethcommon.Big1, // This field is used to derive the gas price for dynamic fee transactions. @@ -234,7 +237,8 @@ func (s *Simulation) deployObscuroERC20s() { GasTipCap: gethcommon.Big1, } - signedTx, err := owner.SignTransaction(&deployContractTx) + deployContractTx := s.RPCHandles.ObscuroWalletRndClient(owner).EstimateGasAndGasPrice(&deployContractTxData) + signedTx, err := owner.SignTransaction(deployContractTx) if err != nil { panic(err) } diff --git a/integration/simulation/stats/stats.go b/integration/simulation/stats/stats.go index 8e63970816..cf97f29dfe 100644 --- a/integration/simulation/stats/stats.go +++ b/integration/simulation/stats/stats.go @@ -27,6 +27,7 @@ type Stats struct { TotalWithdrawalRequestedAmount *big.Int RollupWithMoreRecentProofCount uint64 NrTransferTransactions int + NrNativeTransferTransactions int statsMu *sync.RWMutex } @@ -76,6 +77,12 @@ func (s *Stats) Transfer() { s.statsMu.Unlock() } +func (s *Stats) NativeTransfer() { + s.statsMu.Lock() + s.NrNativeTransferTransactions++ + s.statsMu.Unlock() +} + func (s *Stats) Withdrawal(v *big.Int) { s.statsMu.Lock() s.TotalWithdrawalRequestedAmount = s.TotalWithdrawalRequestedAmount.Add(s.TotalWithdrawalRequestedAmount, v) diff --git a/integration/simulation/transaction_injector.go b/integration/simulation/transaction_injector.go index 497470a86c..cce12e68e0 100644 --- a/integration/simulation/transaction_injector.go +++ b/integration/simulation/transaction_injector.go @@ -181,8 +181,8 @@ func (ti *TransactionInjector) issueRandomValueTransfers() { toWalletAddr := toWallet.Address() txData := &types.LegacyTx{ Nonce: fromWallet.GetNonceAndIncrement(), - Value: big.NewInt(int64(testcommon.RndBtw(1, 500))), - Gas: uint64(1_000_000), + Value: big.NewInt(int64(testcommon.RndBtw(1, 100))), + Gas: uint64(50_000), GasPrice: gethcommon.Big1, To: &toWalletAddr, } @@ -192,9 +192,9 @@ func (ti *TransactionInjector) issueRandomValueTransfers() { if err != nil { panic(err) } - ti.logger.Info("Transfer transaction injected into L2.", log.TxKey, signedTx.Hash(), "fromAddress", fromWallet.Address(), "toAddress", toWallet.Address()) + ti.logger.Info("Native value transfer transaction injected into L2.", log.TxKey, signedTx.Hash(), "fromAddress", fromWallet.Address(), "toAddress", toWallet.Address()) - ti.stats.Transfer() + ti.stats.NativeTransfer() err = obscuroClient.SendTransaction(ti.ctx, signedTx) if err != nil { @@ -219,7 +219,7 @@ func (ti *TransactionInjector) issueRandomTransfers() { for len(ti.wallets.SimObsWallets) > 1 && fromWallet.Address().Hex() == toWallet.Address().Hex() { toWallet = ti.rndObsWallet() } - tx := ti.newObscuroTransferTx(fromWallet, toWallet.Address(), testcommon.RndBtw(1, 500)) + tx := ti.newObscuroTransferTx(fromWallet, toWallet.Address(), testcommon.RndBtw(1, 500), testcommon.HOC) tx = obscuroClient.EstimateGasAndGasPrice(tx) signedTx, err := fromWallet.SignTransaction(tx) if err != nil { @@ -287,24 +287,22 @@ func (ti *TransactionInjector) bridgeRandomGasTransfers() { func (ti *TransactionInjector) issueRandomDeposits() { // todo (@stefan) - this implementation transfers from the hoc and poc owner contracts // a better implementation should use the bridge - fromWalletHoc := ti.wallets.Tokens[testcommon.HOC].L2Owner - fromWalletPoc := ti.wallets.Tokens[testcommon.POC].L2Owner - for txCounter := 0; ti.shouldKeepIssuing(txCounter); txCounter++ { - fromWallet := fromWalletHoc + fromWalletToken := testcommon.HOC if txCounter%2 == 0 { - fromWallet = fromWalletPoc + fromWalletToken = testcommon.POC } + fromWallet := ti.wallets.Tokens[fromWalletToken].L2Owner toWallet := ti.rndObsWallet() obscuroClient := ti.rpcHandles.ObscuroWalletRndClient(fromWallet) v := testcommon.RndBtw(500, 2000) - tx := ti.newObscuroTransferTx(fromWallet, toWallet.Address(), v) - tx = obscuroClient.EstimateGasAndGasPrice(tx) + txData := ti.newObscuroTransferTx(fromWallet, toWallet.Address(), v, fromWalletToken) + tx := obscuroClient.EstimateGasAndGasPrice(txData) signedTx, err := fromWallet.SignTransaction(tx) if err != nil { panic(err) } - ti.logger.Info("Deposit transaction injected into L2.", log.TxKey, signedTx.Hash(), "fromAddress", fromWallet.Address(), "toAddress", toWallet.Address()) + ti.logger.Info("Deposit transaction injected into L2.", log.TxKey, signedTx.Hash(), "fromAddress", fromWallet.Address(), "toAddress", toWallet.Address()) ti.stats.Deposit(big.NewInt(int64(v))) @@ -337,8 +335,9 @@ func (ti *TransactionInjector) issueInvalidL2Txs() { for len(ti.wallets.SimObsWallets) > 1 && fromWallet.Address().Hex() == toWallet.Address().Hex() { toWallet = ti.rndObsWallet() } - tx := ti.newCustomObscuroWithdrawalTx(testcommon.RndBtw(1, 100)) + txData := ti.newCustomObscuroWithdrawalTx(testcommon.RndBtw(1, 100)) + tx := ti.rpcHandles.ObscuroWalletRndClient(fromWallet).EstimateGasAndGasPrice(txData) signedTx := ti.createInvalidSignage(tx, fromWallet) err := ti.rpcHandles.ObscuroWalletRndClient(fromWallet).SendTransaction(ti.ctx, signedTx) @@ -368,24 +367,24 @@ func (ti *TransactionInjector) rndObsWallet() wallet.Wallet { return ti.wallets.SimObsWallets[rand.Intn(len(ti.wallets.SimObsWallets))] //nolint:gosec } -func (ti *TransactionInjector) newObscuroTransferTx(from wallet.Wallet, dest gethcommon.Address, amount uint64) types.TxData { +func (ti *TransactionInjector) newObscuroTransferTx(from wallet.Wallet, dest gethcommon.Address, amount uint64, ercType testcommon.ERC20) types.TxData { data := erc20contractlib.CreateTransferTxData(dest, common.ValueInWei(big.NewInt(int64(amount)))) - return ti.newTx(data, from.GetNonceAndIncrement()) + return ti.newTx(data, from.GetNonceAndIncrement(), ercType) } func (ti *TransactionInjector) newCustomObscuroWithdrawalTx(amount uint64) types.TxData { transferERC20data := erc20contractlib.CreateTransferTxData(testcommon.BridgeAddress, common.ValueInWei(big.NewInt(int64(amount)))) - return ti.newTx(transferERC20data, 1) + return ti.newTx(transferERC20data, 1, testcommon.HOC) } -func (ti *TransactionInjector) newTx(data []byte, nonce uint64) types.TxData { +func (ti *TransactionInjector) newTx(data []byte, nonce uint64, ercType testcommon.ERC20) types.TxData { return &types.LegacyTx{ Nonce: nonce, Value: gethcommon.Big0, - Gas: uint64(1_000_000), + Gas: uint64(1_000_000_000), GasPrice: gethcommon.Big1, Data: data, - To: ti.wallets.Tokens[testcommon.HOC].L2ContractAddress, + To: ti.wallets.Tokens[ercType].L2ContractAddress, } } diff --git a/integration/simulation/transaction_injector_tracker.go b/integration/simulation/transaction_injector_tracker.go index b0fe77dab5..6e189ef84b 100644 --- a/integration/simulation/transaction_injector_tracker.go +++ b/integration/simulation/transaction_injector_tracker.go @@ -28,12 +28,13 @@ type GasBridgingRecord struct { func newCounter() *txInjectorTracker { return &txInjectorTracker{ - l1TransactionsLock: sync.RWMutex{}, - L1Transactions: []ethadapter.L1Transaction{}, - l2TransactionsLock: sync.RWMutex{}, - TransferL2Transactions: []*common.L2Tx{}, - WithdrawalL2Transactions: []*common.L2Tx{}, - GasBridgeTransactions: []GasBridgingRecord{}, + l1TransactionsLock: sync.RWMutex{}, + L1Transactions: []ethadapter.L1Transaction{}, + l2TransactionsLock: sync.RWMutex{}, + TransferL2Transactions: []*common.L2Tx{}, + WithdrawalL2Transactions: []*common.L2Tx{}, + NativeValueTransferL2Transactions: []*common.L2Tx{}, + GasBridgeTransactions: []GasBridgingRecord{}, } } @@ -62,7 +63,7 @@ func (m *txInjectorTracker) trackTransferL2Tx(tx *common.L2Tx) { func (m *txInjectorTracker) trackNativeValueTransferL2Tx(tx *common.L2Tx) { m.l2TransactionsLock.Lock() defer m.l2TransactionsLock.Unlock() - m.NativeValueTransferL2Transactions = append(m.TransferL2Transactions, tx) + m.NativeValueTransferL2Transactions = append(m.NativeValueTransferL2Transactions, tx) } // GetL1Transactions returns all generated L1 L2Txs diff --git a/integration/simulation/validate_chain.go b/integration/simulation/validate_chain.go index 30bbcf5425..fb2c320eee 100644 --- a/integration/simulation/validate_chain.go +++ b/integration/simulation/validate_chain.go @@ -474,6 +474,7 @@ func getLoggedWithdrawals(minObscuroHeight uint64, obscuroClient *obsclient.ObsC // FindNotIncludedL2Txs returns the number of transfers and withdrawals that were injected but are not present in the L2 blockchain. func FindNotIncludedL2Txs(ctx context.Context, nodeIdx int, rpcHandles *network.RPCHandles, txInjector *TransactionInjector) (int, int, int) { transfers, withdrawals, nativeTransfers := txInjector.TxTracker.GetL2Transactions() + notFoundTransfers := 0 for _, tx := range transfers { sender := getSender(tx) diff --git a/tools/faucet/faucet/faucet.go b/tools/faucet/faucet/faucet.go index 2da6f4205a..981fd4dd66 100644 --- a/tools/faucet/faucet/faucet.go +++ b/tools/faucet/faucet/faucet.go @@ -117,18 +117,16 @@ func (f *Faucet) fundNativeToken(address *common.Address, amount *big.Int) (*typ // this isn't great as the tx count might be incremented in between calls // but only after removing the pk from other apps can we use a proper counter - // todo remove hardcoded gas values - gas := uint64(21000) - tx := &types.LegacyTx{ Nonce: nonce, GasPrice: big.NewInt(225), - Gas: gas, To: address, Value: amount, } - signedTx, err := f.wallet.SignTransaction(tx) + estimatedTx := f.client.EstimateGasAndGasPrice(tx) + + signedTx, err := f.wallet.SignTransaction(estimatedTx) if err != nil { return nil, err } diff --git a/tools/hardhatdeployer/contract_deployer.go b/tools/hardhatdeployer/contract_deployer.go index bc5c9b921b..cbb56c78d3 100644 --- a/tools/hardhatdeployer/contract_deployer.go +++ b/tools/hardhatdeployer/contract_deployer.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ten-protocol/go-ten/go/common/constants" "github.com/ten-protocol/go-ten/go/common/retry" "github.com/ten-protocol/go-ten/go/wallet" @@ -91,8 +92,8 @@ func (cd *contractDeployer) run() (string, error) { deployContractTx := types.LegacyTx{ Nonce: cd.wallet.GetNonceAndIncrement(), - GasPrice: big.NewInt(1), - Gas: uint64(5_000_000), + GasPrice: big.NewInt(params.InitialBaseFee), + Gas: uint64(500_000_000), Data: cd.contractCode, } diff --git a/tools/hardhatdeployer/obscuro_deployer.go b/tools/hardhatdeployer/obscuro_deployer.go index a3e4153e75..c3ab1b9cea 100644 --- a/tools/hardhatdeployer/obscuro_deployer.go +++ b/tools/hardhatdeployer/obscuro_deployer.go @@ -60,13 +60,14 @@ func fundDeployerWithFaucet(cfg *Config, client *obsclient.AuthObsClient, logger } destAddr := client.Address() - tx := &types.LegacyTx{ + txData := &types.LegacyTx{ Nonce: nonce, Value: big.NewInt(Prealloc), Gas: uint64(1_000_000), GasPrice: gethcommon.Big1, To: &destAddr, } + tx := faucetClient.EstimateGasAndGasPrice(txData) signedTx, err := faucetWallet.SignTransaction(tx) if err != nil { return fmt.Errorf("failed to sign faucet transaction: %w", err) diff --git a/tools/walletextension/README.md b/tools/walletextension/README.md index b1e568f86b..6afce4c211 100644 --- a/tools/walletextension/README.md +++ b/tools/walletextension/README.md @@ -12,7 +12,7 @@ following commands from the `tools/walletextension/main` folder: ``` env GOOS=darwin GOARCH=amd64 go build -o ../bin/wallet_extension_macos_amd64 . -env GOOS=darwin GOARCH=arm64 go build -o ../bin/wallet_extension_macos_arm64 . + env GOOS=darwin GOARCH=arm64 go build -o ../bin/wallet_extension_macos_arm64 . env GOOS=windows GOARCH=amd64 go build -o ../bin/wallet_extension_win_amd64.exe . env GOOS=linux GOARCH=amd64 go build -o ../bin/wallet_extension_linux_amd64 . ```