diff options
Diffstat (limited to 'ethchain/block.go')
-rw-r--r-- | ethchain/block.go | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/ethchain/block.go b/ethchain/block.go new file mode 100644 index 000000000..b98d806d8 --- /dev/null +++ b/ethchain/block.go @@ -0,0 +1,439 @@ +package ethchain + +import ( + "bytes" + "fmt" + "math/big" + "sort" + _ "strconv" + "time" + + "github.com/ethereum/go-ethereum/ethcrypto" + "github.com/ethereum/go-ethereum/ethstate" + "github.com/ethereum/go-ethereum/ethtrie" + "github.com/ethereum/go-ethereum/ethutil" +) + +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 *ethstate.State + // Difficulty for the current block + Difficulty *big.Int + // Creation time + Time int64 + // The block number + Number *big.Int + // Minimum Gas Price + MinGasPrice *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 []*Transaction + receipts []*Receipt + TxSha []byte +} + +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), + MinGasPrice: new(big.Int), + GasLimit: new(big.Int), + } + block.SetUncles([]*Block{}) + + block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, root)) + + return block +} + +// Returns a hash of the block +func (block *Block) Hash() ethutil.Bytes { + return ethcrypto.Sha3(ethutil.NewValue(block.header()).Encode()) + //return ethcrypto.Sha3(block.Value().Encode()) +} + +func (block *Block) HashNoNonce() []byte { + return ethcrypto.Sha3(ethutil.Encode([]interface{}{block.PrevHash, + block.UncleSha, block.Coinbase, block.state.Trie.Root, + block.TxSha, block.Difficulty, block.Number, block.MinGasPrice, + block.GasLimit, block.GasUsed, block.Time, block.Extra})) +} + +func (block *Block) State() *ethstate.State { + return block.state +} + +func (block *Block) Transactions() []*Transaction { + 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 _, receipt := range self.receipts { + if bytes.Compare(receipt.Tx.Hash(), hash) == 0 { + return receipt.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 + + // Sha of the concatenated uncles + if len(uncles) > 0 { + block.UncleSha = ethcrypto.Sha3(ethutil.Encode(block.rlpUncles())) + } +} + +func (self *Block) SetReceipts(receipts []*Receipt, txs []*Transaction) { + self.receipts = receipts + self.setTransactions(txs) +} + +func (block *Block) setTransactions(txs []*Transaction) { + block.transactions = txs +} + +func CreateTxSha(receipts Receipts) (sha []byte) { + trie := ethtrie.New(ethutil.Config.Db, "") + for i, receipt := range receipts { + trie.Update(string(ethutil.NewValue(i).Encode()), string(ethutil.NewValue(receipt.RlpData()).Encode())) + } + + switch trie.Root.(type) { + case string: + sha = []byte(trie.Root.(string)) + case []byte: + sha = trie.Root.([]byte) + default: + panic(fmt.Sprintf("invalid root type %T", trie.Root)) + } + + return sha +} + +func (self *Block) SetTxHash(receipts Receipts) { + self.TxSha = CreateTxSha(receipts) +} + +func (block *Block) Value() *ethutil.Value { + return ethutil.NewValue([]interface{}{block.header(), block.rlpReceipts(), 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) { + header := decoder.Get(0) + + block.PrevHash = header.Get(0).Bytes() + block.UncleSha = header.Get(1).Bytes() + block.Coinbase = header.Get(2).Bytes() + block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, header.Get(3).Val)) + block.TxSha = header.Get(4).Bytes() + block.Difficulty = header.Get(5).BigInt() + block.Number = header.Get(6).BigInt() + //fmt.Printf("#%v : %x\n", block.Number, block.Coinbase) + block.MinGasPrice = header.Get(7).BigInt() + block.GasLimit = header.Get(8).BigInt() + block.GasUsed = header.Get(9).BigInt() + block.Time = int64(header.Get(10).BigInt().Uint64()) + block.Extra = header.Get(11).Str() + block.Nonce = header.Get(12).Bytes() + + // 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.transactions = make([]*Transaction, receipts.Len()) + block.receipts = make([]*Receipt, receipts.Len()) + for i := 0; i < receipts.Len(); 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 NewUncleBlockFromValue(header *ethutil.Value) *Block { + block := &Block{} + + block.PrevHash = header.Get(0).Bytes() + block.UncleSha = header.Get(1).Bytes() + block.Coinbase = header.Get(2).Bytes() + block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, header.Get(3).Val)) + block.TxSha = header.Get(4).Bytes() + block.Difficulty = header.Get(5).BigInt() + block.Number = header.Get(6).BigInt() + block.MinGasPrice = header.Get(7).BigInt() + block.GasLimit = header.Get(8).BigInt() + block.GasUsed = header.Get(9).BigInt() + block.Time = int64(header.Get(10).BigInt().Uint64()) + block.Extra = header.Get(11).Str() + block.Nonce = header.Get(12).Bytes() + + return block +} + +func (block *Block) Trie() *ethtrie.Trie { + return block.state.Trie +} + +func (block *Block) GetRoot() interface{} { + return block.state.Trie.Root +} + +func (block *Block) Diff() *big.Int { + return block.Difficulty +} + +func (self *Block) Receipts() []*Receipt { + return self.receipts +} + +func (block *Block) header() []interface{} { + return []interface{}{ + // Sha of the previous block + block.PrevHash, + // Sha of uncles + block.UncleSha, + // Coinbase address + block.Coinbase, + // root state + block.state.Trie.Root, + // Sha of tx + block.TxSha, + // Current block Difficulty + block.Difficulty, + // The block number + block.Number, + // Block minimum gas price + block.MinGasPrice, + // Block upper gas bound + block.GasLimit, + // Block gas used + block.GasUsed, + // Time the block was found? + block.Time, + // Extra data + block.Extra, + // Block's Nonce for validation + block.Nonce, + } +} + +func (block *Block) String() string { + return fmt.Sprintf(` + BLOCK(%x): Size: %v + PrevHash: %x + UncleSha: %x + Coinbase: %x + Root: %x + TxSha: %x + Difficulty: %v + Number: %v + MinGas: %v + MaxLimit: %v + GasUsed: %v + Time: %v + Extra: %v + Nonce: %x + NumTx: %v +`, + block.Hash(), + block.Size(), + block.PrevHash, + block.UncleSha, + block.Coinbase, + block.state.Trie.Root, + block.TxSha, + block.Difficulty, + block.Number, + block.MinGasPrice, + 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())) +} |