// Execute is the main entry point for the command. It's invoked by the parser. func (cmd *blockRegionCmd) Execute(args []string) error { // Setup the global config options and ensure they are valid. if err := setupGlobalConfig(); err != nil { return err } // Ensure expected arguments. if len(args) < 1 { return errors.New("required block hash parameter not specified") } if len(args) < 2 { return errors.New("required start offset parameter not " + "specified") } if len(args) < 3 { return errors.New("required region length parameter not " + "specified") } // Parse arguments. blockHash, err := chainhash.NewHashFromStr(args[0]) if err != nil { return err } startOffset, err := strconv.ParseUint(args[1], 10, 32) if err != nil { return err } regionLen, err := strconv.ParseUint(args[2], 10, 32) if err != nil { return err } // Load the block database. db, err := loadBlockDB() if err != nil { return err } defer db.Close() return db.View(func(tx database.Tx) error { log.Infof("Fetching block region %s<%d:%d>", blockHash, startOffset, startOffset+regionLen-1) region := database.BlockRegion{ Hash: blockHash, Offset: uint32(startOffset), Len: uint32(regionLen), } startTime := time.Now() regionBytes, err := tx.FetchBlockRegion(®ion) if err != nil { return err } log.Infof("Loaded block region in %v", time.Since(startTime)) log.Infof("Double Hash: %s", chainhash.DoubleHashH(regionBytes)) log.Infof("Region Hex: %s", hex.EncodeToString(regionBytes)) return nil }) }
// WitnessHash generates the hash of the transaction serialized according to // the new witness serialization defined in BIP0141 and BIP0144. The final // output is used within the Segregated Witness commitment of all the witnesses // within a block. If a transaction has no witness data, then the witness hash, // is the same as its txid. func (msg *MsgTx) WitnessHash() chainhash.Hash { if msg.HasWitness() { buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize())) _ = msg.Serialize(buf) return chainhash.DoubleHashH(buf.Bytes()) } return msg.TxHash() }
// TxHash generates the Hash for the transaction. func (msg *MsgTx) TxHash() chainhash.Hash { // Encode the transaction and calculate double sha256 on the result. // Ignore the error returns since the only way the encode could fail // is being out of memory or due to nil pointers, both of which would // cause a run-time panic. buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSizeStripped())) _ = msg.SerializeNoWitness(buf) return chainhash.DoubleHashH(buf.Bytes()) }
// HashMerkleBranches takes two hashes, treated as the left and right tree // nodes, and returns the hash of their concatenation. This is a helper // function used to aid in the generation of a merkle tree. func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash { // Concatenate the left and right nodes. var hash [chainhash.HashSize * 2]byte copy(hash[:chainhash.HashSize], left[:]) copy(hash[chainhash.HashSize:], right[:]) newHash := chainhash.DoubleHashH(hash[:]) return &newHash }
// BlockHash computes the block identifier hash for the given block header. func (h *BlockHeader) BlockHash() chainhash.Hash { // Encode the header and double sha256 everything prior to the number of // transactions. Ignore the error returns since there is no way the // encode could fail except being out of memory which would cause a // run-time panic. buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload)) _ = writeBlockHeader(buf, 0, h) return chainhash.DoubleHashH(buf.Bytes()) }
// BenchmarkDoubleHashH performs a benchmark on how long it takes to perform // a double hash returning a chainhash.Hash. func BenchmarkDoubleHashH(b *testing.B) { var buf bytes.Buffer if err := genesisCoinbaseTx.Serialize(&buf); err != nil { b.Errorf("Serialize: unexpected error: %v", err) return } txBytes := buf.Bytes() b.ResetTimer() for i := 0; i < b.N; i++ { _ = chainhash.DoubleHashH(txBytes) } }
// TestFullBlocks ensures all tests generated by the fullblocktests package // have the expected result when processed via ProcessBlock. func TestFullBlocks(t *testing.T) { tests, err := fullblocktests.Generate(false) if err != nil { t.Fatalf("failed to generate tests: %v", err) } // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("fullblocktest", &chaincfg.RegressionNetParams) if err != nil { t.Errorf("Failed to setup chain instance: %v", err) return } defer teardownFunc() // testAcceptedBlock attempts to process the block in the provided test // instance and ensures that it was accepted according to the flags // specified in the test. testAcceptedBlock := func(item fullblocktests.AcceptedBlock) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) isMainChain, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { t.Fatalf("block %q (hash %s, height %d) should "+ "have been accepted: %v", item.Name, block.Hash(), blockHeight, err) } // Ensure the main chain and orphan flags match the values // specified in the test. if isMainChain != item.IsMainChain { t.Fatalf("block %q (hash %s, height %d) unexpected main "+ "chain flag -- got %v, want %v", item.Name, block.Hash(), blockHeight, isMainChain, item.IsMainChain) } if isOrphan != item.IsOrphan { t.Fatalf("block %q (hash %s, height %d) unexpected "+ "orphan flag -- got %v, want %v", item.Name, block.Hash(), blockHeight, isOrphan, item.IsOrphan) } } // testRejectedBlock attempts to process the block in the provided test // instance and ensures that it was rejected with the reject code // specified in the test. testRejectedBlock := func(item fullblocktests.RejectedBlock) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) _, _, err := chain.ProcessBlock(block, blockchain.BFNone) if err == nil { t.Fatalf("block %q (hash %s, height %d) should not "+ "have been accepted", item.Name, block.Hash(), blockHeight) } // Ensure the error code is of the expected type and the reject // code matches the value specified in the test instance. rerr, ok := err.(blockchain.RuleError) if !ok { t.Fatalf("block %q (hash %s, height %d) returned "+ "unexpected error type -- got %T, want "+ "blockchain.RuleError", item.Name, block.Hash(), blockHeight, err) } if rerr.ErrorCode != item.RejectCode { t.Fatalf("block %q (hash %s, height %d) does not have "+ "expected reject code -- got %v, want %v", item.Name, block.Hash(), blockHeight, rerr.ErrorCode, item.RejectCode) } } // testRejectedNonCanonicalBlock attempts to decode the block in the // provided test instance and ensures that it failed to decode with a // message error. testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) { headerLen := len(item.RawBlock) if headerLen > 80 { headerLen = 80 } blockHash := chainhash.DoubleHashH(item.RawBlock[0:headerLen]) blockHeight := item.Height t.Logf("Testing block %s (hash %s, height %d)", item.Name, blockHash, blockHeight) // Ensure there is an error due to deserializing the block. var msgBlock wire.MsgBlock err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0, wire.BaseEncoding) if _, ok := err.(*wire.MessageError); !ok { t.Fatalf("block %q (hash %s, height %d) should have "+ "failed to decode", item.Name, blockHash, blockHeight) } } // testOrphanOrRejectedBlock attempts to process the block in the // provided test instance and ensures that it was either accepted as an // orphan or rejected with a rule violation. testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) _, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { // Ensure the error code is of the expected type. if _, ok := err.(blockchain.RuleError); !ok { t.Fatalf("block %q (hash %s, height %d) "+ "returned unexpected error type -- "+ "got %T, want blockchain.RuleError", item.Name, block.Hash(), blockHeight, err) } } if !isOrphan { t.Fatalf("block %q (hash %s, height %d) was accepted, "+ "but is not considered an orphan", item.Name, block.Hash(), blockHeight) } } // testExpectedTip ensures the current tip of the blockchain is the // block specified in the provided test instance. testExpectedTip := func(item fullblocktests.ExpectedTip) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing tip for block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) // Ensure hash and height match. best := chain.BestSnapshot() if *best.Hash != item.Block.BlockHash() || best.Height != blockHeight { t.Fatalf("block %q (hash %s, height %d) should be "+ "the current tip -- got (hash %s, height %d)", item.Name, block.Hash(), blockHeight, best.Hash, best.Height) } } for testNum, test := range tests { for itemNum, item := range test { switch item := item.(type) { case fullblocktests.AcceptedBlock: testAcceptedBlock(item) case fullblocktests.RejectedBlock: testRejectedBlock(item) case fullblocktests.RejectedNonCanonicalBlock: testRejectedNonCanonicalBlock(item) case fullblocktests.OrphanOrRejectedBlock: testOrphanOrRejectedBlock(item) case fullblocktests.ExpectedTip: testExpectedTip(item) default: t.Fatalf("test #%d, item #%d is not one of "+ "the supported test instance types -- "+ "got type: %T", testNum, itemNum, item) } } } }