Skip to content

Commit 48f9bbe

Browse files
committed
core: types: less allocations when hashing and tx handling (ethereum#21265)
1 parent 1776bf1 commit 48f9bbe

File tree

17 files changed

+304
-113
lines changed

17 files changed

+304
-113
lines changed

accounts/accounts.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
package accounts
1919

2020
import (
21+
"fmt"
2122
"math/big"
2223

2324
ethereum "github.com/XinFinOrg/XDPoSChain"
2425
"github.com/XinFinOrg/XDPoSChain/common"
2526
"github.com/XinFinOrg/XDPoSChain/core/types"
2627
"github.com/XinFinOrg/XDPoSChain/event"
28+
"golang.org/x/crypto/sha3"
2729
)
2830

2931
// Account represents an Ethereum account located at a specific location defined
@@ -148,6 +150,34 @@ type Backend interface {
148150
Subscribe(sink chan<- WalletEvent) event.Subscription
149151
}
150152

153+
// TextHash is a helper function that calculates a hash for the given message that can be
154+
// safely used to calculate a signature from.
155+
//
156+
// The hash is calulcated as
157+
//
158+
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
159+
//
160+
// This gives context to the signed message and prevents signing of transactions.
161+
func TextHash(data []byte) []byte {
162+
hash, _ := TextAndHash(data)
163+
return hash
164+
}
165+
166+
// TextAndHash is a helper function that calculates a hash for the given message that can be
167+
// safely used to calculate a signature from.
168+
//
169+
// The hash is calulcated as
170+
//
171+
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
172+
//
173+
// This gives context to the signed message and prevents signing of transactions.
174+
func TextAndHash(data []byte) ([]byte, string) {
175+
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
176+
hasher := sha3.NewLegacyKeccak256()
177+
hasher.Write([]byte(msg))
178+
return hasher.Sum(nil), msg
179+
}
180+
151181
// WalletEventType represents the different event types that can be fired by
152182
// the wallet subscription subsystem.
153183
type WalletEventType int

cmd/wnode/main.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ func processArgs() {
139139
}
140140

141141
if *asymmetricMode && len(*argPub) > 0 {
142-
pub = crypto.ToECDSAPub(common.FromHex(*argPub))
143-
if !isKeyValid(pub) {
142+
var err error
143+
if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
144144
utils.Fatalf("invalid public key")
145145
}
146146
}
@@ -337,9 +337,8 @@ func configureNode() {
337337
if b == nil {
338338
utils.Fatalf("Error: can not convert hexadecimal string")
339339
}
340-
pub = crypto.ToECDSAPub(b)
341-
if !isKeyValid(pub) {
342-
utils.Fatalf("Error: invalid public key")
340+
if pub, err = crypto.UnmarshalPubkey(b); err != nil {
341+
utils.Fatalf("Error: invalid peer public key")
343342
}
344343
}
345344
}

core/tx_list.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
259259
// Have to ensure that the new gas price is higher than the old gas
260260
// price as well as checking the percentage threshold to ensure that
261261
// this is accurate for low (Wei-level) gas price replacements
262-
if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 {
262+
if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 {
263263
return false, nil
264264
}
265265
}
@@ -383,7 +383,7 @@ func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
383383

384384
func (h priceHeap) Less(i, j int) bool {
385385
// Sort primarily by price, returning the cheaper one
386-
switch h[i].GasPrice().Cmp(h[j].GasPrice()) {
386+
switch h[i].GasPriceCmp(h[j]) {
387387
case -1:
388388
return true
389389
case 1:
@@ -460,7 +460,7 @@ func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transact
460460
continue
461461
}
462462
// Stop the discards if we've reached the threshold
463-
if tx.GasPrice().Cmp(threshold) >= 0 {
463+
if tx.GasPriceIntCmp(threshold) >= 0 {
464464
save = append(save, tx)
465465
break
466466
}
@@ -500,7 +500,7 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
500500
return false
501501
}
502502
cheapest := []*types.Transaction(*l.items)[0]
503-
return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0
503+
return cheapest.GasPriceCmp(tx) >= 0
504504
}
505505

506506
// Discard finds a number of most underpriced transactions, removes them from the

core/tx_list_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package core
1818

1919
import (
20+
"math/big"
2021
"math/rand"
2122
"testing"
2223

@@ -49,3 +50,21 @@ func TestStrictTxListAdd(t *testing.T) {
4950
}
5051
}
5152
}
53+
54+
func BenchmarkTxListAdd(t *testing.B) {
55+
// Generate a list of transactions to insert
56+
key, _ := crypto.GenerateKey()
57+
58+
txs := make(types.Transactions, 100000)
59+
for i := 0; i < len(txs); i++ {
60+
txs[i] = transaction(uint64(i), 0, key)
61+
}
62+
// Insert the transactions in a random order
63+
list := newTxList(true)
64+
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
65+
t.ResetTimer()
66+
for _, v := range rand.Perm(len(txs)) {
67+
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
68+
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil)
69+
}
70+
}

