func (db *T) PutBlock(b *block.Block, last bool) (e error) { writable(&e, db, func(dbtx *bolt.Tx) bool { bucket, e := dbtx.CreateBucketIfNotExists([]byte("blocks")) if e != nil { return false } encoded, e := b.Encode() if e != nil { return false } e = bucket.Put(b.Hash(), encoded) if e != nil { return false } // store a reference to this block as a next block e = bucket.Put(append(b.PreviousBlockHash, []byte("+")...), b.Hash()) if e != nil { return false } for i := range b.Transactions { // transaction -> block mapping e = bucket.Put(append([]byte("T"), b.Transactions[i].Hash()...), b.Hash()) if e != nil { return false } // link next transactions e = bucket.Put(append([]byte(">"), b.Transactions[i].PreviousEnvelopeHash...), b.Transactions[i].Hash()) if e != nil { return false } // update "unspendable key" e = bucket.Delete(append([]byte("<"), b.Transactions[i].PublicKey...)) if e != nil { return false } // update "spendable key", has to happen after updating the "unspendable" one as it might be the same one e = bucket.Put(append([]byte("<"), b.Transactions[i].NextPublicKey...), b.Transactions[i].Hash()) if e != nil { return false } } if last { if e = bucket.Put([]byte("last"), b.Hash()); e != nil { return false } } return true }) return }
func (db *T) PutBlock(b *block.Block, last bool) error { dbtx, err := db.DB.Begin(true) success := false defer func() { if success { dbtx.Commit() } else { dbtx.Rollback() } }() if err != nil { return err } bucket, err := dbtx.CreateBucketIfNotExists([]byte("blocks")) if err != nil { return err } encoded, err := b.Encode() if err != nil { return err } err = bucket.Put(b.Hash(), encoded) if err != nil { return err } for i := range b.Transactions { err = bucket.Put(b.Transactions[i].Hash(), b.Hash()) if err != nil { return err } } if last { bucket.Put([]byte("last"), b.Hash()) } success = true return nil }
func TransactionListener() { var msg transaction.T var blk *block.Block blockChannel := make(chan *block.Block) var transactionsPool []transaction.T var previousBlockHash types.Hash var ch chan transaction.T = make(chan transaction.T) router.PermanentSubscribe("/transaction", ch) miningEmpty := false initPool: transactionsPool = make([]transaction.T, 0) loop: select { case msg = <-ch: miningEmpty = false env.DB.PutTransaction(msg) transactionsPool = append(transactionsPool, msg) if blk, _ = env.DB.GetLastBlock(); blk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } if bat := prepareBAT(); bat != nil { transactionsPool = append(transactionsPool, bat) } blk, err := block.NewBlock(previousBlockHash, targetBits(), transactionsPool) if err != nil { log.Printf("Error while creating a new block: %v", err) } else { miningFactoryRequests <- MiningFactoryInstantiationRequest{Block: blk, ResponseChannel: blockChannel} } case blk = <-blockChannel: miningEmpty = false if lastBlk, _ := env.DB.GetLastBlock(); lastBlk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } isLastBlock := bytes.Compare(blk.PreviousBlockHash, previousBlockHash) == 0 env.DB.PutBlock(blk, isLastBlock) goto initPool default: if len(transactionsPool) == 0 && !miningEmpty { // if there are no transactions to be included into a block, try mining an empty/BAT-only block if blk, _ = env.DB.GetLastBlock(); blk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } if bat := prepareBAT(); bat != nil { transactionsPool = append(transactionsPool, bat) } blk, err := block.NewBlock(previousBlockHash, targetBits(), transactionsPool) if err != nil { log.Printf("Error while creating a new block: %v", err) } else { miningFactoryRequests <- MiningFactoryInstantiationRequest{Block: blk, ResponseChannel: blockChannel} miningEmpty = true } } } goto loop }