graphql: encode Long values as hex (#26894)
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.
This commit is contained in:
parent
9a12cc99de
commit
2f98dd3838
|
@ -24,6 +24,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
|
@ -54,16 +55,16 @@ func (b *Long) UnmarshalGraphQL(input interface{}) error {
|
||||||
switch input := input.(type) {
|
switch input := input.(type) {
|
||||||
case string:
|
case string:
|
||||||
// uncomment to support hex values
|
// uncomment to support hex values
|
||||||
//if strings.HasPrefix(input, "0x") {
|
if strings.HasPrefix(input, "0x") {
|
||||||
// // apply leniency and support hex representations of longs.
|
// apply leniency and support hex representations of longs.
|
||||||
// value, err := hexutil.DecodeUint64(input)
|
value, err := hexutil.DecodeUint64(input)
|
||||||
// *b = Long(value)
|
*b = Long(value)
|
||||||
// return err
|
return err
|
||||||
//} else {
|
} else {
|
||||||
value, err := strconv.ParseInt(input, 10, 64)
|
value, err := strconv.ParseInt(input, 10, 64)
|
||||||
*b = Long(value)
|
*b = Long(value)
|
||||||
return err
|
return err
|
||||||
//}
|
}
|
||||||
case int32:
|
case int32:
|
||||||
*b = Long(input)
|
*b = Long(input)
|
||||||
case int64:
|
case int64:
|
||||||
|
@ -156,8 +157,8 @@ func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) Index(ctx context.Context) int32 {
|
func (l *Log) Index(ctx context.Context) hexutil.Uint64 {
|
||||||
return int32(l.log.Index)
|
return hexutil.Uint64(l.log.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) Topics(ctx context.Context) []common.Hash {
|
func (l *Log) Topics(ctx context.Context) []common.Hash {
|
||||||
|
@ -391,7 +392,7 @@ func (t *Transaction) Block(ctx context.Context) (*Block, error) {
|
||||||
return block, nil
|
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)
|
_, block, err := t.resolve(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -400,7 +401,7 @@ func (t *Transaction) Index(ctx context.Context) (*int32, error) {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
index := int32(t.index)
|
index := hexutil.Uint64(t.index)
|
||||||
return &index, nil
|
return &index, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +422,7 @@ func (t *Transaction) getReceipt(ctx context.Context) (*types.Receipt, error) {
|
||||||
return receipts[t.index], nil
|
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)
|
receipt, err := t.getReceipt(ctx)
|
||||||
if err != nil || receipt == nil {
|
if err != nil || receipt == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -429,25 +430,25 @@ func (t *Transaction) Status(ctx context.Context) (*Long, error) {
|
||||||
if len(receipt.PostState) != 0 {
|
if len(receipt.PostState) != 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := Long(receipt.Status)
|
ret := hexutil.Uint64(receipt.Status)
|
||||||
return &ret, nil
|
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)
|
receipt, err := t.getReceipt(ctx)
|
||||||
if err != nil || receipt == nil {
|
if err != nil || receipt == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ret := Long(receipt.GasUsed)
|
ret := hexutil.Uint64(receipt.GasUsed)
|
||||||
return &ret, nil
|
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)
|
receipt, err := t.getReceipt(ctx)
|
||||||
if err != nil || receipt == nil {
|
if err != nil || receipt == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ret := Long(receipt.CumulativeGasUsed)
|
ret := hexutil.Uint64(receipt.CumulativeGasUsed)
|
||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,12 +504,12 @@ func (t *Transaction) getLogs(ctx context.Context, hash common.Hash) (*[]*Log, e
|
||||||
return &ret, nil
|
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)
|
tx, _, err := t.resolve(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
txType := int32(tx.Type())
|
txType := hexutil.Uint64(tx.Type())
|
||||||
return &txType, nil
|
return &txType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,13 +650,13 @@ func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) {
|
||||||
return receipts, nil
|
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)
|
header, err := b.resolveHeader(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
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) {
|
func (b *Block) Hash(ctx context.Context) (common.Hash, error) {
|
||||||
|
@ -664,20 +665,20 @@ func (b *Block) Hash(ctx context.Context) (common.Hash, error) {
|
||||||
return b.hash, nil
|
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)
|
header, err := b.resolveHeader(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
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)
|
header, err := b.resolveHeader(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return Long(header.GasUsed), nil
|
return hexutil.Uint64(header.GasUsed), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) BaseFeePerGas(ctx context.Context) (*hexutil.Big, error) {
|
func (b *Block) BaseFeePerGas(ctx context.Context) (*hexutil.Big, error) {
|
||||||
|
@ -793,12 +794,12 @@ func (b *Block) OmmerHash(ctx context.Context) (common.Hash, error) {
|
||||||
return header.UncleHash, nil
|
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)
|
block, err := b.resolve(ctx)
|
||||||
if err != nil || block == nil {
|
if err != nil || block == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
count := int32(len(block.Uncles()))
|
count := hexutil.Uint64(len(block.Uncles()))
|
||||||
return &count, err
|
return &count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,7 +870,7 @@ type BlockNumberArgs struct {
|
||||||
// TODO: Ideally we could use input unions to allow the query to specify the
|
// 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
|
// 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
|
// 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
|
// NumberOr returns the provided block number argument, or the "current" block number or hash if none
|
||||||
|
@ -900,12 +901,12 @@ func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, erro
|
||||||
}, nil
|
}, 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)
|
block, err := b.resolve(ctx)
|
||||||
if err != nil || block == nil {
|
if err != nil || block == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
count := int32(len(block.Transactions()))
|
count := hexutil.Uint64(len(block.Transactions()))
|
||||||
return &count, err
|
return &count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -927,7 +928,7 @@ func (b *Block) Transactions(ctx context.Context) (*[]*Transaction, error) {
|
||||||
return &ret, nil
|
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)
|
block, err := b.resolve(ctx)
|
||||||
if err != nil || block == nil {
|
if err != nil || block == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -946,7 +947,7 @@ func (b *Block) TransactionAt(ctx context.Context, args struct{ Index int32 }) (
|
||||||
}, nil
|
}, 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)
|
block, err := b.resolve(ctx)
|
||||||
if err != nil || block == nil {
|
if err != nil || block == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1037,7 +1038,7 @@ func (b *Block) Account(ctx context.Context, args struct {
|
||||||
type CallData struct {
|
type CallData struct {
|
||||||
From *common.Address // The Ethereum address the call is from.
|
From *common.Address // The Ethereum address the call is from.
|
||||||
To *common.Address // The Ethereum address the call is to.
|
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.
|
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).
|
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).
|
MaxPriorityFeePerGas *hexutil.Big // The max tip of each unit of gas, in wei (1559).
|
||||||
|
@ -1048,19 +1049,19 @@ type CallData struct {
|
||||||
// CallResult encapsulates the result of an invocation of the `call` accessor.
|
// CallResult encapsulates the result of an invocation of the `call` accessor.
|
||||||
type CallResult struct {
|
type CallResult struct {
|
||||||
data hexutil.Bytes // The return data from the call
|
data hexutil.Bytes // The return data from the call
|
||||||
gasUsed Long // The amount of gas used
|
gasUsed hexutil.Uint64 // The amount of gas used
|
||||||
status Long // The return status of the call - 0 for failure or 1 for success.
|
status hexutil.Uint64 // The return status of the call - 0 for failure or 1 for success.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CallResult) Data() hexutil.Bytes {
|
func (c *CallResult) Data() hexutil.Bytes {
|
||||||
return c.data
|
return c.data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CallResult) GasUsed() Long {
|
func (c *CallResult) GasUsed() hexutil.Uint64 {
|
||||||
return c.gasUsed
|
return c.gasUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CallResult) Status() Long {
|
func (c *CallResult) Status() hexutil.Uint64 {
|
||||||
return c.status
|
return c.status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,32 +1072,31 @@ func (b *Block) Call(ctx context.Context, args struct {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
status := Long(1)
|
status := hexutil.Uint64(1)
|
||||||
if result.Failed() {
|
if result.Failed() {
|
||||||
status = 0
|
status = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CallResult{
|
return &CallResult{
|
||||||
data: result.ReturnData,
|
data: result.ReturnData,
|
||||||
gasUsed: Long(result.UsedGas),
|
gasUsed: hexutil.Uint64(result.UsedGas),
|
||||||
status: status,
|
status: status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (Long, error) {
|
}) (hexutil.Uint64, error) {
|
||||||
gas, err := ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, b.r.backend.RPCGasCap())
|
return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, b.r.backend.RPCGasCap())
|
||||||
return Long(gas), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pending struct {
|
type Pending struct {
|
||||||
r *Resolver
|
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()
|
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) {
|
func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) {
|
||||||
|
@ -1135,24 +1135,23 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
status := Long(1)
|
status := hexutil.Uint64(1)
|
||||||
if result.Failed() {
|
if result.Failed() {
|
||||||
status = 0
|
status = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CallResult{
|
return &CallResult{
|
||||||
data: result.ReturnData,
|
data: result.ReturnData,
|
||||||
gasUsed: Long(result.UsedGas),
|
gasUsed: hexutil.Uint64(result.UsedGas),
|
||||||
status: status,
|
status: status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (Long, error) {
|
}) (hexutil.Uint64, error) {
|
||||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
gas, err := ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, pendingBlockNr, p.r.backend.RPCGasCap())
|
return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, pendingBlockNr, p.r.backend.RPCGasCap())
|
||||||
return Long(gas), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver is the top-level object in the GraphQL hierarchy.
|
// Resolver is the top-level object in the GraphQL hierarchy.
|
||||||
|
@ -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.
|
// FilterCriteria encapsulates the arguments to `logs` on the root resolver object.
|
||||||
type FilterCriteria struct {
|
type FilterCriteria struct {
|
||||||
FromBlock *hexutil.Uint64 // beginning of the queried range, nil means genesis block
|
FromBlock *Long // beginning of the queried range, nil means genesis block
|
||||||
ToBlock *hexutil.Uint64 // end of the range, nil means latest block
|
ToBlock *Long // end of the range, nil means latest block
|
||||||
Addresses *[]common.Address // restricts matches to events created by specific contracts
|
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
|
// The Topic list restricts matches to particular event topics. Each event has a list
|
||||||
|
|
|
@ -80,17 +80,17 @@ func TestGraphQLBlockSerialization(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{ // Should return latest block
|
{ // Should return latest block
|
||||||
body: `{"query": "{block{number}}","variables": null}`,
|
body: `{"query": "{block{number}}","variables": null}`,
|
||||||
want: `{"data":{"block":{"number":10}}}`,
|
want: `{"data":{"block":{"number":"0xa"}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
{ // Should return info about latest block
|
{ // Should return info about latest block
|
||||||
body: `{"query": "{block{number,gasUsed,gasLimit}}","variables": null}`,
|
body: `{"query": "{block{number,gasUsed,gasLimit}}","variables": null}`,
|
||||||
want: `{"data":{"block":{"number":10,"gasUsed":0,"gasLimit":11500000}}}`,
|
want: `{"data":{"block":{"number":"0xa","gasUsed":"0x0","gasLimit":"0xaf79e0"}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: `{"query": "{block(number:0){number,gasUsed,gasLimit}}","variables": null}`,
|
body: `{"query": "{block(number:0){number,gasUsed,gasLimit}}","variables": null}`,
|
||||||
want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`,
|
want: `{"data":{"block":{"number":"0x0","gasUsed":"0x0","gasLimit":"0xaf79e0"}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -105,7 +105,7 @@ func TestGraphQLBlockSerialization(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: `{"query": "{block(number:\"0\"){number,gasUsed,gasLimit}}","variables": null}`,
|
body: `{"query": "{block(number:\"0\"){number,gasUsed,gasLimit}}","variables": null}`,
|
||||||
want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`,
|
want: `{"data":{"block":{"number":"0x0","gasUsed":"0x0","gasLimit":"0xaf79e0"}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -119,14 +119,10 @@ func TestGraphQLBlockSerialization(t *testing.T) {
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: `{"query": "{block(number:\"0xbad\"){number,gasUsed,gasLimit}}","variables": null}`,
|
|
||||||
want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0xbad\": invalid syntax"}],"data":{}}`,
|
|
||||||
code: 400,
|
|
||||||
},
|
|
||||||
{ // hex strings are currently not supported. If that's added to the spec, this test will need to change
|
|
||||||
body: `{"query": "{block(number:\"0x0\"){number,gasUsed,gasLimit}}","variables": null}`,
|
body: `{"query": "{block(number:\"0x0\"){number,gasUsed,gasLimit}}","variables": null}`,
|
||||||
want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0x0\": invalid syntax"}],"data":{}}`,
|
want: `{"data":{"block":{"number":"0x0","gasUsed":"0x0","gasLimit":"0xaf79e0"}}}`,
|
||||||
code: 400,
|
//want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0x0\": invalid syntax"}],"data":{}}`,
|
||||||
|
code: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: `{"query": "{block(number:\"a\"){number,gasUsed,gasLimit}}","variables": null}`,
|
body: `{"query": "{block(number:\"a\"){number,gasUsed,gasLimit}}","variables": null}`,
|
||||||
|
@ -141,13 +137,13 @@ func TestGraphQLBlockSerialization(t *testing.T) {
|
||||||
// should return `estimateGas` as decimal
|
// should return `estimateGas` as decimal
|
||||||
{
|
{
|
||||||
body: `{"query": "{block{ estimateGas(data:{}) }}"}`,
|
body: `{"query": "{block{ estimateGas(data:{}) }}"}`,
|
||||||
want: `{"data":{"block":{"estimateGas":53000}}}`,
|
want: `{"data":{"block":{"estimateGas":"0xcf08"}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
// should return `status` as decimal
|
// should return `status` as decimal
|
||||||
{
|
{
|
||||||
body: `{"query": "{block {number call (data : {from : \"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\", to: \"0x6295ee1b4f6dd65047762f924ecd367c17eabf8f\", data :\"0x12a7b914\"}){data status}}}"}`,
|
body: `{"query": "{block {number call (data : {from : \"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\", to: \"0x6295ee1b4f6dd65047762f924ecd367c17eabf8f\", data :\"0x12a7b914\"}){data status}}}"}`,
|
||||||
want: `{"data":{"block":{"number":10,"call":{"data":"0x","status":1}}}}`,
|
want: `{"data":{"block":{"number":"0xa","call":{"data":"0x","status":"0x1"}}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -231,7 +227,7 @@ func TestGraphQLBlockSerializationEIP2718(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
body: `{"query": "{block {number transactions { from { address } to { address } value hash type accessList { address storageKeys } index}}}"}`,
|
body: `{"query": "{block {number transactions { from { address } to { address } value hash type accessList { address storageKeys } index}}}"}`,
|
||||||
want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0xd864c9d7d37fade6b70164740540c06dd58bb9c3f6b46101908d6339db6a6a7b","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x19b35f8187b4e15fb59a9af469dca5dfa3cd363c11d372058c12f6482477b474","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1}]}}}`,
|
want: `{"data":{"block":{"number":"0x1","transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0xd864c9d7d37fade6b70164740540c06dd58bb9c3f6b46101908d6339db6a6a7b","type":"0x0","accessList":[],"index":"0x0"},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x19b35f8187b4e15fb59a9af469dca5dfa3cd363c11d372058c12f6482477b474","type":"0x1","accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":"0x1"}]}}}`,
|
||||||
code: 200,
|
code: 200,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -327,17 +323,17 @@ func TestGraphQLConcurrentResolvers(t *testing.T) {
|
||||||
// Multiple txes of a block race to set/retrieve receipts of a block.
|
// Multiple txes of a block race to set/retrieve receipts of a block.
|
||||||
{
|
{
|
||||||
body: "{block { transactions { status gasUsed } } }",
|
body: "{block { transactions { status gasUsed } } }",
|
||||||
want: `{"block":{"transactions":[{"status":1,"gasUsed":21768},{"status":1,"gasUsed":21768},{"status":1,"gasUsed":21768}]}}`,
|
want: `{"block":{"transactions":[{"status":"0x1","gasUsed":"0x5508"},{"status":"0x1","gasUsed":"0x5508"},{"status":"0x1","gasUsed":"0x5508"}]}}`,
|
||||||
},
|
},
|
||||||
// Multiple fields of block race to resolve header and body.
|
// Multiple fields of block race to resolve header and body.
|
||||||
{
|
{
|
||||||
body: "{ block { number hash gasLimit ommerCount transactionCount totalDifficulty } }",
|
body: "{ block { number hash gasLimit ommerCount transactionCount totalDifficulty } }",
|
||||||
want: fmt.Sprintf(`{"block":{"number":1,"hash":"%s","gasLimit":11500000,"ommerCount":0,"transactionCount":3,"totalDifficulty":"0x200000"}}`, chain[len(chain)-1].Hash()),
|
want: fmt.Sprintf(`{"block":{"number":"0x1","hash":"%s","gasLimit":"0xaf79e0","ommerCount":"0x0","transactionCount":"0x3","totalDifficulty":"0x200000"}}`, chain[len(chain)-1].Hash()),
|
||||||
},
|
},
|
||||||
// Multiple fields of a block race to resolve the header and body.
|
// Multiple fields of a block race to resolve the header and body.
|
||||||
{
|
{
|
||||||
body: fmt.Sprintf(`{ transaction(hash: "%s") { block { number hash gasLimit ommerCount transactionCount } } }`, tx.Hash()),
|
body: fmt.Sprintf(`{ transaction(hash: "%s") { block { number hash gasLimit ommerCount transactionCount } } }`, tx.Hash()),
|
||||||
want: fmt.Sprintf(`{"transaction":{"block":{"number":1,"hash":"%s","gasLimit":11500000,"ommerCount":0,"transactionCount":3}}}`, chain[len(chain)-1].Hash()),
|
want: fmt.Sprintf(`{"transaction":{"block":{"number":"0x1","hash":"%s","gasLimit":"0xaf79e0","ommerCount":"0x0","transactionCount":"0x3"}}}`, chain[len(chain)-1].Hash()),
|
||||||
},
|
},
|
||||||
// Account fields race the resolve the state object.
|
// Account fields race the resolve the state object.
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,9 @@ const schema string = `
|
||||||
# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all
|
# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all
|
||||||
# 0x-prefixed hexadecimal.
|
# 0x-prefixed hexadecimal.
|
||||||
scalar BigInt
|
scalar BigInt
|
||||||
# Long is a 64 bit unsigned integer.
|
# Long is a 64 bit unsigned integer. Input is accepted as either a JSON number or as a string.
|
||||||
|
# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all
|
||||||
|
# 0x-prefixed hexadecimal.
|
||||||
scalar Long
|
scalar Long
|
||||||
|
|
||||||
schema {
|
schema {
|
||||||
|
@ -57,7 +59,7 @@ const schema string = `
|
||||||
# Log is an Ethereum event log.
|
# Log is an Ethereum event log.
|
||||||
type Log {
|
type Log {
|
||||||
# Index is the index of this log in the block.
|
# Index is the index of this log in the block.
|
||||||
index: Int!
|
index: Long!
|
||||||
# Account is the account which generated this log - this will always
|
# Account is the account which generated this log - this will always
|
||||||
# be a contract account.
|
# be a contract account.
|
||||||
account(block: Long): Account!
|
account(block: Long): Account!
|
||||||
|
@ -83,7 +85,7 @@ const schema string = `
|
||||||
nonce: Long!
|
nonce: Long!
|
||||||
# Index is the index of this transaction in the parent block. This will
|
# Index is the index of this transaction in the parent block. This will
|
||||||
# be null if the transaction has not yet been mined.
|
# be null if the transaction has not yet been mined.
|
||||||
index: Int
|
index: Long
|
||||||
# From is the account that sent this transaction - this will always be
|
# From is the account that sent this transaction - this will always be
|
||||||
# an externally owned account.
|
# an externally owned account.
|
||||||
from(block: Long): Account!
|
from(block: Long): Account!
|
||||||
|
@ -138,7 +140,7 @@ const schema string = `
|
||||||
s: BigInt!
|
s: BigInt!
|
||||||
v: BigInt!
|
v: BigInt!
|
||||||
# Envelope transaction support
|
# Envelope transaction support
|
||||||
type: Int
|
type: Long
|
||||||
accessList: [AccessTuple!]
|
accessList: [AccessTuple!]
|
||||||
# Raw is the canonical encoding of the transaction.
|
# Raw is the canonical encoding of the transaction.
|
||||||
# For legacy transactions, it returns the RLP encoding.
|
# For legacy transactions, it returns the RLP encoding.
|
||||||
|
@ -183,7 +185,7 @@ const schema string = `
|
||||||
transactionsRoot: Bytes32!
|
transactionsRoot: Bytes32!
|
||||||
# TransactionCount is the number of transactions in this block. if
|
# TransactionCount is the number of transactions in this block. if
|
||||||
# transactions are not available for this block, this field will be null.
|
# transactions are not available for this block, this field will be null.
|
||||||
transactionCount: Int
|
transactionCount: Long
|
||||||
# StateRoot is the keccak256 hash of the state trie after this block was processed.
|
# StateRoot is the keccak256 hash of the state trie after this block was processed.
|
||||||
stateRoot: Bytes32!
|
stateRoot: Bytes32!
|
||||||
# ReceiptsRoot is the keccak256 hash of the trie of transaction receipts in this block.
|
# ReceiptsRoot is the keccak256 hash of the trie of transaction receipts in this block.
|
||||||
|
@ -214,7 +216,7 @@ const schema string = `
|
||||||
totalDifficulty: BigInt!
|
totalDifficulty: BigInt!
|
||||||
# OmmerCount is the number of ommers (AKA uncles) associated with this
|
# OmmerCount is the number of ommers (AKA uncles) associated with this
|
||||||
# block. If ommers are unavailable, this field will be null.
|
# block. If ommers are unavailable, this field will be null.
|
||||||
ommerCount: Int
|
ommerCount: Long
|
||||||
# Ommers is a list of ommer (AKA uncle) blocks associated with this block.
|
# Ommers is a list of ommer (AKA uncle) blocks associated with this block.
|
||||||
# If ommers are unavailable, this field will be null. Depending on your
|
# If ommers are unavailable, this field will be null. Depending on your
|
||||||
# node, the transactions, transactionAt, transactionCount, ommers,
|
# node, the transactions, transactionAt, transactionCount, ommers,
|
||||||
|
@ -222,7 +224,7 @@ const schema string = `
|
||||||
ommers: [Block]
|
ommers: [Block]
|
||||||
# OmmerAt returns the ommer (AKA uncle) at the specified index. If ommers
|
# OmmerAt returns the ommer (AKA uncle) at the specified index. If ommers
|
||||||
# are unavailable, or the index is out of bounds, this field will be null.
|
# are unavailable, or the index is out of bounds, this field will be null.
|
||||||
ommerAt(index: Int!): Block
|
ommerAt(index: Long!): Block
|
||||||
# OmmerHash is the keccak256 hash of all the ommers (AKA uncles)
|
# OmmerHash is the keccak256 hash of all the ommers (AKA uncles)
|
||||||
# associated with this block.
|
# associated with this block.
|
||||||
ommerHash: Bytes32!
|
ommerHash: Bytes32!
|
||||||
|
@ -232,7 +234,7 @@ const schema string = `
|
||||||
# TransactionAt returns the transaction at the specified index. If
|
# TransactionAt returns the transaction at the specified index. If
|
||||||
# transactions are unavailable for this block, or if the index is out of
|
# transactions are unavailable for this block, or if the index is out of
|
||||||
# bounds, this field will be null.
|
# bounds, this field will be null.
|
||||||
transactionAt(index: Int!): Transaction
|
transactionAt(index: Long!): Transaction
|
||||||
# Logs returns a filtered set of logs from this block.
|
# Logs returns a filtered set of logs from this block.
|
||||||
logs(filter: BlockFilterCriteria!): [Log!]!
|
logs(filter: BlockFilterCriteria!): [Log!]!
|
||||||
# Account fetches an Ethereum account at the current block's state.
|
# Account fetches an Ethereum account at the current block's state.
|
||||||
|
@ -317,7 +319,7 @@ const schema string = `
|
||||||
# Pending represents the current pending state.
|
# Pending represents the current pending state.
|
||||||
type Pending {
|
type Pending {
|
||||||
# TransactionCount is the number of transactions in the pending state.
|
# TransactionCount is the number of transactions in the pending state.
|
||||||
transactionCount: Int!
|
transactionCount: Long!
|
||||||
# Transactions is a list of transactions in the current pending state.
|
# Transactions is a list of transactions in the current pending state.
|
||||||
transactions: [Transaction!]
|
transactions: [Transaction!]
|
||||||
# Account fetches an Ethereum account for the pending state.
|
# Account fetches an Ethereum account for the pending state.
|
||||||
|
|
Loading…
Reference in New Issue