mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
Get rid of genesis's UTXO dump (#1867)
* Get rid of genesis's UTXO dump * Allow known orphans when AllowSubmitBlockWhenNotSynced=true * gofmt * Avoid IBD without changing the pruning point when only genesis is available * Add DisallowDirectBlocksOnTopOfGenesis=true for mainnet * Remove any mention to nobanning to let stability tests run * Rename ifGenesisSetUtxoSet to loadUTXODataForGenesis Co-authored-by: Ori Newman <>
This commit is contained in:
parent
606b781ca0
commit
11103a36d3
@ -25,6 +25,10 @@ func (f *FlowContext) ShouldMine() (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if virtualSelectedParent.Equal(f.Config().NetParams().GenesisHash) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
virtualSelectedParentHeader, err := f.domain.Consensus().GetBlockHeader(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -81,7 +81,18 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
continue
|
||||
}
|
||||
|
||||
isGenesisVirtualSelectedParent, err := flow.isGenesisVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flow.IsOrphan(inv.Hash) {
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced && isGenesisVirtualSelectedParent {
|
||||
log.Infof("Cannot process orphan %s for a node with only the genesis block. The node needs to IBD "+
|
||||
"to the recent pruning point before normal operation can resume.", inv.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Block %s is a known orphan. Requesting its missing ancestors", inv.Hash)
|
||||
err := flow.AddOrphanRootsToQueue(inv.Hash)
|
||||
if err != nil {
|
||||
@ -111,6 +122,11 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced && !flow.Config().Devnet && flow.isChildOfGenesis(block) {
|
||||
log.Infof("Cannot process %s because it's a direct child of genesis.", consensushashing.BlockHash(block))
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Processing block %s", inv.Hash)
|
||||
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||
if err != nil {
|
||||
@ -265,6 +281,19 @@ func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock) e
|
||||
return err
|
||||
}
|
||||
if isBlockInOrphanResolutionRange {
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced {
|
||||
isGenesisVirtualSelectedParent, err := flow.isGenesisVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isGenesisVirtualSelectedParent {
|
||||
log.Infof("Cannot process orphan %s for a node with only the genesis block. The node needs to IBD "+
|
||||
"to the recent pruning point before normal operation can resume.", blockHash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Block %s is within orphan resolution range. "+
|
||||
"Adding it to the orphan set", blockHash)
|
||||
flow.AddOrphan(block)
|
||||
@ -278,6 +307,20 @@ func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock) e
|
||||
return flow.runIBDIfNotRunning(block)
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) isGenesisVirtualSelectedParent() (bool, error) {
|
||||
virtualSelectedParent, err := flow.Domain().Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return virtualSelectedParent.Equal(flow.Config().NetParams().GenesisHash), nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) isChildOfGenesis(block *externalapi.DomainBlock) bool {
|
||||
parents := block.Header.DirectParents()
|
||||
return len(parents) == 1 && parents[0].Equal(flow.Config().NetParams().GenesisHash)
|
||||
}
|
||||
|
||||
// isBlockInOrphanResolutionRange finds out whether the given blockHash should be
|
||||
// retrieved via the unorphaning mechanism or via IBD. This method sends a
|
||||
// getBlockLocator request to the peer with a limit of orphanResolutionRange.
|
||||
|
@ -55,6 +55,19 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(block *externalapi.DomainBlo
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced {
|
||||
isGenesisVirtualSelectedParent, err := flow.isGenesisVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isGenesisVirtualSelectedParent {
|
||||
log.Infof("Cannot IBD to %s because it won't change the pruning point. The node needs to IBD "+
|
||||
"to the recent pruning point before normal operation can resume.", highHash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err = flow.syncPruningPointFutureHeaders(flow.Domain().Consensus(), highestSharedBlockHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
Binary file not shown.
@ -1,32 +1,22 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"github.com/kaspanet/go-muhash"
|
||||
|
||||
// we need to embed the utxoset of mainnet genesis here
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
//go:embed resources/utxos.gz
|
||||
var utxoDumpFile []byte
|
||||
|
||||
func (bp *blockProcessor) setBlockStatusAfterBlockValidation(
|
||||
stagingArea *model.StagingArea, block *externalapi.DomainBlock, isPruningPoint bool) error {
|
||||
|
||||
@ -139,11 +129,7 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
}
|
||||
}
|
||||
|
||||
err = bp.ifGenesisSetUtxoSet(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bp.loadUTXODataForGenesis(stagingArea, block)
|
||||
var selectedParentChainChanges *externalapi.SelectedChainPath
|
||||
var virtualUTXODiff externalapi.UTXODiff
|
||||
var reversalData *model.UTXODiffReversalData
|
||||
@ -231,91 +217,25 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var mainnetGenesisUTXOSet externalapi.UTXODiff
|
||||
var mainnetGenesisMultiSet model.Multiset
|
||||
var mainnetGenesisOnce sync.Once
|
||||
var mainnetGenesisErr error
|
||||
|
||||
func deserializeMainnetUTXOSet() (externalapi.UTXODiff, model.Multiset, error) {
|
||||
mainnetGenesisOnce.Do(func() {
|
||||
toAdd := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry)
|
||||
mainnetGenesisMultiSet = multiset.New()
|
||||
file, err := gzip.NewReader(bytes.NewReader(utxoDumpFile))
|
||||
if err != nil {
|
||||
mainnetGenesisErr = err
|
||||
return
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
size := make([]byte, 1)
|
||||
_, err = io.ReadFull(file, size)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
mainnetGenesisErr = err
|
||||
return
|
||||
}
|
||||
|
||||
serializedUTXO := make([]byte, size[0])
|
||||
_, err = io.ReadFull(file, serializedUTXO)
|
||||
if err != nil {
|
||||
mainnetGenesisErr = err
|
||||
return
|
||||
}
|
||||
|
||||
mainnetGenesisMultiSet.Add(serializedUTXO)
|
||||
|
||||
entry, outpoint, err := utxo.DeserializeUTXO(serializedUTXO)
|
||||
if err != nil {
|
||||
mainnetGenesisErr = err
|
||||
return
|
||||
}
|
||||
toAdd[*outpoint] = entry
|
||||
}
|
||||
mainnetGenesisUTXOSet, mainnetGenesisErr = utxo.NewUTXODiffFromCollections(utxo.NewUTXOCollection(toAdd), utxo.NewUTXOCollection(make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry)))
|
||||
})
|
||||
return mainnetGenesisUTXOSet, mainnetGenesisMultiSet, mainnetGenesisErr
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) ifGenesisSetUtxoSet(block *externalapi.DomainBlock) error {
|
||||
func (bp *blockProcessor) loadUTXODataForGenesis(stagingArea *model.StagingArea, block *externalapi.DomainBlock) {
|
||||
isGenesis := len(block.Header.DirectParents()) == 0
|
||||
if !isGenesis {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
if !block.Header.UTXOCommitment().Equal(externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray())) {
|
||||
log.Infof("Loading checkpoint UTXO set")
|
||||
diff, utxoSetMultiset, err := deserializeMainnetUTXOSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Finished loading checkpoint UTXO set")
|
||||
utxoSetHash := utxoSetMultiset.Hash()
|
||||
if !utxoSetHash.Equal(block.Header.UTXOCommitment()) {
|
||||
return errors.New("Invalid UTXO set dump")
|
||||
}
|
||||
|
||||
area := model.NewStagingArea()
|
||||
bp.consensusStateStore.StageVirtualUTXODiff(area, diff)
|
||||
bp.utxoDiffStore.Stage(area, blockHash, diff, nil)
|
||||
// commit the multiset of genesis
|
||||
bp.multisetStore.Stage(area, blockHash, utxoSetMultiset)
|
||||
err = staging.CommitAllChanges(bp.databaseContext, area)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// if it's genesis but has an empty muhash then commit an empty multiset and an empty diff
|
||||
area := model.NewStagingArea()
|
||||
bp.consensusStateStore.StageVirtualUTXODiff(area, utxo.NewUTXODiff())
|
||||
bp.utxoDiffStore.Stage(area, blockHash, utxo.NewUTXODiff(), nil)
|
||||
bp.multisetStore.Stage(area, blockHash, multiset.New())
|
||||
err := staging.CommitAllChanges(bp.databaseContext, area)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// Note: The applied UTXO set and multiset do not satisfy the UTXO commitment
|
||||
// of Mainnet's genesis. This is why any block that will be built on top of genesis
|
||||
// will have a wrong UTXO commitment as well, and will not be able to get to a consensus
|
||||
// with the rest of the network.
|
||||
// This is why getting direct blocks on top of genesis is forbidden, and the only way to
|
||||
// get a newer state for a node with genesis only is by requesting a proof for a recent
|
||||
// pruning point.
|
||||
// The actual UTXO set that fits Mainnet's genesis' UTXO commitment was removed from the codebase in order
|
||||
// to make reduce the consensus initialization time and the compiled binary size, but can be still
|
||||
// found here for anyone to verify: https://github.com/kaspanet/kaspad/blob/dbf18d8052f000ba0079be9e79b2d6f5a98b74ca/domain/consensus/processes/blockprocessor/resources/utxos.gz
|
||||
bp.consensusStateStore.StageVirtualUTXODiff(stagingArea, utxo.NewUTXODiff())
|
||||
bp.utxoDiffStore.Stage(stagingArea, blockHash, utxo.NewUTXODiff(), nil)
|
||||
bp.multisetStore.Stage(stagingArea, blockHash, multiset.New())
|
||||
}
|
||||
|
||||
func isHeaderOnlyBlock(block *externalapi.DomainBlock) bool {
|
||||
|
@ -533,12 +533,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const mainnetUTXOSize = 1232643
|
||||
expected := len(outputs) + 1
|
||||
if consensusConfig.Name == "kaspa-mainnet" {
|
||||
expected += mainnetUTXOSize
|
||||
}
|
||||
|
||||
// Make sure the length of the UTXOs is exactly spendingTransaction.Outputs + 1 coinbase
|
||||
// output (includingBlock's coinbase)
|
||||
if len(allOutpointAndUTXOEntryPairs) != expected {
|
||||
|
@ -117,6 +117,10 @@ func (csm *consensusStateManager) validateUTXOCommitment(
|
||||
log.Tracef("validateUTXOCommitment start for block %s", blockHash)
|
||||
defer log.Tracef("validateUTXOCommitment end for block %s", blockHash)
|
||||
|
||||
if blockHash.Equal(csm.genesisHash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
multisetHash := multiset.Hash()
|
||||
if !block.Header.UTXOCommitment().Equal(multisetHash) {
|
||||
return errors.Wrapf(ruleerrors.ErrBadUTXOCommitment, "block %s UTXO commitment is invalid - block "+
|
||||
|
@ -40,12 +40,12 @@ func TestBlockWindow(t *testing.T) {
|
||||
{
|
||||
parents: []string{"C", "D"},
|
||||
id: "E",
|
||||
expectedWindow: []string{"D", "C", "B"},
|
||||
expectedWindow: []string{"C", "D", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"C", "D"},
|
||||
id: "F",
|
||||
expectedWindow: []string{"D", "C", "B"},
|
||||
expectedWindow: []string{"C", "D", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"A"},
|
||||
@ -60,38 +60,38 @@ func TestBlockWindow(t *testing.T) {
|
||||
{
|
||||
parents: []string{"H", "F"},
|
||||
id: "I",
|
||||
expectedWindow: []string{"F", "D", "H", "C", "G", "B"},
|
||||
expectedWindow: []string{"F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"I"},
|
||||
id: "J",
|
||||
expectedWindow: []string{"I", "F", "D", "H", "C", "G", "B"},
|
||||
expectedWindow: []string{"I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
//
|
||||
{
|
||||
parents: []string{"J"},
|
||||
id: "K",
|
||||
expectedWindow: []string{"J", "I", "F", "D", "H", "C", "G", "B"},
|
||||
expectedWindow: []string{"J", "I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"K"},
|
||||
id: "L",
|
||||
expectedWindow: []string{"K", "J", "I", "F", "D", "H", "C", "G", "B"},
|
||||
expectedWindow: []string{"K", "J", "I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"L"},
|
||||
id: "M",
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "D", "H", "C", "G", "B"},
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"M"},
|
||||
id: "N",
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "H", "C", "G"},
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "C", "H", "D", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"N"},
|
||||
id: "O",
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "H", "C"},
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "C", "H", "D"},
|
||||
},
|
||||
},
|
||||
dagconfig.TestnetParams.Name: {
|
||||
|
@ -38,7 +38,7 @@ var genesisTxPayload = []byte{
|
||||
0xd7, 0x91, 0xd7, 0x93, 0xd7, 0x95, 0xd7, 0x9f,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bitcoin block hash 0000000000000000000b1f8e1c17b0133d439174e52efbb0c41c3583a8aa66b0
|
||||
0x00, 0x0b, 0x1f, 0x8e, 0x1c, 0x17, 0xb0, 0x13,
|
||||
0x3d, 0x43, 0x91, 0x74 ,0xe5, 0x2e, 0xfb, 0xb0,
|
||||
0x3d, 0x43, 0x91, 0x74, 0xe5, 0x2e, 0xfb, 0xb0,
|
||||
0xc4, 0x1c, 0x35, 0x83, 0xa8, 0xaa, 0x66, 0xb0,
|
||||
0x0f, 0xca, 0x37, 0xca, 0x66, 0x7c, 0x2d, 0x55, // Checkpoint block hash 0fca37ca667c2d550a6c4416dad9717e50927128c424fa4edbebc436ab13aeef
|
||||
0x0a, 0x6c, 0x44, 0x16, 0xda, 0xd9, 0x71, 0x7e,
|
||||
|
@ -186,6 +186,8 @@ type Params struct {
|
||||
FixedSubsidySwitchPruningPointInterval uint64
|
||||
|
||||
FixedSubsidySwitchHashRateThreshold *big.Int
|
||||
|
||||
DisallowDirectBlocksOnTopOfGenesis bool
|
||||
}
|
||||
|
||||
// NormalizeRPCServerAddress returns addr with the current network default
|
||||
@ -272,6 +274,7 @@ var MainnetParams = Params{
|
||||
PruningProofM: defaultPruningProofM,
|
||||
FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval,
|
||||
FixedSubsidySwitchHashRateThreshold: big.NewInt(150_000_000_000),
|
||||
DisallowDirectBlocksOnTopOfGenesis: true,
|
||||
}
|
||||
|
||||
// TestnetParams defines the network parameters for the test Kaspa network.
|
||||
|
@ -85,8 +85,8 @@
|
||||
; Maximum number of inbound and outbound peers.
|
||||
; maxinpeers=125
|
||||
|
||||
; Disable banning of misbehaving peers.
|
||||
; nobanning=1
|
||||
; Enable banning of misbehaving peers.
|
||||
; enablebanning=1
|
||||
|
||||
; Maximum allowed ban score before disconnecting and banning misbehaving peers.
|
||||
; banthreshold=100
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
rm -rf /tmp/kaspad-temp
|
||||
|
||||
kaspad --devnet --nobanning --appdir=/tmp/kaspad-temp --profile=6061 --loglevel=debug &
|
||||
kaspad --devnet --appdir=/tmp/kaspad-temp --profile=6061 --loglevel=debug &
|
||||
KASPAD_PID=$!
|
||||
KASPAD_KILLED=0
|
||||
function killKaspadIfNotKilled() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
rm -rf /tmp/kaspad-temp
|
||||
|
||||
kaspad --devnet --nobanning --appdir=/tmp/kaspad-temp --profile=6061 &
|
||||
kaspad --devnet --appdir=/tmp/kaspad-temp --profile=6061 &
|
||||
KASPAD_PID=$!
|
||||
|
||||
sleep 1
|
||||
|
@ -1,2 +1 @@
|
||||
--devnet
|
||||
--devnet --nobanning
|
||||
--devnet
|
Loading…
x
Reference in New Issue
Block a user