From 8dedca693e0de337098d06b17d1d2dec338e10a9 Mon Sep 17 00:00:00 2001 From: Evgeny Khirin <32414982+evgeny-khirin@users.noreply.github.com> Date: Tue, 14 May 2019 15:31:23 +0300 Subject: [PATCH] [NOD-164 + NOD-167] AcceptedIDMerkleRoot validation and newBlockTemplate (#295) * [NOD-164] Added validation routine * [NOD-167] Extracted acceptedIDMerkleRoot calculation to its own method and implemented NextAcceptedIDMerkleRoot. * [NOD-164] Fixed TestValidateFeeTransaction. * [NOD-164] Fixed TestFinality. * [NOD-164] Fixed blk_ tests. * [NOD-164] Fixed if -> iff in a comment. * [NOD-164] Minor style changes in comments. * [NOD-164] Moved validateAcceptedIDMerkleRoot to before its population with the block's own transactions. Replaced heavy call to verifyAndBuildUTXO in NextBlockFeeTransaction and NextAcceptedIDMerkleRoot with a call to pastUTXO on the virtual. * [NOD-164] Fixed erroneous comment. * [NOD-164] Inserted the logic from buildAndSortAcceptedTxs into calculateAcceptedIDMerkleRoot, since the former was meaningless on its own. * [NOD-164] Changed looping over txsAcceptanceData instead of over node.blues. --- blockdag/dag.go | 66 ++++++++++++++++++++++++++++++- blockdag/dag_test.go | 16 +++++++- blockdag/testdata/blk_0_to_4.dat | Bin 2108 -> 2108 bytes blockdag/testdata/blk_3A.dat | Bin 489 -> 489 bytes blockdag/testdata/blk_3B.dat | Bin 376 -> 376 bytes blockdag/testdata/blk_3C.dat | Bin 408 -> 408 bytes blockdag/testdata/blk_3D.dat | Bin 534 -> 534 bytes mining/mining.go | 6 ++- mining/test_utils.go | 6 +++ util/daghash/hash.go | 9 ++++- 10 files changed, 96 insertions(+), 7 deletions(-) diff --git a/blockdag/dag.go b/blockdag/dag.go index 8147307fe..92689691c 100644 --- a/blockdag/dag.go +++ b/blockdag/dag.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "math" + "sort" "sync" "time" @@ -507,6 +508,40 @@ func (dag *BlockDAG) addBlock(node *blockNode, parentNodes blockSet, block *util return err } +func calculateAcceptedIDMerkleRoot(txsAcceptanceData MultiBlockTxsAcceptanceData) *daghash.Hash { + var acceptedTxs []*util.Tx + for _, blockTxsAcceptanceData := range txsAcceptanceData { + for _, txAcceptance := range blockTxsAcceptanceData { + if !txAcceptance.IsAccepted { + continue + } + acceptedTxs = append(acceptedTxs, txAcceptance.Tx) + } + } + sort.Slice(acceptedTxs, func(i, j int) bool { + return daghash.LessTxID(acceptedTxs[i].ID(), acceptedTxs[j].ID()) + }) + + acceptedIDMerkleTree := BuildIDMerkleTreeStore(acceptedTxs) + return acceptedIDMerkleTree.Root() +} + +func (node *blockNode) validateAcceptedIDMerkleRoot(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData) error { + if node.isGenesis() { + return nil + } + + calculatedAccepetedIDMerkleRoot := calculateAcceptedIDMerkleRoot(txsAcceptanceData) + header := node.Header() + if !header.AcceptedIDMerkleRoot.IsEqual(calculatedAccepetedIDMerkleRoot) { + str := fmt.Sprintf("block accepted ID merkle root is invalid - block "+ + "header indicates %s, but calculated value is %s", + header.AcceptedIDMerkleRoot, calculatedAccepetedIDMerkleRoot) + return ruleError(ErrBadMerkleRoot, str) + } + return nil +} + // connectBlock handles connecting the passed node/block to the DAG. // // This function MUST be called with the DAG state lock held (for writes). @@ -712,7 +747,7 @@ func (dag *BlockDAG) updateFinalityPoint() { // NextBlockFeeTransaction prepares the fee transaction for the next mined block // -// This function CAN'T be called with the DAG lock not held. +// This function CAN'T be called with the DAG lock held. func (dag *BlockDAG) NextBlockFeeTransaction() (*wire.MsgTx, error) { dag.dagLock.RLock() defer dag.dagLock.RUnlock() @@ -724,13 +759,35 @@ func (dag *BlockDAG) NextBlockFeeTransaction() (*wire.MsgTx, error) { // // This function MUST be called with the DAG read-lock held func (dag *BlockDAG) NextBlockFeeTransactionNoLock() (*wire.MsgTx, error) { - _, txsAcceptanceData, _, err := dag.virtual.blockNode.verifyAndBuildUTXO(dag, nil, true) + _, txsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode) if err != nil { return nil, err } return dag.virtual.blockNode.buildFeeTransaction(dag, txsAcceptanceData) } +// NextAcceptedIDMerkleRoot prepares the acceptedIDMerkleRoot for the next mined block +// +// This function CAN'T be called with the DAG lock held. +func (dag *BlockDAG) NextAcceptedIDMerkleRoot() (*daghash.Hash, error) { + dag.dagLock.RLock() + defer dag.dagLock.RUnlock() + + return dag.NextAcceptedIDMerkleRootNoLock() +} + +// NextAcceptedIDMerkleRootNoLock prepares the acceptedIDMerkleRoot for the next mined block +// +// This function MUST be called with the DAG read-lock held +func (dag *BlockDAG) NextAcceptedIDMerkleRootNoLock() (*daghash.Hash, error) { + _, txsAcceptanceData, err := dag.pastUTXO(&dag.virtual.blockNode) + if err != nil { + return nil, err + } + + return calculateAcceptedIDMerkleRoot(txsAcceptanceData), nil +} + // applyDAGChanges does the following: // 1. Connects each of the new block's parents to the block. // 2. Adds the new block to the DAG's tips. @@ -820,6 +877,11 @@ func (node *blockNode) verifyAndBuildUTXO(dag *BlockDAG, transactions []*util.Tx return nil, nil, nil, err } + err = node.validateAcceptedIDMerkleRoot(dag, txsAcceptanceData) + if err != nil { + return nil, nil, nil, err + } + feeData, err := dag.checkConnectToPastUTXO(node, pastUTXO, transactions, fastAdd) if err != nil { return nil, nil, nil, err diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index 2cd92cf82..731974a91 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -192,7 +192,7 @@ func TestHaveBlock(t *testing.T) { {hash: dagconfig.SimNetParams.GenesisHash.String(), want: true}, // Block 3b should be present (as a second child of Block 2). - {hash: "0a20af2fa5ce154a60faca96e9fa125fc52e0ca8f98484708d0413203626edaf", want: true}, + {hash: "6733a86f4e3a46c0d4df76d6fbfab88eacadf8e44f54eb544a18d6b95570510c", want: true}, // Block 100000 should be present (as an orphan). {hash: "18bcf45b8c0dbccd7690a728f3486c6d5fc84971688f89f4554297b6a278e554", want: true}, @@ -901,6 +901,18 @@ func TestValidateFeeTransaction(t *testing.T) { for i, tx := range transactions { utilTxs[i] = util.NewTx(tx) } + + newVirtual, err := GetVirtualFromParentsForTest(dag, parentHashes) + if err != nil { + t.Fatalf("block %v: unexpected error when setting virtual for test: %v", blockName, err) + } + oldVirtual := SetVirtualForTest(dag, newVirtual) + acceptedIDMerkleRoot, err := dag.NextAcceptedIDMerkleRoot() + if err != nil { + t.Fatalf("block %v: unexpected error when getting next acceptIDMerkleRoot: %v", blockName, err) + } + SetVirtualForTest(dag, oldVirtual) + daghash.Sort(parentHashes) msgBlock := &wire.MsgBlock{ Header: wire.BlockHeader{ @@ -908,7 +920,7 @@ func TestValidateFeeTransaction(t *testing.T) { ParentHashes: parentHashes, HashMerkleRoot: BuildHashMerkleTreeStore(utilTxs).Root(), IDMerkleRoot: BuildIDMerkleTreeStore(utilTxs).Root(), - AcceptedIDMerkleRoot: &daghash.ZeroHash, + AcceptedIDMerkleRoot: acceptedIDMerkleRoot, UTXOCommitment: &daghash.ZeroHash, }, Transactions: transactions, diff --git a/blockdag/testdata/blk_0_to_4.dat b/blockdag/testdata/blk_0_to_4.dat index ed95364c7858de92bb4e73951c37bf091db8c9b6..67e4e30771d29f496d5f524c0114a7ec17922dbc 100644 GIT binary patch delta 575 zcmdlZut#7+8RJA$ykk&8z3Hh2$ohzzNz4O}q1_p*7q=X$@{ z^cgGq8P=)>akE_#T{E?AHKWtSK!wSA%si8M*o`OWGCL^{a?s?3?D~`cF;~`Km~!yM zkMRE2o@+c7oaQYqGg+;`eEGq{=ilDtyfr?iEpb^TC3e2)*_B4x&mG@IT>i2lZEop~ zA6-Fbw-H`hz2)tmn7 zvOZX%Io~3!mECWf1b^6Ob=l3I=JOts;QW;VS%(VY-=v?^QDbqf4 delta 492 zcmdlZut#7+8RNu2n~4c5leaP6NZ9Lh;uBw=b zo3fo!ZU-yh`yIchCE$y?&gX!)XO-rOm2n)BIdfF<(vHYuF9^v)twtK|}#Vz6cZ&@T&#vHw{Q&HfhJM%mHlqVvDqqLPB(FcPW Ot`Y@8mQMC#oDBf&Cro?* diff --git a/blockdag/testdata/blk_3B.dat b/blockdag/testdata/blk_3B.dat index 551991afcd311891b4362c0932d83d199d0132f3..eaf5ff6c340ffae635ba93e48293ab94fbf9ef31 100644 GIT binary patch literal 376 zcmeylZ_CXBMg|ZNV7xHp;E5mM{jWXOcq};0TU=(cT7miUgNM(*y~}xPd`w$nUb#u^ z%immv0d@u+VzKRw7?AY;|MdzmWlSK)Lp%Tz!b1NC0>0odZv6nGU=RJI mlH?S9kPrwkGJImy0vi&DMI&4Ytb$Mgfy@KC4g%m>u`vKu9Cc^_ literal 376 zcmeylZ_CXBMg|ZNV61s|Gk@yE36`_lh;Hr9<)Mo6HvM>- zQXTc{a+v>xSsim9AJ&^^_{6FOHY5;>Mz|1I1)%@}nFn+o1i-ao GV*mi$zGwOX diff --git a/blockdag/testdata/blk_3C.dat b/blockdag/testdata/blk_3C.dat index a41f63b8998a89c15b50e45a7e84c95e55590660..a33cd826751acb768ffacf3395c1c932a6f0d02a 100644 GIT binary patch literal 408 zcmeylZ_CXIj0_+kz;t2C!4p5i`(Jym@mO%0x46t?wF2|y2M?crdzbUp_?WhYRZ`S9 zgT0=6wBEdW{#rX8zQR6D()9$7%c5{T7~eS2RdRaND_%5Z&6H%R?3S`keU0 z*C%<_cIS`ImER>c1TXOr%{Vu&U02Wjjl-sFr^MxVjaEM|>D?~8_Q#7gTb|7;o2b;E zdG-5>;7un}(>dD+$%CCi0kPQjMhr;%|Nr$05I-?6Fo75_4o1tXyV1ephP9R%PrjR64T^t{%3E!cnP(HaLk#|bSS(iM!ygD=g$CjFa`y#LZ_(IszLYi|g*?KPOv syKUK4KF2$~6BllM%lEAL`@iteiG>Oi*YYqjP25$YK*%wZ{TNRG0C*N(?f?J) delta 171 zcmbQnGL0qo=e{jB`Is0$K!CC4+0Fc^7bjTGZjaOUulp@L>#k^!j^MU)AtAc8JC}zl z9=)b=Xx&MlSjUd&x3{+G>}J@UD$v5_v9tDsbl`^{t4|S