core: types: less allocations when hashing and tx handling (#21265)

This commit is contained in:
Daniel Liu 2024-05-13 12:30:40 +08:00
parent 1776bf1313
commit 48f9bbed50
17 changed files with 305 additions and 114 deletions

View file

@ -18,12 +18,14 @@
package accounts package accounts
import ( import (
"fmt"
"math/big" "math/big"
ethereum "github.com/XinFinOrg/XDPoSChain" ethereum "github.com/XinFinOrg/XDPoSChain"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/event"
"golang.org/x/crypto/sha3"
) )
// Account represents an Ethereum account located at a specific location defined // Account represents an Ethereum account located at a specific location defined
@ -148,6 +150,34 @@ type Backend interface {
Subscribe(sink chan<- WalletEvent) event.Subscription Subscribe(sink chan<- WalletEvent) event.Subscription
} }
// TextHash is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from.
//
// The hash is calulcated as
//
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
//
// This gives context to the signed message and prevents signing of transactions.
func TextHash(data []byte) []byte {
hash, _ := TextAndHash(data)
return hash
}
// TextAndHash is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from.
//
// The hash is calulcated as
//
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
//
// This gives context to the signed message and prevents signing of transactions.
func TextAndHash(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
hasher := sha3.NewLegacyKeccak256()
hasher.Write([]byte(msg))
return hasher.Sum(nil), msg
}
// WalletEventType represents the different event types that can be fired by // WalletEventType represents the different event types that can be fired by
// the wallet subscription subsystem. // the wallet subscription subsystem.
type WalletEventType int type WalletEventType int

View file

@ -139,8 +139,8 @@ func processArgs() {
} }
if *asymmetricMode && len(*argPub) > 0 { if *asymmetricMode && len(*argPub) > 0 {
pub = crypto.ToECDSAPub(common.FromHex(*argPub)) var err error
if !isKeyValid(pub) { if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
utils.Fatalf("invalid public key") utils.Fatalf("invalid public key")
} }
} }
@ -337,9 +337,8 @@ func configureNode() {
if b == nil { if b == nil {
utils.Fatalf("Error: can not convert hexadecimal string") utils.Fatalf("Error: can not convert hexadecimal string")
} }
pub = crypto.ToECDSAPub(b) if pub, err = crypto.UnmarshalPubkey(b); err != nil {
if !isKeyValid(pub) { utils.Fatalf("Error: invalid peer public key")
utils.Fatalf("Error: invalid public key")
} }
} }
} }

View file

@ -259,7 +259,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
// Have to ensure that the new gas price is higher than the old gas // Have to ensure that the new gas price is higher than the old gas
// price as well as checking the percentage threshold to ensure that // price as well as checking the percentage threshold to ensure that
// this is accurate for low (Wei-level) gas price replacements // this is accurate for low (Wei-level) gas price replacements
if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 {
return false, nil return false, nil
} }
} }
@ -383,7 +383,7 @@ func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h priceHeap) Less(i, j int) bool { func (h priceHeap) Less(i, j int) bool {
// Sort primarily by price, returning the cheaper one // Sort primarily by price, returning the cheaper one
switch h[i].GasPrice().Cmp(h[j].GasPrice()) { switch h[i].GasPriceCmp(h[j]) {
case -1: case -1:
return true return true
case 1: case 1:
@ -460,7 +460,7 @@ func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transact
continue continue
} }
// Stop the discards if we've reached the threshold // Stop the discards if we've reached the threshold
if tx.GasPrice().Cmp(threshold) >= 0 { if tx.GasPriceIntCmp(threshold) >= 0 {
save = append(save, tx) save = append(save, tx)
break break
} }
@ -500,7 +500,7 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
return false return false
} }
cheapest := []*types.Transaction(*l.items)[0] cheapest := []*types.Transaction(*l.items)[0]
return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0 return cheapest.GasPriceCmp(tx) >= 0
} }
// Discard finds a number of most underpriced transactions, removes them from the // Discard finds a number of most underpriced transactions, removes them from the

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"math/big"
"math/rand" "math/rand"
"testing" "testing"
@ -49,3 +50,21 @@ func TestStrictTxListAdd(t *testing.T) {
} }
} }
} }
func BenchmarkTxListAdd(t *testing.B) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
txs := make(types.Transactions, 100000)
for i := 0; i < len(txs); i++ {
txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
list := newTxList(true)
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
t.ResetTimer()
for _, v := range rand.Perm(len(txs)) {
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil)
}
}

