Svarog dfd8b3423d
Implement new mechanism for updating UTXO Diffs (#1671)
* Use selectedParent instead of selectedTip for non-selectedTip blocks in restoreSingleBlockStatus

* Cache the selectedParent for re-use in a resolveSingleBlockStatus chain

* Implement and use reverseUTXOSet

* Reverse blocks in correct order

* Support resolveBlockStatus without separate stagingAreas for usage of testConsensus

* Handle the case where the tip of the resolved block is not the next selectedTip

* Unify isResolveTip

* Some minor fixes and cleanup

* Add full finality window re-org test to stability-slow

* rename: useSeparateStagingAreasPerBlock -> useSeparateStagingAreaPerBlock

* Better logs in resolveSingleBlockStatus

* A few retouches to reverseUTXODiffs

* TEMPORARY COMMIT: EXTRAT ALL DIFFFROMS TO SEPARATE METHODS

* TEMPORARY COMMIT: REMOVE DIFFICULTY CHECKS IN DEVNET

* Don't pre-allocate in utxo-algebra, since the numbers are not known ahead-of-time

* Add some logs to reverseUTXODiffs

* Revert "TEMPORARY COMMIT: REMOVE DIFFICULTY CHECKS IN DEVNET"

This reverts commit c0af9dc6ade78a914c970e11bc63c34605565f57.

* Revert "TEMPORARY COMMIT: EXTRAT ALL DIFFFROMS TO SEPARATE METHODS"

This reverts commit 4fcca1b48c3a1183598833a355b9bfaf169edba1.

* Remove redundant paranthesis

* Revise some logs messages

* Rename:oneBlockBeforeCurrentUTXOSet -> lastResolvedBlockUTXOSet

* Don't break if the block was resolved as invalid

* rename unverifiedBlocks to recentlyVerifiedBlcks in reverseUTXODiffs

* Add errors.New to the panic, for a stack trace

* Reverse the UTXODiffs after the main block has been commited

* Use the correct value for previousUTXODiff

* Add test for ReverseUTXODiff

* Fix some names and comments

* Update TestReverseUTXODiffs to use consensus.Config

* Fix comments mentioning 'oneBlockBeforeTip'
2021-04-20 10:26:55 +03:00

116 lines
4.2 KiB
Go

package consensusstatemanager_test
import (
"testing"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
)
func TestReverseUTXODiffs(t *testing.T) {
// This test doesn't check ReverseUTXODiffs directly, since that would be quite complicated,
// instead, it creates a situation where a reversal would defenitely happen - a reorg of 5 blocks,
// then verifies that the resulting utxo-diffs and utxo-diff-children are all correct.
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestUTXOCommitment")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
// Create a chain of 5 blocks
const initialChainLength = 5
previousBlockHash := consensusConfig.GenesisHash
for i := 0; i < initialChainLength; i++ {
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
if err != nil {
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
}
}
// Mine a chain of 6 blocks, to re-organize the DAG
const reorgChainLength = initialChainLength + 1
reorgChain := make([]*externalapi.DomainHash, reorgChainLength)
previousBlockHash = consensusConfig.GenesisHash
for i := 0; i < reorgChainLength; i++ {
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
reorgChain[i] = previousBlockHash
if err != nil {
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
}
}
stagingArea := model.NewStagingArea()
// Check that every block in the reorg chain has the next block as it's UTXODiffChild,
// except that tip that has virtual, And that the diff is only `{ toRemove: { coinbase } }`
for i, currentBlockHash := range reorgChain {
if i == reorgChainLength-1 {
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, currentBlockHash)
if err != nil {
t.Fatalf("Error getting HasUTXODiffChild of %s: %+v", currentBlockHash, err)
}
if hasUTXODiffChild {
t.Errorf("Block %s expected utxoDiffChild is virtual, but HasUTXODiffChild returned true",
currentBlockHash)
}
} else {
utxoDiffChild, err := tc.UTXODiffStore().UTXODiffChild(tc.DatabaseContext(), stagingArea, currentBlockHash)
if err != nil {
t.Fatalf("Error getting utxoDiffChild of block No. %d, %s: %+v", i, currentBlockHash, err)
}
expectedUTXODiffChild := reorgChain[i+1]
if !expectedUTXODiffChild.Equal(utxoDiffChild) {
t.Errorf("Block %s expected utxoDiffChild is %s, but got %s instead",
currentBlockHash, expectedUTXODiffChild, utxoDiffChild)
continue
}
}
// skip the first block, since it's coinbase doesn't create outputs
if i == 0 {
continue
}
currentBlock, err := tc.BlockStore().Block(tc.DatabaseContext(), stagingArea, currentBlockHash)
if err != nil {
t.Fatalf("Error getting block %s: %+v", currentBlockHash, err)
}
utxoDiff, err := tc.UTXODiffStore().UTXODiff(tc.DatabaseContext(), stagingArea, currentBlockHash)
if err != nil {
t.Fatalf("Error getting utxoDiffChild of %s: %+v", currentBlockHash, err)
}
if !checkIsUTXODiffOnlyRemoveCoinbase(t, utxoDiff, currentBlock) {
t.Errorf("Expected %s to only have toRemove: {%s}, but got %s instead",
currentBlockHash, consensushashing.TransactionID(currentBlock.Transactions[0]), utxoDiff)
}
}
})
}
func checkIsUTXODiffOnlyRemoveCoinbase(t *testing.T, utxoDiff externalapi.UTXODiff, currentBlock *externalapi.DomainBlock) bool {
if utxoDiff.ToAdd().Len() > 0 || utxoDiff.ToRemove().Len() > 1 {
return false
}
iterator := utxoDiff.ToRemove().Iterator()
iterator.First()
outpoint, _, err := iterator.Get()
if err != nil {
t.Fatalf("Error getting from UTXODiff's iterator: %+v", err)
}
if !outpoint.TransactionID.Equal(consensushashing.TransactionID(currentBlock.Transactions[0])) {
return false
}
return true
}