forked from mirror/go-ethereum
parent
5cd86443ee
commit
4dca5d4db7
@ -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)) |
||||
} |
@ -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) |
||||
} |
||||
|
||||
} |
||||
} |
Loading…
Reference in new issue