diff options
author | obscuren <geffobscura@gmail.com> | 2014-12-04 17:28:02 +0800 |
---|---|---|
committer | obscuren <geffobscura@gmail.com> | 2014-12-04 17:28:02 +0800 |
commit | 9008b155d3c8d2a32c4c8945f1174243d48d4e90 (patch) | |
tree | a55140b7d4802a0db14cbc265b8ca21d44d6f10a /core/types | |
parent | b6cb5272de96185b424d5c6c4a817d99f536d29b (diff) | |
download | go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.gz go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.bz2 go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.lz go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.xz go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.zst go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.zip |
Renamed `chain` => `core`
Diffstat (limited to 'core/types')
-rw-r--r-- | core/types/block.go | 413 | ||||
-rw-r--r-- | core/types/bloom9.go | 56 | ||||
-rw-r--r-- | core/types/bloom9_test.go | 31 | ||||
-rw-r--r-- | core/types/common.go | 11 | ||||
-rw-r--r-- | core/types/derive_sha.go | 20 | ||||
-rw-r--r-- | core/types/receipt.go | 68 | ||||
-rw-r--r-- | core/types/transaction.go | 225 | ||||
-rw-r--r-- | core/types/transaction_test.go | 1 |
8 files changed, 825 insertions, 0 deletions
diff --git a/core/types/block.go b/core/types/block.go new file mode 100644 index 000000000..feab7072f --- /dev/null +++ b/core/types/block.go @@ -0,0 +1,413 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/trie" +) + +type BlockInfo struct { + Number uint64 + Hash []byte + Parent []byte + TD *big.Int +} + +func (bi *BlockInfo) RlpDecode(data []byte) { + decoder := ethutil.NewValueFromBytes(data) + + bi.Number = decoder.Get(0).Uint() + bi.Hash = decoder.Get(1).Bytes() + bi.Parent = decoder.Get(2).Bytes() + bi.TD = decoder.Get(3).BigInt() +} + +func (bi *BlockInfo) RlpEncode() []byte { + return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD}) +} + +type Blocks []*Block + +func (self Blocks) AsSet() ethutil.UniqueSet { + set := make(ethutil.UniqueSet) + for _, block := range self { + set.Insert(block.Hash()) + } + + return set +} + +type BlockBy func(b1, b2 *Block) bool + +func (self BlockBy) Sort(blocks Blocks) { + bs := blockSorter{ + blocks: blocks, + by: self, + } + sort.Sort(bs) +} + +type blockSorter struct { + blocks Blocks + by func(b1, b2 *Block) bool +} + +func (self blockSorter) Len() int { return len(self.blocks) } +func (self blockSorter) Swap(i, j int) { + self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] +} +func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } + +func Number(b1, b2 *Block) bool { return b1.Number.Cmp(b2.Number) < 0 } + +type Block struct { + // Hash to the previous block + PrevHash ethutil.Bytes + // Uncles of this block + Uncles Blocks + UncleSha []byte + // The coin base address + Coinbase []byte + // Block Trie state + //state *ethutil.Trie + state *state.State + // Difficulty for the current block + Difficulty *big.Int + // Creation time + Time int64 + // The block number + Number *big.Int + // Gas limit + GasLimit *big.Int + // Gas used + GasUsed *big.Int + // Extra data + Extra string + // Block Nonce for verification + Nonce ethutil.Bytes + // List of transactions and/or contracts + transactions Transactions + receipts Receipts + TxSha, ReceiptSha []byte + LogsBloom []byte + + Reward *big.Int +} + +func NewBlockFromBytes(raw []byte) *Block { + block := &Block{} + block.RlpDecode(raw) + + return block +} + +// New block takes a raw encoded string +func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block { + block := &Block{} + block.RlpValueDecode(rlpValue) + + return block +} + +func CreateBlock(root interface{}, + prevHash []byte, + base []byte, + Difficulty *big.Int, + Nonce []byte, + extra string) *Block { + + block := &Block{ + PrevHash: prevHash, + Coinbase: base, + Difficulty: Difficulty, + Nonce: Nonce, + Time: time.Now().Unix(), + Extra: extra, + UncleSha: nil, + GasUsed: new(big.Int), + GasLimit: new(big.Int), + } + block.SetUncles([]*Block{}) + + block.state = state.New(trie.New(ethutil.Config.Db, root)) + + return block +} + +// Returns a hash of the block +func (block *Block) Hash() ethutil.Bytes { + return crypto.Sha3(ethutil.NewValue(block.header()).Encode()) + //return crypto.Sha3(block.Value().Encode()) +} + +func (block *Block) HashNoNonce() []byte { + return crypto.Sha3(ethutil.Encode(block.miningHeader())) +} + +func (block *Block) State() *state.State { + return block.state +} + +func (block *Block) Transactions() Transactions { + return block.transactions +} + +func (block *Block) CalcGasLimit(parent *Block) *big.Int { + if block.Number.Cmp(big.NewInt(0)) == 0 { + return ethutil.BigPow(10, 6) + } + + // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024 + + previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit) + current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5)) + curInt := new(big.Int).Div(current.Num(), current.Denom()) + + result := new(big.Int).Add(previous, curInt) + result.Div(result, big.NewInt(1024)) + + min := big.NewInt(125000) + + return ethutil.BigMax(min, result) +} + +func (block *Block) BlockInfo() BlockInfo { + bi := BlockInfo{} + data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) + bi.RlpDecode(data) + + return bi +} + +func (self *Block) GetTransaction(hash []byte) *Transaction { + for _, tx := range self.transactions { + if bytes.Compare(tx.Hash(), hash) == 0 { + return tx + } + } + + return nil +} + +// Sync the block's state and contract respectively +func (block *Block) Sync() { + block.state.Sync() +} + +func (block *Block) Undo() { + // Sync the block state itself + block.state.Reset() +} + +/////// Block Encoding +func (block *Block) rlpReceipts() interface{} { + // Marshal the transactions of this block + encR := make([]interface{}, len(block.receipts)) + for i, r := range block.receipts { + // Cast it to a string (safe) + encR[i] = r.RlpData() + } + + return encR +} + +func (block *Block) rlpUncles() interface{} { + // Marshal the transactions of this block + uncles := make([]interface{}, len(block.Uncles)) + for i, uncle := range block.Uncles { + // Cast it to a string (safe) + uncles[i] = uncle.header() + } + + return uncles +} + +func (block *Block) SetUncles(uncles []*Block) { + block.Uncles = uncles + block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles())) +} + +func (self *Block) SetReceipts(receipts Receipts) { + self.receipts = receipts + self.ReceiptSha = DeriveSha(receipts) + self.LogsBloom = CreateBloom(receipts) +} + +func (self *Block) SetTransactions(txs Transactions) { + self.transactions = txs + self.TxSha = DeriveSha(txs) +} + +func (block *Block) Value() *ethutil.Value { + return ethutil.NewValue([]interface{}{block.header(), block.transactions, block.rlpUncles()}) +} + +func (block *Block) RlpEncode() []byte { + // Encode a slice interface which contains the header and the list of + // transactions. + return block.Value().Encode() +} + +func (block *Block) RlpDecode(data []byte) { + rlpValue := ethutil.NewValueFromBytes(data) + block.RlpValueDecode(rlpValue) +} + +func (block *Block) RlpValueDecode(decoder *ethutil.Value) { + block.setHeader(decoder.Get(0)) + + // Tx list might be empty if this is an uncle. Uncles only have their + // header set. + if decoder.Get(1).IsNil() == false { // Yes explicitness + //receipts := decoder.Get(1) + //block.receipts = make([]*Receipt, receipts.Len()) + txs := decoder.Get(1) + block.transactions = make(Transactions, txs.Len()) + for i := 0; i < txs.Len(); i++ { + block.transactions[i] = NewTransactionFromValue(txs.Get(i)) + //receipt := NewRecieptFromValue(receipts.Get(i)) + //block.transactions[i] = receipt.Tx + //block.receipts[i] = receipt + } + + } + + if decoder.Get(2).IsNil() == false { // Yes explicitness + uncles := decoder.Get(2) + block.Uncles = make([]*Block, uncles.Len()) + for i := 0; i < uncles.Len(); i++ { + block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i)) + } + } + +} + +func (self *Block) setHeader(header *ethutil.Value) { + self.PrevHash = header.Get(0).Bytes() + self.UncleSha = header.Get(1).Bytes() + self.Coinbase = header.Get(2).Bytes() + self.state = state.New(trie.New(ethutil.Config.Db, header.Get(3).Val)) + self.TxSha = header.Get(4).Bytes() + self.ReceiptSha = header.Get(5).Bytes() + self.LogsBloom = header.Get(6).Bytes() + self.Difficulty = header.Get(7).BigInt() + self.Number = header.Get(8).BigInt() + self.GasLimit = header.Get(9).BigInt() + self.GasUsed = header.Get(10).BigInt() + self.Time = int64(header.Get(11).BigInt().Uint64()) + self.Extra = header.Get(12).Str() + self.Nonce = header.Get(13).Bytes() +} + +func NewUncleBlockFromValue(header *ethutil.Value) *Block { + block := &Block{} + block.setHeader(header) + + return block +} + +func (block *Block) Trie() *trie.Trie { + return block.state.Trie +} + +func (block *Block) Root() interface{} { + return block.state.Root() +} + +func (block *Block) Diff() *big.Int { + return block.Difficulty +} + +func (self *Block) Receipts() []*Receipt { + return self.receipts +} + +func (block *Block) miningHeader() []interface{} { + return []interface{}{ + // Sha of the previous block + block.PrevHash, + // Sha of uncles + block.UncleSha, + // Coinbase address + block.Coinbase, + // root state + block.Root(), + // tx root + block.TxSha, + // Sha of tx + block.ReceiptSha, + // Bloom + block.LogsBloom, + // Current block Difficulty + block.Difficulty, + // The block number + block.Number, + // Block upper gas bound + block.GasLimit, + // Block gas used + block.GasUsed, + // Time the block was found? + block.Time, + // Extra data + block.Extra, + } +} + +func (block *Block) header() []interface{} { + return append(block.miningHeader(), block.Nonce) +} + +func (block *Block) String() string { + return fmt.Sprintf(` + BLOCK(%x): Size: %v + PrevHash: %x + UncleSha: %x + Coinbase: %x + Root: %x + TxSha %x + ReceiptSha: %x + Bloom: %x + Difficulty: %v + Number: %v + MaxLimit: %v + GasUsed: %v + Time: %v + Extra: %v + Nonce: %x + NumTx: %v +`, + block.Hash(), + block.Size(), + block.PrevHash, + block.UncleSha, + block.Coinbase, + block.Root(), + block.TxSha, + block.ReceiptSha, + block.LogsBloom, + block.Difficulty, + block.Number, + block.GasLimit, + block.GasUsed, + block.Time, + block.Extra, + block.Nonce, + len(block.transactions), + ) +} + +func (self *Block) Size() ethutil.StorageSize { + return ethutil.StorageSize(len(self.RlpEncode())) +} + +// Implement RlpEncodable +func (self *Block) RlpData() interface{} { + return self.Value().Val +} diff --git a/core/types/bloom9.go b/core/types/bloom9.go new file mode 100644 index 000000000..77aa6eef5 --- /dev/null +++ b/core/types/bloom9.go @@ -0,0 +1,56 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +func CreateBloom(receipts Receipts) []byte { + bin := new(big.Int) + for _, receipt := range receipts { + bin.Or(bin, LogsBloom(receipt.logs)) + } + + return ethutil.LeftPadBytes(bin.Bytes(), 64) +} + +func LogsBloom(logs state.Logs) *big.Int { + bin := new(big.Int) + for _, log := range logs { + data := [][]byte{log.Address} + for _, topic := range log.Topics { + data = append(data, topic) + } + + for _, b := range data { + bin.Or(bin, ethutil.BigD(bloom9(crypto.Sha3(b)).Bytes())) + } + + //if log.Data != nil { + // data = append(data, log.Data) + //} + } + + return bin +} + +func bloom9(b []byte) *big.Int { + r := new(big.Int) + for _, i := range []int{0, 2, 4} { + t := big.NewInt(1) + b := uint(b[i+1]) + 256*(uint(b[i])&1) + r.Or(r, t.Lsh(t, b)) + } + + return r +} + +func BloomLookup(bin, topic []byte) bool { + bloom := ethutil.BigD(bin) + cmp := bloom9(crypto.Sha3(topic)) + + return bloom.And(bloom, cmp).Cmp(cmp) == 0 +} diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go new file mode 100644 index 000000000..74e00cac6 --- /dev/null +++ b/core/types/bloom9_test.go @@ -0,0 +1,31 @@ +package types + +/* +import ( + "testing" + + "github.com/ethereum/go-ethereum/state" +) + +func TestBloom9(t *testing.T) { + testCase := []byte("testtest") + bin := LogsBloom([]state.Log{ + {testCase, [][]byte{[]byte("hellohello")}, nil}, + }).Bytes() + res := BloomLookup(bin, testCase) + + if !res { + t.Errorf("Bloom lookup failed") + } +} + + +func TestAddress(t *testing.T) { + block := &Block{} + block.Coinbase = ethutil.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") + fmt.Printf("%x\n", crypto.Sha3(block.Coinbase)) + + bin := CreateBloom(block) + fmt.Printf("bin = %x\n", ethutil.LeftPadBytes(bin, 64)) +} +*/ diff --git a/core/types/common.go b/core/types/common.go new file mode 100644 index 000000000..ba88b77e1 --- /dev/null +++ b/core/types/common.go @@ -0,0 +1,11 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/state" +) + +type BlockProcessor interface { + Process(*Block) (*big.Int, state.Messages, error) +} diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go new file mode 100644 index 000000000..1897ff198 --- /dev/null +++ b/core/types/derive_sha.go @@ -0,0 +1,20 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/trie" +) + +type DerivableList interface { + Len() int + GetRlp(i int) []byte +} + +func DeriveSha(list DerivableList) []byte { + trie := trie.New(ethutil.Config.Db, "") + for i := 0; i < list.Len(); i++ { + trie.Update(string(ethutil.NewValue(i).Encode()), string(list.GetRlp(i))) + } + + return trie.GetRoot() +} diff --git a/core/types/receipt.go b/core/types/receipt.go new file mode 100644 index 000000000..25fa8fb07 --- /dev/null +++ b/core/types/receipt.go @@ -0,0 +1,68 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +type Receipt struct { + PostState []byte + CumulativeGasUsed *big.Int + Bloom []byte + logs state.Logs +} + +func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt { + return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: cumalativeGasUsed} +} + +func NewRecieptFromValue(val *ethutil.Value) *Receipt { + r := &Receipt{} + r.RlpValueDecode(val) + + return r +} + +func (self *Receipt) SetLogs(logs state.Logs) { + self.logs = logs +} + +func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) { + self.PostState = decoder.Get(0).Bytes() + self.CumulativeGasUsed = decoder.Get(1).BigInt() + self.Bloom = decoder.Get(2).Bytes() + + it := decoder.Get(3).NewIterator() + for it.Next() { + self.logs = append(self.logs, state.NewLogFromValue(it.Value())) + } +} + +func (self *Receipt) RlpData() interface{} { + return []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs.RlpData()} +} + +func (self *Receipt) RlpEncode() []byte { + return ethutil.Encode(self.RlpData()) +} + +func (self *Receipt) Cmp(other *Receipt) bool { + if bytes.Compare(self.PostState, other.PostState) != 0 { + return false + } + + return true +} + +func (self *Receipt) String() string { + return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs) +} + +type Receipts []*Receipt + +func (self Receipts) Len() int { return len(self) } +func (self Receipts) GetRlp(i int) []byte { return ethutil.Rlp(self[i]) } diff --git a/core/types/transaction.go b/core/types/transaction.go new file mode 100644 index 000000000..efc90b6f2 --- /dev/null +++ b/core/types/transaction.go @@ -0,0 +1,225 @@ +package types + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" + "github.com/obscuren/secp256k1-go" +) + +var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +func IsContractAddr(addr []byte) bool { + return len(addr) == 0 + //return bytes.Compare(addr, ContractAddr) == 0 +} + +type Transaction struct { + Nonce uint64 + Recipient []byte + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data []byte + v byte + r, s []byte + + // Indicates whether this tx is a contract creation transaction + contractCreation bool +} + +func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { + return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} +} + +func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { + return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)} +} + +func NewTransactionFromBytes(data []byte) *Transaction { + tx := &Transaction{} + tx.RlpDecode(data) + + return tx +} + +func NewTransactionFromValue(val *ethutil.Value) *Transaction { + tx := &Transaction{} + tx.RlpValueDecode(val) + + return tx +} + +func (self *Transaction) GasValue() *big.Int { + return new(big.Int).Mul(self.Gas, self.GasPrice) +} + +func (self *Transaction) TotalValue() *big.Int { + v := self.GasValue() + return v.Add(v, self.Value) +} + +func (tx *Transaction) Hash() []byte { + data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} + + return crypto.Sha3(ethutil.NewValue(data).Encode()) +} + +func (tx *Transaction) CreatesContract() bool { + return tx.contractCreation +} + +/* Deprecated */ +func (tx *Transaction) IsContract() bool { + return tx.CreatesContract() +} + +func (tx *Transaction) CreationAddress(state *state.State) []byte { + // Generate a new address + return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:] +} + +func (tx *Transaction) Curve() (v byte, r []byte, s []byte) { + v = tx.v + r = ethutil.LeftPadBytes(tx.r, 32) + s = ethutil.LeftPadBytes(tx.s, 32) + + return +} + +func (tx *Transaction) Signature(key []byte) []byte { + hash := tx.Hash() + + sig, _ := secp256k1.Sign(hash, key) + + return sig +} + +func (tx *Transaction) PublicKey() []byte { + hash := tx.Hash() + + v, r, s := tx.Curve() + + sig := append(r, s...) + sig = append(sig, v-27) + + pubkey := crypto.Ecrecover(append(hash, sig...)) + //pubkey, _ := secp256k1.RecoverPubkey(hash, sig) + + return pubkey +} + +func (tx *Transaction) Sender() []byte { + pubkey := tx.PublicKey() + + // Validate the returned key. + // Return nil if public key isn't in full format + if len(pubkey) != 0 && pubkey[0] != 4 { + return nil + } + + return crypto.Sha3(pubkey[1:])[12:] +} + +func (tx *Transaction) Sign(privk []byte) error { + + sig := tx.Signature(privk) + + tx.r = sig[:32] + tx.s = sig[32:64] + tx.v = sig[64] + 27 + + return nil +} + +func (tx *Transaction) RlpData() interface{} { + data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} + + // TODO Remove prefixing zero's + + return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes()) +} + +func (tx *Transaction) RlpValue() *ethutil.Value { + return ethutil.NewValue(tx.RlpData()) +} + +func (tx *Transaction) RlpEncode() []byte { + return tx.RlpValue().Encode() +} + +func (tx *Transaction) RlpDecode(data []byte) { + tx.RlpValueDecode(ethutil.NewValueFromBytes(data)) +} + +func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { + tx.Nonce = decoder.Get(0).Uint() + tx.GasPrice = decoder.Get(1).BigInt() + tx.Gas = decoder.Get(2).BigInt() + tx.Recipient = decoder.Get(3).Bytes() + tx.Value = decoder.Get(4).BigInt() + tx.Data = decoder.Get(5).Bytes() + tx.v = byte(decoder.Get(6).Uint()) + + tx.r = decoder.Get(7).Bytes() + tx.s = decoder.Get(8).Bytes() + + if IsContractAddr(tx.Recipient) { + tx.contractCreation = true + } +} + +func (tx *Transaction) String() string { + return fmt.Sprintf(` + TX(%x) + Contract: %v + From: %x + To: %x + Nonce: %v + GasPrice: %v + Gas: %v + Value: %v + Data: 0x%x + V: 0x%x + R: 0x%x + S: 0x%x + `, + tx.Hash(), + len(tx.Recipient) == 0, + tx.Sender(), + tx.Recipient, + tx.Nonce, + tx.GasPrice, + tx.Gas, + tx.Value, + tx.Data, + tx.v, + tx.r, + tx.s) +} + +// Transaction slice type for basic sorting +type Transactions []*Transaction + +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] } +func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) } + +type TxByNonce struct{ Transactions } + +func (s TxByNonce) Less(i, j int) bool { + return s.Transactions[i].Nonce < s.Transactions[j].Nonce +} diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/core/types/transaction_test.go @@ -0,0 +1 @@ +package types |