@ -24,6 +24,7 @@ import (
"math/big"
"sort"
"strconv"
"sync"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
@ -80,12 +81,26 @@ type Account struct {
r * Resolver
address common . Address
blockNrOrHash rpc . BlockNumberOrHash
state * state . StateDB
mu sync . Mutex
}
// getState fetches the StateDB object for an account.
func ( a * Account ) getState ( ctx context . Context ) ( * state . StateDB , error ) {
a . mu . Lock ( )
defer a . mu . Unlock ( )
if a . state != nil {
return a . state , nil
}
state , _ , err := a . r . backend . StateAndHeaderByNumberOrHash ( ctx , a . blockNrOrHash )
return state , err
if err != nil {
return nil , err
}
a . state = state
// Cache the state object. This is done so that concurrent resolvers
// don't have to fetch the object from DB individually.
a . state . GetOrNewStateObject ( a . address )
return a . state , nil
}
func ( a * Account ) Address ( ctx context . Context ) ( common . Address , error ) {
@ -184,32 +199,39 @@ func (at *AccessTuple) StorageKeys(ctx context.Context) []common.Hash {
// Transaction represents an Ethereum transaction.
// backend and hash are mandatory; all others will be fetched when required.
type Transaction struct {
r * Resolver
hash common . Hash
r * Resolver
hash common . Hash // Must be present after initialization
mu sync . Mutex
// mu protects following resources
tx * types . Transaction
block * Block
index uint64
}
// resolve returns the internal transaction object, fetching it if needed.
func ( t * Transaction ) resolve ( ctx context . Context ) ( * types . Transaction , error ) {
if t . tx == nil {
// Try to return an already finalized transaction
tx , blockHash , _ , index , err := t . r . backend . GetTransaction ( ctx , t . hash )
if err == nil && tx != nil {
t . tx = tx
blockNrOrHash := rpc . BlockNumberOrHashWithHash ( blockHash , false )
t . block = & Block {
r : t . r ,
numberOrHash : & blockNrOrHash ,
}
t . index = index
return t . tx , nil
// It also returns the block the tx blongs to, unless it is a pending tx.
func ( t * Transaction ) resolve ( ctx context . Context ) ( * types . Transaction , * Block , error ) {
t . mu . Lock ( )
defer t . mu . Unlock ( )
if t . tx != nil {
return t . tx , t . block , nil
}
// Try to return an already finalized transaction
tx , blockHash , _ , index , err := t . r . backend . GetTransaction ( ctx , t . hash )
if err == nil && tx != nil {
t . tx = tx
blockNrOrHash := rpc . BlockNumberOrHashWithHash ( blockHash , false )
t . block = & Block {
r : t . r ,
numberOrHash : & blockNrOrHash ,
hash : blockHash ,
}
// No finalized transaction, try to retrieve it from the pool
t . tx = t . r . backend . GetPoolTransaction ( t . hash )
t . index = index
return t . tx , t . block , nil
}
return t . tx , nil
// No finalized transaction, try to retrieve it from the pool
t . tx = t . r . backend . GetPoolTransaction ( t . hash )
return t . tx , nil , nil
}
func ( t * Transaction ) Hash ( ctx context . Context ) common . Hash {
@ -217,7 +239,7 @@ func (t *Transaction) Hash(ctx context.Context) common.Hash {
}
func ( t * Transaction ) InputData ( ctx context . Context ) ( hexutil . Bytes , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Bytes { } , err
}
@ -225,7 +247,7 @@ func (t *Transaction) InputData(ctx context.Context) (hexutil.Bytes, error) {
}
func ( t * Transaction ) Gas ( ctx context . Context ) ( hexutil . Uint64 , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return 0 , err
}
@ -233,7 +255,7 @@ func (t *Transaction) Gas(ctx context.Context) (hexutil.Uint64, error) {
}
func ( t * Transaction ) GasPrice ( ctx context . Context ) ( hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , block , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Big { } , err
}
@ -241,8 +263,8 @@ func (t *Transaction) GasPrice(ctx context.Context) (hexutil.Big, error) {
case types . AccessListTxType :
return hexutil . Big ( * tx . GasPrice ( ) ) , nil
case types . DynamicFeeTxType :
if t . block != nil {
if baseFee , _ := t . block . BaseFeePerGas ( ctx ) ; baseFee != nil {
if block != nil {
if baseFee , _ := block . BaseFeePerGas ( ctx ) ; baseFee != nil {
// price = min(tip, gasFeeCap - baseFee) + baseFee
return ( hexutil . Big ) ( * math . BigMin ( new ( big . Int ) . Add ( tx . GasTipCap ( ) , baseFee . ToInt ( ) ) , tx . GasFeeCap ( ) ) ) , nil
}
@ -254,15 +276,15 @@ func (t *Transaction) GasPrice(ctx context.Context) (hexutil.Big, error) {
}
func ( t * Transaction ) EffectiveGasPrice ( ctx context . Context ) ( * hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , block , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
// Pending tx
if t . block == nil {
if block == nil {
return nil , nil
}
header , err := t . block . resolveHeader ( ctx )
header , err := block . resolveHeader ( ctx )
if err != nil || header == nil {
return nil , err
}
@ -273,7 +295,7 @@ func (t *Transaction) EffectiveGasPrice(ctx context.Context) (*hexutil.Big, erro
}
func ( t * Transaction ) MaxFeePerGas ( ctx context . Context ) ( * hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
@ -288,7 +310,7 @@ func (t *Transaction) MaxFeePerGas(ctx context.Context) (*hexutil.Big, error) {
}
func ( t * Transaction ) MaxPriorityFeePerGas ( ctx context . Context ) ( * hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
@ -303,15 +325,15 @@ func (t *Transaction) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, e
}
func ( t * Transaction ) EffectiveTip ( ctx context . Context ) ( * hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , block , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
// Pending tx
if t . block == nil {
if block == nil {
return nil , nil
}
header , err := t . block . resolveHeader ( ctx )
header , err := block . resolveHeader ( ctx )
if err != nil || header == nil {
return nil , err
}
@ -327,7 +349,7 @@ func (t *Transaction) EffectiveTip(ctx context.Context) (*hexutil.Big, error) {
}
func ( t * Transaction ) Value ( ctx context . Context ) ( hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Big { } , err
}
@ -338,7 +360,7 @@ func (t *Transaction) Value(ctx context.Context) (hexutil.Big, error) {
}
func ( t * Transaction ) Nonce ( ctx context . Context ) ( hexutil . Uint64 , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return 0 , err
}
@ -346,7 +368,7 @@ func (t *Transaction) Nonce(ctx context.Context) (hexutil.Uint64, error) {
}
func ( t * Transaction ) To ( ctx context . Context , args BlockNumberArgs ) ( * Account , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
@ -362,7 +384,7 @@ func (t *Transaction) To(ctx context.Context, args BlockNumberArgs) (*Account, e
}
func ( t * Transaction ) From ( ctx context . Context , args BlockNumberArgs ) ( * Account , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
@ -376,17 +398,20 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account,
}
func ( t * Transaction ) Block ( ctx context . Context ) ( * Block , error ) {
if _ , err := t . resolve ( ctx ) ; err != nil {
_ , block , err := t . resolve ( ctx )
if err != nil {
return nil , err
}
return t . block , nil
return block , nil
}
func ( t * Transaction ) Index ( ctx context . Context ) ( * int32 , error ) {
if _ , err := t . resolve ( ctx ) ; err != nil {
_ , block , err := t . resolve ( ctx )
if err != nil {
return nil , err
}
if t . block == nil {
// Pending tx
if block == nil {
return nil , nil
}
index := int32 ( t . index )
@ -395,13 +420,15 @@ func (t *Transaction) Index(ctx context.Context) (*int32, error) {
// getReceipt returns the receipt associated with this transaction, if any.
func ( t * Transaction ) getReceipt ( ctx context . Context ) ( * types . Receipt , error ) {
if _ , err := t . resolve ( ctx ) ; err != nil {
_ , block , err := t . resolve ( ctx )
if err != nil {
return nil , err
}
if t . block == nil {
// Pending tx
if block == nil {
return nil , nil
}
receipts , err := t . block . resolveReceipts ( ctx )
receipts , err := block . resolveReceipts ( ctx )
if err != nil {
return nil , err
}
@ -451,28 +478,25 @@ func (t *Transaction) CreatedContract(ctx context.Context, args BlockNumberArgs)
}
func ( t * Transaction ) Logs ( ctx context . Context ) ( * [ ] * Log , error ) {
if _ , err := t . resolve ( ctx ) ; err != nil {
_ , block , err := t . resolve ( ctx )
if err != nil {
return nil , err
}
if t . block == nil {
// Pending tx
if block == nil {
return nil , nil
}
if _ , ok := t . block . numberOrHash . Hash ( ) ; ! ok {
header , err := t . r . backend . HeaderByNumberOrHash ( ctx , * t . block . numberOrHash )
if err != nil {
return nil , err
}
hash := header . Hash ( )
t . block . numberOrHash . BlockHash = & hash
h , err := block . Hash ( ctx )
if err != nil {
return nil , err
}
return t . getLogs ( ctx )
return t . getLogs ( ctx , h )
}
// getLogs returns log objects for the given tx.
// Assumes block hash is resolved.
func ( t * Transaction ) getLogs ( ctx context . Context ) ( * [ ] * Log , error ) {
func ( t * Transaction ) getLogs ( ctx context . Context , hash common . Hash ) ( * [ ] * Log , error ) {
var (
hash , _ = t . block . numberOrHash . Hash ( )
filter = t . r . filterSystem . NewBlockFilter ( hash , nil , nil )
logs , err = filter . Logs ( ctx )
)
@ -494,7 +518,7 @@ func (t *Transaction) getLogs(ctx context.Context) (*[]*Log, error) {
}
func ( t * Transaction ) Type ( ctx context . Context ) ( * int32 , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil {
return nil , err
}
@ -503,7 +527,7 @@ func (t *Transaction) Type(ctx context.Context) (*int32, error) {
}
func ( t * Transaction ) AccessList ( ctx context . Context ) ( * [ ] * AccessTuple , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return nil , err
}
@ -519,7 +543,7 @@ func (t *Transaction) AccessList(ctx context.Context) (*[]*AccessTuple, error) {
}
func ( t * Transaction ) R ( ctx context . Context ) ( hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Big { } , err
}
@ -528,7 +552,7 @@ func (t *Transaction) R(ctx context.Context) (hexutil.Big, error) {
}
func ( t * Transaction ) S ( ctx context . Context ) ( hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Big { } , err
}
@ -537,7 +561,7 @@ func (t *Transaction) S(ctx context.Context) (hexutil.Big, error) {
}
func ( t * Transaction ) V ( ctx context . Context ) ( hexutil . Big , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Big { } , err
}
@ -546,7 +570,7 @@ func (t *Transaction) V(ctx context.Context) (hexutil.Big, error) {
}
func ( t * Transaction ) Raw ( ctx context . Context ) ( hexutil . Bytes , error ) {
tx , err := t . resolve ( ctx )
tx , _ , err := t . resolve ( ctx )
if err != nil || tx == nil {
return hexutil . Bytes { } , err
}
@ -568,16 +592,20 @@ type BlockType int
// when required.
type Block struct {
r * Resolver
numberOrHash * rpc . BlockNumberOrHash
hash common . Hash
header * types . Header
block * types . Block
receipts [ ] * types . Receipt
numberOrHash * rpc . BlockNumberOrHash // Field resolvers assume numberOrHash is always present
mu sync . Mutex
// mu protects following resources
hash common . Hash // Must be resolved during initialization
header * types . Header
block * types . Block
receipts [ ] * types . Receipt
}
// resolve returns the internal Block object representing this block, fetching
// it if necessary.
func ( b * Block ) resolve ( ctx context . Context ) ( * types . Block , error ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
if b . block != nil {
return b . block , nil
}
@ -587,10 +615,10 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) {
}
var err error
b . block , err = b . r . backend . BlockByNumberOrHash ( ctx , * b . numberOrHash )
if b . block != nil && b . header == nil {
b . header = b . block . Header ( )
if hash , ok := b . numberOrHash . Hash ( ) ; ok {
b . hash = hash
if b . block != nil {
b . hash = b . block . Hash ( )
if b . header == nil {
b . header = b . block . Header ( )
}
}
return b . block , err
@ -600,39 +628,39 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) {
// if necessary. Call this function instead of `resolve` unless you need the
// additional data (transactions and uncles).
func ( b * Block ) resolveHeader ( ctx context . Context ) ( * types . Header , error ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
if b . header != nil {
return b . header , nil
}
if b . numberOrHash == nil && b . hash == ( common . Hash { } ) {
return nil , errBlockInvariant
}
var err error
if b . header == nil {
if b . hash != ( common . Hash { } ) {
b . header , err = b . r . backend . HeaderByHash ( ctx , b . hash )
} else {
b . header , err = b . r . backend . HeaderByNumberOrHash ( ctx , * b . numberOrHash )
}
b . header , err = b . r . backend . HeaderByNumberOrHash ( ctx , * b . numberOrHash )
if err != nil {
return nil , err
}
if b . hash == ( common . Hash { } ) {
b . hash = b . header . Hash ( )
}
return b . header , err
return b . header , nil
}
// resolveReceipts returns the list of receipts for this block, fetching them
// if necessary.
func ( b * Block ) resolveReceipts ( ctx context . Context ) ( [ ] * types . Receipt , error ) {
if b . receipts == nil {
hash := b . hash
if hash == ( common . Hash { } ) {
header , err := b . resolveHeader ( ctx )
if err != nil {
return nil , err
}
hash = header . Hash ( )
}
receipts , err := b . r . backend . GetReceipts ( ctx , hash )
if err != nil {
return nil , err
}
b . receipts = receipts
b . mu . Lock ( )
defer b . mu . Unlock ( )
if b . receipts != nil {
return b . receipts , nil
}
return b . receipts , nil
receipts , err := b . r . backend . GetReceipts ( ctx , b . hash )
if err != nil {
return nil , err
}
b . receipts = receipts
return receipts , nil
}
func ( b * Block ) Number ( ctx context . Context ) ( Long , error ) {
@ -645,13 +673,8 @@ func (b *Block) Number(ctx context.Context) (Long, error) {
}
func ( b * Block ) Hash ( ctx context . Context ) ( common . Hash , error ) {
if b . hash == ( common . Hash { } ) {
header , err := b . resolveHeader ( ctx )
if err != nil {
return common . Hash { } , err
}
b . hash = header . Hash ( )
}
b . mu . Lock ( )
defer b . mu . Unlock ( )
return b . hash , nil
}
@ -705,11 +728,18 @@ func (b *Block) Parent(ctx context.Context) (*Block, error) {
if b . header == nil || b . header . Number . Uint64 ( ) < 1 {
return nil , nil
}
num := rpc . BlockNumberOrHashWithNumber ( rpc . BlockNumber ( b . header . Number . Uint64 ( ) - 1 ) )
var (
num = rpc . BlockNumber ( b . header . Number . Uint64 ( ) - 1 )
hash = b . header . ParentHash
numOrHash = rpc . BlockNumberOrHash {
BlockNumber : & num ,
BlockHash : & hash ,
}
)
return & Block {
r : b . r ,
numberOrHash : & num ,
hash : b . header . ParentHash ,
numberOrHash : & numOrHash ,
hash : hash ,
} , nil
}
@ -798,6 +828,7 @@ func (b *Block) Ommers(ctx context.Context) (*[]*Block, error) {
r : b . r ,
numberOrHash : & blockNumberOrHash ,
header : uncle ,
hash : uncle . Hash ( ) ,
} )
}
return & ret , nil
@ -820,17 +851,13 @@ func (b *Block) LogsBloom(ctx context.Context) (hexutil.Bytes, error) {
}
func ( b * Block ) TotalDifficulty ( ctx context . Context ) ( hexutil . Big , error ) {
h := b . hash
if h == ( common . Hash { } ) {
header , err := b . resolveHeader ( ctx )
if err != nil {
return hexutil . Big { } , err
}
h = header . Hash ( )
hash , err := b . Hash ( ctx )
if err != nil {
return hexutil . Big { } , err
}
td := b . r . backend . GetTd ( ctx , h )
td := b . r . backend . GetTd ( ctx , hash )
if td == nil {
return hexutil . Big { } , fmt . Errorf ( "total difficulty not found %x" , b . hash )
return hexutil . Big { } , fmt . Errorf ( "total difficulty not found %x" , hash )
}
return hexutil . Big ( * td ) , nil
}
@ -948,6 +975,7 @@ func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block
r : b . r ,
numberOrHash : & blockNumberOrHash ,
header : uncle ,
hash : uncle . Hash ( ) ,
} , nil
}
@ -997,15 +1025,11 @@ func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteri
if args . Filter . Topics != nil {
topics = * args . Filter . Topics
}
hash := b . hash
if hash == ( common . Hash { } ) {
header , err := b . resolveHeader ( ctx )
if err != nil {
return nil , err
}
hash = header . Hash ( )
}
// Construct the range filter
hash , err := b . Hash ( ctx )
if err != nil {
return nil , err
}
filter := b . r . filterSystem . NewBlockFilter ( hash , addresses , topics )
// Run the filter and return all the logs
@ -1015,12 +1039,6 @@ func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteri
func ( b * Block ) Account ( ctx context . Context , args struct {
Address common . Address
} ) ( * Account , error ) {
if b . numberOrHash == nil {
_ , err := b . resolveHeader ( ctx )
if err != nil {
return nil , err
}
}
return & Account {
r : b . r ,
address : args . Address ,
@ -1063,12 +1081,6 @@ func (c *CallResult) Status() Long {
func ( b * Block ) Call ( ctx context . Context , args struct {
Data ethapi . TransactionArgs
} ) ( * CallResult , error ) {
if b . numberOrHash == nil {
_ , err := b . resolve ( ctx )
if err != nil {
return nil , err
}
}
result , err := ethapi . DoCall ( ctx , b . r . backend , args . Data , * b . numberOrHash , nil , b . r . backend . RPCEVMTimeout ( ) , b . r . backend . RPCGasCap ( ) )
if err != nil {
return nil , err
@ -1088,12 +1100,6 @@ func (b *Block) Call(ctx context.Context, args struct {
func ( b * Block ) EstimateGas ( ctx context . Context , args struct {
Data ethapi . TransactionArgs
} ) ( Long , error ) {
if b . numberOrHash == nil {
_ , err := b . resolveHeader ( ctx )
if err != nil {
return 0 , err
}
}
gas , err := ethapi . DoEstimateGas ( ctx , b . r . backend , args . Data , * b . numberOrHash , b . r . backend . RPCGasCap ( ) )
return Long ( gas ) , err
}
@ -1173,29 +1179,21 @@ func (r *Resolver) Block(ctx context.Context, args struct {
Number * Long
Hash * common . Hash
} ) ( * Block , error ) {
var block * Block
var numberOrHash rpc . BlockNumberOrHash
if args . Number != nil {
if * args . Number < 0 {
return nil , nil
}
number := rpc . BlockNumber ( * args . Number )
numberOrHash := rpc . BlockNumberOrHashWithNumber ( number )
block = & Block {
r : r ,
numberOrHash : & numberOrHash ,
}
numberOrHash = rpc . BlockNumberOrHashWithNumber ( number )
} else if args . Hash != nil {
numberOrHash := rpc . BlockNumberOrHashWithHash ( * args . Hash , false )
block = & Block {
r : r ,
numberOrHash : & numberOrHash ,
}
numberOrHash = rpc . BlockNumberOrHashWithHash ( * args . Hash , false )
} else {
numberOrHash : = rpc . BlockNumberOrHashWithNumber ( rpc . LatestBlockNumber )
block = & Block {
r : r ,
numberOrHash : & numberOrHash ,
}
numberOrHash = rpc . BlockNumberOrHashWithNumber ( rpc . LatestBlockNumber )
}
block := & Block {
r : r ,
numberOrHash : & numberOrHash ,
}
// Resolve the header, return nil if it doesn't exist.
// Note we don't resolve block directly here since it will require an
@ -1256,7 +1254,7 @@ func (r *Resolver) Transaction(ctx context.Context, args struct{ Hash common.Has
hash : args . Hash ,
}
// Resolve the transaction; if it doesn't exist, return nil.
t , err := tx . resolve ( ctx )
t , _ , err := tx . resolve ( ctx )
if err != nil {
return nil , err
} else if t == nil {