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()) }
// 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 }
// 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 }
// 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 }
// 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) } } } }
func NewReadableBlockBody(b *coin.Block) ReadableBlockBody { txns := make([]ReadableTransaction, len(b.Body.Transactions)) for i := range b.Body.Transactions { if b.Seq() == uint64(0) { // genesis block txns[i] = NewGenesisReadableTransaction(&Transaction{Txn: b.Body.Transactions[i]}) } else { txns[i] = NewReadableTransaction(&Transaction{Txn: b.Body.Transactions[i]}) } } return ReadableBlockBody{ Transactions: txns, } }
// 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 }
// VerifyBlockHeader Returns error if the BlockHeader is not valid func (bc Blockchain) verifyBlockHeader(b coin.Block) error { //check BkSeq head := bc.Head() if b.Head.BkSeq != head.Head.BkSeq+1 { return errors.New("BkSeq invalid") } //check Time, only requirement is that its monotonely increasing if b.Head.Time <= head.Head.Time { return errors.New("Block time must be > head time") } // Check block hash against previous head if b.Head.PrevHash != head.HashHeader() { return errors.New("PrevHash does not match current head") } if b.HashBody() != b.Head.BodyHash { return errors.New("Computed body hash does not match") } 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) }) }
// GetAddressTransactions returns the Transactions whose unspents give coins to a cipher.Address. // This includes unconfirmed txns' predicted unspents. func (vs *Visor) GetAddressTransactions(a cipher.Address) []Transaction { var txns []Transaction // Look in the blockchain uxs := vs.Blockchain.GetUnspent().AllForAddress(a) mxSeq := vs.HeadBkSeq() var bk *coin.Block for _, ux := range uxs { if bk = vs.GetBlockBySeq(ux.Head.BkSeq); bk == nil { return txns } tx, ok := bk.GetTransaction(ux.Body.SrcTransaction) if ok { h := mxSeq - bk.Head.BkSeq + 1 txns = append(txns, Transaction{ Txn: tx, Status: NewConfirmedTransactionStatus(h), Time: bk.Time(), }) } } // Look in the unconfirmed pool uxs = vs.Unconfirmed.Unspent.AllForAddress(a) for _, ux := range uxs { tx, ok := vs.Unconfirmed.Txns[ux.Body.SrcTransaction] if !ok { logger.Critical("Unconfirmed unspent missing unconfirmed txn") continue } txns = append(txns, Transaction{ Txn: tx.Txn, Status: NewUnconfirmedTransactionStatus(), Time: uint64(tx.Received.Unix()), }) } return txns }
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 TestVerifyBlockHeader(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) gb := bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) b := coin.Block{Body: coin.BlockBody{}} b.Body.Transactions = append(b.Body.Transactions, makeTransaction(t)) h := coin.BlockHeader{} h.BkSeq = 1 h.Time = gb.Head.Time + 1 h.PrevHash = gb.HashHeader() h.BodyHash = b.HashBody() // Valid header b.Head = h assert.Nil(t, bc.verifyBlockHeader(b)) // Invalid bkSeq i := h i.BkSeq++ b.Head = i assertError(t, bc.verifyBlockHeader(b), "BkSeq invalid") // Invalid time i = h i.Time = gb.Head.Time b.Head = i assertError(t, bc.verifyBlockHeader(b), "Block time must be > head time") b.Head.Time-- assertError(t, bc.verifyBlockHeader(b), "Block time must be > head time") // Invalid prevHash i = h i.PrevHash = cipher.SHA256{} b.Head = i assertError(t, bc.verifyBlockHeader(b), "PrevHash does not match current head") // Invalid bodyHash i = h i.BodyHash = cipher.SHA256{} b.Head = i assertError(t, bc.verifyBlockHeader(b), "Computed body hash does not match") }
// GetLastTxs returns last confirmed transactions, return nil if empty func (vs *Visor) GetLastTxs() ([]*Transaction, error) { ltxs, err := vs.history.GetLastTxs() if err != nil { return nil, err } txs := make([]*Transaction, len(ltxs)) var confirms uint64 bh := vs.GetHeadBlock().Seq() var b *coin.Block for i, tx := range ltxs { confirms = bh - tx.BlockSeq + 1 if b = vs.GetBlockBySeq(tx.BlockSeq); b == nil { return nil, fmt.Errorf("found no block in seq %v", tx.BlockSeq) } txs[i] = &Transaction{ Txn: tx.Tx, Status: NewConfirmedTransactionStatus(confirms), Time: b.Time(), } } return txs, nil }
// ProcessBlock will index the transaction, outputs,etc. func (hd *HistoryDB) ProcessBlock(b *coin.Block) error { // index the transactions for _, t := range b.Body.Transactions { tx := Transaction{ Tx: t, BlockSeq: b.Seq(), } if err := hd.txns.Add(&tx); err != nil { return err } // handle tx in, genesis transaction's vin is empty, so should be ignored. if b.Seq() > 0 { for _, in := range t.In { o, err := hd.outputs.Get(in) if err != nil { return err } // update output's spent block seq and txid. o.SpentBlockSeq = b.Seq() o.SpentTxID = t.Hash() if err := hd.outputs.Set(*o); err != nil { return err } } } // handle the tx out uxArray := coin.CreateUnspents(b.Head, t) for _, ux := range uxArray { uxOut := UxOut{ Out: ux, } if err := hd.outputs.Set(uxOut); err != nil { return err } if err := hd.addrUx.Add(ux.Body.Address, ux.Hash()); err != nil { return err } } } return nil }
func setBlock(bkt *bolt.Bucket, b *coin.Block) error { bin := encoder.Serialize(b) key := b.HashHeader() return bkt.Put(key[:], bin) }
// 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) }) }