diff --git a/.circleci/config.yml b/.circleci/config.yml index 0389b17567e0..98ce708deafa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -55,7 +55,7 @@ jobs: mkdir -p ~/.ssh/ echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config export CELO_MONOREPO_DIR="./celo-monorepo" - git clone --depth 1 https://${GH_AUTH_USERNAME}:${GH_AUTH_TOKEN}@github.com/celo-org/celo-monorepo.git ${CELO_MONOREPO_DIR} -b master + git clone --depth 1 https://${GH_AUTH_USERNAME}:${GH_AUTH_TOKEN}@github.com/celo-org/celo-monorepo.git ${CELO_MONOREPO_DIR} -b nitya/stability-fee # Change these paths to use https login since the SSH key does not have access to these repositories. # Once we open source this code, these modifications can be eliminated. # These environment variables are configured at https://circleci.com/gh/celo-org/geth/edit#env-vars diff --git a/core/state_transition.go b/core/state_transition.go index 3216debe373c..d19f6f74a123 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -127,7 +127,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool, gasCurrency *co // In this case, however, the user always ends up paying maxGasForDebitAndCreditTransactions // keeping it consistent. if gasCurrency != nil { - gas += 3*params.MaxGasForDebitAndCreditTransactions + params.MaxGasToReadErc20Balance + gas += params.AdditionalGasForNonGoldCurrencies } return gas, nil @@ -207,22 +207,41 @@ func (st *StateTransition) canBuyGas(accountOwner common.Address, gasNeeded *big return balanceOf.Cmp(gasNeeded) > 0 } -func (st *StateTransition) debitOrCreditErc20Balance(functionSelector []byte, address common.Address, amount *big.Int, gasCurrency *common.Address) error { +func (st *StateTransition) debitFrom(address common.Address, amount *big.Int, gasCurrency *common.Address) error { if amount.Cmp(big.NewInt(0)) == 0 { return nil } evm := st.evm + // Function is "debitFrom(address from, uint256 value)" + // selector is first 4 bytes of keccak256 of "debitFrom(address,uint256)" + // Source: + // pip3 install pyethereum + // python3 -c 'from ethereum.utils import sha3; print(sha3("debitFrom(address,uint256)")[0:4].hex())' + functionSelector := hexutil.MustDecode("0x362a5f80") transactionData := common.GetEncodedAbi(functionSelector, [][]byte{common.AddressToAbi(address), common.AmountToAbi(amount)}) rootCaller := vm.AccountRef(common.HexToAddress("0x0")) // The caller was already charged for the cost of this operation via IntrinsicGas. - ret, leftoverGas, err := evm.Call(rootCaller, *gasCurrency, transactionData, params.MaxGasForDebitAndCreditTransactions, big.NewInt(0)) - if err != nil { - log.Error("failed to debit or credit ERC20 balance", "functionSelector", functionSelector, "ret", hexutil.Encode(ret), "leftoverGas", leftoverGas, "err", err) - return err - } + _, _, err := evm.Call(rootCaller, *gasCurrency, transactionData, params.MaxGasForDebitFromTransactions, big.NewInt(0)) + return err +} - return nil +func (st *StateTransition) creditTo(address common.Address, amount *big.Int, gasCurrency *common.Address) error { + if amount.Cmp(big.NewInt(0)) == 0 { + return nil + } + evm := st.evm + // Function is "creditTo(address from, uint256 value)" + // selector is first 4 bytes of keccak256 of "creditTo(address,uint256)" + // Source: + // pip3 install pyethereum + // python3 -c 'from ethereum.utils import sha3; print(sha3("creditTo(address,uint256)")[0:4].hex())' + functionSelector := hexutil.MustDecode("0x9951b90c") + transactionData := common.GetEncodedAbi(functionSelector, [][]byte{common.AddressToAbi(address), common.AmountToAbi(amount)}) + rootCaller := vm.AccountRef(common.HexToAddress("0x0")) + // The caller was already charged for the cost of this operation via IntrinsicGas. + _, _, err := evm.Call(rootCaller, *gasCurrency, transactionData, params.MaxGasForCreditToTransactions, big.NewInt(0)) + return err } func (st *StateTransition) debitGas(from common.Address, amount *big.Int, gasCurrency *common.Address) (err error) { @@ -231,9 +250,9 @@ func (st *StateTransition) debitGas(from common.Address, amount *big.Int, gasCur if gasCurrency == nil { st.state.SubBalance(from, amount) return nil + } else { + return st.debitFrom(from, amount, gasCurrency) } - - return st.debitOrCreditErc20Balance(getDebitFromFunctionSelector(), from, amount, gasCurrency) } func (st *StateTransition) creditGas(to common.Address, amount *big.Int, gasCurrency *common.Address) (err error) { @@ -242,27 +261,9 @@ func (st *StateTransition) creditGas(to common.Address, amount *big.Int, gasCurr if gasCurrency == nil { st.state.AddBalance(to, amount) return nil + } else { + return st.creditTo(to, amount, gasCurrency) } - - return st.debitOrCreditErc20Balance(getCreditToFunctionSelector(), to, amount, gasCurrency) -} - -func getDebitFromFunctionSelector() []byte { - // Function is "debitFrom(address from, uint256 value)" - // selector is first 4 bytes of keccak256 of "debitFrom(address,uint256)" - // Source: - // pip3 install pyethereum - // python3 -c 'from ethereum.utils import sha3; print(sha3("debitFrom(address,uint256)")[0:4].hex())' - return hexutil.MustDecode("0x362a5f80") -} - -func getCreditToFunctionSelector() []byte { - // Function is "creditTo(address from, uint256 value)" - // selector is first 4 bytes of keccak256 of "creditTo(address,uint256)" - // Source: - // pip3 install pyethereum - // python3 -c 'from ethereum.utils import sha3; print(sha3("creditTo(address,uint256)")[0:4].hex())' - return hexutil.MustDecode("0x9951b90c") } func (st *StateTransition) preCheck() error { diff --git a/core/tx_pool.go b/core/tx_pool.go index d6d5a8dc7c0e..d40cfc82c7a5 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -649,7 +649,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { "txn cost", tx.Cost().String()) return ErrInsufficientFunds } else if tx.GasCurrency() != nil { - gasCurrencyBalance, _, err := GetBalanceOf(from, *tx.GasCurrency(), pool.iEvmH, nil, 10*1000) + gasCurrencyBalance, _, err := GetBalanceOf(from, *tx.GasCurrency(), pool.iEvmH, nil, params.MaxGasToReadErc20Balance) if err != nil { log.Debug("validateTx error in getting gas currency balance", "gasCurrency", tx.GasCurrency(), "error", err) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 767ab9afb21b..c7ff2451eae3 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1226,7 +1226,7 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { } else { // When paying for gas in a currency other than Celo Gold, the intrinsic gas use is greater than when paying for gas in Celo Gold. // We need to cover the gas use of one 'balanceOf', one 'debitFrom', and two 'creditTo' calls. - *(*uint64)(args.Gas) = defaultGas + 3*params.MaxGasForDebitAndCreditTransactions + params.MaxGasToReadErc20Balance + *(*uint64)(args.Gas) = defaultGas + params.AdditionalGasForNonGoldCurrencies } } if args.GasPrice == nil { diff --git a/params/protocol_params.go b/params/protocol_params.go index 4e0a19df9b69..c671283b7a1b 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -124,6 +124,9 @@ const ( // TODO(asa): Make these operations less expensive by charging only what is used. // The problem is we don't know how much to refund until the refund is complete. // If these values are changed, "setDefaults" will need updating. - MaxGasForDebitAndCreditTransactions uint64 = 30 * 1000 - MaxGasToReadErc20Balance uint64 = 3 * 1000 + MaxGasForDebitFromTransactions uint64 = 50 * 1000 + ExpectedGasForDebitFromTransactions uint64 = 35 * 1000 + MaxGasForCreditToTransactions uint64 = 30 * 1000 + MaxGasToReadErc20Balance uint64 = 10 * 1000 + AdditionalGasForNonGoldCurrencies uint64 = 2*MaxGasForCreditToTransactions + ExpectedGasForDebitFromTransactions + MaxGasToReadErc20Balance )