aboutsummaryrefslogtreecommitdiff
path: root/core/types/transaction.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/types/transaction.go')
-rw-r--r--core/types/transaction.go75
1 files changed, 49 insertions, 26 deletions
diff --git a/core/types/transaction.go b/core/types/transaction.go
index d9ada38..ec19744 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -22,11 +22,12 @@ import (
"io"
"math/big"
"sync/atomic"
+ "time"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
)
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
@@ -36,7 +37,9 @@ var (
)
type Transaction struct {
- data txdata
+ data txdata // Consensus contents of a transaction
+ time time.Time // Time first seen locally (spam avoidance)
+
// caches
hash atomic.Value
size atomic.Value
@@ -100,8 +103,10 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit
if gasPrice != nil {
d.Price.Set(gasPrice)
}
-
- return &Transaction{data: d}
+ return &Transaction{
+ data: d,
+ time: time.Now(),
+ }
}
// ChainId returns which chain id this transaction was signed for (if at all)
@@ -134,8 +139,8 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
err := s.Decode(&tx.data)
if err == nil {
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
+ tx.time = time.Now()
}
-
return err
}
@@ -153,7 +158,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
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
@@ -167,17 +171,25 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return ErrInvalidSig
}
}
-
- *tx = Transaction{data: dec}
+ *tx = Transaction{
+ data: dec,
+ time: time.Now(),
+ }
return nil
}
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) 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 }
+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 }
// To returns the recipient address of the transaction.
// It returns nil if the transaction is a contract creation.
@@ -240,7 +252,10 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
if err != nil {
return nil, err
}
- cpy := &Transaction{data: tx.data}
+ cpy := &Transaction{
+ data: tx.data,
+ time: tx.time,
+ }
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
return cpy, nil
}
@@ -300,19 +315,27 @@ 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) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-// TxByPrice implements both the sort and the heap interface, making it useful
+// TxByPriceAndTime implements both the sort and the heap interface, making it useful
// for all at once sorting as well as individually adding and removing elements.
-type TxByPrice Transactions
-
-func (s TxByPrice) Len() int { return len(s) }
-func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 }
-func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+type TxByPriceAndTime Transactions
+
+func (s TxByPriceAndTime) Len() int { return len(s) }
+func (s TxByPriceAndTime) Less(i, j int) bool {
+ // If the prices are equal, use the time the transaction was first seen for
+ // deterministic sorting
+ cmp := s[i].data.Price.Cmp(s[j].data.Price)
+ if cmp == 0 {
+ return s[i].time.Before(s[j].time)
+ }
+ return cmp > 0
+}
+func (s TxByPriceAndTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s *TxByPrice) Push(x interface{}) {
+func (s *TxByPriceAndTime) Push(x interface{}) {
*s = append(*s, x.(*Transaction))
}
-func (s *TxByPrice) Pop() interface{} {
+func (s *TxByPriceAndTime) Pop() interface{} {
old := *s
n := len(old)
x := old[n-1]
@@ -325,7 +348,7 @@ func (s *TxByPrice) Pop() interface{} {
// entire batches of transactions for non-executable accounts.
type TransactionsByPriceAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
- heads TxByPrice // Next transaction for each unique account (price heap)
+ heads TxByPriceAndTime // Next transaction for each unique account (price heap)
signer Signer // Signer for the set of transactions
}
@@ -335,8 +358,8 @@ type TransactionsByPriceAndNonce struct {
// Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor.
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
- // Initialize a price based heap with the head transactions
- heads := make(TxByPrice, 0, len(txs))
+ // Initialize a price and received time based heap with the head transactions
+ heads := make(TxByPriceAndTime, 0, len(txs))
for from, accTxs := range txs {
heads = append(heads, accTxs[0])
// Ensure the sender address is from the signer