status-go/rpc/chain/rpc.go

109 lines
3.1 KiB
Go

package chain
import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
)
// The code below is mostly copied from go-ethereum/ethclient (see TransactionInBlock), to keep the exact same behavior as the
// normal Transaction items, but exposing the additional data obtained in the `rpcTransaction` struct`.
// Unfortunately, the functions and classes used are not exposed outside of the package.
type FullTransaction struct {
Tx *types.Transaction
TxExtraInfo
}
type TxExtraInfo struct {
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
BlockHash *common.Hash `json:"blockHash,omitempty"`
From *common.Address `json:"from,omitempty"`
}
func callFullTransactionByBlockNumberAndIndex(ctx context.Context, rpc *rpc.Client, number *big.Int, index uint) (*FullTransaction, error) {
var json *FullTransaction
err := rpc.CallContext(ctx, &json, "eth_getTransactionByBlockNumberAndIndex", toBlockNumArg(number), hexutil.Uint64(index))
if err != nil {
return nil, err
}
if json == nil {
return nil, ethereum.NotFound
} else if _, r, _ := json.Tx.RawSignatureValues(); r == nil {
return nil, fmt.Errorf("server returned transaction without signature")
}
if json.From != nil && json.BlockHash != nil {
setSenderFromServer(json.Tx, *json.From, *json.BlockHash)
}
return json, nil
}
func (tx *FullTransaction) UnmarshalJSON(msg []byte) error {
if err := json.Unmarshal(msg, &tx.Tx); err != nil {
return err
}
return json.Unmarshal(msg, &tx.TxExtraInfo)
}
func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
}
pending := big.NewInt(-1)
if number.Cmp(pending) == 0 {
return "pending"
}
finalized := big.NewInt(int64(rpc.FinalizedBlockNumber))
if number.Cmp(finalized) == 0 {
return "finalized"
}
safe := big.NewInt(int64(rpc.SafeBlockNumber))
if number.Cmp(safe) == 0 {
return "safe"
}
return hexutil.EncodeBig(number)
}
type senderFromServer struct {
addr common.Address
blockhash common.Hash
}
var errNotCached = errors.New("sender not cached")
//nolint:errcheck
func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) {
// Use types.Sender for side-effect to store our signer into the cache.
types.Sender(&senderFromServer{addr, block}, tx)
}
func (s *senderFromServer) Equal(other types.Signer) bool {
os, ok := other.(*senderFromServer)
return ok && os.blockhash == s.blockhash
}
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
if s.addr == (common.Address{}) {
return common.Address{}, errNotCached
}
return s.addr, nil
}
func (s *senderFromServer) ChainID() *big.Int {
panic("can't sign with senderFromServer")
}
func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash {
panic("can't sign with senderFromServer")
}
func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) {
panic("can't sign with senderFromServer")
}