View file

@ -585,7 +585,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
} }
// Drop non-local transactions under our own minimal accepted gas price // Drop non-local transactions under our own minimal accepted gas price
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 { if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) { if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
return ErrUnderpriced return ErrUnderpriced
} }
@ -1337,8 +1337,8 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range forwards { for _, tx := range forwards {
hash := tx.Hash() hash := tx.Hash()
pool.all.Remove(hash) pool.all.Remove(hash)
log.Trace("Removed old queued transaction", "hash", hash)
} }
log.Trace("Removed old queued transactions", "count", len(forwards))
// Drop all transactions that are too costly (low balance or out of gas) // Drop all transactions that are too costly (low balance or out of gas)
var number *big.Int = nil var number *big.Int = nil
if pool.chain.CurrentHeader() != nil { if pool.chain.CurrentHeader() != nil {
@ -1348,8 +1348,8 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range drops { for _, tx := range drops {
hash := tx.Hash() hash := tx.Hash()
pool.all.Remove(hash) pool.all.Remove(hash)
log.Trace("Removed unpayable queued transaction", "hash", hash)
} }
log.Trace("Removed unpayable queued transactions", "count", len(drops))
queuedNofundsMeter.Mark(int64(len(drops))) queuedNofundsMeter.Mark(int64(len(drops)))
// Gather all executable transactions and promote them // Gather all executable transactions and promote them
@ -1357,10 +1357,10 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range readies { for _, tx := range readies {
hash := tx.Hash() hash := tx.Hash()
if pool.promoteTx(addr, hash, tx) { if pool.promoteTx(addr, hash, tx) {
log.Trace("Promoting queued transaction", "hash", hash)
promoted = append(promoted, tx) promoted = append(promoted, tx)
} }
} }
log.Trace("Promoted queued transactions", "count", len(promoted))
queuedGauge.Dec(int64(len(readies))) queuedGauge.Dec(int64(len(readies)))
// Drop all transactions over the allowed limit // Drop all transactions over the allowed limit

View file

@ -23,14 +23,16 @@ import (
"io" "io"
"math/big" "math/big"
"sort" "sort"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
"unsafe" "unsafe"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"golang.org/x/crypto/sha3"
) )
var ( var (
@ -155,10 +157,19 @@ func (h *Header) Size() common.StorageSize {
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8) return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
} }
// hasherPool holds LegacyKeccak hashers.
var hasherPool = sync.Pool{
New: func() interface{} {
return sha3.NewLegacyKeccak256()
},
}
func rlpHash(x interface{}) (h common.Hash) { func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewKeccak256() sha := hasherPool.Get().(crypto.KeccakState)
rlp.Encode(hw, x) defer hasherPool.Put(sha)
hw.Sum(h[:0]) sha.Reset()
rlp.Encode(sha, x)
sha.Read(h[:])
return h return h
} }

View file

@ -188,9 +188,15 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } 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) 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) GasPriceCmp(other *Transaction) int {
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } return tx.data.Price.Cmp(other.data.Price)
func (tx *Transaction) CheckNonce() bool { return true } }
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. // To returns the recipient address of the transaction.
// It returns nil if the transaction is a contract creation. // It returns nil if the transaction is a contract creation.

View file

@ -17,57 +17,81 @@
package crypto package crypto
import ( import (
"bufio"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"hash"
"io" "io"
"io/ioutil"
"math/big" "math/big"
"os" "os"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"golang.org/x/crypto/sha3"
) )
//SignatureLength indicates the byte length required to carry a signature with recovery id.
const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id
// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
const RecoveryIDOffset = 64
// DigestLength sets the signature digest exact length
const DigestLength = 32
var ( var (
secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2)) secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
) )
var errInvalidPubkey = errors.New("invalid secp256k1 public key")
// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
type KeccakState interface {
hash.Hash
Read([]byte) (int, error)
}
// Keccak256 calculates and returns the Keccak256 hash of the input data. // Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte { func Keccak256(data ...[]byte) []byte {
d := sha3.NewKeccak256() b := make([]byte, 32)
d := sha3.NewLegacyKeccak256().(KeccakState)
for _, b := range data { for _, b := range data {
d.Write(b) d.Write(b)
} }
return d.Sum(nil) d.Read(b)
return b
} }
// Keccak256Hash calculates and returns the Keccak256 hash of the input data, // Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure. // converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h common.Hash) { func Keccak256Hash(data ...[]byte) (h common.Hash) {
d := sha3.NewKeccak256() d := sha3.NewLegacyKeccak256().(KeccakState)
for _, b := range data { for _, b := range data {
d.Write(b) d.Write(b)
} }
d.Sum(h[:0]) d.Read(h[:])
return h return h
} }
// Keccak512 calculates and returns the Keccak512 hash of the input data. // Keccak512 calculates and returns the Keccak512 hash of the input data.
func Keccak512(data ...[]byte) []byte { func Keccak512(data ...[]byte) []byte {
d := sha3.NewKeccak512() d := sha3.NewLegacyKeccak512()
for _, b := range data { for _, b := range data {
d.Write(b) d.Write(b)
} }
return d.Sum(nil) return d.Sum(nil)
} }
// Creates an ethereum address given the bytes and the nonce // CreateAddress creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address { func CreateAddress(b common.Address, nonce uint64) common.Address {
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce}) data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
return common.BytesToAddress(Keccak256(data)[12:]) return common.BytesToAddress(Keccak256(data)[12:])
@ -104,7 +128,7 @@ func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
priv.D = new(big.Int).SetBytes(d) priv.D = new(big.Int).SetBytes(d)
// The priv.D must < N // The priv.D must < N
if priv.D.Cmp(secp256k1_N) >= 0 { if priv.D.Cmp(secp256k1N) >= 0 {
return nil, fmt.Errorf("invalid private key, >=N") return nil, fmt.Errorf("invalid private key, >=N")
} }
// The priv.D must not be zero or negative. // The priv.D must not be zero or negative.
@ -127,12 +151,13 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8) return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
} }
func ToECDSAPub(pub []byte) *ecdsa.PublicKey { // UnmarshalPubkey converts bytes to a secp256k1 public key.
if len(pub) == 0 { func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
return nil
}
x, y := elliptic.Unmarshal(S256(), pub) x, y := elliptic.Unmarshal(S256(), pub)
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y} if x == nil {
return nil, errInvalidPubkey
}
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
} }
func FromECDSAPub(pub *ecdsa.PublicKey) []byte { func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
@ -145,38 +170,77 @@ func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
// HexToECDSA parses a secp256k1 private key. // HexToECDSA parses a secp256k1 private key.
func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) { func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) {
b, err := hex.DecodeString(hexkey) b, err := hex.DecodeString(hexkey)
if err != nil { if byteErr, ok := err.(hex.InvalidByteError); ok {
return nil, errors.New("invalid hex string") return nil, fmt.Errorf("invalid hex character %q in private key", byte(byteErr))
} else if err != nil {
return nil, errors.New("invalid hex data for private key")
} }
return ToECDSA(b) return ToECDSA(b)
} }
// LoadECDSA loads a secp256k1 private key from the given file. // LoadECDSA loads a secp256k1 private key from the given file.
func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
buf := make([]byte, 64)
fd, err := os.Open(file) fd, err := os.Open(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer fd.Close() defer fd.Close()
if _, err := io.ReadFull(fd, buf); err != nil {
r := bufio.NewReader(fd)
buf := make([]byte, 64)
n, err := readASCII(buf, r)
if err != nil {
return nil, err
} else if n != len(buf) {
return nil, fmt.Errorf("key file too short, want 64 hex characters")
}
if err := checkKeyFileEnd(r); err != nil {
return nil, err return nil, err
} }
key, err := hex.DecodeString(string(buf)) return HexToECDSA(string(buf))
if err != nil { }
return nil, err
// readASCII reads into 'buf', stopping when the buffer is full or
// when a non-printable control character is encountered.
func readASCII(buf []byte, r *bufio.Reader) (n int, err error) {
for ; n < len(buf); n++ {
buf[n], err = r.ReadByte()
switch {
case err == io.EOF || buf[n] < '!':
return n, nil
case err != nil:
return n, err
}
}
return n, nil
}
// checkKeyFileEnd skips over additional newlines at the end of a key file.
func checkKeyFileEnd(r *bufio.Reader) error {
for i := 0; ; i++ {
b, err := r.ReadByte()
switch {
case err == io.EOF:
return nil
case err != nil:
return err
case b != '\n' && b != '\r':
return fmt.Errorf("invalid character %q at end of key file", b)
case i >= 2:
return errors.New("key file too long, want 64 hex characters")
}
} }
return ToECDSA(key)
} }
// SaveECDSA saves a secp256k1 private key to the given file with // SaveECDSA saves a secp256k1 private key to the given file with
// restrictive permissions. The key data is saved hex-encoded. // restrictive permissions. The key data is saved hex-encoded.
func SaveECDSA(file string, key *ecdsa.PrivateKey) error { func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
k := hex.EncodeToString(FromECDSA(key)) k := hex.EncodeToString(FromECDSA(key))
return os.WriteFile(file, []byte(k), 0600) return ioutil.WriteFile(file, []byte(k), 0600)
} }
// GenerateKey generates a new private key.
func GenerateKey() (*ecdsa.PrivateKey, error) { func GenerateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(S256(), rand.Reader) return ecdsa.GenerateKey(S256(), rand.Reader)
} }
@ -189,11 +253,11 @@ func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
} }
// reject upper range of s values (ECDSA malleability) // reject upper range of s values (ECDSA malleability)
// see discussion in secp256k1/libsecp256k1/include/secp256k1.h // see discussion in secp256k1/libsecp256k1/include/secp256k1.h
if homestead && s.Cmp(secp256k1_halfN) > 0 { if homestead && s.Cmp(secp256k1halfN) > 0 {
return false return false
} }
// Frontier: allow s to be in full N range // Frontier: allow s to be in full N range
return r.Cmp(secp256k1_N) < 0 && s.Cmp(secp256k1_N) < 0 && (v == 0 || v == 1) return r.Cmp(secp256k1N) < 0 && s.Cmp(secp256k1N) < 0 && (v == 0 || v == 1)
} }
func PubkeyToAddress(p ecdsa.PublicKey) common.Address { func PubkeyToAddress(p ecdsa.PublicKey) common.Address {

View file

@ -20,11 +20,14 @@ import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/hex" "encoding/hex"
"io/ioutil"
"math/big" "math/big"
"os" "os"
"reflect"
"testing" "testing"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
) )
var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791" var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
@ -55,6 +58,33 @@ func BenchmarkSha3(b *testing.B) {
} }
} }
func TestUnmarshalPubkey(t *testing.T) {
key, err := UnmarshalPubkey(nil)
if err != errInvalidPubkey || key != nil {
t.Fatalf("expected error, got %v, %v", err, key)
}
key, err = UnmarshalPubkey([]byte{1, 2, 3})
if err != errInvalidPubkey || key != nil {
t.Fatalf("expected error, got %v, %v", err, key)
}
var (
enc, _ = hex.DecodeString("04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
dec = &ecdsa.PublicKey{
Curve: S256(),
X: hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
Y: hexutil.MustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"),
}
)
key, err = UnmarshalPubkey(enc)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if !reflect.DeepEqual(key, dec) {
t.Fatal("wrong result")
}
}
func TestSign(t *testing.T) { func TestSign(t *testing.T) {
key, _ := HexToECDSA(testPrivHex) key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex) addr := common.HexToAddress(testAddrHex)
@ -68,7 +98,7 @@ func TestSign(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("ECRecover error: %s", err) t.Errorf("ECRecover error: %s", err)
} }
pubKey := ToECDSAPub(recoveredPub) pubKey, _ := UnmarshalPubkey(recoveredPub)
recoveredAddr := PubkeyToAddress(*pubKey) recoveredAddr := PubkeyToAddress(*pubKey)
if addr != recoveredAddr { if addr != recoveredAddr {
t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr) t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)
@ -109,39 +139,82 @@ func TestNewContractAddress(t *testing.T) {
checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2) checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2)
} }
func TestLoadECDSAFile(t *testing.T) { func TestLoadECDSA(t *testing.T) {
keyBytes := common.FromHex(testPrivHex) tests := []struct {
fileName0 := "test_key0" input string
fileName1 := "test_key1" err string
checkKey := func(k *ecdsa.PrivateKey) { }{
checkAddr(t, PubkeyToAddress(k.PublicKey), common.HexToAddress(testAddrHex)) // good
loadedKeyBytes := FromECDSA(k) {input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"},
if !bytes.Equal(loadedKeyBytes, keyBytes) { {input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n"},
t.Fatalf("private key mismatch: want: %x have: %x", keyBytes, loadedKeyBytes) {input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n\r"},
{input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\r\n"},
{input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n\n"},
{input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n\r"},
// bad
{
input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde",
err: "key file too short, want 64 hex characters",
},
{
input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde\n",
err: "key file too short, want 64 hex characters",
},
{
input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeX",
err: "invalid hex character 'X' in private key",
},
{
input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefX",
err: "invalid character 'X' at end of key file",
},
{
input: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n\n\n",
err: "key file too long, want 64 hex characters",
},
}
for _, test := range tests {
f, err := ioutil.TempFile("", "loadecdsa_test.*.txt")
if err != nil {
t.Fatal(err)
}
filename := f.Name()
f.WriteString(test.input)
f.Close()
_, err = LoadECDSA(filename)
switch {
case err != nil && test.err == "":
t.Fatalf("unexpected error for input %q:\n %v", test.input, err)
case err != nil && err.Error() != test.err:
t.Fatalf("wrong error for input %q:\n %v", test.input, err)
case err == nil && test.err != "":
t.Fatalf("LoadECDSA did not return error for input %q", test.input)
} }
} }
}
os.WriteFile(fileName0, []byte(testPrivHex), 0600) func TestSaveECDSA(t *testing.T) {
defer os.Remove(fileName0) f, err := ioutil.TempFile("", "saveecdsa_test.*.txt")
key0, err := LoadECDSA(fileName0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
checkKey(key0) file := f.Name()
f.Close()
defer os.Remove(file)
// again, this time with SaveECDSA instead of manual save: key, _ := HexToECDSA(testPrivHex)
err = SaveECDSA(fileName1, key0) if err := SaveECDSA(file, key); err != nil {
t.Fatal(err)
}
loaded, err := LoadECDSA(file)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer os.Remove(fileName1) if !reflect.DeepEqual(key, loaded) {
t.Fatal("loaded key not equal to saved key")
key1, err := LoadECDSA(fileName1)
if err != nil {
t.Fatal(err)
} }
checkKey(key1)
} }
func TestValidateSignatureValues(t *testing.T) { func TestValidateSignatureValues(t *testing.T) {
@ -153,7 +226,7 @@ func TestValidateSignatureValues(t *testing.T) {
minusOne := big.NewInt(-1) minusOne := big.NewInt(-1)
one := common.Big1 one := common.Big1
zero := common.Big0 zero := common.Big0
secp256k1nMinus1 := new(big.Int).Sub(secp256k1_N, common.Big1) secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1)
// correct v,r,s // correct v,r,s
check(true, 0, one, one) check(true, 0, one, one)
@ -180,9 +253,9 @@ func TestValidateSignatureValues(t *testing.T) {
// correct sig with max r,s // correct sig with max r,s
check(true, 0, secp256k1nMinus1, secp256k1nMinus1) check(true, 0, secp256k1nMinus1, secp256k1nMinus1)
// correct v, combinations of incorrect r,s at upper limit // correct v, combinations of incorrect r,s at upper limit
check(false, 0, secp256k1_N, secp256k1nMinus1) check(false, 0, secp256k1N, secp256k1nMinus1)
check(false, 0, secp256k1nMinus1, secp256k1_N) check(false, 0, secp256k1nMinus1, secp256k1N)
check(false, 0, secp256k1_N, secp256k1_N) check(false, 0, secp256k1N, secp256k1N)
// current callers ensures r,s cannot be negative, but let's test for that too // current callers ensures r,s cannot be negative, but let's test for that too
// as crypto package could be used stand-alone // as crypto package could be used stand-alone

View file

@ -162,7 +162,7 @@ type transactionsByGasPrice []*types.Transaction
func (t transactionsByGasPrice) Len() int { return len(t) } func (t transactionsByGasPrice) Len() int { return len(t) }
func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 } func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPriceCmp(t[j]) < 0 }
// getBlockPrices calculates the lowest transaction gas price in a given block // getBlockPrices calculates the lowest transaction gas price in a given block
// and sends it to the result channel. If the block is empty, price is nil. // and sends it to the result channel. If the block is empty, price is nil.

View file

@ -473,21 +473,19 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
// //
// https://github.com/XinFinOrg/XDPoSChain/wiki/Management-APIs#personal_ecRecover // https://github.com/XinFinOrg/XDPoSChain/wiki/Management-APIs#personal_ecRecover
func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
if len(sig) != 65 { if len(sig) != crypto.SignatureLength {
return common.Address{}, fmt.Errorf("signature must be 65 bytes long") return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength)
} }
if sig[64] != 27 && sig[64] != 28 { if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 {
return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
} }
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
rpk, err := crypto.Ecrecover(signHash(data), sig) rpk, err := crypto.SigToPub(accounts.TextHash(data), sig)
if err != nil { if err != nil {
return common.Address{}, err return common.Address{}, err
} }
pubKey := crypto.ToECDSAPub(rpk) return crypto.PubkeyToAddress(*rpk), nil
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
return recoveredAddr, nil
} }
// SignAndSendTransaction was renamed to SendTransaction. This method is deprecated // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated

View file

@ -528,9 +528,9 @@ func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) {
return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey)) return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey))
} }
// TODO: fewer pointless conversions // TODO: fewer pointless conversions
pub := crypto.ToECDSAPub(pubKey65) pub, err := crypto.UnmarshalPubkey(pubKey65)
if pub.X == nil { if err != nil {
return nil, fmt.Errorf("invalid public key") return nil, err
} }
return ecies.ImportECDSAPublic(pub), nil return ecies.ImportECDSAPublic(pub), nil
} }

View file

@ -80,7 +80,7 @@ type PayProfile struct {
lock sync.RWMutex lock sync.RWMutex
} }
//create params with default values // create params with default values
func NewDefaultSwapParams() *SwapParams { func NewDefaultSwapParams() *SwapParams {
return &SwapParams{ return &SwapParams{
PayProfile: &PayProfile{}, PayProfile: &PayProfile{},
@ -102,8 +102,8 @@ func NewDefaultSwapParams() *SwapParams {
} }
} }
//this can only finally be set after all config options (file, cmd line, env vars) // this can only finally be set after all config options (file, cmd line, env vars)
//have been evaluated // have been evaluated
func (self *SwapParams) Init(contract common.Address, prvkey *ecdsa.PrivateKey) { func (self *SwapParams) Init(contract common.Address, prvkey *ecdsa.PrivateKey) {
pubkey := &prvkey.PublicKey pubkey := &prvkey.PublicKey
@ -141,8 +141,12 @@ func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend,
if !ok { if !ok {
log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)) log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err))
} else { } else {
pub, err := crypto.UnmarshalPubkey(common.FromHex(remote.PublicKey))
if err != nil {
return nil, err
}
// remote contract valid, create inbox // remote contract valid, create inbox
in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend) in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, pub, backend)
if err != nil { if err != nil {
log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)) log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err))
} }