core/tx_pool.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
585585
}
586586
// Drop non-local transactions under our own minimal accepted gas price
587587
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
588-
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
588+
if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
589589
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
590590
return ErrUnderpriced
591591
}
@@ -1337,8 +1337,8 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
13371337
for _, tx := range forwards {
13381338
hash := tx.Hash()
13391339
pool.all.Remove(hash)
1340-
log.Trace("Removed old queued transaction", "hash", hash)
13411340
}
1341+
log.Trace("Removed old queued transactions", "count", len(forwards))
13421342
// Drop all transactions that are too costly (low balance or out of gas)
13431343
var number *big.Int = nil
13441344
if pool.chain.CurrentHeader() != nil {
@@ -1348,19 +1348,19 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
13481348
for _, tx := range drops {
13491349
hash := tx.Hash()
13501350
pool.all.Remove(hash)
1351-
log.Trace("Removed unpayable queued transaction", "hash", hash)
13521351
}
1352+
log.Trace("Removed unpayable queued transactions", "count", len(drops))
13531353
queuedNofundsMeter.Mark(int64(len(drops)))
13541354

13551355
// Gather all executable transactions and promote them
13561356
readies := list.Ready(pool.pendingNonces.get(addr))
13571357
for _, tx := range readies {
13581358
hash := tx.Hash()
13591359
if pool.promoteTx(addr, hash, tx) {
1360-
log.Trace("Promoting queued transaction", "hash", hash)
13611360
promoted = append(promoted, tx)
13621361
}
13631362
}
1363+
log.Trace("Promoted queued transactions", "count", len(promoted))
13641364
queuedGauge.Dec(int64(len(readies)))
13651365

13661366
// Drop all transactions over the allowed limit

core/types/block.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ import (
2323
"io"
2424
"math/big"
2525
"sort"
26+
"sync"
2627
"sync/atomic"
2728
"time"
2829
"unsafe"
2930

3031
"github.com/XinFinOrg/XDPoSChain/common"
3132
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
32-
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
33+
"github.com/XinFinOrg/XDPoSChain/crypto"
3334
"github.com/XinFinOrg/XDPoSChain/rlp"
35+
"golang.org/x/crypto/sha3"
3436
)
3537

3638
var (
@@ -155,10 +157,19 @@ func (h *Header) Size() common.StorageSize {
155157
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
156158
}
157159

160+
// hasherPool holds LegacyKeccak hashers.
161+
var hasherPool = sync.Pool{
162+
New: func() interface{} {
163+
return sha3.NewLegacyKeccak256()
164+
},
165+
}
166+
158167
func rlpHash(x interface{}) (h common.Hash) {
159-
hw := sha3.NewKeccak256()
160-
rlp.Encode(hw, x)
161-
hw.Sum(h[:0])
168+
sha := hasherPool.Get().(crypto.KeccakState)
169+
defer hasherPool.Put(sha)
170+
sha.Reset()
171+
rlp.Encode(sha, x)
172+
sha.Read(h[:])
162173
return h
163174
}
164175

core/types/transaction.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,15 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
188188
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
189189
func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
190190
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
191-
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
192-
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
193-
func (tx *Transaction) CheckNonce() bool { return true }
191+
func (tx *Transaction) GasPriceCmp(other *Transaction) int {
192+
return tx.data.Price.Cmp(other.data.Price)
193+
}
194+
func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
195+
return tx.data.Price.Cmp(other)
196+
}
197+
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
198+
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
199+
func (tx *Transaction) CheckNonce() bool { return true }
194200

195201
// To returns the recipient address of the transaction.
196202
// It returns nil if the transaction is a contract creation.

0 commit comments

Comments
 (0)