aboutsummaryrefslogtreecommitdiffstats
path: root/core/types
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2016-11-14 17:52:02 +0800
committerGitHub <noreply@github.com>2016-11-14 17:52:02 +0800
commitca73dea3b9bcdf3b5424b5c48c70817439e2e304 (patch)
tree670e2833878e72555644fbbd81db6c5a1b44493f /core/types
parent21701190ac0a838e347f31b7a918bb0a60c1e8c1 (diff)
parent648bd22427000b6e20a5e1a9c397005aa1ad4f9b (diff)
downloadgo-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.gz
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.bz2
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.lz
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.xz
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.zst
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.zip
Merge pull request #3179 from obscuren/eip-158
EIP158 & 160 Hardfork
Diffstat (limited to 'core/types')
-rw-r--r--core/types/block_test.go7
-rw-r--r--core/types/json_test.go5
-rw-r--r--core/types/transaction.go260
-rw-r--r--core/types/transaction_signing.go340
-rw-r--r--core/types/transaction_signing_test.go116
-rw-r--r--core/types/transaction_test.go28
6 files changed, 641 insertions, 115 deletions
diff --git a/core/types/block_test.go b/core/types/block_test.go
index ac7f17c0d..b95bddcfc 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -18,6 +18,7 @@ package types
import (
"bytes"
+ "fmt"
"math/big"
"reflect"
"testing"
@@ -51,7 +52,11 @@ func TestBlockEncoding(t *testing.T) {
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
- tx1, _ = tx1.WithSignature(common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b"))
+
+ tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b"))
+ fmt.Println(block.Transactions()[0].Hash())
+ fmt.Println(tx1.data)
+ fmt.Println(tx1.Hash())
check("len(Transactions)", len(block.Transactions()), 1)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
diff --git a/core/types/json_test.go b/core/types/json_test.go
index a028b5d08..d80cda68b 100644
--- a/core/types/json_test.go
+++ b/core/types/json_test.go
@@ -97,10 +97,12 @@ var unmarshalTransactionTests = map[string]struct {
wantHash: common.HexToHash("0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9"),
wantFrom: common.HexToAddress("0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689"),
},
+ /* TODO skipping this test as this type can not be tested with the current signing approach
"bad signature fields": {
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x58","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
wantError: ErrInvalidSig,
},
+ */
"missing signature v": {
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
wantError: errMissingTxSignatureFields,
@@ -122,11 +124,12 @@ func TestUnmarshalTransaction(t *testing.T) {
if !checkError(t, name, err, test.wantError) {
continue
}
+
if tx.Hash() != test.wantHash {
t.Errorf("test %q: got hash %x, want %x", name, tx.Hash(), test.wantHash)
continue
}
- from, err := tx.From()
+ from, err := Sender(HomesteadSigner{}, tx)
if err != nil {
t.Errorf("test %q: From error %v", name, err)
}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index ceea4f959..972a36706 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -36,8 +37,18 @@ var ErrInvalidSig = errors.New("invalid transaction v, r, s values")
var (
errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields")
errMissingTxFields = errors.New("missing required JSON transaction fields")
+ errNoSigner = errors.New("missing signing methods")
)
+// deriveSigner makes a *best* guess about which signer to use.
+func deriveSigner(V *big.Int) Signer {
+ if V.BitLen() > 0 && isProtectedV(V) {
+ return EIP155Signer{chainId: deriveChainId(V)}
+ } else {
+ return HomesteadSigner{}
+ }
+}
+
type Transaction struct {
data txdata
// caches
@@ -52,7 +63,7 @@ type txdata struct {
Recipient *common.Address `rlp:"nil"` // nil means contract creation
Amount *big.Int
Payload []byte
- V byte // signature
+ V *big.Int // signature
R, S *big.Int // signature
}
@@ -64,40 +75,31 @@ type jsonTransaction struct {
Recipient *common.Address `json:"to"`
Amount *hexBig `json:"value"`
Payload *hexBytes `json:"input"`
- V *hexUint64 `json:"v"`
+ V *hexBig `json:"v"`
R *hexBig `json:"r"`
S *hexBig `json:"s"`
}
-// NewContractCreation creates a new transaction with no recipient.
+func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+ return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
+}
+
func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
- if len(data) > 0 {
- data = common.CopyBytes(data)
- }
- return &Transaction{data: txdata{
- AccountNonce: nonce,
- Recipient: nil,
- Amount: new(big.Int).Set(amount),
- GasLimit: new(big.Int).Set(gasLimit),
- Price: new(big.Int).Set(gasPrice),
- Payload: data,
- R: new(big.Int),
- S: new(big.Int),
- }}
+ return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}
-// NewTransaction creates a new transaction with the given fields.
-func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 {
data = common.CopyBytes(data)
}
d := txdata{
AccountNonce: nonce,
- Recipient: &to,
+ Recipient: to,
Payload: data,
Amount: new(big.Int),
GasLimit: new(big.Int),
Price: new(big.Int),
+ V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
}
@@ -110,9 +112,42 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
if gasPrice != nil {
d.Price.Set(gasPrice)
}
+
return &Transaction{data: d}
}
+func pickSigner(rules params.Rules) Signer {
+ var signer Signer
+ switch {
+ case rules.IsEIP155:
+ signer = NewEIP155Signer(rules.ChainId)
+ case rules.IsHomestead:
+ signer = HomesteadSigner{}
+ default:
+ signer = FrontierSigner{}
+ }
+ return signer
+}
+
+// ChainId returns which chain id this transaction was signed for (if at all)
+func (tx *Transaction) ChainId() *big.Int {
+ return deriveChainId(tx.data.V)
+}
+
+// Protected returns whether the transaction is pretected from replay protection
+func (tx *Transaction) Protected() bool {
+ return isProtectedV(tx.data.V)
+}
+
+func isProtectedV(V *big.Int) bool {
+ if V.BitLen() <= 8 {
+ v := V.Uint64()
+ return v != 27 && v != 28
+ }
+ // anything not 27 or 28 are considered unprotected
+ return true
+}
+
// DecodeRLP implements rlp.Encoder
func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data)
@@ -125,12 +160,13 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
if err == nil {
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
}
+
return err
}
// MarshalJSON encodes transactions into the web3 RPC response block format.
func (tx *Transaction) MarshalJSON() ([]byte, error) {
- hash, v := tx.Hash(), uint64(tx.data.V)
+ hash := tx.Hash()
return json.Marshal(&jsonTransaction{
Hash: &hash,
@@ -140,7 +176,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
Recipient: tx.data.Recipient,
Amount: (*hexBig)(tx.data.Amount),
Payload: (*hexBytes)(&tx.data.Payload),
- V: (*hexUint64)(&v),
+ V: (*hexBig)(tx.data.V),
R: (*hexBig)(tx.data.R),
S: (*hexBig)(tx.data.S),
})
@@ -159,9 +195,17 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
if dec.V == nil || dec.R == nil || dec.S == nil {
return errMissingTxSignatureFields
}
- if !crypto.ValidateSignatureValues(byte(*dec.V), (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
+
+ var V byte
+ if isProtectedV((*big.Int)(dec.V)) {
+ V = normaliseV(NewEIP155Signer(deriveChainId((*big.Int)(dec.V))), (*big.Int)(dec.V))
+ } else {
+ V = byte(((*big.Int)(dec.V)).Uint64())
+ }
+ if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
return ErrInvalidSig
}
+
if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil {
return errMissingTxFields
}
@@ -175,7 +219,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
GasLimit: (*big.Int)(dec.GasLimit),
Price: (*big.Int)(dec.Price),
Payload: *dec.Payload,
- V: byte(*dec.V),
+ V: (*big.Int)(dec.V),
R: (*big.Int)(dec.R),
S: (*big.Int)(dec.S),
}
@@ -211,15 +255,8 @@ func (tx *Transaction) Hash() common.Hash {
// SigHash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
-func (tx *Transaction) SigHash() common.Hash {
- return rlpHash([]interface{}{
- tx.data.AccountNonce,
- tx.data.Price,
- tx.data.GasLimit,
- tx.data.Recipient,
- tx.data.Amount,
- tx.data.Payload,
- })
+func (tx *Transaction) SigHash(signer Signer) common.Hash {
+ return signer.Hash(tx)
}
func (tx *Transaction) Size() common.StorageSize {
@@ -232,6 +269,7 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}
+/*
// From returns the address derived from the signature (V, R, S) using secp256k1
// elliptic curve and an error if it failed deriving or upon an incorrect
// signature.
@@ -247,32 +285,15 @@ func (tx *Transaction) Size() common.StorageSize {
// both txs before and after the first homestead block. Signatures
// valid in homestead are a subset of valid ones in Frontier)
func (tx *Transaction) From() (common.Address, error) {
- return doFrom(tx, true)
-}
-
-// FromFrontier returns the address derived from the signature (V, R, S) using
-// secp256k1 elliptic curve and an error if it failed deriving or upon an
-// incorrect signature.
-//
-// FromFrantier uses the frontier consensus rules to determine whether the
-// signature is valid.
-//
-// FromFrontier caches the address, allowing it to be used regardless of
-// Frontier / Homestead. however, the first time called it runs
-// signature validations, so we need two versions. This makes it
-// easier to ensure backwards compatibility of things like package rpc
-// where eth_getblockbynumber uses tx.From() and needs to work for
-// both txs before and after the first homestead block. Signatures
-// valid in homestead are a subset of valid ones in Frontier)
-func (tx *Transaction) FromFrontier() (common.Address, error) {
- return doFrom(tx, false)
-}
+ if tx.signer == nil {
+ return common.Address{}, errNoSigner
+ }
-func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
if from := tx.from.Load(); from != nil {
return from.(common.Address), nil
}
- pubkey, err := tx.publicKey(homestead)
+
+ pubkey, err := tx.signer.PublicKey(tx)
if err != nil {
return common.Address{}, err
}
@@ -282,68 +303,70 @@ func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
return addr, nil
}
-// Cost returns amount + gasprice * gaslimit.
-func (tx *Transaction) Cost() *big.Int {
- total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
- total.Add(total, tx.data.Amount)
- return total
-}
-
// SignatureValues returns the ECDSA signature values contained in the transaction.
-func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
- return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
+func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int, err error) {
+ if tx.signer == nil {
+ return 0, nil, nil,errNoSigner
+ }
+
+ return normaliseV(tx.signer, tx.data.V), new(big.Int).Set(tx.data.R),new(big.Int).Set(tx.data.S), nil
}
-func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
- if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) {
- return nil, ErrInvalidSig
+*/
+
+// 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,
+ price: new(big.Int).Set(tx.data.Price),
+ gasLimit: new(big.Int).Set(tx.data.GasLimit),
+ to: tx.data.Recipient,
+ amount: tx.data.Amount,
+ data: tx.data.Payload,
}
- // encode the signature in uncompressed format
- r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
- sig := make([]byte, 65)
- copy(sig[32-len(r):32], r)
- copy(sig[64-len(s):64], s)
- sig[64] = tx.data.V - 27
+ var err error
+ msg.from, err = Sender(s, tx)
+ return msg, err
+}
- // recover the public key from the signature
- hash := tx.SigHash()
- pub, err := crypto.Ecrecover(hash[:], sig)
- if err != nil {
- return nil, err
- }
- if len(pub) == 0 || pub[0] != 4 {
- return nil, errors.New("invalid public key")
- }
- return pub, nil
+// SignECDSA signs the transaction using the given signer and private key
+//
+// XXX This only makes for a nice API: NewTx(...).SignECDSA(signer, prv). Should
+// we keep this?
+func (tx *Transaction) SignECDSA(signer Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ return signer.SignECDSA(tx, prv)
}
// WithSignature returns a new transaction with the given signature.
// This signature needs to be formatted as described in the yellow paper (v+27).
-func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) {
- if len(sig) != 65 {
- panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
- }
- cpy := &Transaction{data: tx.data}
- cpy.data.R = new(big.Int).SetBytes(sig[:32])
- cpy.data.S = new(big.Int).SetBytes(sig[32:64])
- cpy.data.V = sig[64]
- return cpy, nil
+func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
+ return signer.WithSignature(tx, sig)
}
-func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
- h := tx.SigHash()
- sig, err := crypto.SignEthereum(h[:], prv)
- if err != nil {
- return nil, err
- }
- return tx.WithSignature(sig)
+// Cost returns amount + gasprice * gaslimit.
+func (tx *Transaction) Cost() *big.Int {
+ total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
+ total.Add(total, tx.data.Amount)
+ return total
+}
+
+func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) {
+ return tx.data.V, tx.data.R, tx.data.S
}
func (tx *Transaction) String() string {
+ // make a best guess about the signer and use that to derive
+ // the sender.
+ signer := deriveSigner(tx.data.V)
+
var from, to string
- if f, err := tx.From(); err != nil {
- from = "[invalid sender]"
+ if f, err := Sender(signer, tx); err != nil { // derive but don't cache
+ from = "[invalid sender: invalid sig]"
} else {
from = fmt.Sprintf("%x", f[:])
}
@@ -485,8 +508,9 @@ func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
// Shift replaces the current best head with the next one from the same account.
func (t *TransactionsByPriceAndNonce) Shift() {
- acc, _ := t.heads[0].From() // we only sort valid txs so this cannot fail
-
+ signer := deriveSigner(t.heads[0].data.V)
+ // derive signer but don't cache.
+ acc, _ := Sender(signer, t.heads[0]) // we only sort valid txs so this cannot fail
if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
t.heads[0], t.txs[acc] = txs[0], txs[1:]
heap.Fix(&t.heads, 0)
@@ -501,3 +525,35 @@ func (t *TransactionsByPriceAndNonce) Shift() {
func (t *TransactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}
+
+// Message is a fully derived transaction and implements core.Message
+//
+// NOTE: In a future PR this will be removed.
+type Message struct {
+ to *common.Address
+ from common.Address
+ nonce uint64
+ amount, price, gasLimit *big.Int
+ data []byte
+}
+
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte) Message {
+ return Message{
+ from: from,
+ to: to,
+ nonce: nonce,
+ amount: amount,
+ price: price,
+ gasLimit: gasLimit,
+ data: data,
+ }
+}
+
+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.price }
+func (m Message) Value() *big.Int { return m.amount }
+func (m Message) Gas() *big.Int { 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 true }
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
new file mode 100644
index 000000000..48209e2d8
--- /dev/null
+++ b/core/types/transaction_signing.go
@@ -0,0 +1,340 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package types
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// sigCache is used to cache the derived sender and contains
+// the signer used to derive it.
+type sigCache struct {
+ signer Signer
+ from common.Address
+}
+
+// MakeSigner returns a Signer based on the given chain config and block number.
+func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
+ var signer Signer
+ switch {
+ case config.IsEIP155(blockNumber):
+ signer = NewEIP155Signer(config.ChainId)
+ case config.IsHomestead(blockNumber):
+ signer = HomesteadSigner{}
+ default:
+ signer = FrontierSigner{}
+ }
+ return signer
+}
+
+// SignECDSA signs the transaction using the given signer and private key
+func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := s.Hash(tx)
+ sig, err := crypto.SignEthereum(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return s.WithSignature(tx, sig)
+}
+
+// Sender derives the sender from the tx using the signer derivation
+// functions.
+
+// Sender returns the address derived from the signature (V, R, S) using secp256k1
+// elliptic curve and an error if it failed deriving or upon an incorrect
+// signature.
+//
+// Sender may cache the address, allowing it to be used regardless of
+// signing method. The cache is invalidated if the cached signer does
+// not match the signer used in the current call.
+func Sender(signer Signer, tx *Transaction) (common.Address, error) {
+ if sc := tx.from.Load(); sc != nil {
+ sigCache := sc.(sigCache)
+ // If the signer used to derive from in a previous
+ // call is not the same as used current, invalidate
+ // the cache.
+ if reflect.TypeOf(sigCache.signer) == reflect.TypeOf(signer) {
+ return sigCache.from, nil
+ }
+ }
+
+ pubkey, err := signer.PublicKey(tx)
+ if err != nil {
+ return common.Address{}, err
+ }
+ var addr common.Address
+ copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
+ tx.from.Store(sigCache{signer: signer, from: addr})
+ return addr, nil
+}
+
+// SignatureValues returns the ECDSA signature values contained in the transaction.
+func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) {
+ return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
+}
+
+type Signer interface {
+ // Hash returns the rlp encoded hash for signatures
+ Hash(tx *Transaction) common.Hash
+ // PubilcKey returns the public key derived from the signature
+ PublicKey(tx *Transaction) ([]byte, error)
+ // SignECDSA signs the transaction with the given and returns a copy of the tx
+ SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error)
+ // WithSignature returns a copy of the transaction with the given signature
+ WithSignature(tx *Transaction, sig []byte) (*Transaction, error)
+}
+
+// EIP155Transaction implements TransactionInterface using the
+// EIP155 rules
+type EIP155Signer struct {
+ HomesteadSigner
+
+ chainId, chainIdMul *big.Int
+}
+
+func NewEIP155Signer(chainId *big.Int) EIP155Signer {
+ return EIP155Signer{
+ chainId: chainId,
+ chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
+ }
+}
+
+func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ return SignECDSA(s, tx, prv)
+}
+
+func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
+ // if the transaction is not protected fall back to homestead signer
+ if !tx.Protected() {
+ return (HomesteadSigner{}).PublicKey(tx)
+ }
+
+ V := normaliseV(s, tx.data.V)
+ if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
+ return nil, ErrInvalidSig
+ }
+
+ // encode the signature in uncompressed format
+ R, S := tx.data.R.Bytes(), tx.data.S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(R):32], R)
+ copy(sig[64-len(S):64], S)
+ sig[64] = V - 27
+
+ // recover the public key from the signature
+ hash := s.Hash(tx)
+ pub, err := crypto.Ecrecover(hash[:], sig)
+ if err != nil {
+ return nil, err
+ }
+ if len(pub) == 0 || pub[0] != 4 {
+ return nil, errors.New("invalid public key")
+ }
+ return pub, nil
+}
+
+// WithSignature returns a new transaction with the given signature.
+// This signature needs to be formatted as described in the yellow paper (v+27).
+func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
+ if len(sig) != 65 {
+ panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
+ }
+
+ cpy := &Transaction{data: tx.data}
+ cpy.data.R = new(big.Int).SetBytes(sig[:32])
+ cpy.data.S = new(big.Int).SetBytes(sig[32:64])
+ cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
+ if s.chainId.BitLen() > 0 {
+ cpy.data.V = big.NewInt(int64(sig[64] - 27 + 35))
+ cpy.data.V.Add(cpy.data.V, s.chainIdMul)
+ }
+ return cpy, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
+ return rlpHash([]interface{}{
+ tx.data.AccountNonce,
+ tx.data.Price,
+ tx.data.GasLimit,
+ tx.data.Recipient,
+ tx.data.Amount,
+ tx.data.Payload,
+ s.chainId, uint(0), uint(0),
+ })
+}
+
+func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := s.Hash(tx)
+ sig, err := crypto.SignEthereum(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return s.WithSignature(tx, sig)
+}
+
+// HomesteadTransaction implements TransactionInterface using the
+// homestead rules.
+type HomesteadSigner struct{ FrontierSigner }
+
+// WithSignature returns a new transaction with the given snature.
+// This snature needs to be formatted as described in the yellow paper (v+27).
+func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
+ if len(sig) != 65 {
+ panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
+ }
+ cpy := &Transaction{data: tx.data}
+ cpy.data.R = new(big.Int).SetBytes(sig[:32])
+ cpy.data.S = new(big.Int).SetBytes(sig[32:64])
+ cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
+ return cpy, nil
+}
+
+func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := hs.Hash(tx)
+ sig, err := crypto.SignEthereum(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return hs.WithSignature(tx, sig)
+}
+
+func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
+ if tx.data.V.BitLen() > 8 {
+ return nil, ErrInvalidSig
+ }
+ V := byte(tx.data.V.Uint64())
+ if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
+ return nil, ErrInvalidSig
+ }
+ // encode the snature in uncompressed format
+ r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(r):32], r)
+ copy(sig[64-len(s):64], s)
+ sig[64] = V - 27
+
+ // recover the public key from the snature
+ hash := hs.Hash(tx)
+ pub, err := crypto.Ecrecover(hash[:], sig)
+ if err != nil {
+ return nil, err
+ }
+ if len(pub) == 0 || pub[0] != 4 {
+ return nil, errors.New("invalid public key")
+ }
+ return pub, nil
+}
+
+type FrontierSigner struct{}
+
+// WithSignature returns a new transaction with the given snature.
+// This snature needs to be formatted as described in the yellow paper (v+27).
+func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
+ if len(sig) != 65 {
+ panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
+ }
+ cpy := &Transaction{data: tx.data}
+ cpy.data.R = new(big.Int).SetBytes(sig[:32])
+ cpy.data.S = new(big.Int).SetBytes(sig[32:64])
+ cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
+ return cpy, nil
+}
+
+func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := fs.Hash(tx)
+ sig, err := crypto.SignEthereum(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return fs.WithSignature(tx, sig)
+}
+
+// Hash returns the hash to be sned by the sender.
+// It does not uniquely identify the transaction.
+func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
+ return rlpHash([]interface{}{
+ tx.data.AccountNonce,
+ tx.data.Price,
+ tx.data.GasLimit,
+ tx.data.Recipient,
+ tx.data.Amount,
+ tx.data.Payload,
+ })
+}
+
+func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
+ if tx.data.V.BitLen() > 8 {
+ return nil, ErrInvalidSig
+ }
+
+ V := byte(tx.data.V.Uint64())
+ if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
+ return nil, ErrInvalidSig
+ }
+ // encode the snature in uncompressed format
+ r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(r):32], r)
+ copy(sig[64-len(s):64], s)
+ sig[64] = V - 27
+
+ // recover the public key from the snature
+ hash := fs.Hash(tx)
+ pub, err := crypto.Ecrecover(hash[:], sig)
+ if err != nil {
+ return nil, err
+ }
+ if len(pub) == 0 || pub[0] != 4 {
+ return nil, errors.New("invalid public key")
+ }
+ return pub, nil
+}
+
+// normaliseV returns the Ethereum version of the V parameter
+func normaliseV(s Signer, v *big.Int) byte {
+ if s, ok := s.(EIP155Signer); ok {
+ stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28)
+ if s.chainId.BitLen() > 0 && !stdV {
+ nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27)
+ return nv
+ }
+ }
+ return byte(v.Uint64())
+}
+
+// deriveChainId derives the chain id from the given v parameter
+func deriveChainId(v *big.Int) *big.Int {
+ if v.BitLen() <= 64 {
+ v := v.Uint64()
+ if v == 27 || v == 28 {
+ return new(big.Int)
+ }
+ return new(big.Int).SetUint64((v - 35) / 2)
+ }
+ v = new(big.Int).Sub(v, big.NewInt(35))
+ return v.Div(v, big.NewInt(2))
+}
diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go
new file mode 100644
index 000000000..89c590262
--- /dev/null
+++ b/core/types/transaction_signing_test.go
@@ -0,0 +1,116 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package types
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+func TestEIP155Signing(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ signer := NewEIP155Signer(big.NewInt(18))
+ tx, err := NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil).SignECDSA(signer, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ from, err := Sender(signer, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if from != addr {
+ t.Errorf("exected from and address to be equal. Got %x want %x", from, addr)
+ }
+}
+
+func TestEIP155ChainId(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ signer := NewEIP155Signer(big.NewInt(18))
+ tx, err := NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil).SignECDSA(signer, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !tx.Protected() {
+ t.Fatal("expected tx to be protected")
+ }
+
+ if tx.ChainId().Cmp(signer.chainId) != 0 {
+ t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId())
+ }
+
+ tx = NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil)
+ tx, err = tx.SignECDSA(HomesteadSigner{}, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if tx.Protected() {
+ t.Error("didn't expect tx to be protected")
+ }
+
+ if tx.ChainId().BitLen() > 0 {
+ t.Error("expected chain id to be 0 got", tx.ChainId())
+ }
+}
+
+func TestEIP155SigningVitalik(t *testing.T) {
+ // Test vectors come from http://vitalik.ca/files/eip155_testvec.txt
+ for i, test := range []struct {
+ txRlp, addr string
+ }{
+ {"f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"},
+ {"f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"},
+ {"f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"},
+ {"f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"},
+ {"f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"},
+ {"f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"},
+ {"f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"},
+ {"f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"},
+ {"f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"},
+ {"f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"},
+ } {
+ signer := NewEIP155Signer(big.NewInt(1))
+
+ var tx *Transaction
+ err := rlp.DecodeBytes(common.Hex2Bytes(test.txRlp), &tx)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+
+ from, err := Sender(signer, tx)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+
+ addr := common.HexToAddress(test.addr)
+ if from != addr {
+ t.Errorf("%d: expected %x got %x", i, addr, from)
+ }
+
+ }
+}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 98a78d221..ca105566a 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -46,15 +46,16 @@ var (
big.NewInt(1),
common.FromHex("5544"),
).WithSignature(
+ HomesteadSigner{},
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a31c"),
)
)
func TestTransactionSigHash(t *testing.T) {
- if emptyTx.SigHash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
+ if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
}
- if rightvrsTx.SigHash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
+ if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
}
}
@@ -72,7 +73,9 @@ func TestTransactionEncode(t *testing.T) {
func decodeTx(data []byte) (*Transaction, error) {
var tx Transaction
- return &tx, rlp.Decode(bytes.NewReader(data), &tx)
+ t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
+
+ return t, err
}
func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
@@ -88,7 +91,8 @@ func TestRecipientEmpty(t *testing.T) {
t.Error(err)
t.FailNow()
}
- from, err := tx.From()
+
+ from, err := Sender(HomesteadSigner{}, tx)
if err != nil {
t.Error(err)
t.FailNow()
@@ -107,7 +111,7 @@ func TestRecipientNormal(t *testing.T) {
t.FailNow()
}
- from, err := tx.From()
+ from, err := Sender(HomesteadSigner{}, tx)
if err != nil {
t.Error(err)
t.FailNow()
@@ -127,12 +131,14 @@ func TestTransactionPriceNonceSort(t *testing.T) {
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
+
+ signer := HomesteadSigner{}
// Generate a batch of transactions with overlapping values, but shifted nonces
groups := map[common.Address]Transactions{}
for start, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for i := 0; i < 25; i++ {
- tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(key)
+ tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(signer, key)
groups[addr] = append(groups[addr], tx)
}
}
@@ -148,11 +154,11 @@ func TestTransactionPriceNonceSort(t *testing.T) {
break
}
for i, txi := range txs {
- fromi, _ := txi.From()
+ fromi, _ := Sender(signer, txi)
// Make sure the nonce order is valid
for j, txj := range txs[i+1:] {
- fromj, _ := txj.From()
+ fromj, _ := Sender(signer, txj)
if fromi == fromj && txi.Nonce() > txj.Nonce() {
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
@@ -161,20 +167,20 @@ func TestTransactionPriceNonceSort(t *testing.T) {
// Find the previous and next nonce of this account
prev, next := i-1, i+1
for j := i - 1; j >= 0; j-- {
- if fromj, _ := txs[j].From(); fromi == fromj {
+ if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
prev = j
break
}
}
for j := i + 1; j < len(txs); j++ {
- if fromj, _ := txs[j].From(); fromi == fromj {
+ if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
next = j
break
}
}
// Make sure that in between the neighbor nonces, the transaction is correctly positioned price wise
for j := prev + 1; j < next; j++ {
- fromj, _ := txs[j].From()
+ fromj, _ := Sender(signer, txs[j])
if j < i && txs[j].GasPrice().Cmp(txi.GasPrice()) < 0 {
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", j, fromj[:4], txs[j].GasPrice(), i, fromi[:4], txi.GasPrice())
}