mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
Add Call and EstimateGas to graphQL API
This commit is contained in:
parent
42bbab1b03
commit
c6c6a7a68f
2 changed files with 124 additions and 10 deletions
|
|
@ -23,12 +23,14 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -892,6 +894,96 @@ func (r *Resolver) SendRawTransaction(ctx context.Context, args struct{ Data Hex
|
||||||
return Bytes32{hash}, err
|
return Bytes32{hash}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CallData struct {
|
||||||
|
From *Address
|
||||||
|
To *Address
|
||||||
|
Gas *int32
|
||||||
|
GasPrice *BigNum
|
||||||
|
Value *BigNum
|
||||||
|
Data *HexBytes
|
||||||
|
BlockNumber *int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallResult struct {
|
||||||
|
data HexBytes
|
||||||
|
gasUsed int32
|
||||||
|
status int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CallResult) Data() HexBytes {
|
||||||
|
return c.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CallResult) GasUsed() int32 {
|
||||||
|
return c.gasUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CallResult) Status() int32 {
|
||||||
|
return c.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertCallData(data CallData) (ethapi.CallArgs, rpc.BlockNumber) {
|
||||||
|
callArgs := ethapi.CallArgs{}
|
||||||
|
if data.From != nil {
|
||||||
|
callArgs.From = data.From.Address
|
||||||
|
}
|
||||||
|
if data.To != nil {
|
||||||
|
addr := data.To.Address
|
||||||
|
callArgs.To = &addr
|
||||||
|
}
|
||||||
|
if data.Gas != nil {
|
||||||
|
callArgs.Gas = hexutil.Uint64(*data.Gas)
|
||||||
|
}
|
||||||
|
if data.GasPrice != nil {
|
||||||
|
callArgs.GasPrice = hexutil.Big(*data.GasPrice.Int)
|
||||||
|
}
|
||||||
|
if data.Value != nil {
|
||||||
|
callArgs.Value = hexutil.Big(*data.Value.Int)
|
||||||
|
}
|
||||||
|
if data.Data != nil {
|
||||||
|
callArgs.Data = data.Data.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
blockNumber := rpc.LatestBlockNumber
|
||||||
|
if data.BlockNumber != nil {
|
||||||
|
blockNumber = rpc.BlockNumber(*data.BlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
return callArgs, blockNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) Call(ctx context.Context, args struct{ Data CallData }) (*CallResult, error) {
|
||||||
|
be, err := getBackend(r.node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
callArgs, blockNumber := convertCallData(args.Data)
|
||||||
|
|
||||||
|
result, gas, failed, err := ethapi.DoCall(ctx, be, callArgs, blockNumber, vm.Config{}, 5*time.Second)
|
||||||
|
status := int32(1)
|
||||||
|
if failed {
|
||||||
|
status = 0
|
||||||
|
}
|
||||||
|
return &CallResult{
|
||||||
|
data: HexBytes{result},
|
||||||
|
gasUsed: int32(gas),
|
||||||
|
status: status,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) EstimateGas(ctx context.Context, args struct{ Data CallData }) (int32, error) {
|
||||||
|
be, err := getBackend(r.node)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
callArgs, blockNumber := convertCallData(args.Data)
|
||||||
|
|
||||||
|
gas, err := ethapi.DoEstimateGas(ctx, be, callArgs, blockNumber)
|
||||||
|
return int32(gas), err
|
||||||
|
}
|
||||||
|
|
||||||
func NewHandler(n *node.Node) (http.Handler, error) {
|
func NewHandler(n *node.Node) (http.Handler, error) {
|
||||||
q := Resolver{n}
|
q := Resolver{n}
|
||||||
|
|
||||||
|
|
@ -967,11 +1059,29 @@ func NewHandler(n *node.Node) (http.Handler, error) {
|
||||||
transactionAt(index: Int!): Transaction
|
transactionAt(index: Int!): Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input CallData {
|
||||||
|
from: Address
|
||||||
|
to: Address
|
||||||
|
gas: Int
|
||||||
|
gasPrice: BigNum
|
||||||
|
value: BigNum
|
||||||
|
data: HexBytes
|
||||||
|
blockNumber: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallResult {
|
||||||
|
data: HexBytes!
|
||||||
|
gasUsed: Int!
|
||||||
|
status: Int!
|
||||||
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
account(address: Address!, blockNumber: Int): Account!
|
account(address: Address!, blockNumber: Int): Account!
|
||||||
block(number: Int, hash: Bytes32): Block
|
block(number: Int, hash: Bytes32): Block
|
||||||
blocks(from: Int!, to: Int): [Block!]!
|
blocks(from: Int!, to: Int): [Block!]!
|
||||||
transaction(hash: Bytes32!): Transaction
|
transaction(hash: Bytes32!): Transaction
|
||||||
|
call(data: CallData!): CallResult
|
||||||
|
estimateGas(data: CallData!): Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
|
|
||||||
|
|
@ -617,17 +617,17 @@ type CallArgs struct {
|
||||||
Data hexutil.Bytes `json:"data"`
|
Data hexutil.Bytes `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
|
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, header, err := b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, 0, false, err
|
return nil, 0, false, err
|
||||||
}
|
}
|
||||||
// Set sender address or use a default if none specified
|
// Set sender address or use a default if none specified
|
||||||
addr := args.From
|
addr := args.From
|
||||||
if addr == (common.Address{}) {
|
if addr == (common.Address{}) {
|
||||||
if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 {
|
if wallets := b.AccountManager().Wallets(); len(wallets) > 0 {
|
||||||
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
|
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
|
||||||
addr = accounts[0].Address
|
addr = accounts[0].Address
|
||||||
}
|
}
|
||||||
|
|
@ -658,7 +658,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Get a new instance of the EVM.
|
// Get a new instance of the EVM.
|
||||||
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
|
evm, vmError, err := b.GetEVM(ctx, msg, state, header, vmCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err
|
return nil, 0, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -682,13 +682,13 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{}, 5*time.Second)
|
result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second)
|
||||||
return (hexutil.Bytes)(result), err
|
return (hexutil.Bytes)(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
// DoEstimateGas returns an estimate of the amount of gas needed to execute the
|
||||||
// given transaction against the current pending block.
|
// given transaction against the current pending block.
|
||||||
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Uint64, error) {
|
||||||
// Binary search the gas requirement, as it may be higher than the amount used
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var (
|
var (
|
||||||
lo uint64 = params.TxGas - 1
|
lo uint64 = params.TxGas - 1
|
||||||
|
|
@ -698,8 +698,8 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
||||||
if uint64(args.Gas) >= params.TxGas {
|
if uint64(args.Gas) >= params.TxGas {
|
||||||
hi = uint64(args.Gas)
|
hi = uint64(args.Gas)
|
||||||
} else {
|
} else {
|
||||||
// Retrieve the current pending block to act as the gas ceiling
|
// Retrieve the block to act as the gas ceiling
|
||||||
block, err := s.b.BlockByNumber(ctx, rpc.PendingBlockNumber)
|
block, err := b.BlockByNumber(ctx, blockNr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -711,7 +711,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) bool {
|
||||||
args.Gas = hexutil.Uint64(gas)
|
args.Gas = hexutil.Uint64(gas)
|
||||||
|
|
||||||
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}, 0)
|
_, _, failed, err := DoCall(ctx, b, args, blockNr, vm.Config{}, 0)
|
||||||
if err != nil || failed {
|
if err != nil || failed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -735,6 +735,10 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
||||||
return hexutil.Uint64(hi), nil
|
return hexutil.Uint64(hi), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
||||||
|
return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
// ExecutionResult groups all structured logs emitted by the EVM
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as transaction
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// execution status, the amount of gas used and the return value
|
// execution status, the amount of gas used and the return value
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue