aboutsummaryrefslogtreecommitdiffstats
path: root/core/types
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2015-06-11 20:05:32 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2015-06-30 00:51:47 +0800
commit654564e164b3b6f7f4ba1e8bbd6fcd64776068fa (patch)
treebac9d2b3094d23001f3c1a70389ddb57de37477d /core/types
parent9d8b512b27f691fc1980b850e04eb436a3938626 (diff)
downloaddexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.tar
dexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.tar.gz
dexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.tar.bz2
dexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.tar.lz
dexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.tar.xz
dexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.tar.zst
dexon-654564e164b3b6f7f4ba1e8bbd6fcd64776068fa.zip
core/types: make transactions immutable
Diffstat (limited to 'core/types')
-rw-r--r--core/types/block_test.go19
-rw-r--r--core/types/transaction.go246
-rw-r--r--core/types/transaction_test.go32
3 files changed, 143 insertions, 154 deletions
diff --git a/core/types/block_test.go b/core/types/block_test.go
index b52ddffdc..03e6881be 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -13,7 +13,6 @@ import (
// from bcValidBlockTest.json, "SimpleTx"
func TestBlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
-
var block Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
@@ -35,20 +34,10 @@ func TestBlockEncoding(t *testing.T) {
check("Time", block.Time(), int64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
- to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
- check("Transactions", block.Transactions(), Transactions{
- {
- Payload: []byte{},
- Amount: big.NewInt(10),
- Price: big.NewInt(10),
- GasLimit: big.NewInt(50000),
- AccountNonce: 0,
- V: 27,
- R: common.String2Big("0x9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f"),
- S: common.String2Big("0x8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1"),
- Recipient: &to,
- },
- })
+ tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
+ tx1, _ = tx1.WithSignature(common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
+ check("len(Transactions)", len(block.Transactions()), 1)
+ check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
ourBlockEnc, err := rlp.EncodeToBytes(&block)
if err != nil {
diff --git a/core/types/transaction.go b/core/types/transaction.go
index a03a6b847..14b0e4174 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -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
}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 492059c28..dd9c5e87b 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -15,40 +15,35 @@ import (
// at github.com/ethereum/tests.
var (
- emptyTx = NewTransactionMessage(
+ emptyTx = NewTransaction(
+ 0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), big.NewInt(0), big.NewInt(0),
nil,
)
- rightvrsRecipient = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b")
- rightvrsTx = &Transaction{
- Recipient: &rightvrsRecipient,
- AccountNonce: 3,
- Price: big.NewInt(1),
- GasLimit: big.NewInt(2000),
- Amount: big.NewInt(10),
- Payload: common.FromHex("5544"),
- V: 28,
- R: common.String2Big("0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a"),
- S: common.String2Big("0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"),
- }
+ rightvrsTx, _ = NewTransaction(
+ 3,
+ common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
+ big.NewInt(10),
+ big.NewInt(2000),
+ big.NewInt(1),
+ common.FromHex("5544"),
+ ).WithSignature(
+ common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
+ )
)
func TestTransactionHash(t *testing.T) {
- // "EmptyTransaction"
if emptyTx.Hash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
}
-
- // "RightVRSTest"
if rightvrsTx.Hash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
}
}
func TestTransactionEncode(t *testing.T) {
- // "RightVRSTest"
txb, err := rlp.EncodeToBytes(rightvrsTx)
if err != nil {
t.Fatalf("encode error: %v", err)
@@ -72,19 +67,16 @@ func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
func TestRecipientEmpty(t *testing.T) {
_, addr := defaultTestKey()
-
tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d"))
if err != nil {
t.Error(err)
t.FailNow()
}
-
from, err := tx.From()
if err != nil {
t.Error(err)
t.FailNow()
}
-
if addr != from {
t.Error("derived address doesn't match")
}