// UpdateBlockHeightCache updates the dir block height cache in db func (db *LevelDb) UpdateBlockHeightCache(dirBlkHeigh uint32, dirBlkHash *common.Hash) error { // Update DirBlock Height cache db.lastDirBlkHeight = int64(dirBlkHeigh) db.lastDirBlkSha, _ = wire.NewShaHash(dirBlkHash.Bytes()) db.lastDirBlkShaCached = true return nil }
// ProcessDBlockBatche inserts the DBlock and update all it's dbentries in DB func (db *LevelDb) ProcessDBlockBatch(dblock *common.DirectoryBlock) error { if dblock != nil { if db.lbatch == nil { db.lbatch = new(leveldb.Batch) } defer db.lbatch.Reset() binaryDblock, err := dblock.MarshalBinary() if err != nil { return err } if dblock.DBHash == nil { dblock.DBHash = common.Sha(binaryDblock) } if dblock.KeyMR == nil { dblock.BuildKeyMerkleRoot() } // Insert the binary directory block var key []byte = []byte{byte(TBL_DB)} key = append(key, dblock.DBHash.Bytes()...) db.lbatch.Put(key, binaryDblock) // Insert block height cross reference var dbNumkey []byte = []byte{byte(TBL_DB_NUM)} var buf bytes.Buffer binary.Write(&buf, binary.BigEndian, dblock.Header.DBHeight) dbNumkey = append(dbNumkey, buf.Bytes()...) db.lbatch.Put(dbNumkey, dblock.DBHash.Bytes()) // Insert the directory block merkle root cross reference key = []byte{byte(TBL_DB_MR)} key = append(key, dblock.KeyMR.Bytes()...) binaryDBHash, _ := dblock.DBHash.MarshalBinary() db.lbatch.Put(key, binaryDBHash) // Update the chain head reference key = []byte{byte(TBL_CHAIN_HEAD)} key = append(key, common.D_CHAINID...) db.lbatch.Put(key, dblock.KeyMR.Bytes()) err = db.lDb.Write(db.lbatch, db.wo) if err != nil { return err } // Update DirBlock Height cache db.lastDirBlkHeight = int64(dblock.Header.DBHeight) db.lastDirBlkSha, _ = wire.NewShaHash(dblock.DBHash.Bytes()) db.lastDirBlkShaCached = true } return nil }
// handleABlockMsg is invoked when a peer receives a entry credit block message. func (p *peer) handleABlockMsg(msg *wire.MsgABlock, buf []byte) { binary, _ := msg.ABlk.MarshalBinary() commonHash := common.Sha(binary) hash, _ := wire.NewShaHash(commonHash.Bytes()) iv := wire.NewInvVect(wire.InvTypeFactomAdminBlock, hash) p.AddKnownInventory(iv) inMsgQueue <- msg }
// handleFactoidMsg func (p *peer) handleFactoidMsg(msg *wire.MsgFactoidTX, buf []byte) { binary, _ := msg.Transaction.MarshalBinary() commonHash := common.Sha(binary) hash, _ := wire.NewShaHash(commonHash.Bytes()) iv := wire.NewInvVect(wire.InvTypeTx, hash) p.AddKnownInventory(iv) inMsgQueue <- msg }
// pushGetEntryDataMsg takes the passed EBlock // and return all the corresponding EBEntries func (p *peer) pushGetEntryDataMsg(eblock *common.EBlock) { binary, _ := eblock.MarshalBinary() commonHash := common.Sha(binary) hash, _ := wire.NewShaHash(commonHash.Bytes()) iv := wire.NewInvVect(wire.InvTypeFactomEntry, hash) gdmsg := wire.NewMsgGetEntryData() gdmsg.AddInvVect(iv) if len(gdmsg.InvList) > 0 { p.QueueMessage(gdmsg, nil) } }
// handleEBlockMsg is invoked when a peer receives an entry block bitcoin message. func (p *peer) handleEBlockMsg(msg *wire.MsgEBlock, buf []byte) { binary, _ := msg.EBlk.MarshalBinary() commonHash := common.Sha(binary) hash, _ := wire.NewShaHash(commonHash.Bytes()) iv := wire.NewInvVect(wire.InvTypeFactomEntryBlock, hash) p.AddKnownInventory(iv) p.pushGetEntryDataMsg(msg.EBlk) inMsgQueue <- msg }
// handleDirBlockMsg is invoked when a peer receives a dir block message. func (p *peer) handleDirBlockMsg(msg *wire.MsgDirBlock, buf []byte) { binary, _ := msg.DBlk.MarshalBinary() commonHash := common.Sha(binary) hash, _ := wire.NewShaHash(commonHash.Bytes()) iv := wire.NewInvVect(wire.InvTypeFactomDirBlock, hash) p.AddKnownInventory(iv) p.pushGetNonDirDataMsg(msg.DBlk) inMsgQueue <- msg delete(p.requestedBlocks, *hash) delete(p.server.blockManager.requestedBlocks, *hash) }
// BenchmarkMruInventoryList performs basic benchmarks on the most recently // used inventory handling. func BenchmarkMruInventoryList(b *testing.B) { // Create a bunch of fake inventory vectors to use in benchmarking // the mru inventory code. b.StopTimer() numInvVects := 100000 invVects := make([]*wire.InvVect, 0, numInvVects) for i := 0; i < numInvVects; i++ { hashBytes := make([]byte, wire.HashSize) rand.Read(hashBytes) hash, _ := wire.NewShaHash(hashBytes) iv := wire.NewInvVect(wire.InvTypeBlock, hash) invVects = append(invVects, iv) } b.StartTimer() // Benchmark the add plus evicition code. limit := 20000 mruInvMap := NewMruInventoryMap(uint(limit)) for i := 0; i < b.N; i++ { mruInvMap.Add(invVects[i%numInvVects]) } }
// Validate the new blocks in mem pool and store them in db // Need to make a batch insert in db in milestone 2 func storeBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool, db database.Db) error { fMemPool.RLock() defer fMemPool.RUnlock() for _, dbEntry := range b.DBEntries { switch dbEntry.ChainID.String() { case ecchain.ChainID.String(): ecBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgECBlock) err := db.ProcessECBlockBatch(ecBlkMsg.ECBlock) if err != nil { return err } // needs to be improved?? initializeECreditMap(ecBlkMsg.ECBlock) // for debugging exportECBlock(ecBlkMsg.ECBlock) case achain.ChainID.String(): aBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgABlock) err := db.ProcessABlockBatch(aBlkMsg.ABlk) if err != nil { return err } // for debugging exportABlock(aBlkMsg.ABlk) case fchain.ChainID.String(): fBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgFBlock) err := db.ProcessFBlockBatch(fBlkMsg.SC) if err != nil { return err } // Initialize the Factoid State err = common.FactoidState.AddTransactionBlock(fBlkMsg.SC) FactoshisPerCredit = fBlkMsg.SC.GetExchRate() if err != nil { return err } // for debugging exportFctBlock(fBlkMsg.SC) default: // handle Entry Block eBlkMsg, _ := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgEBlock) // store entry in db first for _, ebEntry := range eBlkMsg.EBlk.Body.EBEntries { if msg, foundInMemPool := fMemPool.blockpool[ebEntry.String()]; foundInMemPool { err := db.InsertEntry(msg.(*wire.MsgEntry).Entry) if err != nil { return err } } } // Store Entry Block in db err := db.ProcessEBlockBatch(eBlkMsg.EBlk) if err != nil { return err } // create a chain when it's the first block of the entry chain if eBlkMsg.EBlk.Header.EBSequence == 0 { chain := new(common.EChain) chain.ChainID = eBlkMsg.EBlk.Header.ChainID chain.FirstEntry, _ = db.FetchEntryByHash(eBlkMsg.EBlk.Body.EBEntries[0]) if chain.FirstEntry == nil { return errors.New("First entry not found for chain:" + eBlkMsg.EBlk.Header.ChainID.String()) } db.InsertChain(chain) chainIDMap[chain.ChainID.String()] = chain } // for debugging exportEBlock(eBlkMsg.EBlk) } } dbhash, dbHeight, _ := db.FetchBlockHeightCache() //fmt.Printf("last block height is %d, to-be-saved block height is %d\n", dbHeight, b.Header.DBHeight) // Store the dir block err := db.ProcessDBlockBatch(b) if err != nil { return err } lastDirBlockTimestamp = b.Header.Timestamp // Update dir block height cache in db commonHash, _ := common.CreateHash(b) db.UpdateBlockHeightCache(b.Header.DBHeight, commonHash) // for debugging exportDBlock(b) // this means, there's syncup breakage happened, and let's renew syncup. if uint32(dbHeight) < b.Header.DBHeight-1 { startHash, _ := wire.NewShaHash(dbhash.Bytes()) stopHash, _ := wire.NewShaHash(commonHash.Bytes()) outMsgQueue <- &wire.MsgInt_ReSyncup{ StartHash: startHash, StopHash: stopHash, } } return nil }
// build blocks from all process lists func buildBlocks() error { // Allocate the first three dbentries for Admin block, ECBlock and Factoid block dchain.AddDBEntry(&common.DBEntry{}) // AdminBlock dchain.AddDBEntry(&common.DBEntry{}) // ECBlock dchain.AddDBEntry(&common.DBEntry{}) // factoid if plMgr != nil && plMgr.MyProcessList.IsValid() { buildFromProcessList(plMgr.MyProcessList) } // Entry Credit Chain ecBlock := newEntryCreditBlock(ecchain) dchain.AddECBlockToDBEntry(ecBlock) exportECBlock(ecBlock) // Admin chain aBlock := newAdminBlock(achain) dchain.AddABlockToDBEntry(aBlock) exportABlock(aBlock) // Factoid chain fBlock := newFactoidBlock(fchain) dchain.AddFBlockToDBEntry(fBlock) exportFctBlock(fBlock) // sort the echains by chain id var keys []string for k := range chainIDMap { keys = append(keys, k) } sort.Strings(keys) // Entry Chains for _, k := range keys { chain := chainIDMap[k] eblock := newEntryBlock(chain) if eblock != nil { dchain.AddEBlockToDBEntry(eblock) } exportEBlock(eblock) } // Directory Block chain procLog.Debug("in buildBlocks") dbBlock := newDirectoryBlock(dchain) // Generate the inventory vector and relay it. binary, _ := dbBlock.MarshalBinary() commonHash := common.Sha(binary) hash, _ := wire.NewShaHash(commonHash.Bytes()) outMsgQueue <- (&wire.MsgInt_DirBlock{hash}) // Update dir block height cache in db db.UpdateBlockHeightCache(dbBlock.Header.DBHeight, commonHash) db.UpdateNextBlockHeightCache(dchain.NextDBHeight) exportDBlock(dbBlock) // re-initialize the process lit manager initProcessListMgr() // Initialize timer for the new dblock if nodeMode == common.SERVER_NODE { timer := &BlockTimer{ nextDBlockHeight: dchain.NextDBHeight, inCtlMsgQueue: inCtlMsgQueue, } go timer.StartBlockTimer() } // place an anchor into btc placeAnchor(dbBlock) return nil }
// processRevealEntry validates the MsgRevealEntry and adds it to processlist func processRevealEntry(msg *wire.MsgRevealEntry) error { e := msg.Entry bin, _ := e.MarshalBinary() h, _ := wire.NewShaHash(e.Hash().Bytes()) // Check if the chain id is valid if e.ChainID.IsSameAs(zeroHash) || e.ChainID.IsSameAs(dchain.ChainID) || e.ChainID.IsSameAs(achain.ChainID) || e.ChainID.IsSameAs(ecchain.ChainID) || e.ChainID.IsSameAs(fchain.ChainID) { return fmt.Errorf("This entry chain is not supported: %s", e.ChainID.String()) } if c, ok := commitEntryMap[e.Hash().String()]; ok { if chainIDMap[e.ChainID.String()] == nil { fMemPool.addOrphanMsg(msg, h) return fmt.Errorf("This chain is not supported: %s", msg.Entry.ChainID.String()) } // Calculate the entry credits required for the entry cred, err := util.EntryCost(bin) if err != nil { return err } if c.Credits < cred { fMemPool.addOrphanMsg(msg, h) return fmt.Errorf("Credit needs to paid first before an entry is revealed: %s", e.Hash().String()) } // Add the msg to the Mem pool fMemPool.addMsg(msg, h) // Add to MyPL if Server Node if nodeMode == common.SERVER_NODE { if plMgr.IsMyPListExceedingLimit() { procLog.Warning("Exceeding MyProcessList size limit!") return fMemPool.addOrphanMsg(msg, h) } ack, err := plMgr.AddMyProcessListItem(msg, h, wire.ACK_REVEAL_ENTRY) if err != nil { return err } else { // Broadcast the ack to the network if no errors outMsgQueue <- ack } } delete(commitEntryMap, e.Hash().String()) return nil } else if c, ok := commitChainMap[e.Hash().String()]; ok { //Reveal chain --------------------------- if chainIDMap[e.ChainID.String()] != nil { fMemPool.addOrphanMsg(msg, h) return fmt.Errorf("This chain is not supported: %s", msg.Entry.ChainID.String()) } // add new chain to chainIDMap newChain := common.NewEChain() newChain.ChainID = e.ChainID newChain.FirstEntry = e chainIDMap[e.ChainID.String()] = newChain // Calculate the entry credits required for the entry cred, err := util.EntryCost(bin) if err != nil { return err } // 10 credit is additional for the chain creation if c.Credits < cred+10 { fMemPool.addOrphanMsg(msg, h) return fmt.Errorf("Credit needs to paid first before an entry is revealed: %s", e.Hash().String()) } //validate chain id for the first entry expectedChainID := common.NewChainID(e) if !expectedChainID.IsSameAs(e.ChainID) { return fmt.Errorf("Invalid ChainID for entry: %s", e.Hash().String()) } //validate chainid hash in the commitChain chainIDHash := common.DoubleSha(e.ChainID.Bytes()) if !bytes.Equal(c.ChainIDHash.Bytes()[:], chainIDHash[:]) { return fmt.Errorf("RevealChain's chainid hash does not match with CommitChain: %s", e.Hash().String()) } //validate Weld in the commitChain weld := common.DoubleSha(append(c.EntryHash.Bytes(), e.ChainID.Bytes()...)) if !bytes.Equal(c.Weld.Bytes()[:], weld[:]) { return fmt.Errorf("RevealChain's weld does not match with CommitChain: %s", e.Hash().String()) } // Add the msg to the Mem pool fMemPool.addMsg(msg, h) // Add to MyPL if Server Node if nodeMode == common.SERVER_NODE { if plMgr.IsMyPListExceedingLimit() { procLog.Warning("Exceeding MyProcessList size limit!") return fMemPool.addOrphanMsg(msg, h) } ack, err := plMgr.AddMyProcessListItem(msg, h, wire.ACK_REVEAL_CHAIN) if err != nil { return err } else { // Broadcast the ack to the network if no errors outMsgQueue <- ack } } delete(commitChainMap, e.Hash().String()) return nil } else { return fmt.Errorf("No commit for entry") } return nil }