func ParseFactoidBlock(chainID, hash string, rawBlock []byte, blockTime string) (*Block, error) { answer := new(Block) fBlock := new(block.FBlock) _, err := fBlock.UnmarshalBinaryData(rawBlock) if err != nil { return nil, err } answer.ChainID = chainID answer.PartialHash = fBlock.GetHash().String() answer.FullHash = fBlock.GetLedgerKeyMR().String() answer.PrevBlockHash = fmt.Sprintf("%x", fBlock.PrevKeyMR.Bytes()) transactions := fBlock.GetTransactions() answer.EntryCount = len(transactions) answer.EntryList = make([]*Entry, answer.EntryCount) answer.BinaryString = fmt.Sprintf("%x", rawBlock) for i, v := range transactions { entry := new(Entry) bin, err := v.MarshalBinary() if err != nil { return nil, err } entry.BinaryString = fmt.Sprintf("%x", bin) entry.Timestamp = TimestampToString(v.GetMilliTimestamp() / 1000) entry.Hash = v.GetHash().String() entry.ChainID = chainID entry.JSONString, err = v.JSONString() if err != nil { return nil, err } entry.SpewString = v.Spew() answer.EntryList[i] = entry } answer.JSONString, err = fBlock.JSONString() if err != nil { return nil, err } answer.SpewString = fBlock.Spew() answer.IsFactoidBlock = true return answer, nil }
// Run a simulation of Transactions and blocks designed to test a pseudo random transaction set. // If randomize = 0, then we will use the clock to seed the random number generator, and print the // 64 bit seed used. If randomize is set to some value, we use that vaule (allowing us to repeat // tests if we like. func Test_create_genesis_FactoidState(test *testing.T) { randomize := int64(0) numBlocks := 15 // Average number of BTC transactions per block is <1000 as of Aug 8, 2015 // https://blockchain.info/charts/n-transactions-per-block numTransactions := 10 // Maximum Transactions if testing.Short() { fmt.Print("\nDoing Short Tests\n") numBlocks = 5 numTransactions = 20 } block.UpdateAmount(10000000) if randomize == 0 { randomize = time.Now().UnixNano() rand.Seed(randomize) randomize = rand.Int63() rand.Seed(randomize) } else { rand.Seed(randomize) } fmt.Println("Randomize Seed Used: ", randomize) cp.CP.AddUpdate( "Test Parms", "status", // Category fmt.Sprintf("Number of Blocks %d Max number Transactions %d", numBlocks, numTransactions), fmt.Sprintf("Randomize Seed: %v", randomize), 0) // Use Bolt DB if !testing.Short() { fs.SetDB(new(database.MapDB)) fs.GetDB().Init() db := stateinit.GetDatabase("./fct_test.db") fs.GetDB().SetPersist(db) fs.GetDB().SetBacker(db) fs.GetDB().DoNotPersist(fct.DB_F_BALANCES) fs.GetDB().DoNotPersist(fct.DB_EC_BALANCES) fs.GetDB().DoNotPersist(fct.DB_BUILD_TRANS) fs.GetDB().DoNotCache(fct.DB_FACTOID_BLOCKS) fs.GetDB().DoNotCache(fct.DB_BAD_TRANS) fs.GetDB().DoNotCache(fct.DB_TRANSACTIONS) } else { fs.SetDB(new(database.MapDB)) fs.GetDB().Init() } // Set the price for Factoids fs.SetFactoshisPerEC(100000) err := fs.LoadState() if err != nil { fmt.Println("Faid to initialize: ", err) os.Exit(1) } pre := fs.GetTransactionBlock(fs.GetCurrentBlock().GetPrevKeyMR()) if !bytes.Equal(pre.GetHash().Bytes(), fs.GetCurrentBlock().GetPrevKeyMR().Bytes()) { fmt.Printf("Something is ill!") test.Fail() return } // Print the Genesis Block. If we don't know the past, then print it. past := fs.GetTransactionBlock(pre.GetPrevKeyMR()) if past == nil { for _, trans := range pre.GetTransactions() { PrtTrans(trans) } } if err != nil { fct.Prtln("Failed to load:", err) test.Fail() return } var max, min, maxblk int min = 100000 // Create a number of blocks (i) for i := 0; i < numBlocks; i++ { fmt.Println("Block", fs.GetCurrentBlock().GetDBHeight()) PrtTrans(fs.GetCurrentBlock().GetTransactions()[0]) thisRunLimit := (rand.Int() % numTransactions) + 1 cp.CP.AddUpdate( "This Block", "status", // Category fmt.Sprintf("Number of Transactions we are putting in this block: %d", thisRunLimit), "", 0) var transCnt int periodMark := 1 // Create a new block for j := fs.stats.transactions; fs.stats.transactions < j+thisRunLimit; { // Execute for some number RECORDED transactions transCnt++ periodvalue := thisRunLimit / 10 delta := thisRunLimit / 10 / 4 if delta == 0 { delta = 1 } if rand.Int()%100 > 50 { delta = -delta } periodvalue += delta if periodvalue == 0 { periodvalue = 1 } if periodMark <= 10 && transCnt%(periodvalue) == 0 { fs.EndOfPeriod(periodMark) periodMark++ } tx := fs.newTransaction() addtest := true flip := rand.Int() % 100 if rand.Int()%100 < 5 { // Mess up the timestamp on 5 percent of the transactions addtest = false blkts := uint64(fs.GetCurrentBlock().GetCoinbaseTimestamp()) if flip < 49 { // Flip a coin tx.SetMilliTimestamp(blkts - uint64(fct.TRANSACTION_PRIOR_LIMIT) - 1) fs.stats.errors["trans too early"] += 1 fs.stats.full["trans too early"] = "trans too early" } else { tx.SetMilliTimestamp(blkts + uint64(fct.TRANSACTION_POST_LIMIT) + 1) fs.stats.errors["trans too late"] += 1 fs.stats.full["trans too late"] = "trans too late" } fs.twallet.SignInputs(tx) } // Test Marshal/UnMarshal m, err := tx.MarshalBinary() if err != nil { fmt.Println("\n Failed to Marshal: ", err) test.Fail() return } if len(m) > max { max = len(m) cp.CP.AddUpdate( "max transaction size", // tag "info", // Category fmt.Sprintf("Max Transaction Size %d", max), // Title fmt.Sprintf("<pre>#inputs = %-3d #outputs = %-3d #ecoutputs = %-3d<pre>", len(tx.GetInputs()), len(tx.GetOutputs()), len(tx.GetECOutputs())), // Msg 0) // Expire } if len(m) < min { min = len(m) cp.CP.AddUpdate( "min transaction size", // tag "info", // Category fmt.Sprintf("Min Transaction Size %d", min), // Title fmt.Sprintf("<pre>#inputs = %-3d #outputs = %-3d #ecoutputs = %-3d<pre>", len(tx.GetInputs()), len(tx.GetOutputs()), len(tx.GetECOutputs())), // Msg 0) // Expire } k := rand.Int() % (len(m) - 2) k++ good := true flip = rand.Int() % 100 // To simulate bad data, I mess up some of the data here. if rand.Int()%100 < 5 { // Mess up 5 percent of the transactions good = false if flip < 49 { // Flip a coin m = m[k:] fs.stats.errors["lost start of trans"] += 1 fs.stats.full["lost start of trans"] = "lost start of trans" } else { m = m[:k] fs.stats.errors["lost end of trans"] += 1 fs.stats.full["lost end of trans"] = "lost end of trans" } } t := new(fct.Transaction) err = t.UnmarshalBinary(m) if good && tx.IsEqual(t) != nil { fmt.Println("Fail valid Unmarshal") test.Fail() return } if err == nil { if good && err != nil { fmt.Println("Added a transaction that should have failed to be added") fmt.Println(err) test.Fail() return } if !good { fmt.Println("Failed to add a transaction that should have added") test.Fail() return } } if good { err = fs.AddTransaction(j+1, t) } if !addtest && err == nil { ts := int64(t.GetMilliTimestamp()) bts := int64(fs.GetCurrentBlock().GetCoinbaseTimestamp()) fmt.Println("timestamp failure ", ts, bts, ts-bts, fct.TRANSACTION_POST_LIMIT) test.Fail() return } if !addtest && err == nil { fmt.Println("failed to catch error") test.Fail() return } if addtest && good && err != nil { fmt.Println(err) fmt.Println("Unmarshal Failed. trans is good", "\nand the error detected: ", err, "\nand k:", k, "and flip:", flip) test.Fail() return } if good && addtest { PrtTrans(t) fs.stats.transactions += 1 title := fmt.Sprintf("Bad Transactions: %d Total transaactions %d", fs.stats.badAddresses, fs.stats.transactions) cp.CP.AddUpdate("Bad Transaction", "status", title, "", 0) time.Sleep(time.Second / 100) } else { fs.stats.badAddresses += 1 } } // // Serialization deserialization tests for blocks // blkdata, err := fs.GetCurrentBlock().MarshalBinary() if err != nil { test.Fail() return } blk := fs.GetCurrentBlock().GetNewInstance().(block.IFBlock) err = blk.UnmarshalBinary(blkdata) if err != nil { test.Fail() return } fmt.Println("Block Check") // Collect our block pointers, MR Key, and LMR Key before we process the end of block blk1 := fs.GetCurrentBlock() blk1KMR := blk1.GetHash() blk1LKMR := blk1.GetLedgerKeyMR() // Process the end of block. This simulates what Factomd will do. fmt.Println("ProcessEndOfBlock") fs.ProcessEndOfBlock() // Process the block. fmt.Println("Check ProcessEndOfBlock") // Collect the new block pointer, Previous MR Key and LMR Key after we processed the block. blk2 := fs.GetCurrentBlock() blk2PKMR := blk2.GetPrevKeyMR() blk2PLKMR := blk2.GetPrevLedgerKeyMR() // Check that the Previous MR and LMR match what we had from the previous block. if !bytes.Equal(blk1KMR.Bytes(), blk2PKMR.Bytes()) { fmt.Println("MR's don't match") test.Fail() return } if !bytes.Equal(blk1LKMR.Bytes(), blk2PLKMR.Bytes()) { fmt.Println("LKMR's don't match") fmt.Println(" block: ", blk1LKMR) fmt.Println(" Prev: ", blk2PLKMR) test.Fail() return } // Now we marshal and unmarshal to simulate a reboot, getting our blocks for the DB data, err := blk1.MarshalBinary() if err != nil { fmt.Println("Failed to Marshal") test.Fail() return } blk1b := new(block.FBlock) err = blk1b.UnmarshalBinary(data) if err != nil { fmt.Println("Failed to Unmarshal") test.Fail() return } // Does the marshal and unmarshal of the block yeild the same MR and LMR? If so GOOD! if !bytes.Equal(blk2PKMR.Bytes(), blk1b.GetKeyMR().Bytes()) { fmt.Println("Unmarshaled KeyMR values do not match") test.Fail() return } if !bytes.Equal(blk2PLKMR.Bytes(), blk1b.GetLedgerKeyMR().Bytes()) { fmt.Println("Unmarshaled LedgerKeyMR values do not match") test.Fail() return } /************************************************************ * Reporting to the Control Panel ************************************************************/ { c := 1 keys := make([]string, 0, len(fs.stats.errors)) for k := range fs.stats.errors { keys = append(keys, k) } for i := 0; i < len(keys)-1; i++ { for j := 0; j < len(keys)-i-1; j++ { if keys[j] < keys[j+1] { t := keys[j] keys[j] = keys[j+1] keys[j+1] = t } } } var out bytes.Buffer for _, key := range keys { ecnt := fs.stats.errors[key] by := []byte(fs.stats.full[key]) prt := string(by) if len(prt) > 80 { prt = string(by[:80]) + "..." } prt = strings.Replace(prt, "\n", " ", -1) out.WriteString(fmt.Sprintf("%6d %s\n", ecnt, prt)) c++ } cp.CP.AddUpdate( "transerrors", // tag "errors", // Category "Transaction Info & Errors:", // Title "<pre>"+string(out.Bytes())+"</pre>", // Msg 0) // Expires } if len(blkdata) > maxblk { maxblk = len(blkdata) cp.CP.AddUpdate( "maxblocksize", // tag "info", // Category fmt.Sprintf("Max Block Size: %dK", maxblk/1024), // Title "", // Msg 0) // Expires } sec1 := fs.stats.TransactionsPerSec() sec2 := fs.stats.TotalTransactionsPerSec() cp.CP.AddUpdate( "transpersec", // tag "status", // Category fmt.Sprintf("Transactions per second %4.2f, (+ bad) %4.2f", sec1, sec2), // Title "", // Msg 0) // Expires } fmt.Println("\nDone") // // Get the head of the Factoid Chain // blk := fs.GetTransactionBlock(fct.FACTOID_CHAINID_HASH) // hashes := make([]fct.IHash,0,10) // // First run back from the head back to the genesis block, collecting hashes. // for { // h := blk.GetHash() // hashes = append(hashes,h) // if bytes.Compare(blk.GetPrevKeyMR().Bytes(),fct.ZERO) == 0 { // break // } // tblk := fs.GetTransactionBlock(blk.GetPrevKeyMR()) // blk = tblk // time.Sleep(time.Second/100) // } // // // Now run forward, and build our accounting // for i := len(hashes)-1; i>=0; i-- { // blk = fs.GetTransactionBlock(hashes[i]) // fmt.Println("Block",blk.GetDBHeight()) // for _,trans := range blk.GetTransactions() { // PrtTrans(trans) // } // } }