// AddBlock write the block into blocks bucket, add the pair of block hash and pre block hash into // tree in the block depth. func (bt *BlockTree) AddBlock(b *coin.Block) error { return UpdateTx(func(tx *bolt.Tx) error { blocks := tx.Bucket(bt.blocks.Name) // can't store block if it's not genesis block and has no parent. if b.Seq() > 0 && b.PreHashHeader() == emptyHash { return errNoParent } // check if the block already exist. hash := b.HashHeader() if blk := blocks.Get(hash[:]); blk != nil { return errBlockExist } // write block into blocks bucket. if err := setBlock(blocks, b); err != nil { return err } // get tree bucket. tree := tx.Bucket(bt.tree.Name) // the pre hash must be in depth - 1. if b.Seq() > 0 { preHash := b.PreHashHeader() parentHashPair, err := getHashPairInDepth(tree, b.Seq()-1, func(hp coin.HashPair) bool { return hp.Hash == preHash }) if err != nil { return err } if len(parentHashPair) == 0 { return errWrongParent } } hp := coin.HashPair{hash, b.Head.PrevHash} // get block pairs in the depth hashPairs, err := getHashPairInDepth(tree, b.Seq(), allPairs) if err != nil { return err } if len(hashPairs) == 0 { // no hash pair exist in the depth. // write the hash pair into tree. return setHashPairInDepth(tree, b.Seq(), []coin.HashPair{hp}) } // check dup block if containHash(hashPairs, hp) { return errBlockExist } hashPairs = append(hashPairs, hp) return setHashPairInDepth(tree, b.Seq(), hashPairs) }) }
// ExecuteBlock Attempts to append block to blockchain. Returns the UxOuts created, // and an error if the block is invalid. func (bc *Blockchain) ExecuteBlock(b *coin.Block) (coin.UxArray, error) { var uxs coin.UxArray err := bc.verifyBlock(*b) if err != nil { return nil, err } txns := b.Body.Transactions for _, tx := range txns { // Remove spent outputs bc.unspent.DelMultiple(tx.In) // Create new outputs txUxs := coin.CreateUnspents(b.Head, tx) for i := range txUxs { bc.unspent.Add(txUxs[i]) } uxs = append(uxs, txUxs...) } b.Head.PrevHash = bc.head bc.addBlock(b) // update the head bc.head = b.HashHeader() bc.notify(*b) return uxs, nil }
func TestNewBlock(t *testing.T) { // TODO -- update this test for newBlock changes prev := coin.Block{Head: coin.BlockHeader{Version: 0x02, Time: 100, BkSeq: 98}} unsp := coin.NewUnspentPool() unsp.XorHash = randSHA256() txns := coin.Transactions{coin.Transaction{}} // invalid txn fees panics assert.Panics(t, func() { coin.NewBlock(prev, 133, unsp, txns, _badFeeCalc) }) // no txns panics assert.Panics(t, func() { coin.NewBlock(prev, 133, unsp, nil, _feeCalc) }) assert.Panics(t, func() { coin.NewBlock(prev, 133, unsp, coin.Transactions{}, _feeCalc) }) // valid block is fine fee := uint64(121) currentTime := uint64(133) b := coin.NewBlock(prev, currentTime, unsp, txns, _makeFeeCalc(fee)) assert.Equal(t, b.Body.Transactions, txns) assert.Equal(t, b.Head.Fee, fee*uint64(len(txns))) assert.Equal(t, b.Body, coin.BlockBody{txns}) assert.Equal(t, b.Head.PrevHash, prev.HashHeader()) assert.Equal(t, b.Head.Time, currentTime) assert.Equal(t, b.Head.BkSeq, prev.Head.BkSeq+1) assert.Equal(t, b.Head.UxHash, unsp.GetUxHash()) }
// Signs a block for master. Will panic if anything is invalid func (self *Visor) SignBlock(b coin.Block) SignedBlock { if !self.Config.IsMaster { log.Panic("Only master chain can sign blocks") } sig := cipher.SignHash(b.HashHeader(), self.Config.BlockchainSeckey) sb := SignedBlock{ Block: b, Sig: sig, } return sb }
// Signs a block for master. Will panic if anything is invalid func (self *Blockchain) signBlock(b coin.Block) SignedBlock { if !self.IsMaster { log.Panic("Only master chain can sign blocks") } sig := coin.SignHash(b.HashHeader(), self.SecKey) sb := SignedBlock{ Block: b, Sig: sig, } return sb }
// check if this block has children func hasChild(bkt *bolt.Bucket, b coin.Block) (bool, error) { // get the child block hash pair, whose pre hash point to current block. childHashPair, err := getHashPairInDepth(bkt, b.Head.BkSeq+1, func(hp coin.HashPair) bool { return hp.PreHash == b.HashHeader() }) if err != nil { return false, nil } return len(childHashPair) > 0, nil }
func testCase(t *testing.T, cases []blockCase) { _, teardown, err := setup(t) if err != nil { t.Fatal(err) } defer teardown() btree := NewBlockTree() blocks := make([]coin.Block, len(cases)) for i, d := range cases { var preHash cipher.SHA256 if d.BInfo.Pre != -1 { preHash = blocks[d.BInfo.Pre].HashHeader() } b := coin.Block{ Head: coin.BlockHeader{ BkSeq: d.BInfo.Seq, Time: d.BInfo.Time, Fee: d.BInfo.Fee, PrevHash: preHash, }, } blocks[i] = b switch d.Action { case "add": err := btree.AddBlock(&b) if err != d.Err { t.Fatal(fmt.Sprintf("expect err:%v, but get err:%v", d.Err, err)) } if err == nil { b1 := btree.GetBlock(b.HashHeader()) assert.Equal(t, *b1, b) } case "remove": err := btree.RemoveBlock(&b) if err != d.Err { t.Fatal(fmt.Sprintf("expect err:%v, but get err:%v", d.Err, err)) } if err == nil { b1 := btree.GetBlock(b.HashHeader()) assert.Nil(t, b1) } } } }
// VerifyBlock verifies the BlockHeader and BlockBody func (bc Blockchain) verifyBlock(b coin.Block) error { gb := bc.GetGenesisBlock() if gb.HashHeader() != b.HashHeader() { if err := bc.verifyBlockHeader(b); err != nil { return err } if err := bc.verifyTransactions(b.Body.Transactions); err != nil { return err } } if err := bc.verifyUxHash(b); err != nil { return err } return nil }
// RemoveBlock remove block from blocks bucket and tree bucket. // can't remove block if it has children. func (bt *BlockTree) RemoveBlock(b *coin.Block) error { return UpdateTx(func(tx *bolt.Tx) error { // delete block in blocks bucket. blocks := tx.Bucket(bt.blocks.Name) hash := b.HashHeader() if err := blocks.Delete(hash[:]); err != nil { return err } // get tree bucket. tree := tx.Bucket(bt.tree.Name) // check if this block has children has, err := hasChild(tree, *b) if err != nil { return err } if has { return errHasChild } // get block hash pairs in depth hashPairs, err := getHashPairInDepth(tree, b.Seq(), func(hp coin.HashPair) bool { return true }) if err != nil { return err } // remove block hash pair in tree. ps := removePairs(hashPairs, coin.HashPair{hash, b.PreHashHeader()}) if len(ps) == 0 { tree.Delete(itob(b.Seq())) return nil } // update the hash pairs in tree. return setHashPairInDepth(tree, b.Seq(), ps) }) }
func (bc *Blockchain) walkTree() { var dep uint64 var preBlock *coin.Block for { b := bc.tree.GetBlockInDepth(dep, bc.walker) if b == nil { break } if dep > 0 { if b.PreHashHeader() != preBlock.HashHeader() { logger.Panicf("walk tree failed, pre hash header not match") } } preBlock = b if err := bc.updateUnspent(*b); err != nil { logger.Panicf("update unspent failed, err: %v", err.Error()) } bc.head = b.HashHeader() dep++ } }
// CreateGenesisBlock creates genesis block in blockchain. func (bc *Blockchain) CreateGenesisBlock(genesisAddr cipher.Address, genesisCoins, timestamp uint64) coin.Block { txn := coin.Transaction{} txn.PushOutput(genesisAddr, genesisCoins, genesisCoins) body := coin.BlockBody{coin.Transactions{txn}} prevHash := cipher.SHA256{} head := coin.BlockHeader{ Time: timestamp, BodyHash: body.Hash(), PrevHash: prevHash, BkSeq: 0, Version: 0, Fee: 0, UxHash: coin.NewUnspentPool().GetUxHash(), } b := coin.Block{ Head: head, Body: body, } // b.Body.Transactions[0].UpdateHeader() bc.addBlock(&b) bc.head = b.HashHeader() ux := coin.UxOut{ Head: coin.UxHead{ Time: timestamp, BkSeq: 0, }, Body: coin.UxBody{ SrcTransaction: txn.InnerHash, //user inner hash Address: genesisAddr, Coins: genesisCoins, Hours: genesisCoins, // Allocate 1 coin hour per coin }, } bc.unspent.Add(ux) bc.notify(b) return b }
func setBlock(bkt *bolt.Bucket, b *coin.Block) error { bin := encoder.Serialize(b) key := b.HashHeader() return bkt.Put(key[:], bin) }