Skip to content

Commit

Permalink
graphql: encode Long values as hex (ethereum#26894)
Browse files Browse the repository at this point in the history
This is a breaking GraphQL API change. All numeric values are now encoded as
hex strings. The motivation for this change is matching JSON-RPC outputs more
closely.

Numbers in query parameters are accepted as both decimal integers and hex strings.
  • Loading branch information
s1na authored Apr 25, 2023
1 parent 9a12cc9 commit 2f98dd3
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 81 deletions.
109 changes: 54 additions & 55 deletions graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"math/big"
"sort"
"strconv"
"strings"
"sync"

"github.com/ethereum/go-ethereum"
Expand Down Expand Up @@ -54,16 +55,16 @@ func (b *Long) UnmarshalGraphQL(input interface{}) error {
switch input := input.(type) {
case string:
// uncomment to support hex values
//if strings.HasPrefix(input, "0x") {
// // apply leniency and support hex representations of longs.
// value, err := hexutil.DecodeUint64(input)
// *b = Long(value)
// return err
//} else {
value, err := strconv.ParseInt(input, 10, 64)
*b = Long(value)
return err
//}
if strings.HasPrefix(input, "0x") {
// apply leniency and support hex representations of longs.
value, err := hexutil.DecodeUint64(input)
*b = Long(value)
return err
} else {
value, err := strconv.ParseInt(input, 10, 64)
*b = Long(value)
return err
}
case int32:
*b = Long(input)
case int64:
Expand Down Expand Up @@ -156,8 +157,8 @@ func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account {
}
}

func (l *Log) Index(ctx context.Context) int32 {
return int32(l.log.Index)
func (l *Log) Index(ctx context.Context) hexutil.Uint64 {
return hexutil.Uint64(l.log.Index)
}

func (l *Log) Topics(ctx context.Context) []common.Hash {
Expand Down Expand Up @@ -391,7 +392,7 @@ func (t *Transaction) Block(ctx context.Context) (*Block, error) {
return block, nil
}

func (t *Transaction) Index(ctx context.Context) (*int32, error) {
func (t *Transaction) Index(ctx context.Context) (*hexutil.Uint64, error) {
_, block, err := t.resolve(ctx)
if err != nil {
return nil, err
Expand All @@ -400,7 +401,7 @@ func (t *Transaction) Index(ctx context.Context) (*int32, error) {
if block == nil {
return nil, nil
}
index := int32(t.index)
index := hexutil.Uint64(t.index)
return &index, nil
}

Expand All @@ -421,33 +422,33 @@ func (t *Transaction) getReceipt(ctx context.Context) (*types.Receipt, error) {
return receipts[t.index], nil
}

func (t *Transaction) Status(ctx context.Context) (*Long, error) {
func (t *Transaction) Status(ctx context.Context) (*hexutil.Uint64, error) {
receipt, err := t.getReceipt(ctx)
if err != nil || receipt == nil {
return nil, err
}
if len(receipt.PostState) != 0 {
return nil, nil
}
ret := Long(receipt.Status)
ret := hexutil.Uint64(receipt.Status)
return &ret, nil
}

func (t *Transaction) GasUsed(ctx context.Context) (*Long, error) {
func (t *Transaction) GasUsed(ctx context.Context) (*hexutil.Uint64, error) {
receipt, err := t.getReceipt(ctx)
if err != nil || receipt == nil {
return nil, err
}
ret := Long(receipt.GasUsed)
ret := hexutil.Uint64(receipt.GasUsed)
return &ret, nil
}

func (t *Transaction) CumulativeGasUsed(ctx context.Context) (*Long, error) {
func (t *Transaction) CumulativeGasUsed(ctx context.Context) (*hexutil.Uint64, error) {
receipt, err := t.getReceipt(ctx)
if err != nil || receipt == nil {
return nil, err
}
ret := Long(receipt.CumulativeGasUsed)
ret := hexutil.Uint64(receipt.CumulativeGasUsed)
return &ret, nil
}

Expand Down Expand Up @@ -503,12 +504,12 @@ func (t *Transaction) getLogs(ctx context.Context, hash common.Hash) (*[]*Log, e
return &ret, nil
}

func (t *Transaction) Type(ctx context.Context) (*int32, error) {
func (t *Transaction) Type(ctx context.Context) (*hexutil.Uint64, error) {
tx, _, err := t.resolve(ctx)
if err != nil {
return nil, err
}
txType := int32(tx.Type())
txType := hexutil.Uint64(tx.Type())
return &txType, nil
}

Expand Down Expand Up @@ -649,13 +650,13 @@ func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) {
return receipts, nil
}

func (b *Block) Number(ctx context.Context) (Long, error) {
func (b *Block) Number(ctx context.Context) (hexutil.Uint64, error) {
header, err := b.resolveHeader(ctx)
if err != nil {
return 0, err
}

return Long(header.Number.Uint64()), nil
return hexutil.Uint64(header.Number.Uint64()), nil
}

func (b *Block) Hash(ctx context.Context) (common.Hash, error) {
Expand All @@ -664,20 +665,20 @@ func (b *Block) Hash(ctx context.Context) (common.Hash, error) {
return b.hash, nil
}

func (b *Block) GasLimit(ctx context.Context) (Long, error) {
func (b *Block) GasLimit(ctx context.Context) (hexutil.Uint64, error) {
header, err := b.resolveHeader(ctx)
if err != nil {
return 0, err
}
return Long(header.GasLimit), nil
return hexutil.Uint64(header.GasLimit), nil
}

func (b *Block) GasUsed(ctx context.Context) (Long, error) {
func (b *Block) GasUsed(ctx context.Context) (hexutil.Uint64, error) {
header, err := b.resolveHeader(ctx)
if err != nil {
return 0, err
}
return Long(header.GasUsed), nil
return hexutil.Uint64(header.GasUsed), nil
}

func (b *Block) BaseFeePerGas(ctx context.Context) (*hexutil.Big, error) {
Expand Down Expand Up @@ -793,12 +794,12 @@ func (b *Block) OmmerHash(ctx context.Context) (common.Hash, error) {
return header.UncleHash, nil
}

func (b *Block) OmmerCount(ctx context.Context) (*int32, error) {
func (b *Block) OmmerCount(ctx context.Context) (*hexutil.Uint64, error) {
block, err := b.resolve(ctx)
if err != nil || block == nil {
return nil, err
}
count := int32(len(block.Uncles()))
count := hexutil.Uint64(len(block.Uncles()))
return &count, err
}

Expand Down Expand Up @@ -869,7 +870,7 @@ type BlockNumberArgs struct {
// TODO: Ideally we could use input unions to allow the query to specify the
// block parameter by hash, block number, or tag but input unions aren't part of the
// standard GraphQL schema SDL yet, see: https://github.com/graphql/graphql-spec/issues/488
Block *hexutil.Uint64
Block *Long
}

// NumberOr returns the provided block number argument, or the "current" block number or hash if none
Expand Down Expand Up @@ -900,12 +901,12 @@ func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, erro
}, nil
}

func (b *Block) TransactionCount(ctx context.Context) (*int32, error) {
func (b *Block) TransactionCount(ctx context.Context) (*hexutil.Uint64, error) {
block, err := b.resolve(ctx)
if err != nil || block == nil {
return nil, err
}
count := int32(len(block.Transactions()))
count := hexutil.Uint64(len(block.Transactions()))
return &count, err
}

Expand All @@ -927,7 +928,7 @@ func (b *Block) Transactions(ctx context.Context) (*[]*Transaction, error) {
return &ret, nil
}

func (b *Block) TransactionAt(ctx context.Context, args struct{ Index int32 }) (*Transaction, error) {
func (b *Block) TransactionAt(ctx context.Context, args struct{ Index Long }) (*Transaction, error) {
block, err := b.resolve(ctx)
if err != nil || block == nil {
return nil, err
Expand All @@ -946,7 +947,7 @@ func (b *Block) TransactionAt(ctx context.Context, args struct{ Index int32 }) (
}, nil
}

func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block, error) {
func (b *Block) OmmerAt(ctx context.Context, args struct{ Index Long }) (*Block, error) {
block, err := b.resolve(ctx)
if err != nil || block == nil {
return nil, err
Expand Down Expand Up @@ -1037,7 +1038,7 @@ func (b *Block) Account(ctx context.Context, args struct {
type CallData struct {
From *common.Address // The Ethereum address the call is from.
To *common.Address // The Ethereum address the call is to.
Gas *hexutil.Uint64 // The amount of gas provided for the call.
Gas *Long // The amount of gas provided for the call.
GasPrice *hexutil.Big // The price of each unit of gas, in wei.
MaxFeePerGas *hexutil.Big // The max price of each unit of gas, in wei (1559).
MaxPriorityFeePerGas *hexutil.Big // The max tip of each unit of gas, in wei (1559).
Expand All @@ -1047,20 +1048,20 @@ type CallData struct {

// CallResult encapsulates the result of an invocation of the `call` accessor.
type CallResult struct {
data hexutil.Bytes // The return data from the call
gasUsed Long // The amount of gas used
status Long // The return status of the call - 0 for failure or 1 for success.
data hexutil.Bytes // The return data from the call
gasUsed hexutil.Uint64 // The amount of gas used
status hexutil.Uint64 // The return status of the call - 0 for failure or 1 for success.
}

func (c *CallResult) Data() hexutil.Bytes {
return c.data
}

func (c *CallResult) GasUsed() Long {
func (c *CallResult) GasUsed() hexutil.Uint64 {
return c.gasUsed
}

func (c *CallResult) Status() Long {
func (c *CallResult) Status() hexutil.Uint64 {
return c.status
}

Expand All @@ -1071,32 +1072,31 @@ func (b *Block) Call(ctx context.Context, args struct {
if err != nil {
return nil, err
}
status := Long(1)
status := hexutil.Uint64(1)
if result.Failed() {
status = 0
}

return &CallResult{
data: result.ReturnData,
gasUsed: Long(result.UsedGas),
gasUsed: hexutil.Uint64(result.UsedGas),
status: status,
}, nil
}

func (b *Block) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (Long, error) {
gas, err := ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, b.r.backend.RPCGasCap())
return Long(gas), err
}) (hexutil.Uint64, error) {
return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, b.r.backend.RPCGasCap())
}

type Pending struct {
r *Resolver
}

func (p *Pending) TransactionCount(ctx context.Context) (int32, error) {
func (p *Pending) TransactionCount(ctx context.Context) (hexutil.Uint64, error) {
txs, err := p.r.backend.GetPoolTransactions()
return int32(len(txs)), err
return hexutil.Uint64(len(txs)), err
}

func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) {
Expand Down Expand Up @@ -1135,24 +1135,23 @@ func (p *Pending) Call(ctx context.Context, args struct {
if err != nil {
return nil, err
}
status := Long(1)
status := hexutil.Uint64(1)
if result.Failed() {
status = 0
}

return &CallResult{
data: result.ReturnData,
gasUsed: Long(result.UsedGas),
gasUsed: hexutil.Uint64(result.UsedGas),
status: status,
}, nil
}

func (p *Pending) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (Long, error) {
}) (hexutil.Uint64, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
gas, err := ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, pendingBlockNr, p.r.backend.RPCGasCap())
return Long(gas), err
return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, pendingBlockNr, p.r.backend.RPCGasCap())
}

// Resolver is the top-level object in the GraphQL hierarchy.
Expand Down Expand Up @@ -1260,8 +1259,8 @@ func (r *Resolver) SendRawTransaction(ctx context.Context, args struct{ Data hex

// FilterCriteria encapsulates the arguments to `logs` on the root resolver object.
type FilterCriteria struct {
FromBlock *hexutil.Uint64 // beginning of the queried range, nil means genesis block
ToBlock *hexutil.Uint64 // end of the range, nil means latest block
FromBlock *Long // beginning of the queried range, nil means genesis block
ToBlock *Long // end of the range, nil means latest block
Addresses *[]common.Address // restricts matches to events created by specific contracts

// The Topic list restricts matches to particular event topics. Each event has a list
Expand Down
Loading

0 comments on commit 2f98dd3

Please sign in to comment.