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  }