perf(core): use uint256 in state #28598 (#1977)

This commit is contained in:
Daniel Liu 2026-02-05 16:29:23 +08:00 committed by GitHub
parent 1655f801d1
commit d74c23cca1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 57 additions and 71 deletions

View file

@ -25,6 +25,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/holiman/uint256"
)
// ChainContext supports retrieving headers and consensus parameters from the
@ -119,12 +120,13 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
// This does not take the necessary gas in to account to make the transfer valid.
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
return db.GetBalance(addr).Cmp(amount) >= 0
func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
return amount.CmpBig(db.GetBalance(addr)) <= 0
}
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
amtBig := amount.ToBig()
db.SubBalance(sender, amtBig, tracing.BalanceChangeTransfer)
db.AddBalance(recipient, amtBig, tracing.BalanceChangeTransfer)
}

View file

@ -404,7 +404,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
if overflow {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value.ToBig()) {
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
@ -423,7 +423,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
)
if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, msg.Value)
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1)
@ -446,7 +446,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
}
// Execute the transaction's call.
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value)
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
}
if !rules.IsEIP1559 {

View file

@ -32,7 +32,7 @@ type OpContext interface {
StackData() []uint256.Int
Caller() common.Address
Address() common.Address
CallValue() *big.Int
CallValue() *uint256.Int
CallInput() []byte
}

View file

@ -17,8 +17,6 @@
package vm
import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/holiman/uint256"
@ -42,7 +40,8 @@ type AccountRef common.Address
func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }
// Contract represents an ethereum contract in the state database. It contains
// the contract code, calling arguments. Contract implements ContractRef
// the contract code, calling arguments. Contract stores the caller and
// contract address directly.
type Contract struct {
// CallerAddress is the result of the caller which initialised this
// contract. However when the "call method" is delegated this value
@ -60,11 +59,11 @@ type Contract struct {
Input []byte
Gas uint64
value *big.Int
value *uint256.Int
}
// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok {
@ -188,7 +187,7 @@ func (c *Contract) Address() common.Address {
}
// Value returns the contract's value (sent to it from it's caller)
func (c *Contract) Value() *big.Int {
func (c *Contract) Value() *uint256.Int {
return c.value
}

View file

@ -34,9 +34,9 @@ import (
type (
// CanTransferFunc is the signature of a transfer guard function
CanTransferFunc func(StateDB, common.Address, *big.Int) bool
CanTransferFunc func(StateDB, common.Address, *uint256.Int) bool
// TransferFunc is the signature of a transfer function
TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int)
// GetHashFunc returns the n'th block hash in the blockchain
// and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash
@ -218,10 +218,10 @@ func (evm *EVM) Cancelled() bool {
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALL, caller.Address(), addr, input, gas, value)
evm.captureBegin(evm.depth, CALL, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
}(gas)
@ -290,10 +290,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
//
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALLCODE, caller.Address(), addr, input, gas, value)
evm.captureBegin(evm.depth, CALLCODE, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
}(gas)
@ -348,7 +348,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// that caller is something other than a Contract.
parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call
evm.captureBegin(evm.depth, DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
evm.captureBegin(evm.depth, DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
}(gas)
@ -422,7 +422,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas)
contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy))
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
@ -457,9 +457,9 @@ func (c *codeAndHash) Hash() common.Hash {
}
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value)
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
}(gas)
@ -551,7 +551,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
}
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
@ -560,7 +560,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
//
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)

View file

@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
)
func TestMemoryGasCost(t *testing.T) {
@ -92,12 +93,12 @@ func TestEIP2200(t *testing.T) {
statedb.Finalise(true) // Push the state into the "original" slot
vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
if !errors.Is(err, tt.failure) {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
}
@ -141,8 +142,8 @@ func TestCreateGas(t *testing.T) {
statedb.SetCode(address, hexutil.MustDecode(tt.code))
statedb.Finalise(true)
vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
BlockNumber: big.NewInt(0),
}
config := Config{}
@ -152,7 +153,7 @@ func TestCreateGas(t *testing.T) {
vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, config)
var startGas = uint64(testGas)
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int))
if err != nil {
return false
}

View file

