|
@@ -17,6 +17,7 @@
|
|
|
package types
|
|
package types
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "bytes"
|
|
|
"container/heap"
|
|
"container/heap"
|
|
|
"errors"
|
|
"errors"
|
|
|
"io"
|
|
"io"
|
|
@@ -25,20 +26,28 @@ import (
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
- "github.com/ethereum/go-ethereum/common/hexutil"
|
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
|
|
|
|
-
|
|
|
|
|
var (
|
|
var (
|
|
|
- ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
|
|
|
|
|
|
+ ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
|
|
|
|
+ ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
|
|
|
|
+ ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
|
|
|
|
+ ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
|
|
|
|
+ errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// Transaction types.
|
|
|
|
|
+const (
|
|
|
|
|
+ LegacyTxType = iota
|
|
|
|
|
+ AccessListTxType
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+// Transaction is an Ethereum transaction.
|
|
|
type Transaction struct {
|
|
type Transaction struct {
|
|
|
- data txdata // Consensus contents of a transaction
|
|
|
|
|
- time time.Time // Time first seen locally (spam avoidance)
|
|
|
|
|
|
|
+ inner TxData // Consensus contents of a transaction
|
|
|
|
|
+ time time.Time // Time first seen locally (spam avoidance)
|
|
|
|
|
|
|
|
// caches
|
|
// caches
|
|
|
hash atomic.Value
|
|
hash atomic.Value
|
|
@@ -46,170 +55,266 @@ type Transaction struct {
|
|
|
from atomic.Value
|
|
from atomic.Value
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-type txdata struct {
|
|
|
|
|
- AccountNonce uint64 `json:"nonce" gencodec:"required"`
|
|
|
|
|
- Price *big.Int `json:"gasPrice" gencodec:"required"`
|
|
|
|
|
- GasLimit uint64 `json:"gas" gencodec:"required"`
|
|
|
|
|
- Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
|
|
|
|
|
- Amount *big.Int `json:"value" gencodec:"required"`
|
|
|
|
|
- Payload []byte `json:"input" gencodec:"required"`
|
|
|
|
|
|
|
+// NewTx creates a new transaction.
|
|
|
|
|
+func NewTx(inner TxData) *Transaction {
|
|
|
|
|
+ tx := new(Transaction)
|
|
|
|
|
+ tx.setDecoded(inner.copy(), 0)
|
|
|
|
|
+ return tx
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// TxData is the underlying data of a transaction.
|
|
|
|
|
+//
|
|
|
|
|
+// This is implemented by LegacyTx and AccessListTx.
|
|
|
|
|
+type TxData interface {
|
|
|
|
|
+ txType() byte // returns the type ID
|
|
|
|
|
+ copy() TxData // creates a deep copy and initializes all fields
|
|
|
|
|
|
|
|
- // Signature values
|
|
|
|
|
- V *big.Int `json:"v" gencodec:"required"`
|
|
|
|
|
- R *big.Int `json:"r" gencodec:"required"`
|
|
|
|
|
- S *big.Int `json:"s" gencodec:"required"`
|
|
|
|
|
|
|
+ chainID() *big.Int
|
|
|
|
|
+ accessList() AccessList
|
|
|
|
|
+ data() []byte
|
|
|
|
|
+ gas() uint64
|
|
|
|
|
+ gasPrice() *big.Int
|
|
|
|
|
+ value() *big.Int
|
|
|
|
|
+ nonce() uint64
|
|
|
|
|
+ to() *common.Address
|
|
|
|
|
|
|
|
- // This is only used when marshaling to JSON.
|
|
|
|
|
- Hash *common.Hash `json:"hash" rlp:"-"`
|
|
|
|
|
|
|
+ rawSignatureValues() (v, r, s *big.Int)
|
|
|
|
|
+ setSignatureValues(chainID, v, r, s *big.Int)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-type txdataMarshaling struct {
|
|
|
|
|
- AccountNonce hexutil.Uint64
|
|
|
|
|
- Price *hexutil.Big
|
|
|
|
|
- GasLimit hexutil.Uint64
|
|
|
|
|
- Amount *hexutil.Big
|
|
|
|
|
- Payload hexutil.Bytes
|
|
|
|
|
- V *hexutil.Big
|
|
|
|
|
- R *hexutil.Big
|
|
|
|
|
- S *hexutil.Big
|
|
|
|
|
|
|
+// EncodeRLP implements rlp.Encoder
|
|
|
|
|
+func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
|
|
|
|
+ if tx.Type() == LegacyTxType {
|
|
|
|
|
+ return rlp.Encode(w, tx.inner)
|
|
|
|
|
+ }
|
|
|
|
|
+ // It's an EIP-2718 typed TX envelope.
|
|
|
|
|
+ buf := encodeBufferPool.Get().(*bytes.Buffer)
|
|
|
|
|
+ defer encodeBufferPool.Put(buf)
|
|
|
|
|
+ buf.Reset()
|
|
|
|
|
+ if err := tx.encodeTyped(buf); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ return rlp.Encode(w, buf.Bytes())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
|
|
|
|
- return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
|
|
|
|
|
|
|
+// encodeTyped writes the canonical encoding of a typed transaction to w.
|
|
|
|
|
+func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
|
|
|
|
|
+ w.WriteByte(tx.Type())
|
|
|
|
|
+ return rlp.Encode(w, tx.inner)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
|
|
|
|
- return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
|
|
|
|
|
|
|
+// MarshalBinary returns the canonical encoding of the transaction.
|
|
|
|
|
+// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed
|
|
|
|
|
+// transactions, it returns the type and payload.
|
|
|
|
|
+func (tx *Transaction) MarshalBinary() ([]byte, error) {
|
|
|
|
|
+ if tx.Type() == LegacyTxType {
|
|
|
|
|
+ return rlp.EncodeToBytes(tx.inner)
|
|
|
|
|
+ }
|
|
|
|
|
+ var buf bytes.Buffer
|
|
|
|
|
+ err := tx.encodeTyped(&buf)
|
|
|
|
|
+ return buf.Bytes(), err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
|
|
|
|
- if len(data) > 0 {
|
|
|
|
|
- data = common.CopyBytes(data)
|
|
|
|
|
|
|
+// DecodeRLP implements rlp.Decoder
|
|
|
|
|
+func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|
|
|
|
+ kind, size, err := s.Kind()
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case err != nil:
|
|
|
|
|
+ return err
|
|
|
|
|
+ case kind == rlp.List:
|
|
|
|
|
+ // It's a legacy transaction.
|
|
|
|
|
+ var inner LegacyTx
|
|
|
|
|
+ err := s.Decode(&inner)
|
|
|
|
|
+ if err == nil {
|
|
|
|
|
+ tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
|
|
|
|
+ }
|
|
|
|
|
+ return err
|
|
|
|
|
+ case kind == rlp.String:
|
|
|
|
|
+ // It's an EIP-2718 typed TX envelope.
|
|
|
|
|
+ var b []byte
|
|
|
|
|
+ if b, err = s.Bytes(); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ inner, err := tx.decodeTyped(b)
|
|
|
|
|
+ if err == nil {
|
|
|
|
|
+ tx.setDecoded(inner, len(b))
|
|
|
|
|
+ }
|
|
|
|
|
+ return err
|
|
|
|
|
+ default:
|
|
|
|
|
+ return rlp.ErrExpectedList
|
|
|
}
|
|
}
|
|
|
- d := txdata{
|
|
|
|
|
- AccountNonce: nonce,
|
|
|
|
|
- Recipient: to,
|
|
|
|
|
- Payload: data,
|
|
|
|
|
- Amount: new(big.Int),
|
|
|
|
|
- GasLimit: gasLimit,
|
|
|
|
|
- Price: new(big.Int),
|
|
|
|
|
- V: new(big.Int),
|
|
|
|
|
- R: new(big.Int),
|
|
|
|
|
- S: new(big.Int),
|
|
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// UnmarshalBinary decodes the canonical encoding of transactions.
|
|
|
|
|
+// It supports legacy RLP transactions and EIP2718 typed transactions.
|
|
|
|
|
+func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
|
|
|
|
+ if len(b) > 0 && b[0] > 0x7f {
|
|
|
|
|
+ // It's a legacy transaction.
|
|
|
|
|
+ var data LegacyTx
|
|
|
|
|
+ err := rlp.DecodeBytes(b, &data)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ tx.setDecoded(&data, len(b))
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
- if amount != nil {
|
|
|
|
|
- d.Amount.Set(amount)
|
|
|
|
|
|
|
+ // It's an EIP2718 typed transaction envelope.
|
|
|
|
|
+ inner, err := tx.decodeTyped(b)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- if gasPrice != nil {
|
|
|
|
|
- d.Price.Set(gasPrice)
|
|
|
|
|
|
|
+ tx.setDecoded(inner, len(b))
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// decodeTyped decodes a typed transaction from the canonical format.
|
|
|
|
|
+func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
|
|
|
|
+ if len(b) == 0 {
|
|
|
|
|
+ return nil, errEmptyTypedTx
|
|
|
}
|
|
}
|
|
|
- return &Transaction{
|
|
|
|
|
- data: d,
|
|
|
|
|
- time: time.Now(),
|
|
|
|
|
|
|
+ switch b[0] {
|
|
|
|
|
+ case AccessListTxType:
|
|
|
|
|
+ var inner AccessListTx
|
|
|
|
|
+ err := rlp.DecodeBytes(b[1:], &inner)
|
|
|
|
|
+ return &inner, err
|
|
|
|
|
+ default:
|
|
|
|
|
+ return nil, ErrTxTypeNotSupported
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ChainId returns which chain id this transaction was signed for (if at all)
|
|
|
|
|
-func (tx *Transaction) ChainId() *big.Int {
|
|
|
|
|
- return deriveChainId(tx.data.V)
|
|
|
|
|
|
|
+// setDecoded sets the inner transaction and size after decoding.
|
|
|
|
|
+func (tx *Transaction) setDecoded(inner TxData, size int) {
|
|
|
|
|
+ tx.inner = inner
|
|
|
|
|
+ tx.time = time.Now()
|
|
|
|
|
+ if size > 0 {
|
|
|
|
|
+ tx.size.Store(common.StorageSize(size))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Protected returns whether the transaction is protected from replay protection.
|
|
|
|
|
-func (tx *Transaction) Protected() bool {
|
|
|
|
|
- return isProtectedV(tx.data.V)
|
|
|
|
|
|
|
+func sanityCheckSignature(v *big.Int, r *big.Int, s *big.Int, maybeProtected bool) error {
|
|
|
|
|
+ if isProtectedV(v) && !maybeProtected {
|
|
|
|
|
+ return ErrUnexpectedProtection
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var plainV byte
|
|
|
|
|
+ if isProtectedV(v) {
|
|
|
|
|
+ chainID := deriveChainId(v).Uint64()
|
|
|
|
|
+ plainV = byte(v.Uint64() - 35 - 2*chainID)
|
|
|
|
|
+ } else if maybeProtected {
|
|
|
|
|
+ // Only EIP-155 signatures can be optionally protected. Since
|
|
|
|
|
+ // we determined this v value is not protected, it must be a
|
|
|
|
|
+ // raw 27 or 28.
|
|
|
|
|
+ plainV = byte(v.Uint64() - 27)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // If the signature is not optionally protected, we assume it
|
|
|
|
|
+ // must already be equal to the recovery id.
|
|
|
|
|
+ plainV = byte(v.Uint64())
|
|
|
|
|
+ }
|
|
|
|
|
+ if !crypto.ValidateSignatureValues(plainV, r, s, false) {
|
|
|
|
|
+ return ErrInvalidSig
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func isProtectedV(V *big.Int) bool {
|
|
func isProtectedV(V *big.Int) bool {
|
|
|
if V.BitLen() <= 8 {
|
|
if V.BitLen() <= 8 {
|
|
|
v := V.Uint64()
|
|
v := V.Uint64()
|
|
|
- return v != 27 && v != 28
|
|
|
|
|
|
|
+ return v != 27 && v != 28 && v != 1 && v != 0
|
|
|
}
|
|
}
|
|
|
// anything not 27 or 28 is considered protected
|
|
// anything not 27 or 28 is considered protected
|
|
|
return true
|
|
return true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// EncodeRLP implements rlp.Encoder
|
|
|
|
|
-func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
|
|
|
|
- return rlp.Encode(w, &tx.data)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// DecodeRLP implements rlp.Decoder
|
|
|
|
|
-func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|
|
|
|
- _, size, _ := s.Kind()
|
|
|
|
|
- err := s.Decode(&tx.data)
|
|
|
|
|
- if err == nil {
|
|
|
|
|
- tx.size.Store(common.StorageSize(rlp.ListSize(size)))
|
|
|
|
|
- tx.time = time.Now()
|
|
|
|
|
|
|
+// Protected says whether the transaction is replay-protected.
|
|
|
|
|
+func (tx *Transaction) Protected() bool {
|
|
|
|
|
+ switch tx := tx.inner.(type) {
|
|
|
|
|
+ case *LegacyTx:
|
|
|
|
|
+ return tx.V != nil && isProtectedV(tx.V)
|
|
|
|
|
+ default:
|
|
|
|
|
+ return true
|
|
|
}
|
|
}
|
|
|
- return err
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// MarshalJSON encodes the web3 RPC transaction format.
|
|
|
|
|
-func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
|
|
|
|
- hash := tx.Hash()
|
|
|
|
|
- data := tx.data
|
|
|
|
|
- data.Hash = &hash
|
|
|
|
|
- return data.MarshalJSON()
|
|
|
|
|
|
|
+// Type returns the transaction type.
|
|
|
|
|
+func (tx *Transaction) Type() uint8 {
|
|
|
|
|
+ return tx.inner.txType()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// UnmarshalJSON decodes the web3 RPC transaction format.
|
|
|
|
|
-func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
|
|
|
|
- var dec txdata
|
|
|
|
|
- if err := dec.UnmarshalJSON(input); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
- withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0
|
|
|
|
|
- if withSignature {
|
|
|
|
|
- var V byte
|
|
|
|
|
- if isProtectedV(dec.V) {
|
|
|
|
|
- chainID := deriveChainId(dec.V).Uint64()
|
|
|
|
|
- V = byte(dec.V.Uint64() - 35 - 2*chainID)
|
|
|
|
|
- } else {
|
|
|
|
|
- V = byte(dec.V.Uint64() - 27)
|
|
|
|
|
- }
|
|
|
|
|
- if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
|
|
|
|
|
- return ErrInvalidSig
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- *tx = Transaction{
|
|
|
|
|
- data: dec,
|
|
|
|
|
- time: time.Now(),
|
|
|
|
|
- }
|
|
|
|
|
- return nil
|
|
|
|
|
|
|
+// ChainId returns the EIP155 chain ID of the transaction. The return value will always be
|
|
|
|
|
+// non-nil. For legacy transactions which are not replay-protected, the return value is
|
|
|
|
|
+// zero.
|
|
|
|
|
+func (tx *Transaction) ChainId() *big.Int {
|
|
|
|
|
+ return tx.inner.chainID()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
|
|
|
|
-func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
|
|
|
|
|
-func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
|
|
|
|
|
-func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
|
|
|
|
- return tx.data.Price.Cmp(other.data.Price)
|
|
|
|
|
-}
|
|
|
|
|
-func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
|
|
|
|
- return tx.data.Price.Cmp(other)
|
|
|
|
|
-}
|
|
|
|
|
-func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
|
|
|
|
|
-func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
|
|
|
|
|
-func (tx *Transaction) CheckNonce() bool { return true }
|
|
|
|
|
|
|
+// Data returns the input data of the transaction.
|
|
|
|
|
+func (tx *Transaction) Data() []byte { return tx.inner.data() }
|
|
|
|
|
+
|
|
|
|
|
+// AccessList returns the access list of the transaction.
|
|
|
|
|
+func (tx *Transaction) AccessList() AccessList { return tx.inner.accessList() }
|
|
|
|
|
+
|
|
|
|
|
+// Gas returns the gas limit of the transaction.
|
|
|
|
|
+func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
|
|
|
|
|
+
|
|
|
|
|
+// GasPrice returns the gas price of the transaction.
|
|
|
|
|
+func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
|
|
|
|
|
+
|
|
|
|
|
+// Value returns the ether amount of the transaction.
|
|
|
|
|
+func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
|
|
|
|
+
|
|
|
|
|
+// Nonce returns the sender account nonce of the transaction.
|
|
|
|
|
+func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
|
|
|
|
|
|
|
|
// To returns the recipient address of the transaction.
|
|
// To returns the recipient address of the transaction.
|
|
|
-// It returns nil if the transaction is a contract creation.
|
|
|
|
|
|
|
+// For contract-creation transactions, To returns nil.
|
|
|
func (tx *Transaction) To() *common.Address {
|
|
func (tx *Transaction) To() *common.Address {
|
|
|
- if tx.data.Recipient == nil {
|
|
|
|
|
|
|
+ // Copy the pointed-to address.
|
|
|
|
|
+ ito := tx.inner.to()
|
|
|
|
|
+ if ito == nil {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
- to := *tx.data.Recipient
|
|
|
|
|
- return &to
|
|
|
|
|
|
|
+ cpy := *ito
|
|
|
|
|
+ return &cpy
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Cost returns gas * gasPrice + value.
|
|
|
|
|
+func (tx *Transaction) Cost() *big.Int {
|
|
|
|
|
+ total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
|
|
|
|
|
+ total.Add(total, tx.Value())
|
|
|
|
|
+ return total
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// RawSignatureValues returns the V, R, S signature values of the transaction.
|
|
|
|
|
+// The return values should not be modified by the caller.
|
|
|
|
|
+func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
|
|
|
|
+ return tx.inner.rawSignatureValues()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GasPriceCmp compares the gas prices of two transactions.
|
|
|
|
|
+func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
|
|
|
|
+ return tx.inner.gasPrice().Cmp(other.GasPrice())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Hash hashes the RLP encoding of tx.
|
|
|
|
|
-// It uniquely identifies the transaction.
|
|
|
|
|
|
|
+// GasPriceIntCmp compares the gas price of the transaction against the given price.
|
|
|
|
|
+func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
|
|
|
|
+ return tx.inner.gasPrice().Cmp(other)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Hash returns the transaction hash.
|
|
|
func (tx *Transaction) Hash() common.Hash {
|
|
func (tx *Transaction) Hash() common.Hash {
|
|
|
if hash := tx.hash.Load(); hash != nil {
|
|
if hash := tx.hash.Load(); hash != nil {
|
|
|
return hash.(common.Hash)
|
|
return hash.(common.Hash)
|
|
|
}
|
|
}
|
|
|
- v := rlpHash(tx)
|
|
|
|
|
- tx.hash.Store(v)
|
|
|
|
|
- return v
|
|
|
|
|
|
|
+
|
|
|
|
|
+ var h common.Hash
|
|
|
|
|
+ if tx.Type() == LegacyTxType {
|
|
|
|
|
+ h = rlpHash(tx.inner)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ h = prefixedRlpHash(tx.Type(), tx.inner)
|
|
|
|
|
+ }
|
|
|
|
|
+ tx.hash.Store(h)
|
|
|
|
|
+ return h
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Size returns the true RLP encoded storage size of the transaction, either by
|
|
// Size returns the true RLP encoded storage size of the transaction, either by
|
|
@@ -219,32 +324,11 @@ func (tx *Transaction) Size() common.StorageSize {
|
|
|
return size.(common.StorageSize)
|
|
return size.(common.StorageSize)
|
|
|
}
|
|
}
|
|
|
c := writeCounter(0)
|
|
c := writeCounter(0)
|
|
|
- rlp.Encode(&c, &tx.data)
|
|
|
|
|
|
|
+ rlp.Encode(&c, &tx.inner)
|
|
|
tx.size.Store(common.StorageSize(c))
|
|
tx.size.Store(common.StorageSize(c))
|
|
|
return common.StorageSize(c)
|
|
return common.StorageSize(c)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// AsMessage returns the transaction as a core.Message.
|
|
|
|
|
-//
|
|
|
|
|
-// AsMessage requires a signer to derive the sender.
|
|
|
|
|
-//
|
|
|
|
|
-// XXX Rename message to something less arbitrary?
|
|
|
|
|
-func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
|
|
|
|
- msg := Message{
|
|
|
|
|
- nonce: tx.data.AccountNonce,
|
|
|
|
|
- gasLimit: tx.data.GasLimit,
|
|
|
|
|
- gasPrice: new(big.Int).Set(tx.data.Price),
|
|
|
|
|
- to: tx.data.Recipient,
|
|
|
|
|
- amount: tx.data.Amount,
|
|
|
|
|
- data: tx.data.Payload,
|
|
|
|
|
- checkNonce: true,
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var err error
|
|
|
|
|
- msg.from, err = Sender(s, tx)
|
|
|
|
|
- return msg, err
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// WithSignature returns a new transaction with the given signature.
|
|
// WithSignature returns a new transaction with the given signature.
|
|
|
// This signature needs to be in the [R || S || V] format where V is 0 or 1.
|
|
// This signature needs to be in the [R || S || V] format where V is 0 or 1.
|
|
|
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
|
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
|
@@ -252,40 +336,27 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
- cpy := &Transaction{
|
|
|
|
|
- data: tx.data,
|
|
|
|
|
- time: tx.time,
|
|
|
|
|
- }
|
|
|
|
|
- cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
|
|
|
|
|
- return cpy, nil
|
|
|
|
|
|
|
+ cpy := tx.inner.copy()
|
|
|
|
|
+ cpy.setSignatureValues(signer.ChainID(), v, r, s)
|
|
|
|
|
+ return &Transaction{inner: cpy, time: tx.time}, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Cost returns amount + gasprice * gaslimit.
|
|
|
|
|
-func (tx *Transaction) Cost() *big.Int {
|
|
|
|
|
- total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
|
|
|
|
|
- total.Add(total, tx.data.Amount)
|
|
|
|
|
- return total
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// RawSignatureValues returns the V, R, S signature values of the transaction.
|
|
|
|
|
-// The return values should not be modified by the caller.
|
|
|
|
|
-func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
|
|
|
|
- return tx.data.V, tx.data.R, tx.data.S
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Transactions is a Transaction slice type for basic sorting.
|
|
|
|
|
|
|
+// Transactions implements DerivableList for transactions.
|
|
|
type Transactions []*Transaction
|
|
type Transactions []*Transaction
|
|
|
|
|
|
|
|
// Len returns the length of s.
|
|
// Len returns the length of s.
|
|
|
func (s Transactions) Len() int { return len(s) }
|
|
func (s Transactions) Len() int { return len(s) }
|
|
|
|
|
|
|
|
-// Swap swaps the i'th and the j'th element in s.
|
|
|
|
|
-func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
|
|
-
|
|
|
|
|
-// GetRlp implements Rlpable and returns the i'th element of s in rlp.
|
|
|
|
|
-func (s Transactions) GetRlp(i int) []byte {
|
|
|
|
|
- enc, _ := rlp.EncodeToBytes(s[i])
|
|
|
|
|
- return enc
|
|
|
|
|
|
|
+// EncodeIndex encodes the i'th transaction to w. Note that this does not check for errors
|
|
|
|
|
+// because we assume that *Transaction will only ever contain valid txs that were either
|
|
|
|
|
+// constructed by decoding or via public API in this package.
|
|
|
|
|
+func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) {
|
|
|
|
|
+ tx := s[i]
|
|
|
|
|
+ if tx.Type() == LegacyTxType {
|
|
|
|
|
+ rlp.Encode(w, tx.inner)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tx.encodeTyped(w)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// TxDifference returns a new set which is the difference between a and b.
|
|
// TxDifference returns a new set which is the difference between a and b.
|
|
@@ -312,7 +383,7 @@ func TxDifference(a, b Transactions) Transactions {
|
|
|
type TxByNonce Transactions
|
|
type TxByNonce Transactions
|
|
|
|
|
|
|
|
func (s TxByNonce) Len() int { return len(s) }
|
|
func (s TxByNonce) Len() int { return len(s) }
|
|
|
-func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
|
|
|
|
|
|
|
+func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() }
|
|
|
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
|
|
|
|
|
// TxByPriceAndTime implements both the sort and the heap interface, making it useful
|
|
// TxByPriceAndTime implements both the sort and the heap interface, making it useful
|
|
@@ -323,7 +394,7 @@ func (s TxByPriceAndTime) Len() int { return len(s) }
|
|
|
func (s TxByPriceAndTime) Less(i, j int) bool {
|
|
func (s TxByPriceAndTime) Less(i, j int) bool {
|
|
|
// If the prices are equal, use the time the transaction was first seen for
|
|
// If the prices are equal, use the time the transaction was first seen for
|
|
|
// deterministic sorting
|
|
// deterministic sorting
|
|
|
- cmp := s[i].data.Price.Cmp(s[j].data.Price)
|
|
|
|
|
|
|
+ cmp := s[i].GasPrice().Cmp(s[j].GasPrice())
|
|
|
if cmp == 0 {
|
|
if cmp == 0 {
|
|
|
return s[i].time.Before(s[j].time)
|
|
return s[i].time.Before(s[j].time)
|
|
|
}
|
|
}
|
|
@@ -361,13 +432,13 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
|
|
|
// Initialize a price and received time based heap with the head transactions
|
|
// Initialize a price and received time based heap with the head transactions
|
|
|
heads := make(TxByPriceAndTime, 0, len(txs))
|
|
heads := make(TxByPriceAndTime, 0, len(txs))
|
|
|
for from, accTxs := range txs {
|
|
for from, accTxs := range txs {
|
|
|
- heads = append(heads, accTxs[0])
|
|
|
|
|
// Ensure the sender address is from the signer
|
|
// Ensure the sender address is from the signer
|
|
|
- acc, _ := Sender(signer, accTxs[0])
|
|
|
|
|
- txs[acc] = accTxs[1:]
|
|
|
|
|
- if from != acc {
|
|
|
|
|
|
|
+ if acc, _ := Sender(signer, accTxs[0]); acc != from {
|
|
|
delete(txs, from)
|
|
delete(txs, from)
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
|
|
+ heads = append(heads, accTxs[0])
|
|
|
|
|
+ txs[from] = accTxs[1:]
|
|
|
}
|
|
}
|
|
|
heap.Init(&heads)
|
|
heap.Init(&heads)
|
|
|
|
|
|
|
@@ -416,10 +487,11 @@ type Message struct {
|
|
|
gasLimit uint64
|
|
gasLimit uint64
|
|
|
gasPrice *big.Int
|
|
gasPrice *big.Int
|
|
|
data []byte
|
|
data []byte
|
|
|
|
|
+ accessList AccessList
|
|
|
checkNonce bool
|
|
checkNonce bool
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
|
|
|
|
|
|
|
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool) Message {
|
|
|
return Message{
|
|
return Message{
|
|
|
from: from,
|
|
from: from,
|
|
|
to: to,
|
|
to: to,
|
|
@@ -428,15 +500,35 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
|
|
gasLimit: gasLimit,
|
|
gasLimit: gasLimit,
|
|
|
gasPrice: gasPrice,
|
|
gasPrice: gasPrice,
|
|
|
data: data,
|
|
data: data,
|
|
|
|
|
+ accessList: accessList,
|
|
|
checkNonce: checkNonce,
|
|
checkNonce: checkNonce,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (m Message) From() common.Address { return m.from }
|
|
|
|
|
-func (m Message) To() *common.Address { return m.to }
|
|
|
|
|
-func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
|
|
|
|
-func (m Message) Value() *big.Int { return m.amount }
|
|
|
|
|
-func (m Message) Gas() uint64 { return m.gasLimit }
|
|
|
|
|
-func (m Message) Nonce() uint64 { return m.nonce }
|
|
|
|
|
-func (m Message) Data() []byte { return m.data }
|
|
|
|
|
-func (m Message) CheckNonce() bool { return m.checkNonce }
|
|
|
|
|
|
|
+// AsMessage returns the transaction as a core.Message.
|
|
|
|
|
+func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
|
|
|
|
+ msg := Message{
|
|
|
|
|
+ nonce: tx.Nonce(),
|
|
|
|
|
+ gasLimit: tx.Gas(),
|
|
|
|
|
+ gasPrice: new(big.Int).Set(tx.GasPrice()),
|
|
|
|
|
+ to: tx.To(),
|
|
|
|
|
+ amount: tx.Value(),
|
|
|
|
|
+ data: tx.Data(),
|
|
|
|
|
+ accessList: tx.AccessList(),
|
|
|
|
|
+ checkNonce: true,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var err error
|
|
|
|
|
+ msg.from, err = Sender(s, tx)
|
|
|
|
|
+ return msg, err
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (m Message) From() common.Address { return m.from }
|
|
|
|
|
+func (m Message) To() *common.Address { return m.to }
|
|
|
|
|
+func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
|
|
|
|
+func (m Message) Value() *big.Int { return m.amount }
|
|
|
|
|
+func (m Message) Gas() uint64 { return m.gasLimit }
|
|
|
|
|
+func (m Message) Nonce() uint64 { return m.nonce }
|
|
|
|
|
+func (m Message) Data() []byte { return m.data }
|
|
|
|
|
+func (m Message) AccessList() AccessList { return m.accessList }
|
|
|
|
|
+func (m Message) CheckNonce() bool { return m.checkNonce }
|