View file

@ -22,6 +22,7 @@ import (
"sync" "sync"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -46,7 +47,7 @@ type leaf struct {
// processed sequentially - onleaf will never be called in parallel or out of order. // processed sequentially - onleaf will never be called in parallel or out of order.
type committer struct { type committer struct {
tmp sliceBuffer tmp sliceBuffer
sha keccakState sha crypto.KeccakState
onleaf LeafCallback onleaf LeafCallback
leafCh chan *leaf leafCh chan *leaf
@ -57,7 +58,7 @@ var committerPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return &committer{ return &committer{
tmp: make(sliceBuffer, 0, 550), // cap is as large as a full FullNode. tmp: make(sliceBuffer, 0, 550), // cap is as large as a full FullNode.
sha: sha3.NewLegacyKeccak256().(keccakState), sha: sha3.NewLegacyKeccak256().(crypto.KeccakState),
} }
}, },
} }

View file

@ -17,21 +17,13 @@
package trie package trie
import ( import (
"hash"
"sync" "sync"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
type keccakState interface {
hash.Hash
Read([]byte) (int, error)
}
type sliceBuffer []byte type sliceBuffer []byte
func (b *sliceBuffer) Write(data []byte) (n int, err error) { func (b *sliceBuffer) Write(data []byte) (n int, err error) {
@ -46,7 +38,7 @@ func (b *sliceBuffer) Reset() {
// hasher is a type used for the trie Hash operation. A hasher has some // hasher is a type used for the trie Hash operation. A hasher has some
// internal preallocated temp space // internal preallocated temp space
type hasher struct { type hasher struct {
sha keccakState sha crypto.KeccakState
tmp sliceBuffer tmp sliceBuffer
parallel bool // Whether to use paralallel threads when hashing parallel bool // Whether to use paralallel threads when hashing
} }
@ -56,7 +48,7 @@ var hasherPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return &hasher{ return &hasher{
tmp: make(sliceBuffer, 0, 550), // cap is as large as a full FullNode. tmp: make(sliceBuffer, 0, 550), // cap is as large as a full FullNode.
sha: sha3.NewLegacyKeccak256().(keccakState), sha: sha3.NewLegacyKeccak256().(crypto.KeccakState),
} }
}, },
} }

View file

@ -256,8 +256,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
// Set asymmetric key that is used to encrypt the message // Set asymmetric key that is used to encrypt the message
if pubKeyGiven { if pubKeyGiven {
params.Dst = crypto.ToECDSAPub(req.PublicKey) if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
if !ValidatePublicKey(params.Dst) {
return false, ErrInvalidPublicKey return false, ErrInvalidPublicKey
} }
} }
@ -333,8 +332,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
} }
if len(crit.Sig) > 0 { if len(crit.Sig) > 0 {
filter.Src = crypto.ToECDSAPub(crit.Sig) if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
if !ValidatePublicKey(filter.Src) {
return nil, ErrInvalidSigningPubKey return nil, ErrInvalidSigningPubKey
} }
} }
@ -517,8 +515,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
} }
if len(req.Sig) > 0 { if len(req.Sig) > 0 {
src = crypto.ToECDSAPub(req.Sig) if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
if !ValidatePublicKey(src) {
return "", ErrInvalidSigningPubKey return "", ErrInvalidSigningPubKey
} }
} }

View file

@ -275,8 +275,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
// Set asymmetric key that is used to encrypt the message // Set asymmetric key that is used to encrypt the message
if pubKeyGiven { if pubKeyGiven {
params.Dst = crypto.ToECDSAPub(req.PublicKey) if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
if !ValidatePublicKey(params.Dst) {
return false, ErrInvalidPublicKey return false, ErrInvalidPublicKey
} }
} }
@ -352,8 +351,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
} }
if len(crit.Sig) > 0 { if len(crit.Sig) > 0 {
filter.Src = crypto.ToECDSAPub(crit.Sig) if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
if !ValidatePublicKey(filter.Src) {
return nil, ErrInvalidSigningPubKey return nil, ErrInvalidSigningPubKey
} }
} }
@ -536,8 +534,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
} }
if len(req.Sig) > 0 { if len(req.Sig) > 0 {
src = crypto.ToECDSAPub(req.Sig) if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
if !ValidatePublicKey(src) {
return "", ErrInvalidSigningPubKey return "", ErrInvalidSigningPubKey
} }
} }