|
|
@@ -4,6 +4,7 @@ import (
|
|
|
"crypto/ecdsa"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
+ "io"
|
|
|
"math/big"
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
@@ -18,38 +19,59 @@ func IsContractAddr(addr []byte) bool {
|
|
|
}
|
|
|
|
|
|
type Transaction struct {
|
|
|
- AccountNonce uint64
|
|
|
- Price *big.Int
|
|
|
- GasLimit *big.Int
|
|
|
- Recipient *common.Address `rlp:"nil"` // nil means contract creation
|
|
|
- Amount *big.Int
|
|
|
- Payload []byte
|
|
|
- V byte
|
|
|
- R, S *big.Int
|
|
|
-}
|
|
|
-
|
|
|
-func NewContractCreationTx(amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
|
|
- return &Transaction{
|
|
|
- Recipient: nil,
|
|
|
- Amount: amount,
|
|
|
- GasLimit: gasLimit,
|
|
|
- Price: gasPrice,
|
|
|
- Payload: data,
|
|
|
- R: new(big.Int),
|
|
|
- S: new(big.Int),
|
|
|
- }
|
|
|
+ data txdata
|
|
|
+}
|
|
|
+
|
|
|
+type txdata struct {
|
|
|
+ AccountNonce uint64
|
|
|
+ Price, GasLimit *big.Int
|
|
|
+ Recipient *common.Address `rlp:"nil"` // nil means contract creation
|
|
|
+ Amount *big.Int
|
|
|
+ Payload []byte
|
|
|
+ V byte // signature
|
|
|
+ R, S *big.Int // signature
|
|
|
}
|
|
|
|
|
|
-func NewTransactionMessage(to common.Address, amount, gasAmount, gasPrice *big.Int, data []byte) *Transaction {
|
|
|
- return &Transaction{
|
|
|
- Recipient: &to,
|
|
|
- Amount: amount,
|
|
|
- GasLimit: gasAmount,
|
|
|
- Price: gasPrice,
|
|
|
- Payload: data,
|
|
|
- R: new(big.Int),
|
|
|
- S: new(big.Int),
|
|
|
+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),
|
|
|
+ }}
|
|
|
+}
|
|
|
+
|
|
|
+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,
|
|
|
+ Payload: data,
|
|
|
+ Amount: new(big.Int),
|
|
|
+ GasLimit: new(big.Int),
|
|
|
+ Price: new(big.Int),
|
|
|
+ R: new(big.Int),
|
|
|
+ S: new(big.Int),
|
|
|
}
|
|
|
+ if amount != nil {
|
|
|
+ d.Amount.Set(amount)
|
|
|
+ }
|
|
|
+ if gasLimit != nil {
|
|
|
+ d.GasLimit.Set(gasLimit)
|
|
|
+ }
|
|
|
+ if gasPrice != nil {
|
|
|
+ d.Price.Set(gasPrice)
|
|
|
+ }
|
|
|
+ return &Transaction{data: d}
|
|
|
}
|
|
|
|
|
|
func NewTransactionFromBytes(data []byte) *Transaction {
|
|
|
@@ -61,112 +83,110 @@ func NewTransactionFromBytes(data []byte) *Transaction {
|
|
|
return tx
|
|
|
}
|
|
|
|
|
|
-func (tx *Transaction) Hash() common.Hash {
|
|
|
- return rlpHash([]interface{}{
|
|
|
- tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload,
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// Size returns the encoded RLP size of tx.
|
|
|
-func (self *Transaction) Size() common.StorageSize {
|
|
|
- c := writeCounter(0)
|
|
|
- rlp.Encode(&c, self)
|
|
|
- return common.StorageSize(c)
|
|
|
-}
|
|
|
-
|
|
|
-func (self *Transaction) Data() []byte {
|
|
|
- return self.Payload
|
|
|
+func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
|
|
+ return rlp.Encode(w, &tx.data)
|
|
|
}
|
|
|
|
|
|
-func (self *Transaction) Gas() *big.Int {
|
|
|
- return self.GasLimit
|
|
|
+func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|
|
+ return s.Decode(&tx.data)
|
|
|
}
|
|
|
|
|
|
-func (self *Transaction) GasPrice() *big.Int {
|
|
|
- return self.Price
|
|
|
-}
|
|
|
+func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
|
|
+func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) }
|
|
|
+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) Nonce() uint64 { return tx.data.AccountNonce }
|
|
|
|
|
|
-func (self *Transaction) Value() *big.Int {
|
|
|
- return self.Amount
|
|
|
+func (tx *Transaction) To() *common.Address {
|
|
|
+ if tx.data.Recipient == nil {
|
|
|
+ return nil
|
|
|
+ } else {
|
|
|
+ to := *tx.data.Recipient
|
|
|
+ return &to
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-func (self *Transaction) Nonce() uint64 {
|
|
|
- return self.AccountNonce
|
|
|
+func (tx *Transaction) Hash() common.Hash {
|
|
|
+ v := rlpHash([]interface{}{
|
|
|
+ tx.data.AccountNonce,
|
|
|
+ tx.data.Price,
|
|
|
+ tx.data.GasLimit,
|
|
|
+ tx.data.Recipient,
|
|
|
+ tx.data.Amount,
|
|
|
+ tx.data.Payload,
|
|
|
+ })
|
|
|
+ return v
|
|
|
}
|
|
|
|
|
|
-func (self *Transaction) SetNonce(AccountNonce uint64) {
|
|
|
- self.AccountNonce = AccountNonce
|
|
|
+func (tx *Transaction) Size() common.StorageSize {
|
|
|
+ v, _, _ := rlp.EncodeToReader(&tx.data)
|
|
|
+ return common.StorageSize(v)
|
|
|
}
|
|
|
|
|
|
-func (self *Transaction) From() (common.Address, error) {
|
|
|
- pubkey, err := self.PublicKey()
|
|
|
+func (tx *Transaction) From() (common.Address, error) {
|
|
|
+ pubkey, err := tx.PublicKey()
|
|
|
if err != nil {
|
|
|
return common.Address{}, err
|
|
|
}
|
|
|
-
|
|
|
var addr common.Address
|
|
|
copy(addr[:], crypto.Sha3(pubkey[1:])[12:])
|
|
|
return addr, nil
|
|
|
}
|
|
|
|
|
|
-// To returns the recipient of the transaction.
|
|
|
-// If transaction is a contract creation (with no recipient address)
|
|
|
-// To returns nil.
|
|
|
-func (tx *Transaction) To() *common.Address {
|
|
|
- return tx.Recipient
|
|
|
+// 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) GetSignatureValues() (v byte, r []byte, s []byte) {
|
|
|
- v = byte(tx.V)
|
|
|
- r = common.LeftPadBytes(tx.R.Bytes(), 32)
|
|
|
- s = common.LeftPadBytes(tx.S.Bytes(), 32)
|
|
|
- return
|
|
|
+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) PublicKey() ([]byte, error) {
|
|
|
- if !crypto.ValidateSignatureValues(tx.V, tx.R, tx.S) {
|
|
|
+ if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S) {
|
|
|
return nil, errors.New("invalid v, r, s values")
|
|
|
}
|
|
|
|
|
|
- hash := tx.Hash()
|
|
|
- v, r, s := tx.GetSignatureValues()
|
|
|
- sig := append(r, s...)
|
|
|
- sig = append(sig, v-27)
|
|
|
+ // 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
|
|
|
|
|
|
- p, err := crypto.SigToPub(hash[:], sig)
|
|
|
+ // recover the public key from the signature
|
|
|
+ hash := tx.Hash()
|
|
|
+ pub, err := crypto.Ecrecover(hash[:], sig)
|
|
|
if err != nil {
|
|
|
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
|
|
|
return nil, err
|
|
|
}
|
|
|
-
|
|
|
- pubkey := crypto.FromECDSAPub(p)
|
|
|
- if len(pubkey) == 0 || pubkey[0] != 4 {
|
|
|
+ if len(pub) == 0 || pub[0] != 4 {
|
|
|
return nil, errors.New("invalid public key")
|
|
|
}
|
|
|
- return pubkey, nil
|
|
|
+ return pub, nil
|
|
|
}
|
|
|
|
|
|
-func (tx *Transaction) SetSignatureValues(sig []byte) error {
|
|
|
- tx.R = common.Bytes2Big(sig[:32])
|
|
|
- tx.S = common.Bytes2Big(sig[32:64])
|
|
|
- tx.V = sig[64] + 27
|
|
|
- return nil
|
|
|
+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] + 27
|
|
|
+ return cpy, nil
|
|
|
}
|
|
|
|
|
|
-func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) error {
|
|
|
+func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
|
|
|
h := tx.Hash()
|
|
|
sig, err := crypto.Sign(h[:], prv)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- tx.SetSignatureValues(sig)
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-// TODO: remove
|
|
|
-func (tx *Transaction) RlpData() interface{} {
|
|
|
- data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
|
|
|
- return append(data, tx.V, tx.R.Bytes(), tx.S.Bytes())
|
|
|
+ return tx.WithSignature(sig)
|
|
|
}
|
|
|
|
|
|
func (tx *Transaction) String() string {
|
|
|
@@ -176,12 +196,12 @@ func (tx *Transaction) String() string {
|
|
|
} else {
|
|
|
from = fmt.Sprintf("%x", f[:])
|
|
|
}
|
|
|
- if t := tx.To(); t == nil {
|
|
|
+ if tx.data.Recipient == nil {
|
|
|
to = "[contract creation]"
|
|
|
} else {
|
|
|
- to = fmt.Sprintf("%x", t[:])
|
|
|
+ to = fmt.Sprintf("%x", tx.data.Recipient[:])
|
|
|
}
|
|
|
- enc, _ := rlp.EncodeToBytes(tx)
|
|
|
+ enc, _ := rlp.EncodeToBytes(&tx.data)
|
|
|
return fmt.Sprintf(`
|
|
|
TX(%x)
|
|
|
Contract: %v
|
|
|
@@ -198,36 +218,24 @@ func (tx *Transaction) String() string {
|
|
|
Hex: %x
|
|
|
`,
|
|
|
tx.Hash(),
|
|
|
- len(tx.Recipient) == 0,
|
|
|
+ len(tx.data.Recipient) == 0,
|
|
|
from,
|
|
|
to,
|
|
|
- tx.AccountNonce,
|
|
|
- tx.Price,
|
|
|
- tx.GasLimit,
|
|
|
- tx.Amount,
|
|
|
- tx.Payload,
|
|
|
- tx.V,
|
|
|
- tx.R,
|
|
|
- tx.S,
|
|
|
+ tx.data.AccountNonce,
|
|
|
+ tx.data.Price,
|
|
|
+ tx.data.GasLimit,
|
|
|
+ tx.data.Amount,
|
|
|
+ tx.data.Payload,
|
|
|
+ tx.data.V,
|
|
|
+ tx.data.R,
|
|
|
+ tx.data.S,
|
|
|
enc,
|
|
|
)
|
|
|
}
|
|
|
|
|
|
-// Transaction slice type for basic sorting
|
|
|
+// Transaction slice type for basic sorting.
|
|
|
type Transactions []*Transaction
|
|
|
|
|
|
-// TODO: remove
|
|
|
-func (self Transactions) RlpData() interface{} {
|
|
|
- // Marshal the transactions of this block
|
|
|
- enc := make([]interface{}, len(self))
|
|
|
- for i, tx := range self {
|
|
|
- // Cast it to a string (safe)
|
|
|
- enc[i] = tx.RlpData()
|
|
|
- }
|
|
|
-
|
|
|
- return enc
|
|
|
-}
|
|
|
-
|
|
|
func (s Transactions) Len() int { return len(s) }
|
|
|
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
|
|
|
@@ -239,5 +247,5 @@ func (s Transactions) GetRlp(i int) []byte {
|
|
|
type TxByNonce struct{ Transactions }
|
|
|
|
|
|
func (s TxByNonce) Less(i, j int) bool {
|
|
|
- return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce
|
|
|
+ return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce
|
|
|
}
|