aboutsummaryrefslogtreecommitdiffstats
path: root/block_manager.go
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-01-24 03:16:52 +0800
committerobscuren <geffobscura@gmail.com>2014-01-24 03:16:52 +0800
commit477e8a7a73820ece05bbb990607a1919d3788960 (patch)
treec13d1d7f709f0934b54ef10377e671446d165db7 /block_manager.go
parent3616080db46931202003157bacf10748008bebc0 (diff)
downloadgo-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar
go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.gz
go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.bz2
go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.lz
go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.xz
go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.zst
go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.zip
Rearrange packages
Diffstat (limited to 'block_manager.go')
-rw-r--r--block_manager.go591
1 files changed, 0 insertions, 591 deletions
diff --git a/block_manager.go b/block_manager.go
deleted file mode 100644
index 87af9f293..000000000
--- a/block_manager.go
+++ /dev/null
@@ -1,591 +0,0 @@
-package main
-
-import (
- "bytes"
- "errors"
- "fmt"
- "github.com/ethereum/ethutil-go"
- "github.com/obscuren/secp256k1-go"
- "log"
- "math"
- "math/big"
- "strconv"
- "time"
-)
-
-type BlockChain struct {
- // Last block
- LastBlock *ethutil.Block
- // The famous, the fabulous Mister GENESIIIIIIS (block)
- genesisBlock *ethutil.Block
- // Last known total difficulty
- TD *big.Int
-}
-
-func NewBlockChain() *BlockChain {
- bc := &BlockChain{}
- bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis))
-
- // Set the last know difficulty (might be 0x0 as initial value, Genesis)
- bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
-
- // TODO get last block from the database
- bc.LastBlock = bc.genesisBlock
-
- return bc
-}
-
-func (bc *BlockChain) HasBlock(hash string) bool {
- data, _ := ethutil.Config.Db.Get([]byte(hash))
- return len(data) != 0
-}
-
-func (bc *BlockChain) GenesisBlock() *ethutil.Block {
- return bc.genesisBlock
-}
-
-type BlockManager struct {
- server *Server
- // The block chain :)
- bc *BlockChain
-
- // Last known block number
- LastBlockNumber *big.Int
-
- // Stack for processing contracts
- stack *Stack
- // non-persistent key/value memory storage
- mem map[string]*big.Int
-}
-
-func NewBlockManager(s *Server) *BlockManager {
- bm := &BlockManager{
- server: s,
- bc: NewBlockChain(),
- stack: NewStack(),
- mem: make(map[string]*big.Int),
- }
-
- // Set the last known block number based on the blockchains last
- // block
- bm.LastBlockNumber = bm.BlockInfo(bm.bc.LastBlock).Number
-
- return bm
-}
-
-// Process a block.
-func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error {
- // Block validation
- if err := bm.ValidateBlock(block); err != nil {
- return err
- }
-
- // I'm not sure, but I don't know if there should be thrown
- // any errors at this time.
- if err := bm.AccumelateRewards(block); err != nil {
- return err
- }
-
- // Get the tx count. Used to create enough channels to 'join' the go routines
- txCount := len(block.Transactions())
- // Locking channel. When it has been fully buffered this method will return
- lockChan := make(chan bool, txCount)
-
- // Process each transaction/contract
- for _, tx := range block.Transactions() {
- // If there's no recipient, it's a contract
- if tx.IsContract() {
- go bm.ProcessContract(tx, block, lockChan)
- } else {
- // "finish" tx which isn't a contract
- lockChan <- true
- }
- }
-
- // Wait for all Tx to finish processing
- for i := 0; i < txCount; i++ {
- <-lockChan
- }
-
- // Calculate the new total difficulty and sync back to the db
- if bm.CalculateTD(block) {
- ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
- bm.bc.LastBlock = block
- }
-
- return nil
-}
-
-// Unexported method for writing extra non-essential block info to the db
-func (bm *BlockManager) writeBlockInfo(block *ethutil.Block) {
- bi := ethutil.BlockInfo{Number: bm.LastBlockNumber.Add(bm.LastBlockNumber, big.NewInt(1))}
-
- // For now we use the block hash with the words "info" appended as key
- ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
-}
-
-func (bm *BlockManager) BlockInfo(block *ethutil.Block) ethutil.BlockInfo {
- bi := ethutil.BlockInfo{}
- data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
- bi.RlpDecode(data)
-
- return bi
-}
-
-func (bm *BlockManager) CalculateTD(block *ethutil.Block) bool {
- uncleDiff := new(big.Int)
- for _, uncle := range block.Uncles {
- uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
- }
-
- // TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
- td := new(big.Int)
- td = td.Add(bm.bc.TD, uncleDiff)
- td = td.Add(td, block.Difficulty)
-
- // The new TD will only be accepted if the new difficulty is
- // is greater than the previous.
- if td.Cmp(bm.bc.TD) > 0 {
- bm.bc.LastBlock = block
- // Set the new total difficulty back to the block chain
- bm.bc.TD = td
-
- if Debug {
- log.Println("TD(block) =", td)
- }
-
- return true
- }
-
- return false
-}
-
-// Validates the current block. Returns an error if the block was invalid,
-// an uncle or anything that isn't on the current block chain.
-// Validation validates easy over difficult (dagger takes longer time = difficult)
-func (bm *BlockManager) ValidateBlock(block *ethutil.Block) error {
- // Genesis block
- if bm.bc.LastBlock == nil && block.PrevHash == "" {
- return nil
- }
- // TODO
- // 2. Check if the difficulty is correct
-
- // Check if we have the parent hash, if it isn't known we discard it
- // Reasons might be catching up or simply an invalid block
- if !bm.bc.HasBlock(block.PrevHash) {
- return errors.New("Block's parent unknown")
- }
-
- // Check each uncle's previous hash. In order for it to be valid
- // is if it has the same block hash as the current
- for _, uncle := range block.Uncles {
- if uncle.PrevHash != block.PrevHash {
- if Debug {
- log.Printf("Uncle prvhash mismatch %x %x\n", block.PrevHash, uncle.PrevHash)
- }
-
- return errors.New("Mismatching Prvhash from uncle")
- }
- }
-
- diff := block.Time - bm.bc.LastBlock.Time
- if diff < 0 {
- return fmt.Errorf("Block timestamp less then prev block %v", diff)
- }
-
- // New blocks must be within the 15 minute range of the last block.
- if diff > int64(15*time.Minute) {
- return errors.New("Block is too far in the future of last block (> 15 minutes)")
- }
-
- // Verify the nonce of the block. Return an error if it's not valid
- if !DaggerVerify(ethutil.BigD(block.Hash()), block.Difficulty, block.Nonce) {
-
- return errors.New("Block's nonce is invalid")
- }
-
- log.Println("Block validation PASSED")
-
- return nil
-}
-
-func (bm *BlockManager) AccumelateRewards(block *ethutil.Block) error {
- // Get the coinbase rlp data
- d := block.State().Get(block.Coinbase)
-
- ether := ethutil.NewEtherFromData([]byte(d))
-
- // Reward amount of ether to the coinbase address
- ether.AddFee(ethutil.CalculateBlockReward(block, len(block.Uncles)))
- block.State().Update(block.Coinbase, string(ether.RlpEncode()))
-
- // TODO Reward each uncle
-
- return nil
-}
-
-func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.Block, lockChan chan bool) {
- // Recovering function in case the VM had any errors
- defer func() {
- if r := recover(); r != nil {
- fmt.Println("Recovered from VM execution with err =", r)
- // Let the channel know where done even though it failed (so the execution may resume normally)
- lockChan <- true
- }
- }()
-
- // Process contract
- bm.ProcContract(tx, block, func(opType OpType) bool {
- // TODO turn on once big ints are in place
- //if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
- // return false
- //}
-
- return true // Continue
- })
-
- // Broadcast we're done
- lockChan <- true
-}
-
-// Contract evaluation is done here.
-func (bm *BlockManager) ProcContract(tx *ethutil.Transaction, block *ethutil.Block, cb TxCallback) {
- // Instruction pointer
- pc := 0
- blockInfo := bm.BlockInfo(block)
-
- contract := block.GetContract(tx.Hash())
- if contract == nil {
- fmt.Println("Contract not found")
- return
- }
-
- Pow256 := ethutil.BigPow(2, 256)
-
- //fmt.Printf("# op arg\n")
-out:
- for {
- // The base big int for all calculations. Use this for any results.
- base := new(big.Int)
- // XXX Should Instr return big int slice instead of string slice?
- // Get the next instruction from the contract
- //op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
- nb := ethutil.NumberToBytes(uint64(pc), 32)
- o, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
- op := OpCode(o)
-
- if !cb(0) {
- break
- }
-
- if Debug {
- //fmt.Printf("%-3d %-4s\n", pc, op.String())
- }
-
- switch op {
- case oSTOP:
- break out
- case oADD:
- x, y := bm.stack.Popn()
- // (x + y) % 2 ** 256
- base.Add(x, y)
- base.Mod(base, Pow256)
- // Pop result back on the stack
- bm.stack.Push(base)
- case oSUB:
- x, y := bm.stack.Popn()
- // (x - y) % 2 ** 256
- base.Sub(x, y)
- base.Mod(base, Pow256)
- // Pop result back on the stack
- bm.stack.Push(base)
- case oMUL:
- x, y := bm.stack.Popn()
- // (x * y) % 2 ** 256
- base.Mul(x, y)
- base.Mod(base, Pow256)
- // Pop result back on the stack
- bm.stack.Push(base)
- case oDIV:
- x, y := bm.stack.Popn()
- // floor(x / y)
- base.Div(x, y)
- // Pop result back on the stack
- bm.stack.Push(base)
- case oSDIV:
- x, y := bm.stack.Popn()
- // n > 2**255
- if x.Cmp(Pow256) > 0 {
- x.Sub(Pow256, x)
- }
- if y.Cmp(Pow256) > 0 {
- y.Sub(Pow256, y)
- }
- z := new(big.Int)
- z.Div(x, y)
- if z.Cmp(Pow256) > 0 {
- z.Sub(Pow256, z)
- }
- // Push result on to the stack
- bm.stack.Push(z)
- case oMOD:
- x, y := bm.stack.Popn()
- base.Mod(x, y)
- bm.stack.Push(base)
- case oSMOD:
- x, y := bm.stack.Popn()
- // n > 2**255
- if x.Cmp(Pow256) > 0 {
- x.Sub(Pow256, x)
- }
- if y.Cmp(Pow256) > 0 {
- y.Sub(Pow256, y)
- }
- z := new(big.Int)
- z.Mod(x, y)
- if z.Cmp(Pow256) > 0 {
- z.Sub(Pow256, z)
- }
- // Push result on to the stack
- bm.stack.Push(z)
- case oEXP:
- x, y := bm.stack.Popn()
- base.Exp(x, y, Pow256)
-
- bm.stack.Push(base)
- case oNEG:
- base.Sub(Pow256, bm.stack.Pop())
- bm.stack.Push(base)
- case oLT:
- x, y := bm.stack.Popn()
- // x < y
- if x.Cmp(y) < 0 {
- bm.stack.Push(ethutil.BigTrue)
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
- case oLE:
- x, y := bm.stack.Popn()
- // x <= y
- if x.Cmp(y) < 1 {
- bm.stack.Push(ethutil.BigTrue)
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
- case oGT:
- x, y := bm.stack.Popn()
- // x > y
- if x.Cmp(y) > 0 {
- bm.stack.Push(ethutil.BigTrue)
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
- case oGE:
- x, y := bm.stack.Popn()
- // x >= y
- if x.Cmp(y) > -1 {
- bm.stack.Push(ethutil.BigTrue)
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
- case oNOT:
- x, y := bm.stack.Popn()
- // x != y
- if x.Cmp(y) != 0 {
- bm.stack.Push(ethutil.BigTrue)
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
-
- // Please note that the following code contains some
- // ugly string casting. This will have to change to big
- // ints. TODO :)
- case oMYADDRESS:
- bm.stack.Push(ethutil.BigD(tx.Hash()))
- case oTXSENDER:
- bm.stack.Push(ethutil.BigD(tx.Sender()))
- case oTXVALUE:
- bm.stack.Push(tx.Value)
- case oTXDATAN:
- bm.stack.Push(big.NewInt(int64(len(tx.Data))))
- case oTXDATA:
- v := bm.stack.Pop()
- // v >= len(data)
- if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 {
- bm.stack.Push(ethutil.Big("0"))
- } else {
- bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()]))
- }
- case oBLK_PREVHASH:
- bm.stack.Push(ethutil.Big(block.PrevHash))
- case oBLK_COINBASE:
- bm.stack.Push(ethutil.Big(block.Coinbase))
- case oBLK_TIMESTAMP:
- bm.stack.Push(big.NewInt(block.Time))
- case oBLK_NUMBER:
- bm.stack.Push(blockInfo.Number)
- case oBLK_DIFFICULTY:
- bm.stack.Push(block.Difficulty)
- case oBASEFEE:
- // e = 10^21
- e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0))
- d := new(big.Rat)
- d.SetInt(block.Difficulty)
- c := new(big.Rat)
- c.SetFloat64(0.5)
- // d = diff / 0.5
- d.Quo(d, c)
- // base = floor(d)
- base.Div(d.Num(), d.Denom())
-
- x := new(big.Int)
- x.Div(e, base)
-
- // x = floor(10^21 / floor(diff^0.5))
- bm.stack.Push(x)
- case oSHA256, oSHA3, oRIPEMD160:
- // This is probably save
- // ceil(pop / 32)
- length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0))
- // New buffer which will contain the concatenated popped items
- data := new(bytes.Buffer)
- for i := 0; i < length; i++ {
- // Encode the number to bytes and have it 32bytes long
- num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256)
- data.WriteString(string(num))
- }
-
- if op == oSHA256 {
- bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
- } else if op == oSHA3 {
- bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
- } else {
- bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
- }
- case oECMUL:
- y := bm.stack.Pop()
- x := bm.stack.Pop()
- //n := bm.stack.Pop()
-
- //if ethutil.Big(x).Cmp(ethutil.Big(y)) {
- data := new(bytes.Buffer)
- data.WriteString(x.String())
- data.WriteString(y.String())
- if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 {
- // TODO
- } else {
- // Invalid, push infinity
- bm.stack.Push(ethutil.Big("0"))
- bm.stack.Push(ethutil.Big("0"))
- }
- //} else {
- // // Invalid, push infinity
- // bm.stack.Push("0")
- // bm.stack.Push("0")
- //}
-
- case oECADD:
- case oECSIGN:
- case oECRECOVER:
- case oECVALID:
- case oPUSH:
- pc++
- bm.stack.Push(bm.mem[strconv.Itoa(pc)])
- case oPOP:
- // Pop current value of the stack
- bm.stack.Pop()
- case oDUP:
- // Dup top stack
- x := bm.stack.Pop()
- bm.stack.Push(x)
- bm.stack.Push(x)
- case oSWAP:
- // Swap two top most values
- x, y := bm.stack.Popn()
- bm.stack.Push(y)
- bm.stack.Push(x)
- case oMLOAD:
- x := bm.stack.Pop()
- bm.stack.Push(bm.mem[x.String()])
- case oMSTORE:
- x, y := bm.stack.Popn()
- bm.mem[x.String()] = y
- case oSLOAD:
- // Load the value in storage and push it on the stack
- x := bm.stack.Pop()
- // decode the object as a big integer
- decoder := ethutil.NewRlpDecoder([]byte(contract.State().Get(x.String())))
- if !decoder.IsNil() {
- bm.stack.Push(decoder.AsBigInt())
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
- case oSSTORE:
- // Store Y at index X
- x, y := bm.stack.Popn()
- contract.State().Update(x.String(), string(ethutil.Encode(y)))
- case oJMP:
- x := int(bm.stack.Pop().Uint64())
- // Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
- pc = x
- pc--
- case oJMPI:
- x := bm.stack.Pop()
- // Set pc to x if it's non zero
- if x.Cmp(ethutil.BigFalse) != 0 {
- pc = int(x.Uint64())
- pc--
- }
- case oIND:
- bm.stack.Push(big.NewInt(int64(pc)))
- case oEXTRO:
- memAddr := bm.stack.Pop()
- contractAddr := bm.stack.Pop().Bytes()
-
- // Push the contract's memory on to the stack
- bm.stack.Push(getContractMemory(block, contractAddr, memAddr))
- case oBALANCE:
- // Pushes the balance of the popped value on to the stack
- d := block.State().Get(bm.stack.Pop().String())
- ether := ethutil.NewEtherFromData([]byte(d))
- bm.stack.Push(ether.Amount)
- case oMKTX:
- value, addr := bm.stack.Popn()
- from, length := bm.stack.Popn()
-
- j := 0
- dataItems := make([]string, int(length.Uint64()))
- for i := from.Uint64(); i < length.Uint64(); i++ {
- dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes())
- j++
- }
- // TODO sign it?
- tx := ethutil.NewTransaction(string(addr.Bytes()), value, dataItems)
- // Add the transaction to the tx pool
- bm.server.txPool.QueueTransaction(tx)
- case oSUICIDE:
- //addr := bm.stack.Pop()
- }
- pc++
- }
-
- bm.stack.Print()
-}
-
-// Returns an address from the specified contract's address
-func getContractMemory(block *ethutil.Block, contractAddr []byte, memAddr *big.Int) *big.Int {
- contract := block.GetContract(contractAddr)
- if contract == nil {
- log.Panicf("invalid contract addr %x", contractAddr)
- }
- val := contract.State().Get(memAddr.String())
-
- // decode the object as a big integer
- decoder := ethutil.NewRlpDecoder([]byte(val))
- if decoder.IsNil() {
- return ethutil.BigFalse
- }
-
- return decoder.AsBigInt()
-}