@ -267,8 +267,7 @@ func opCaller(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
func opCallValue(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(scope.Contract.value)
scope.Stack.push(v)
scope.Stack.push(scope.Contract.value)
return nil, nil
}
@ -678,12 +677,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
// TODO(daniel): use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() {
bigVal = value.ToBig()
}
res, addr, returnGas, suberr := evm.Create(scope.Contract, input, gas, bigVal)
res, addr, returnGas, suberr := evm.Create(scope.Contract, input, gas, &value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
@ -723,13 +717,8 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
// reuse size int for stackvalue
stackvalue := size
// TODO(daniel): use uint256.Int instead of converting with toBig()
bigEndowment := big0
if !endowment.IsZero() {
bigEndowment = endowment.ToBig()
}
res, addr, returnGas, suberr := evm.Create2(scope.Contract, input, gas,
bigEndowment, &salt)
&endowment, &salt)
// Push item on the stack based on the returned error.
if suberr != nil {
stackvalue.Clear()
@ -763,15 +752,10 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.readOnly && !value.IsZero() {
return nil, ErrWriteProtection
}
var bigVal = big0
// TODO(daniel): use uint256.Int instead of converting with toBig()
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
// but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() {
gas += params.CallStipend
bigVal = value.ToBig()
}
ret, returnGas, err := evm.Call(scope.Contract, toAddr, args, gas, bigVal)
ret, returnGas, err := evm.Call(scope.Contract, toAddr, args, gas, &value)
if err != nil {
temp.Clear()
@ -801,14 +785,11 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// Get arguments from the memory.
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
// TODO(daniel): use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() {
gas += params.CallStipend
bigVal = value.ToBig()
}
ret, returnGas, err := evm.CallCode(scope.Contract, toAddr, args, gas, bigVal)
ret, returnGas, err := evm.CallCode(scope.Contract, toAddr, args, gas, &value)
if err != nil {
temp.Clear()
} else {

View file

@ -584,7 +584,7 @@ func TestOpTstore(t *testing.T) {
caller = common.Address{}
to = common.Address{1}
contractRef = contractRef{caller}
contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0)
contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
scopeContext = ScopeContext{mem, stack, contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
)

View file

@ -18,7 +18,6 @@ package vm
import (
"fmt"
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math"
@ -71,7 +70,7 @@ func (ctx *ScopeContext) Address() common.Address {
}
// CallValue returns the value supplied with this call.
func (ctx *ScopeContext) CallValue() *big.Int {
func (ctx *ScopeContext) CallValue() *uint256.Int {
return ctx.Contract.Value()
}

View file

@ -27,6 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
)
// Config is a basic type specifying certain configuration flags for running
@ -136,7 +137,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
common.BytesToAddress([]byte("contract")),
input,
cfg.GasLimit,
cfg.Value,
uint256.MustFromBig(cfg.Value),
)
return ret, cfg.State, err
}
@ -170,7 +171,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
sender,
input,
cfg.GasLimit,
cfg.Value,
uint256.MustFromBig(cfg.Value),
)
return code, address, leftOverGas, err
}
@ -203,7 +204,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
address,
input,
cfg.GasLimit,
cfg.Value,
uint256.MustFromBig(cfg.Value),
)
return ret, leftOverGas, err
}

View file

@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
)
type account struct{}
@ -66,7 +67,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
evm = vm.NewEVM(vmctx.ctx, vmctx.txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer.Hooks})
gasLimit uint64 = 31000
startGas uint64 = 10000
value = big.NewInt(0)
value = new(uint256.Int)
contract = vm.NewContract(account{}, account{}, value, startGas)
)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@ -75,7 +76,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
}
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller())
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value)
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := evm.Run(contract, []byte{}, false)
tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
// Rest gas assumes no refund
@ -188,7 +189,7 @@ func TestHaltBetweenSteps(t *testing.T) {
t.Fatal(err)
}
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
Contract: vm.NewContract(&account{}, &account{}, new(uint256.Int), 0),
}
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, nil, chainConfig, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
@ -301,7 +302,7 @@ func TestEnterExit(t *testing.T) {
t.Fatal(err)
}
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
Contract: vm.NewContract(&account{}, &account{}, new(uint256.Int), 0),
}
tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.OnExit(1, []byte{}, 400, nil, false)

View file

@ -26,6 +26,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
)
type dummyContractRef struct {
@ -58,7 +59,7 @@ func TestStoreCapture(t *testing.T) {
var (
logger = NewStructLogger(nil)
evm = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()})
contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000)
contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 100000)
)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash

View file

@ -33,6 +33,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
)
// VMTest checks EVM execution without block or transaction context.
@ -120,19 +121,19 @@ func (t *VMTest) Run(vmconfig vm.Config) error {
func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) {
evm := t.newEVM(statedb, vmconfig)
e := t.json.Exec
return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, e.Value)
return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, uint256.MustFromBig(e.Value))
}
func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM {
initialCall := true
canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool {
canTransfer := func(db vm.StateDB, address common.Address, amount *uint256.Int) bool {
if initialCall {
initialCall = false
return true
}
return core.CanTransfer(db, address, amount)
}
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {}
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {}
txContext := vm.TxContext{
Origin: t.json.Exec.Origin,
GasPrice: t.json.Exec.GasPrice,