aboutsummaryrefslogtreecommitdiffstats
path: root/core/types/transaction.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/types/transaction.go')
-rw-r--r--core/types/transaction.go260
1 files changed, 158 insertions, 102 deletions
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 }