func buildTestBlock(t *testing.T) (*protos.Block, error) { transactions := []*protos.Transaction{} tx, _ := buildTestTx(t) transactions = append(transactions, tx) block := protos.NewBlock(transactions, nil) return block, nil }
func TestIndexes_GetTransactionByUUID(t *testing.T) { testDBWrapper.CreateFreshDB(t) testBlockchainWrapper := newTestBlockchainWrapper(t) tx1, uuid1 := buildTestTx(t) tx2, uuid2 := buildTestTx(t) block1 := protos.NewBlock([]*protos.Transaction{tx1, tx2}, nil) testBlockchainWrapper.addNewBlock(block1, []byte("stateHash1")) tx3, uuid3 := buildTestTx(t) tx4, uuid4 := buildTestTx(t) block2 := protos.NewBlock([]*protos.Transaction{tx3, tx4}, nil) testBlockchainWrapper.addNewBlock(block2, []byte("stateHash2")) testutil.AssertEquals(t, testBlockchainWrapper.getTransactionByUUID(uuid1), tx1) testutil.AssertEquals(t, testBlockchainWrapper.getTransactionByUUID(uuid2), tx2) testutil.AssertEquals(t, testBlockchainWrapper.getTransactionByUUID(uuid3), tx3) testutil.AssertEquals(t, testBlockchainWrapper.getTransactionByUUID(uuid4), tx4) }
func (testWrapper *blockchainTestWrapper) populateBlockChainWithSampleData() (blocks []*protos.Block, hashes [][]byte, err error) { var allBlocks []*protos.Block var allHashes [][]byte // -----------------------------<Genesis block>------------------------------- // Add the first (genesis block) block1 := protos.NewBlock(nil, []byte(testutil.GenerateUUID(testWrapper.t))) allBlocks = append(allBlocks, block1) allHashes = append(allHashes, []byte("stateHash1")) testWrapper.addNewBlock(block1, []byte("stateHash1")) // -----------------------------</Genesis block>------------------------------ // -----------------------------<Block 2>------------------------------------- // Deploy a chaincode transaction2a, err := protos.NewTransaction(protos.ChaincodeID{Path: "Contracts"}, testutil.GenerateUUID(testWrapper.t), "NewContract", []string{"name: MyContract1, code: var x; function setX(json) {x = json.x}}"}) if err != nil { return nil, nil, err } // Now we add the transaction to the block 2 and add the block to the chain transactions2a := []*protos.Transaction{transaction2a} block2 := protos.NewBlock(transactions2a, nil) allBlocks = append(allBlocks, block2) allHashes = append(allHashes, []byte("stateHash2")) testWrapper.addNewBlock(block2, []byte("stateHash2")) // -----------------------------</Block 2>------------------------------------ // -----------------------------<Block 3>------------------------------------- // Create a transaction' transaction3a, err := protos.NewTransaction(protos.ChaincodeID{Path: "MyContract"}, testutil.GenerateUUID(testWrapper.t), "setX", []string{"{x: \"hello\"}"}) if err != nil { return nil, nil, err } // Create the third block and add it to the chain transactions3a := []*protos.Transaction{transaction3a} block3 := protos.NewBlock(transactions3a, nil) allBlocks = append(allBlocks, block3) allHashes = append(allHashes, []byte("stateHash3")) testWrapper.addNewBlock(block3, []byte("stateHash3")) // -----------------------------</Block 3>------------------------------------ return allBlocks, allHashes, nil }
func TestVerifyChain(t *testing.T) { ledgerTestWrapper := createFreshDBAndTestLedgerWrapper(t) ledger := ledgerTestWrapper.ledger // Build a big blockchain for i := 0; i < 100; i++ { ledger.BeginTxBatch(i) ledger.TxBegin("txUuid" + strconv.Itoa(i)) ledger.SetState("chaincode"+strconv.Itoa(i), "key"+strconv.Itoa(i), []byte("value"+strconv.Itoa(i))) ledger.TxFinished("txUuid"+strconv.Itoa(i), true) transaction, _ := buildTestTx(t) ledger.CommitTxBatch(i, []*protos.Transaction{transaction}, nil, []byte("proof")) } // Verify the chain for lowBlock := uint64(0); lowBlock < ledger.GetBlockchainSize()-1; lowBlock++ { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(ledger.GetBlockchainSize()-1, lowBlock), lowBlock) } for highBlock := ledger.GetBlockchainSize() - 1; highBlock > 0; highBlock-- { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(highBlock, 0), uint64(0)) } // Add bad blocks and test badBlock := protos.NewBlock(nil, nil) badBlock.PreviousBlockHash = []byte("evil") for i := uint64(0); i < ledger.GetBlockchainSize(); i++ { goodBlock := ledgerTestWrapper.GetBlockByNumber(i) ledger.PutRawBlock(badBlock, i) for lowBlock := uint64(0); lowBlock < ledger.GetBlockchainSize()-1; lowBlock++ { if i == ledger.GetBlockchainSize()-1 { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(ledger.GetBlockchainSize()-1, lowBlock), uint64(i)) } else if i >= lowBlock { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(ledger.GetBlockchainSize()-1, lowBlock), uint64(i+1)) } else { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(ledger.GetBlockchainSize()-1, lowBlock), lowBlock) } } for highBlock := ledger.GetBlockchainSize() - 1; highBlock != ^uint64(0); highBlock-- { if i == highBlock { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(highBlock, 0), uint64(i)) } else if i < highBlock { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(highBlock, 0), uint64(i+1)) } else { testutil.AssertEquals(t, ledgerTestWrapper.VerifyChain(highBlock, 0), uint64(0)) } } ledgerTestWrapper.PutRawBlock(goodBlock, i) } // Test edge cases _, err := ledger.VerifyChain(2, 10) testutil.AssertError(t, err, "Expected error as high block is less than low block") _, err = ledger.VerifyChain(0, 100) testutil.AssertError(t, err, "Expected error as high block is out of bounds") }
// GetTXBatchPreviewBlockInfo returns a preview block info that will // contain the same information as GetBlockchainInfo will return after // ledger.CommitTxBatch is called with the same parameters. If the // state is modified by a transaction between these two calls, the // contained hash will be different. func (ledger *Ledger) GetTXBatchPreviewBlockInfo(id interface{}, transactions []*protos.Transaction, metadata []byte) (*protos.BlockchainInfo, error) { err := ledger.checkValidIDCommitORRollback(id) if err != nil { return nil, err } stateHash, err := ledger.state.GetHash() if err != nil { return nil, err } block := ledger.blockchain.buildBlock(protos.NewBlock(transactions, metadata), stateHash) info := ledger.blockchain.getBlockchainInfoForBlock(ledger.blockchain.getSize()+1, block) return info, nil }
func TestBlockchainBlockLedgerCommitTimestamp(t *testing.T) { testDBWrapper.CleanDB(t) blockchainTestWrapper := newTestBlockchainWrapper(t) block1 := protos.NewBlock(nil, nil) startTime := util.CreateUtcTimestamp() time.Sleep(2 * time.Second) blockchainTestWrapper.addNewBlock(block1, []byte("stateHash1")) lastBlock := blockchainTestWrapper.getLastBlock() if lastBlock.NonHashData == nil { t.Fatal("Expected block to have non-hash-data, but it was nil") } if lastBlock.NonHashData.LocalLedgerCommitTimestamp == nil { t.Fatal("Expected block to have non-hash-data timestamp, but it was nil") } if startTime.Seconds >= lastBlock.NonHashData.LocalLedgerCommitTimestamp.Seconds { t.Fatal("Expected block time to be after start time") } }
// CommitTxBatch - gets invoked when the current transaction-batch needs to be committed // This function returns successfully iff the transactions details and state changes (that // may have happened during execution of this transaction-batch) have been committed to permanent storage func (ledger *Ledger) CommitTxBatch(id interface{}, transactions []*protos.Transaction, transactionResults []*protos.TransactionResult, metadata []byte) error { err := ledger.checkValidIDCommitORRollback(id) if err != nil { return err } stateHash, err := ledger.state.GetHash() if err != nil { ledger.resetForNextTxGroup(false) ledger.blockchain.blockPersistenceStatus(false) return err } writeBatch := gorocksdb.NewWriteBatch() defer writeBatch.Destroy() block := protos.NewBlock(transactions, metadata) block.NonHashData = &protos.NonHashData{} newBlockNumber, err := ledger.blockchain.addPersistenceChangesForNewBlock(context.TODO(), block, stateHash, writeBatch) if err != nil { ledger.resetForNextTxGroup(false) ledger.blockchain.blockPersistenceStatus(false) return err } ledger.state.AddChangesForPersistence(newBlockNumber, writeBatch) opt := gorocksdb.NewDefaultWriteOptions() defer opt.Destroy() dbErr := db.GetDBHandle().DB.Write(opt, writeBatch) if dbErr != nil { ledger.resetForNextTxGroup(false) ledger.blockchain.blockPersistenceStatus(false) return dbErr } ledger.resetForNextTxGroup(true) ledger.blockchain.blockPersistenceStatus(true) sendProducerBlockEvent(block) if len(transactionResults) != 0 { ledgerLogger.Debug("There were some erroneous transactions. We need to send a 'TX rejected' message here.") } return nil }
func TestBlockChain_SingleBlock(t *testing.T) { testDBWrapper.CleanDB(t) blockchainTestWrapper := newTestBlockchainWrapper(t) blockchain := blockchainTestWrapper.blockchain // Create the Chaincode specification chaincodeSpec := &protos.ChaincodeSpec{Type: protos.ChaincodeSpec_GOLANG, ChaincodeID: &protos.ChaincodeID{Path: "Contracts"}, CtorMsg: &protos.ChaincodeInput{Function: "Initialize", Args: []string{"param1"}}} chaincodeDeploymentSepc := &protos.ChaincodeDeploymentSpec{ChaincodeSpec: chaincodeSpec} uuid := testutil.GenerateUUID(t) newChaincodeTx, err := protos.NewChaincodeDeployTransaction(chaincodeDeploymentSepc, uuid) testutil.AssertNoError(t, err, "Failed to create new chaincode Deployment Transaction") t.Logf("New chaincode tx: %v", newChaincodeTx) block1 := protos.NewBlock([]*protos.Transaction{newChaincodeTx}, nil) blockNumber := blockchainTestWrapper.addNewBlock(block1, []byte("stateHash1")) t.Logf("New chain: %v", blockchain) testutil.AssertEquals(t, blockNumber, uint64(0)) testutil.AssertEquals(t, blockchain.getSize(), uint64(1)) testutil.AssertEquals(t, blockchainTestWrapper.fetchBlockchainSizeFromDB(), uint64(1)) }
err = ledgerPtr.CommitTxBatch(i, []*protos.Transaction{tx}, nil, []byte("proof")) Expect(err).To(BeNil()) } }) It("verifies the blockchain", func() { // Verify the chain for lowBlock := uint64(0); lowBlock < ledgerPtr.GetBlockchainSize()-1; lowBlock++ { Expect(ledgerPtr.VerifyChain(ledgerPtr.GetBlockchainSize()-1, lowBlock)).To(Equal(lowBlock)) } for highBlock := ledgerPtr.GetBlockchainSize() - 1; highBlock > 0; highBlock-- { Expect(ledgerPtr.VerifyChain(highBlock, 0)).To(Equal(uint64(0))) } }) It("adds bad blocks to the blockchain", func() { // Add bad blocks and test badBlock := protos.NewBlock(nil, nil) badBlock.PreviousBlockHash = []byte("evil") for i := uint64(0); i < ledgerPtr.GetBlockchainSize(); i++ { goodBlock, _ := ledgerPtr.GetBlockByNumber(i) ledgerPtr.PutRawBlock(badBlock, i) for lowBlock := uint64(0); lowBlock < ledgerPtr.GetBlockchainSize()-1; lowBlock++ { if i == ledgerPtr.GetBlockchainSize()-1 { Expect(ledgerPtr.VerifyChain(ledgerPtr.GetBlockchainSize()-1, lowBlock)).To(Equal(uint64(i))) } else if i >= lowBlock { Expect(ledgerPtr.VerifyChain(ledgerPtr.GetBlockchainSize()-1, lowBlock)).To(Equal(uint64(i + 1))) } else { Expect(ledgerPtr.VerifyChain(ledgerPtr.GetBlockchainSize()-1, lowBlock)).To(Equal(lowBlock)) } } for highBlock := ledgerPtr.GetBlockchainSize() - 1; highBlock > 0; highBlock-- { if i == highBlock {
// CommitTxBatch - gets invoked when the current transaction-batch needs to be committed // This function returns successfully iff the transactions details and state changes (that // may have happened during execution of this transaction-batch) have been committed to permanent storage func (ledger *Ledger) CommitTxBatch(id interface{}, transactions []*protos.Transaction, transactionResults []*protos.TransactionResult, metadata []byte) error { err := ledger.checkValidIDCommitORRollback(id) if err != nil { return err } stateHash, err := ledger.state.GetHash() if err != nil { ledger.resetForNextTxGroup(false) ledger.blockchain.blockPersistenceStatus(false) return err } writeBatch := gorocksdb.NewWriteBatch() defer writeBatch.Destroy() block := protos.NewBlock(transactions, metadata) ccEvents := []*protos.ChaincodeEvent{} if transactionResults != nil { ccEvents = make([]*protos.ChaincodeEvent, len(transactionResults)) for i := 0; i < len(transactionResults); i++ { if transactionResults[i].ChaincodeEvent != nil { ccEvents[i] = transactionResults[i].ChaincodeEvent } else { //We need the index so we can map the chaincode //event to the transaction that generated it. //Hence need an entry for cc event even if one //wasn't generated for the transaction. We cannot //use a nil cc event as protobuf does not like //elements of a repeated array to be nil. // //We should discard empty events without chaincode //ID when sending out events. ccEvents[i] = &protos.ChaincodeEvent{} } } } //store chaincode events directly in NonHashData. This will likely change in New Consensus where we can move them to Transaction block.NonHashData = &protos.NonHashData{ChaincodeEvents: ccEvents} newBlockNumber, err := ledger.blockchain.addPersistenceChangesForNewBlock(context.TODO(), block, stateHash, writeBatch) if err != nil { ledger.resetForNextTxGroup(false) ledger.blockchain.blockPersistenceStatus(false) return err } ledger.state.AddChangesForPersistence(newBlockNumber, writeBatch) opt := gorocksdb.NewDefaultWriteOptions() defer opt.Destroy() dbErr := db.GetDBHandle().DB.Write(opt, writeBatch) if dbErr != nil { ledger.resetForNextTxGroup(false) ledger.blockchain.blockPersistenceStatus(false) return dbErr } ledger.resetForNextTxGroup(true) ledger.blockchain.blockPersistenceStatus(true) sendProducerBlockEvent(block) //send chaincode events from transaction results sendChaincodeEvents(transactionResults) if len(transactionResults) != 0 { ledgerLogger.Debug("There were some erroneous transactions. We need to send a 'TX rejected' message here.") } return nil }