Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions client/accounts/abi/bind/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,12 @@ type ContractTransactor interface {
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
// PendingNonceAt retrieves the current pending nonce associated with an account.
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction.
SuggestGasPrice(ctx context.Context) (*big.Int, error)
// EstimateGas tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as other
// transactions may be added or removed by miners, but it should provide a basis
// for setting a reasonable default.
EstimateGas(ctx context.Context, call kcoin.CallMsg) (gas uint64, err error)
// EstimateComputationalEffort tries to estimate the required computational
// effort to execute a specific transaction based on the current pending state of
// the backend blockchain. There is no guarantee that this is the true required
// effort as other transactions may be added or removed by miners, but it
// should provide a basis for setting a reasonable default.
EstimateComputationalEffort(ctx context.Context, call kcoin.CallMsg) (effort uint64, err error)
// SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(ctx context.Context, tx *types.Transaction) error
}
Expand Down
62 changes: 26 additions & 36 deletions client/accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import (
"github.com/kowala-tech/kcoin/client/event"
"github.com/kowala-tech/kcoin/client/kcoindb"
"github.com/kowala-tech/kcoin/client/knode/filters"

"github.com/kowala-tech/kcoin/client/log"
"github.com/kowala-tech/kcoin/client/params"
"github.com/kowala-tech/kcoin/client/params/effort"
"github.com/kowala-tech/kcoin/client/rpc"
"github.com/kowala-tech/kcoin/client/log"
)

var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
var errCompEffortEstimationFailed = errors.New("required computational effort exceeds allowance or always failing transaction")

// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
Expand Down Expand Up @@ -189,49 +189,43 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
}

// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doens't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}

// EstimateGas executes the requested code against the currently pending block/state and
// returns the used amount of gas.
func (b *SimulatedBackend) EstimateGas(ctx context.Context, call kowala.CallMsg) (uint64, error) {
// EstimateComputationalEffort executes the requested code against the currently pending block/state and
// returns the used amount of computational resources in compute units.
func (b *SimulatedBackend) EstimateComputationalEffort(ctx context.Context, call kowala.CallMsg) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()

// Determine the lowest and highest possible gas limits to binary search in between
// Determine the lowest and highest possible effort limits to binary search in between
var (
lo uint64 = params.TxGas - 1
lo uint64 = effort.Tx - 1
hi uint64
cap uint64
)
if call.Gas >= params.TxGas {
hi = call.Gas
if call.ComputeLimit >= effort.Tx {
hi = call.ComputeLimit
} else {
hi = b.pendingBlock.GasLimit()
hi = params.ComputeCapacity
}
cap = hi

// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64) bool {
call.Gas = gas
// Create a helper to check if a computational resources allowance results in an executable transaction
executable := func(computeLimit uint64) bool {
call.ComputeLimit = computeLimit

snapshot := b.pendingState.Snapshot()
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
b.pendingState.RevertToSnapshot(snapshot)

if err != nil || failed {
if err != nil {
log.Error("can't estimate gas limit", "err", err)
log.Error("can't estimate computational effort", "err", err)
}

return false
}
return true
}
// Execute the binary search and hone in on an executable gas limit
// Execute the binary search and hone in on an executable compute limit
for lo+1 < hi {
mid := (hi + lo) / 2
if !executable(mid) {
Expand All @@ -243,7 +237,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call kowala.CallMsg)
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
if !executable(hi) {
return 0, errGasEstimationFailed
return 0, errCompEffortEstimationFailed
}
}
return hi, nil
Expand All @@ -253,11 +247,8 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call kowala.CallMsg)
// state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call kowala.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
// Ensure message is initialized properly.
if call.GasPrice == nil {
call.GasPrice = big.NewInt(1)
}
if call.Gas == 0 {
call.Gas = 50000000
if call.ComputeLimit == 0 {
call.ComputeLimit = 50000000
}
if call.Value == nil {
call.Value = new(big.Int)
Expand All @@ -268,13 +259,13 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call kowala.CallMsg
// Execute the call.
msg := callmsg{call}

evmContext := core.NewEVMContext(msg, block.Header(), b.BlockChain, nil)
vmContext := core.NewVMContext(msg, block.Header(), b.BlockChain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
vmenv := vm.New(vmContext, statedb, b.config, vm.Config{})
crpool := new(core.ComputationalResourcePool).AddResource(math.MaxUint64)

return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return core.NewStateTransition(vmenv, msg, crpool).TransitionDb()
}

// SendTransaction updates the pending block to include the given transaction.
Expand Down Expand Up @@ -394,8 +385,7 @@ func (m callmsg) From() common.Address { return m.CallMsg.From }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) ComputeLimit() uint64 { return m.CallMsg.ComputeLimit }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }

Expand All @@ -406,8 +396,8 @@ type filterBackend struct {
bc *core.BlockChain
}

func (fb *filterBackend) ChainDb() kcoindb.Database { return fb.db }
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
func (fb *filterBackend) ChainDb() kcoindb.Database { return fb.db }
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }

func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
if block == rpc.LatestBlockNumber {
Expand Down
28 changes: 10 additions & 18 deletions client/accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ type TransactOpts struct {
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
Signer SignerFn // Method to use for signing the transaction (mandatory)

Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
ComputeLimit uint64 // Compute limit to set for the transaction execution (0 = estimate)

Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
}
Expand Down Expand Up @@ -183,17 +182,10 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
} else {
nonce = opts.Nonce.Uint64()
}
// Figure out the gas allowance and gas price values
gasPrice := opts.GasPrice
if gasPrice == nil {
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
}
}
gasLimit := opts.GasLimit
if gasLimit == 0 {
// Gas estimation cannot succeed without code for method invocations

computeLimit := opts.ComputeLimit
if computeLimit == 0 {
// Computational effort estimation cannot succeed without code for method invocations
if contract != nil {
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
return nil, err
Expand All @@ -203,17 +195,17 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
}
// If the contract surely has code (or code is not needed), estimate the transaction
msg := kcoin.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
computeLimit, err = c.transactor.EstimateComputationalEffort(ensureContext(opts.Context), msg)
if err != nil {
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
return nil, fmt.Errorf("failed to estimate required computational effort: %v", err)
}
}
// Create the transaction, sign it and schedule it for execution
var rawTx *types.Transaction
if contract == nil {
rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
rawTx = types.NewContractCreation(nonce, value, computeLimit, input)
} else {
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
rawTx = types.NewTransaction(nonce, c.address, value, computeLimit, input)
}
if opts.Signer == nil {
return nil, errors.New("no signer to authorize the transaction with")
Expand Down
Loading