decred.org/dcrwallet/v3@v3.1.0/validate/validate.go (about) 1 // Copyright (c) 2018 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package validate provides context-free consensus validation. 7 */ 8 package validate 9 10 import ( 11 "decred.org/dcrwallet/v3/errors" 12 blockchain "github.com/decred/dcrd/blockchain/standalone/v2" 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/gcs/v4" 15 "github.com/decred/dcrd/wire" 16 ) 17 18 const ( 19 // DCP0005ActiveHeightTestNet3 is the height of activation for DCP0005 20 // in TestNet3. 21 DCP0005ActiveHeightTestNet3 int32 = 323328 22 23 // DCP0005ActiveHeightMainNet is the height of activation for DCP0005 24 // in MainNet. 25 DCP0005ActiveHeightMainNet int32 = 431488 26 ) 27 28 // MerkleRoots recreates the merkle roots of regular and stake transactions from 29 // a block and compares them against the recorded merkle roots in the block 30 // header. 31 func MerkleRoots(block *wire.MsgBlock) error { 32 const opf = "validate.MerkleRoots(%v)" 33 34 mroot := blockchain.CalcTxTreeMerkleRoot(block.Transactions) 35 if block.Header.MerkleRoot != mroot { 36 blockHash := block.BlockHash() 37 op := errors.Opf(opf, &blockHash) 38 return errors.E(op, errors.Consensus, "invalid regular merkle root") 39 } 40 mroot = blockchain.CalcTxTreeMerkleRoot(block.STransactions) 41 if block.Header.StakeRoot != mroot { 42 blockHash := block.BlockHash() 43 op := errors.Opf(opf, &blockHash) 44 return errors.E(op, errors.Consensus, "invalid stake merkle root") 45 } 46 47 return nil 48 } 49 50 // DCP0005MerkleRoot recreates the combined regular and stake transaction merkle 51 // root and compares it against the merkle root in the block header. 52 // 53 // DCP0005 (https://github.com/decred/dcps/blob/master/dcp-0005/dcp-0005.mediawiki) 54 // describes (among other changes) the hard forking change which combined the 55 // individual regular and stake merkle roots into a single root. 56 func DCP0005MerkleRoot(block *wire.MsgBlock) error { 57 const opf = "validate.DCP0005MerkleRoot(%v)" 58 59 mroot := blockchain.CalcCombinedTxTreeMerkleRoot(block.Transactions, block.STransactions) 60 if block.Header.MerkleRoot != mroot { 61 blockHash := block.BlockHash() 62 op := errors.Opf(opf, &blockHash) 63 return errors.E(op, errors.Consensus, "invalid combined merkle root") 64 } 65 66 return nil 67 } 68 69 // CFilterV2HeaderCommitment ensures the given v2 committed filter has actually 70 // been committed to in the header, assuming dcp0005 was activated. 71 func CFilterV2HeaderCommitment(net wire.CurrencyNet, header *wire.BlockHeader, filter *gcs.FilterV2, leafIndex uint32, proof []chainhash.Hash) error { 72 const opf = "validate.CFilterV2HeaderCommitment(%v)" 73 74 // The commitment for cfilters of blocks before dcp0005 activates are 75 // _not_ stored in the stakeroot. They are validated by hashing the 76 // full set of cfilters prior to DCP0005 and comparing that hash to a 77 // known target hash. This is done by the wallet on a different 78 // function, so for headers before DCP0005 we simply consider them as 79 // valid. 80 switch { 81 case net == wire.TestNet3: 82 if header.Height < uint32(DCP0005ActiveHeightTestNet3) { 83 return nil 84 } 85 case net == wire.MainNet: 86 if header.Height < uint32(DCP0005ActiveHeightMainNet) { 87 return nil 88 } 89 } 90 91 // The inclusion proof should verify that the filter hash is included 92 // in the stake root of the header (root for header commitments as 93 // defined in DCP0005). 94 filterHash := filter.Hash() 95 root := header.StakeRoot 96 if !blockchain.VerifyInclusionProof(&root, &filterHash, leafIndex, proof) { 97 blockHash := header.BlockHash() 98 op := errors.Opf(opf, &blockHash) 99 err := errors.Errorf("invalid header inclusion proof for cfilterv2") 100 return errors.E(op, errors.Consensus, err) 101 } 102 return nil 103 } 104 105 // PreDCP0005CfilterHash returns nil if the hash for the given set of cf data 106 // matches the exepected hash for the given network. 107 func PreDCP0005CFilterHash(net wire.CurrencyNet, cfsethash *chainhash.Hash) error { 108 var targetHash string 109 110 switch net { 111 case wire.TestNet3: 112 targetHash = "619c08f5adda6f834212bbdaee3002fdc4efed731477af6c0fed490bbe2488d0" 113 case wire.MainNet: 114 targetHash = "f95e09f9ded38f8d6c32e5158a1f286633881393659218c63f5ab0fc86b36c83" 115 default: 116 return errors.Errorf("unknown network %d", net) 117 } 118 119 var target chainhash.Hash 120 err := chainhash.Decode(&target, targetHash) 121 if err != nil { 122 return err 123 } 124 125 if !target.IsEqual(cfsethash) { 126 return errors.Errorf("hash of provided cfilters %s does not match expected hash %s", cfsethash, target) 127 } 128 129 return nil 130 }