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") }