diff --git a/blockdag/blocknode.go b/blockdag/blocknode.go index befffd7d1..170c8082f 100644 --- a/blockdag/blocknode.go +++ b/blockdag/blocknode.go @@ -223,7 +223,7 @@ func (node *blockNode) isGenesis() bool { } func (node *blockNode) finalityScore(dag *BlockDAG) uint64 { - return node.blueScore / uint64(dag.dagParams.FinalityInterval) + return node.blueScore / uint64(dag.Params.FinalityInterval) } // String returns a string that contains the block hash. diff --git a/blockdag/coinbase.go b/blockdag/coinbase.go index 28d35d500..e9f7cea19 100644 --- a/blockdag/coinbase.go +++ b/blockdag/coinbase.go @@ -177,7 +177,7 @@ func coinbaseOutputForBlueBlock(dag *BlockDAG, blueBlock *blockNode, } } - totalReward := CalcBlockSubsidy(blueBlock.blueScore, dag.dagParams) + totalFees + totalReward := CalcBlockSubsidy(blueBlock.blueScore, dag.Params) + totalFees if totalReward == 0 { return nil, nil diff --git a/blockdag/common_test.go b/blockdag/common_test.go index 7b55e48a0..232d33332 100644 --- a/blockdag/common_test.go +++ b/blockdag/common_test.go @@ -7,7 +7,6 @@ package blockdag import ( "compress/bzip2" "encoding/binary" - "github.com/kaspanet/kaspad/util/mstime" "io" "os" "path/filepath" @@ -15,6 +14,8 @@ import ( "testing" "time" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/pkg/errors" "github.com/kaspanet/kaspad/dagconfig" @@ -87,7 +88,7 @@ func loadUTXOSet(filename string) (UTXOSet, error) { // TestSetCoinbaseMaturity makes the ability to set the coinbase maturity // available when running tests. func (dag *BlockDAG) TestSetCoinbaseMaturity(maturity uint64) { - dag.dagParams.BlockCoinbaseMaturity = maturity + dag.Params.BlockCoinbaseMaturity = maturity } // newTestDAG returns a DAG that is usable for syntetic tests. It is @@ -98,7 +99,7 @@ func newTestDAG(params *dagconfig.Params) *BlockDAG { index := newBlockIndex(params) targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) dag := &BlockDAG{ - dagParams: params, + Params: params, timeSource: NewTimeSource(), targetTimePerBlock: targetTimePerBlock, difficultyAdjustmentWindowSize: params.DifficultyAdjustmentWindowSize, diff --git a/blockdag/dag.go b/blockdag/dag.go index f57c54139..daeb5d4fb 100644 --- a/blockdag/dag.go +++ b/blockdag/dag.go @@ -6,12 +6,13 @@ package blockdag import ( "fmt" - "github.com/kaspanet/kaspad/util/mstime" "math" "sort" "sync" "time" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/kaspanet/kaspad/dbaccess" "github.com/pkg/errors" @@ -62,7 +63,7 @@ type BlockDAG struct { // The following fields are set when the instance is created and can't // be changed afterwards, so there is no need to protect them with a // separate mutex. - dagParams *dagconfig.Params + Params *dagconfig.Params timeSource TimeSource sigCache *txscript.SigCache indexManager IndexManager @@ -889,7 +890,7 @@ func (dag *BlockDAG) finalizeNodesBelowFinalityPoint(deleteDiffData bool) { } var nodesToDelete []*blockNode if deleteDiffData { - nodesToDelete = make([]*blockNode, 0, dag.dagParams.FinalityInterval) + nodesToDelete = make([]*blockNode, 0, dag.Params.FinalityInterval) } for len(queue) > 0 { var current *blockNode @@ -1341,7 +1342,7 @@ func (dag *BlockDAG) isSynced() bool { var dagTimestamp int64 selectedTip := dag.selectedTip() if selectedTip == nil { - dagTimestamp = dag.dagParams.GenesisBlock.Header.Timestamp.UnixMilliseconds() + dagTimestamp = dag.Params.GenesisBlock.Header.Timestamp.UnixMilliseconds() } else { dagTimestamp = selectedTip.timestamp } @@ -2042,7 +2043,7 @@ func New(config *Config) (*BlockDAG, error) { index := newBlockIndex(params) dag := &BlockDAG{ - dagParams: params, + Params: params, timeSource: config.TimeSource, sigCache: config.SigCache, indexManager: config.IndexManager, @@ -2087,7 +2088,7 @@ func New(config *Config) (*BlockDAG, error) { genesis, ok := index.LookupNode(params.GenesisHash) if !ok { - genesisBlock := util.NewBlock(dag.dagParams.GenesisBlock) + genesisBlock := util.NewBlock(dag.Params.GenesisBlock) // To prevent the creation of a new err variable unintentionally so the // defered function above could read err - declare isOrphan and isDelayed explicitly. var isOrphan, isDelayed bool diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index 49d1ebb86..06cbf7588 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -699,7 +699,7 @@ func TestConfirmations(t *testing.T) { // Add a chain of blocks chainBlocks := make([]*wire.MsgBlock, 5) - chainBlocks[0] = dag.dagParams.GenesisBlock + chainBlocks[0] = dag.Params.GenesisBlock for i := uint32(1); i < 5; i++ { chainBlocks[i] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[i-1]) } @@ -808,7 +808,7 @@ func TestAcceptingBlock(t *testing.T) { numChainBlocks := uint32(10) chainBlocks := make([]*wire.MsgBlock, numChainBlocks) - chainBlocks[0] = dag.dagParams.GenesisBlock + chainBlocks[0] = dag.Params.GenesisBlock for i := uint32(1); i <= numChainBlocks-1; i++ { chainBlocks[i] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[i-1]) } @@ -954,7 +954,7 @@ func testFinalizeNodesBelowFinalityPoint(t *testing.T, deleteDiffData bool) { flushUTXODiffStore() return node } - finalityInterval := dag.dagParams.FinalityInterval + finalityInterval := dag.Params.FinalityInterval nodes := make([]*blockNode, 0, finalityInterval) currentNode := dag.genesis nodes = append(nodes, currentNode) diff --git a/blockdag/dagio.go b/blockdag/dagio.go index d7588c1dc..544fff07a 100644 --- a/blockdag/dagio.go +++ b/blockdag/dagio.go @@ -165,8 +165,8 @@ func saveDAGState(dbContext dbaccess.Context, state *dagState) error { // genesis block and the node's local subnetwork id. func (dag *BlockDAG) createDAGState(localSubnetworkID *subnetworkid.SubnetworkID) error { return saveDAGState(dbaccess.NoTx(), &dagState{ - TipHashes: []*daghash.Hash{dag.dagParams.GenesisHash}, - LastFinalityPoint: dag.dagParams.GenesisHash, + TipHashes: []*daghash.Hash{dag.Params.GenesisHash}, + LastFinalityPoint: dag.Params.GenesisHash, LocalSubnetworkID: localSubnetworkID, }) } @@ -293,7 +293,7 @@ func (dag *BlockDAG) initBlockIndex() (unprocessedBlockNodes []*blockNode, err e } if dag.blockCount == 0 { - if !node.hash.IsEqual(dag.dagParams.GenesisHash) { + if !node.hash.IsEqual(dag.Params.GenesisHash) { return nil, errors.Errorf("Expected "+ "first entry in block index to be genesis block, "+ "found %s", node.hash) diff --git a/blockdag/difficulty.go b/blockdag/difficulty.go index a12f985f0..229be7e7a 100644 --- a/blockdag/difficulty.go +++ b/blockdag/difficulty.go @@ -34,7 +34,7 @@ func (dag *BlockDAG) requiredDifficulty(bluestParent *blockNode, newBlockTime ms defer bigintpool.Release(newTarget) windowTimeStampDifference := bigintpool.Acquire(windowMaxTimeStamp - windowMinTimestamp) defer bigintpool.Release(windowTimeStampDifference) - targetTimePerBlock := bigintpool.Acquire(dag.dagParams.TargetTimePerBlock.Milliseconds()) + targetTimePerBlock := bigintpool.Acquire(dag.Params.TargetTimePerBlock.Milliseconds()) defer bigintpool.Release(targetTimePerBlock) difficultyAdjustmentWindowSize := bigintpool.Acquire(int64(dag.difficultyAdjustmentWindowSize)) defer bigintpool.Release(difficultyAdjustmentWindowSize) @@ -44,7 +44,7 @@ func (dag *BlockDAG) requiredDifficulty(bluestParent *blockNode, newBlockTime ms Mul(newTarget, windowTimeStampDifference). Div(newTarget, targetTimePerBlock). Div(newTarget, difficultyAdjustmentWindowSize) - if newTarget.Cmp(dag.dagParams.PowMax) > 0 { + if newTarget.Cmp(dag.Params.PowMax) > 0 { return dag.powMaxBits } newTargetBits := util.BigToCompact(newTarget) diff --git a/blockdag/external_dag_test.go b/blockdag/external_dag_test.go index 2910a59fa..d43eec280 100644 --- a/blockdag/external_dag_test.go +++ b/blockdag/external_dag_test.go @@ -2,11 +2,12 @@ package blockdag_test import ( "fmt" - "github.com/pkg/errors" "math" "strings" "testing" + "github.com/pkg/errors" + "github.com/kaspanet/kaspad/util/subnetworkid" "github.com/kaspanet/kaspad/util/daghash" @@ -49,7 +50,7 @@ func TestFinality(t *testing.T) { } defer teardownFunc() buildNodeToDag := func(parentHashes []*daghash.Hash) (*util.Block, error) { - msgBlock, err := mining.PrepareBlockForTest(dag, ¶ms, parentHashes, nil, false) + msgBlock, err := mining.PrepareBlockForTest(dag, parentHashes, nil, false) if err != nil { return nil, err } @@ -221,7 +222,7 @@ func TestChainedTransactions(t *testing.T) { } defer teardownFunc() - block1, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{params.GenesisHash}, nil, false) + block1, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{params.GenesisHash}, nil, false) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -269,7 +270,7 @@ func TestChainedTransactions(t *testing.T) { } chainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{chainedTxIn}, []*wire.TxOut{chainedTxOut}) - block2, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx}, false) + block2, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx}, false) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -315,7 +316,7 @@ func TestChainedTransactions(t *testing.T) { } nonChainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{nonChainedTxIn}, []*wire.TxOut{nonChainedTxOut}) - block3, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{nonChainedTx}, false) + block3, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{nonChainedTx}, false) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -372,7 +373,7 @@ func TestOrderInDiffFromAcceptanceData(t *testing.T) { } // Create the block - msgBlock, err := mining.PrepareBlockForTest(dag, ¶ms, []*daghash.Hash{previousBlock.Hash()}, txs, false) + msgBlock, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{previousBlock.Hash()}, txs, false) if err != nil { t.Fatalf("TestOrderInDiffFromAcceptanceData: Failed to prepare block: %s", err) } @@ -429,7 +430,7 @@ func TestGasLimit(t *testing.T) { cbTxs := []*wire.MsgTx{} for i := 0; i < 4; i++ { - fundsBlock, err := mining.PrepareBlockForTest(dag, ¶ms, dag.TipHashes(), nil, false) + fundsBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), nil, false) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -481,7 +482,7 @@ func TestGasLimit(t *testing.T) { tx2 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx2In}, []*wire.TxOut{tx2Out}, subnetworkID, 10000, []byte{}) // Here we check that we can't process a block that has transactions that exceed the gas limit - overLimitBlock, err := mining.PrepareBlockForTest(dag, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1, tx2}, true) + overLimitBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*wire.MsgTx{tx1, tx2}, true) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -516,7 +517,7 @@ func TestGasLimit(t *testing.T) { subnetworkID, math.MaxUint64, []byte{}) // Here we check that we can't process a block that its transactions' gas overflows uint64 - overflowGasBlock, err := mining.PrepareBlockForTest(dag, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1, overflowGasTx}, true) + overflowGasBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*wire.MsgTx{tx1, overflowGasTx}, true) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -550,7 +551,7 @@ func TestGasLimit(t *testing.T) { nonExistentSubnetworkTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{nonExistentSubnetworkTxIn}, []*wire.TxOut{nonExistentSubnetworkTxOut}, nonExistentSubnetwork, 1, []byte{}) - nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, ¶ms, dag.TipHashes(), []*wire.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true) + nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*wire.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } @@ -571,7 +572,7 @@ func TestGasLimit(t *testing.T) { } // Here we check that we can process a block with a transaction that doesn't exceed the gas limit - validBlock, err := mining.PrepareBlockForTest(dag, ¶ms, dag.TipHashes(), []*wire.MsgTx{tx1}, true) + validBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*wire.MsgTx{tx1}, true) if err != nil { t.Fatalf("PrepareBlockForTest: %v", err) } diff --git a/blockdag/ghostdag.go b/blockdag/ghostdag.go index 8c2a5e240..2aa96b28d 100644 --- a/blockdag/ghostdag.go +++ b/blockdag/ghostdag.go @@ -78,13 +78,13 @@ func (dag *BlockDAG) ghostdag(newNode *blockNode) (selectedParentAnticone []*blo } candidateAnticoneSize++ - if candidateAnticoneSize > dag.dagParams.K { + if candidateAnticoneSize > dag.Params.K { // k-cluster violation: The candidate's blue anticone exceeded k possiblyBlue = false break } - if candidateBluesAnticoneSizes[block] == dag.dagParams.K { + if candidateBluesAnticoneSizes[block] == dag.Params.K { // k-cluster violation: A block in candidate's blue anticone already // has k blue blocks in its own anticone possiblyBlue = false @@ -93,7 +93,7 @@ func (dag *BlockDAG) ghostdag(newNode *blockNode) (selectedParentAnticone []*blo // This is a sanity check that validates that a blue // block's blue anticone is not already larger than K. - if candidateBluesAnticoneSizes[block] > dag.dagParams.K { + if candidateBluesAnticoneSizes[block] > dag.Params.K { return nil, errors.New("found blue anticone size larger than k") } } @@ -109,7 +109,7 @@ func (dag *BlockDAG) ghostdag(newNode *blockNode) (selectedParentAnticone []*blo // The maximum length of node.blues can be K+1 because // it contains the selected parent. - if dagconfig.KType(len(newNode.blues)) == dag.dagParams.K+1 { + if dagconfig.KType(len(newNode.blues)) == dag.Params.K+1 { break } } diff --git a/blockdag/ghostdag_test.go b/blockdag/ghostdag_test.go index 11ae97ae1..9aa387406 100644 --- a/blockdag/ghostdag_test.go +++ b/blockdag/ghostdag_test.go @@ -294,15 +294,15 @@ func TestBlueAnticoneSizeErrors(t *testing.T) { defer teardownFunc() // Prepare a block chain with size K beginning with the genesis block - currentBlockA := dag.dagParams.GenesisBlock - for i := dagconfig.KType(0); i < dag.dagParams.K; i++ { + currentBlockA := dag.Params.GenesisBlock + for i := dagconfig.KType(0); i < dag.Params.K; i++ { newBlock := prepareAndProcessBlockByParentMsgBlocks(t, dag, currentBlockA) currentBlockA = newBlock } // Prepare another block chain with size K beginning with the genesis block - currentBlockB := dag.dagParams.GenesisBlock - for i := dagconfig.KType(0); i < dag.dagParams.K; i++ { + currentBlockB := dag.Params.GenesisBlock + for i := dagconfig.KType(0); i < dag.Params.K; i++ { newBlock := prepareAndProcessBlockByParentMsgBlocks(t, dag, currentBlockB) currentBlockB = newBlock } @@ -342,8 +342,8 @@ func TestGHOSTDAGErrors(t *testing.T) { defer teardownFunc() // Add two child blocks to the genesis - block1 := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock) - block2 := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock) + block1 := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.Params.GenesisBlock) + block2 := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.Params.GenesisBlock) // Add a child block to the previous two blocks block3 := prepareAndProcessBlockByParentMsgBlocks(t, dag, block1, block2) diff --git a/blockdag/process.go b/blockdag/process.go index 93c8974f2..094bd604b 100644 --- a/blockdag/process.go +++ b/blockdag/process.go @@ -221,7 +221,7 @@ func (dag *BlockDAG) processBlockNoLock(block *util.Block, flags BehaviorFlags) // The number K*2 was chosen since in peace times anticone is limited to K blocks, // while some red block can make it a bit bigger, but much more than that indicates // there might be some problem with the netsync process. - if flags&BFIsSync == BFIsSync && dagconfig.KType(len(dag.orphans)) < dag.dagParams.K*2 { + if flags&BFIsSync == BFIsSync && dagconfig.KType(len(dag.orphans)) < dag.Params.K*2 { log.Debugf("Adding orphan block %s. This is normal part of netsync process", blockHash) } else { log.Infof("Adding orphan block %s", blockHash) diff --git a/blockdag/process_test.go b/blockdag/process_test.go index c4456d9ed..71ea6a9b1 100644 --- a/blockdag/process_test.go +++ b/blockdag/process_test.go @@ -1,11 +1,12 @@ package blockdag import ( - "github.com/kaspanet/kaspad/util" "path/filepath" "testing" "time" + "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/dagconfig" "github.com/kaspanet/kaspad/util/daghash" ) @@ -89,18 +90,18 @@ func TestProcessDelayedBlocks(t *testing.T) { } }() - initialTime := dag1.dagParams.GenesisBlock.Header.Timestamp + initialTime := dag1.Params.GenesisBlock.Header.Timestamp // Here we use a fake time source that returns a timestamp // one hour into the future to make delayedBlock artificially // valid. dag1.timeSource = newFakeTimeSource(initialTime.Add(time.Hour)) - delayedBlock, err := PrepareBlockForTest(dag1, []*daghash.Hash{dag1.dagParams.GenesisBlock.BlockHash()}, nil) + delayedBlock, err := PrepareBlockForTest(dag1, []*daghash.Hash{dag1.Params.GenesisBlock.BlockHash()}, nil) if err != nil { t.Fatalf("error in PrepareBlockForTest: %s", err) } - blockDelay := time.Duration(dag1.dagParams.TimestampDeviationTolerance*uint64(dag1.targetTimePerBlock)+5) * time.Second + blockDelay := time.Duration(dag1.Params.TimestampDeviationTolerance*uint64(dag1.targetTimePerBlock)+5) * time.Second delayedBlock.Header.Timestamp = initialTime.Add(blockDelay) isOrphan, isDelayed, err := dag1.ProcessBlock(util.NewBlock(delayedBlock), BFNoPoWCheck) @@ -177,7 +178,7 @@ func TestProcessDelayedBlocks(t *testing.T) { t.Errorf("dag.IsKnownBlock should return true for a child of a delayed block") } - blockBeforeDelay, err := PrepareBlockForTest(dag2, []*daghash.Hash{dag2.dagParams.GenesisBlock.BlockHash()}, nil) + blockBeforeDelay, err := PrepareBlockForTest(dag2, []*daghash.Hash{dag2.Params.GenesisBlock.BlockHash()}, nil) if err != nil { t.Fatalf("error in PrepareBlockForTest: %s", err) } @@ -210,7 +211,7 @@ func TestProcessDelayedBlocks(t *testing.T) { dag2.timeSource = newFakeTimeSource(initialTime.Add(timeUntilDelayedBlockIsValid)) blockAfterDelay, err := PrepareBlockForTest(dag2, - []*daghash.Hash{dag2.dagParams.GenesisBlock.BlockHash()}, + []*daghash.Hash{dag2.Params.GenesisBlock.BlockHash()}, nil) if err != nil { t.Fatalf("error in PrepareBlockForTest: %s", err) diff --git a/blockdag/reachability.go b/blockdag/reachability.go index 1b69cd4ae..78cc82007 100644 --- a/blockdag/reachability.go +++ b/blockdag/reachability.go @@ -903,7 +903,7 @@ func (rt *reachabilityTree) init(dbContext dbaccess.Context) error { if !dbaccess.IsNotFoundError(err) { return err } - reindexRootHash = rt.dag.dagParams.GenesisHash + reindexRootHash = rt.dag.Params.GenesisHash } // Init the reindex root diff --git a/blockdag/sync_rate.go b/blockdag/sync_rate.go index 12945a44b..56ceb87bc 100644 --- a/blockdag/sync_rate.go +++ b/blockdag/sync_rate.go @@ -52,7 +52,7 @@ func (dag *BlockDAG) IsSyncRateBelowThreshold(maxDeviation float64) bool { return false } - return dag.syncRate() < 1/dag.dagParams.TargetTimePerBlock.Seconds()*maxDeviation + return dag.syncRate() < 1/dag.Params.TargetTimePerBlock.Seconds()*maxDeviation } func (dag *BlockDAG) uptime() time.Duration { diff --git a/blockdag/test_utils.go b/blockdag/test_utils.go index 40ffdc1c3..ad431732a 100644 --- a/blockdag/test_utils.go +++ b/blockdag/test_utils.go @@ -249,7 +249,7 @@ func PrepareBlockForTest(dag *BlockDAG, parentHashes []*daghash.Hash, transactio oldVirtual := SetVirtualForTest(dag, newVirtual) defer SetVirtualForTest(dag, oldVirtual) - OpTrueAddr, err := opTrueAddress(dag.dagParams.Prefix) + OpTrueAddr, err := opTrueAddress(dag.Params.Prefix) if err != nil { return nil, err } diff --git a/blockdag/thresholdstate.go b/blockdag/thresholdstate.go index 6cb5802ff..42de37073 100644 --- a/blockdag/thresholdstate.go +++ b/blockdag/thresholdstate.go @@ -297,11 +297,11 @@ func (dag *BlockDAG) IsDeploymentActive(deploymentID uint32) (bool, error) { // // This function MUST be called with the DAG state lock held (for writes). func (dag *BlockDAG) deploymentState(prevNode *blockNode, deploymentID uint32) (ThresholdState, error) { - if deploymentID > uint32(len(dag.dagParams.Deployments)) { + if deploymentID > uint32(len(dag.Params.Deployments)) { return ThresholdFailed, errors.Errorf("deployment ID %d does not exist", deploymentID) } - deployment := &dag.dagParams.Deployments[deploymentID] + deployment := &dag.Params.Deployments[deploymentID] checker := deploymentChecker{deployment: deployment, dag: dag} cache := &dag.deploymentCaches[deploymentID] @@ -325,8 +325,8 @@ func (dag *BlockDAG) initThresholdCaches() error { return err } } - for id := 0; id < len(dag.dagParams.Deployments); id++ { - deployment := &dag.dagParams.Deployments[id] + for id := 0; id < len(dag.Params.Deployments); id++ { + deployment := &dag.Params.Deployments[id] cache := &dag.deploymentCaches[id] checker := deploymentChecker{deployment: deployment, dag: dag} _, err := dag.thresholdState(prevNode, checker, cache) diff --git a/blockdag/validate.go b/blockdag/validate.go index 252bc3367..1e1062e0c 100644 --- a/blockdag/validate.go +++ b/blockdag/validate.go @@ -265,9 +265,9 @@ func (dag *BlockDAG) checkProofOfWork(header *wire.BlockHeader, flags BehaviorFl } // The target difficulty must be less than the maximum allowed. - if target.Cmp(dag.dagParams.PowMax) > 0 { + if target.Cmp(dag.Params.PowMax) > 0 { str := fmt.Sprintf("block target difficulty of %064x is "+ - "higher than max of %064x", target, dag.dagParams.PowMax) + "higher than max of %064x", target, dag.Params.PowMax) return ruleError(ErrUnexpectedDifficulty, str) } @@ -403,7 +403,7 @@ func (dag *BlockDAG) checkBlockHeaderSanity(block *util.Block, flags BehaviorFla } if len(header.ParentHashes) == 0 { - if !header.BlockHash().IsEqual(dag.dagParams.GenesisHash) { + if !header.BlockHash().IsEqual(dag.Params.GenesisHash) { return 0, ruleError(ErrNoParents, "block has no parents") } } else { @@ -554,7 +554,7 @@ func (dag *BlockDAG) checkBlockTransactionOrder(block *util.Block) error { func (dag *BlockDAG) checkNoNonNativeTransactions(block *util.Block) error { // Disallow non-native/coinbase subnetworks in networks that don't allow them - if !dag.dagParams.EnableNonNativeSubnetworks { + if !dag.Params.EnableNonNativeSubnetworks { transactions := block.Transactions() for _, tx := range transactions { if !(tx.MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) || @@ -939,7 +939,7 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet, for _, tx := range transactions { txFee, err := CheckTransactionInputsAndCalulateFee(tx, block.blueScore, pastUTXO, - dag.dagParams, fastAdd) + dag.Params, fastAdd) if err != nil { return nil, err } diff --git a/blockdag/validate_test.go b/blockdag/validate_test.go index b79390755..8fcfcb220 100644 --- a/blockdag/validate_test.go +++ b/blockdag/validate_test.go @@ -5,12 +5,13 @@ package blockdag import ( - "github.com/kaspanet/kaspad/util/mstime" "math" "path/filepath" "testing" "time" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/pkg/errors" "github.com/kaspanet/kaspad/dagconfig" @@ -554,9 +555,9 @@ func TestValidateParents(t *testing.T) { } defer teardownFunc() - a := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock) + a := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.Params.GenesisBlock) b := prepareAndProcessBlockByParentMsgBlocks(t, dag, a) - c := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.dagParams.GenesisBlock) + c := prepareAndProcessBlockByParentMsgBlocks(t, dag, dag.Params.GenesisBlock) aNode := nodeByMsgBlock(t, dag, a) bNode := nodeByMsgBlock(t, dag, b) diff --git a/blockdag/versionbits.go b/blockdag/versionbits.go index e2f894a90..6f2db5fcc 100644 --- a/blockdag/versionbits.go +++ b/blockdag/versionbits.go @@ -78,7 +78,7 @@ func (c bitConditionChecker) EndTime() uint64 { // // This is part of the thresholdConditionChecker interface implementation. func (c bitConditionChecker) RuleChangeActivationThreshold() uint64 { - return c.dag.dagParams.RuleChangeActivationThreshold + return c.dag.Params.RuleChangeActivationThreshold } // MinerConfirmationWindow is the number of blocks in each threshold state @@ -89,7 +89,7 @@ func (c bitConditionChecker) RuleChangeActivationThreshold() uint64 { // // This is part of the thresholdConditionChecker interface implementation. func (c bitConditionChecker) MinerConfirmationWindow() uint64 { - return c.dag.dagParams.MinerConfirmationWindow + return c.dag.Params.MinerConfirmationWindow } // Condition returns true when the specific bit associated with the checker is @@ -159,7 +159,7 @@ func (c deploymentChecker) EndTime() uint64 { // // This is part of the thresholdConditionChecker interface implementation. func (c deploymentChecker) RuleChangeActivationThreshold() uint64 { - return c.dag.dagParams.RuleChangeActivationThreshold + return c.dag.Params.RuleChangeActivationThreshold } // MinerConfirmationWindow is the number of blocks in each threshold state @@ -170,7 +170,7 @@ func (c deploymentChecker) RuleChangeActivationThreshold() uint64 { // // This is part of the thresholdConditionChecker interface implementation. func (c deploymentChecker) MinerConfirmationWindow() uint64 { - return c.dag.dagParams.MinerConfirmationWindow + return c.dag.Params.MinerConfirmationWindow } // Condition returns true when the specific bit defined by the deployment @@ -198,8 +198,8 @@ func (dag *BlockDAG) calcNextBlockVersion(prevNode *blockNode) (int32, error) { // that is either in the process of being voted on, or locked in for the // activation at the next threshold window change. expectedVersion := uint32(vbTopBits) - for id := 0; id < len(dag.dagParams.Deployments); id++ { - deployment := &dag.dagParams.Deployments[id] + for id := 0; id < len(dag.Params.Deployments); id++ { + deployment := &dag.Params.Deployments[id] cache := &dag.deploymentCaches[id] checker := deploymentChecker{deployment: deployment, dag: dag} state, err := dag.thresholdState(prevNode, checker, cache) diff --git a/kaspad.go b/kaspad.go index 5d33de171..0ab7b30b8 100644 --- a/kaspad.go +++ b/kaspad.go @@ -1,291 +1,162 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - package main import ( - "fmt" - _ "net/http/pprof" - "os" - "path/filepath" - "runtime" - "runtime/pprof" - "strings" - "time" - - "github.com/kaspanet/kaspad/dbaccess" + "sync/atomic" + "github.com/kaspanet/kaspad/blockdag" "github.com/kaspanet/kaspad/blockdag/indexers" "github.com/kaspanet/kaspad/config" - "github.com/kaspanet/kaspad/limits" - "github.com/kaspanet/kaspad/server" + "github.com/kaspanet/kaspad/mempool" + "github.com/kaspanet/kaspad/mining" + "github.com/kaspanet/kaspad/server/rpc" "github.com/kaspanet/kaspad/signal" - "github.com/kaspanet/kaspad/util/fs" - "github.com/kaspanet/kaspad/util/panics" - "github.com/kaspanet/kaspad/util/profiling" - "github.com/kaspanet/kaspad/version" + "github.com/kaspanet/kaspad/txscript" + "github.com/kaspanet/kaspad/util" ) -const ( - // blockDbNamePrefix is the prefix for the block database name. The - // database type is appended to this value to form the full block - // database name. - blockDbNamePrefix = "blocks" -) +// kaspad is a wrapper for all the kaspad services +type kaspad struct { + rpcServer *rpc.Server -var ( - cfg *config.Config -) + started, shutdown int32 +} -// winServiceMain is only invoked on Windows. It detects when kaspad is running -// as a service and reacts accordingly. -var winServiceMain func() (bool, error) - -// kaspadMain is the real main function for kaspad. It is necessary to work -// around the fact that deferred functions do not run when os.Exit() is called. -// The optional serverChan parameter is mainly used by the service code to be -// notified with the server once it is setup so it can gracefully stop it when -// requested from the service control manager. -func kaspadMain(serverChan chan<- *server.Server) error { - interrupt := signal.InterruptListener() - - // Load configuration and parse command line. This function also - // initializes logging and configures it accordingly. - err := config.LoadAndSetActiveConfig() - if err != nil { - return err +// start launches all the kaspad services. +func (s *kaspad) start() { + // Already started? + if atomic.AddInt32(&s.started, 1) != 1 { + return } - cfg = config.ActiveConfig() - defer panics.HandlePanic(kasdLog, nil) - // Get a channel that will be closed when a shutdown signal has been - // triggered either from an OS signal such as SIGINT (Ctrl+C) or from - // another subsystem such as the RPC server. - defer kasdLog.Info("Shutdown complete") + log.Trace("Starting kaspad") - // Show version at startup. - kasdLog.Infof("Version %s", version.Version()) + cfg := config.ActiveConfig() - // Enable http profiling server if requested. - if cfg.Profile != "" { + if !cfg.DisableRPC { + s.rpcServer.Start() + } +} + +// stop gracefully shuts down all the kaspad services. +func (s *kaspad) stop() error { + // Make sure this only happens once. + if atomic.AddInt32(&s.shutdown, 1) != 1 { + log.Infof("Kaspad is already in the process of shutting down") + return nil + } + + log.Warnf("Kaspad shutting down") + + // Shutdown the RPC server if it's not disabled. + if !config.ActiveConfig().DisableRPC { + s.rpcServer.Stop() + } + + return nil +} + +// newKaspad returns a new kaspad instance configured to listen on addr for the +// kaspa network type specified by dagParams. Use start to begin accepting +// connections from peers. +func newKaspad(listenAddrs []string, interrupt <-chan struct{}) (*kaspad, error) { + indexManager, acceptanceIndex := setupIndexes() + + sigCache := txscript.NewSigCache(config.ActiveConfig().SigCacheMaxSize) + + // Create a new block DAG instance with the appropriate configuration. + dag, err := setupDAG(interrupt, sigCache, indexManager) + if err != nil { + return nil, err + } + + txMempool := setupMempool(dag, sigCache) + + rpcServer, err := setupRPC(dag, txMempool, sigCache, acceptanceIndex) + if err != nil { + return nil, err + } + + return &kaspad{ + rpcServer: rpcServer, + }, nil +} + +func setupDAG(interrupt <-chan struct{}, sigCache *txscript.SigCache, indexManager blockdag.IndexManager) (*blockdag.BlockDAG, error) { + dag, err := blockdag.New(&blockdag.Config{ + Interrupt: interrupt, + DAGParams: config.ActiveConfig().NetParams(), + TimeSource: blockdag.NewTimeSource(), + SigCache: sigCache, + IndexManager: indexManager, + SubnetworkID: config.ActiveConfig().SubnetworkID, + }) + return dag, err +} + +func setupIndexes() (blockdag.IndexManager, *indexers.AcceptanceIndex) { + // Create indexes if needed. + var indexes []indexers.Indexer + var acceptanceIndex *indexers.AcceptanceIndex + if config.ActiveConfig().AcceptanceIndex { + log.Info("acceptance index is enabled") + indexes = append(indexes, acceptanceIndex) + } + + // Create an index manager if any of the optional indexes are enabled. + if len(indexes) < 0 { + return nil, nil + } + indexManager := indexers.NewManager(indexes) + return indexManager, acceptanceIndex +} + +func setupMempool(dag *blockdag.BlockDAG, sigCache *txscript.SigCache) *mempool.TxPool { + mempoolConfig := mempool.Config{ + Policy: mempool.Policy{ + AcceptNonStd: config.ActiveConfig().RelayNonStd, + MaxOrphanTxs: config.ActiveConfig().MaxOrphanTxs, + MaxOrphanTxSize: config.DefaultMaxOrphanTxSize, + MinRelayTxFee: config.ActiveConfig().MinRelayTxFee, + MaxTxVersion: 1, + }, + CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) { + return dag.CalcSequenceLockNoLock(tx, utxoSet, true) + }, + IsDeploymentActive: dag.IsDeploymentActive, + SigCache: sigCache, + DAG: dag, + } + + return mempool.New(&mempoolConfig) +} + +func setupRPC(dag *blockdag.BlockDAG, txMempool *mempool.TxPool, sigCache *txscript.SigCache, + acceptanceIndex *indexers.AcceptanceIndex) (*rpc.Server, error) { + cfg := config.ActiveConfig() + if !cfg.DisableRPC { + policy := mining.Policy{ + BlockMaxMass: cfg.BlockMaxMass, + } + blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, txMempool, dag, sigCache) + + rpcServer, err := rpc.NewRPCServer(dag, txMempool, acceptanceIndex, blockTemplateGenerator) + if err != nil { + return nil, err + } + + // Signal process shutdown when the RPC server requests it. spawn(func() { - profiling.Start(cfg.Profile, kasdLog) + <-rpcServer.RequestedProcessShutdown() + signal.ShutdownRequestChannel <- struct{}{} }) + + return rpcServer, nil } - - // Write cpu profile if requested. - if cfg.CPUProfile != "" { - f, err := os.Create(cfg.CPUProfile) - if err != nil { - kasdLog.Errorf("Unable to create cpu profile: %s", err) - return err - } - pprof.StartCPUProfile(f) - defer f.Close() - defer pprof.StopCPUProfile() - } - - // Perform upgrades to kaspad as new versions require it. - if err := doUpgrades(); err != nil { - kasdLog.Errorf("%s", err) - return err - } - - // Return now if an interrupt signal was triggered. - if signal.InterruptRequested(interrupt) { - return nil - } - - if cfg.ResetDatabase { - err := removeDatabase() - if err != nil { - kasdLog.Errorf("%s", err) - return err - } - } - - // Open the database - err = openDB() - if err != nil { - kasdLog.Errorf("%s", err) - return err - } - defer func() { - kasdLog.Infof("Gracefully shutting down the database...") - err := dbaccess.Close() - if err != nil { - kasdLog.Errorf("Failed to close the database: %s", err) - } - }() - - // Return now if an interrupt signal was triggered. - if signal.InterruptRequested(interrupt) { - return nil - } - - // Drop indexes and exit if requested. - if cfg.DropAcceptanceIndex { - if err := indexers.DropAcceptanceIndex(); err != nil { - kasdLog.Errorf("%s", err) - return err - } - - return nil - } - - // Create server and start it. - server, err := server.NewServer(cfg.Listeners, config.ActiveConfig().NetParams(), - interrupt) - if err != nil { - kasdLog.Errorf("Unable to start server on %s: %+v", - strings.Join(cfg.Listeners, ", "), err) - return err - } - defer func() { - kasdLog.Infof("Gracefully shutting down the server...") - server.Stop() - - shutdownDone := make(chan struct{}) - go func() { - server.WaitForShutdown() - shutdownDone <- struct{}{} - }() - - const shutdownTimeout = 2 * time.Minute - - select { - case <-shutdownDone: - case <-time.After(shutdownTimeout): - kasdLog.Criticalf("Graceful shutdown timed out %s. Terminating...", shutdownTimeout) - } - srvrLog.Infof("Server shutdown complete") - }() - server.Start() - if serverChan != nil { - serverChan <- server - } - - // Wait until the interrupt signal is received from an OS signal or - // shutdown is requested through one of the subsystems such as the RPC - // server. - <-interrupt - return nil + return nil, nil } -func removeDatabase() error { - dbPath := blockDbPath(cfg.DbType) - return os.RemoveAll(dbPath) -} - -// removeRegressionDB removes the existing regression test database if running -// in regression test mode and it already exists. -func removeRegressionDB(dbPath string) error { - // Don't do anything if not in regression test mode. - if !cfg.RegressionTest { - return nil - } - - // Remove the old regression test database if it already exists. - fi, err := os.Stat(dbPath) - if err == nil { - kasdLog.Infof("Removing regression test database from '%s'", dbPath) - if fi.IsDir() { - err := os.RemoveAll(dbPath) - if err != nil { - return err - } - } else { - err := os.Remove(dbPath) - if err != nil { - return err - } - } - } - - return nil -} - -// dbPath returns the path to the block database given a database type. -func blockDbPath(dbType string) string { - // The database name is based on the database type. - dbName := blockDbNamePrefix + "_" + dbType - if dbType == "sqlite" { - dbName = dbName + ".db" - } - dbPath := filepath.Join(cfg.DataDir, dbName) - return dbPath -} - -// warnMultipleDBs shows a warning if multiple block database types are detected. -// This is not a situation most users want. It is handy for development however -// to support multiple side-by-side databases. -func warnMultipleDBs() { - // This is intentionally not using the known db types which depend - // on the database types compiled into the binary since we want to - // detect legacy db types as well. - dbTypes := []string{"ffldb", "leveldb", "sqlite"} - duplicateDbPaths := make([]string, 0, len(dbTypes)-1) - for _, dbType := range dbTypes { - if dbType == cfg.DbType { - continue - } - - // Store db path as a duplicate db if it exists. - dbPath := blockDbPath(dbType) - if fs.FileExists(dbPath) { - duplicateDbPaths = append(duplicateDbPaths, dbPath) - } - } - - // Warn if there are extra databases. - if len(duplicateDbPaths) > 0 { - selectedDbPath := blockDbPath(cfg.DbType) - kasdLog.Warnf("WARNING: There are multiple block DAG databases "+ - "using different database types.\nYou probably don't "+ - "want to waste disk space by having more than one.\n"+ - "Your current database is located at [%s].\nThe "+ - "additional database is located at %s", selectedDbPath, - strings.Join(duplicateDbPaths, ", ")) - } -} - -func openDB() error { - dbPath := filepath.Join(cfg.DataDir, "db") - kasdLog.Infof("Loading database from '%s'", dbPath) - err := dbaccess.Open(dbPath) - if err != nil { - return err - } - return nil -} - -func main() { - // Use all processor cores. - runtime.GOMAXPROCS(runtime.NumCPU()) - - // Up some limits. - if err := limits.SetLimits(); err != nil { - fmt.Fprintf(os.Stderr, "failed to set limits: %s\n", err) - os.Exit(1) - } - - // Call serviceMain on Windows to handle running as a service. When - // the return isService flag is true, exit now since we ran as a - // service. Otherwise, just fall through to normal operation. - if runtime.GOOS == "windows" { - isService, err := winServiceMain() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - if isService { - os.Exit(0) - } - } - - // Work around defer not working after os.Exit() - if err := kaspadMain(nil); err != nil { - os.Exit(1) - } +// WaitForShutdown blocks until the main listener and peer handlers are stopped. +func (s *kaspad) WaitForShutdown() { + // TODO(libp2p) + // s.p2pServer.WaitForShutdown() } diff --git a/log.go b/log.go index 9c3691de4..7cdf58c98 100644 --- a/log.go +++ b/log.go @@ -10,6 +10,5 @@ import ( "github.com/kaspanet/kaspad/util/panics" ) -var kasdLog, _ = logger.Get(logger.SubsystemTags.KASD) -var spawn = panics.GoroutineWrapperFunc(kasdLog) -var srvrLog, _ = logger.Get(logger.SubsystemTags.SRVR) +var log, _ = logger.Get(logger.SubsystemTags.KASD) +var spawn = panics.GoroutineWrapperFunc(log) diff --git a/main.go b/main.go new file mode 100644 index 000000000..b67d600f4 --- /dev/null +++ b/main.go @@ -0,0 +1,253 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + _ "net/http/pprof" + "os" + "path/filepath" + "runtime" + "runtime/pprof" + "strings" + "time" + + "github.com/kaspanet/kaspad/dbaccess" + + "github.com/kaspanet/kaspad/blockdag/indexers" + "github.com/kaspanet/kaspad/config" + "github.com/kaspanet/kaspad/limits" + "github.com/kaspanet/kaspad/signal" + "github.com/kaspanet/kaspad/util/panics" + "github.com/kaspanet/kaspad/util/profiling" + "github.com/kaspanet/kaspad/version" +) + +const ( + // blockDbNamePrefix is the prefix for the block database name. The + // database type is appended to this value to form the full block + // database name. + blockDbNamePrefix = "blocks" +) + +var ( + cfg *config.Config +) + +// winServiceMain is only invoked on Windows. It detects when kaspad is running +// as a service and reacts accordingly. +var winServiceMain func() (bool, error) + +// kaspadMain is the real main function for kaspad. It is necessary to work +// around the fact that deferred functions do not run when os.Exit() is called. +// The optional startedChan writes once all services has started. +func kaspadMain(startedChan chan<- struct{}) error { + interrupt := signal.InterruptListener() + + // Load configuration and parse command line. This function also + // initializes logging and configures it accordingly. + err := config.LoadAndSetActiveConfig() + if err != nil { + return err + } + cfg = config.ActiveConfig() + defer panics.HandlePanic(log, nil) + + // Get a channel that will be closed when a shutdown signal has been + // triggered either from an OS signal such as SIGINT (Ctrl+C) or from + // another subsystem such as the RPC server. + defer log.Info("Shutdown complete") + + // Show version at startup. + log.Infof("Version %s", version.Version()) + + // Enable http profiling server if requested. + if cfg.Profile != "" { + spawn(func() { + profiling.Start(cfg.Profile, log) + }) + } + + // Write cpu profile if requested. + if cfg.CPUProfile != "" { + f, err := os.Create(cfg.CPUProfile) + if err != nil { + log.Errorf("Unable to create cpu profile: %s", err) + return err + } + pprof.StartCPUProfile(f) + defer f.Close() + defer pprof.StopCPUProfile() + } + + // Perform upgrades to kaspad as new versions require it. + if err := doUpgrades(); err != nil { + log.Errorf("%s", err) + return err + } + + // Return now if an interrupt signal was triggered. + if signal.InterruptRequested(interrupt) { + return nil + } + + if cfg.ResetDatabase { + err := removeDatabase() + if err != nil { + log.Errorf("%s", err) + return err + } + } + + // Open the database + err = openDB() + if err != nil { + log.Errorf("%s", err) + return err + } + defer func() { + log.Infof("Gracefully shutting down the database...") + err := dbaccess.Close() + if err != nil { + log.Errorf("Failed to close the database: %s", err) + } + }() + + // Return now if an interrupt signal was triggered. + if signal.InterruptRequested(interrupt) { + return nil + } + + // Drop indexes and exit if requested. + if cfg.DropAcceptanceIndex { + if err := indexers.DropAcceptanceIndex(); err != nil { + log.Errorf("%s", err) + return err + } + + return nil + } + + // Create kaspad and start it. + kaspad, err := newKaspad(cfg.Listeners, interrupt) + if err != nil { + log.Errorf("Unable to start kaspad on %s: %+v", + strings.Join(cfg.Listeners, ", "), err) + return err + } + defer func() { + log.Infof("Gracefully shutting down kaspad...") + kaspad.stop() + + shutdownDone := make(chan struct{}) + go func() { + kaspad.WaitForShutdown() + shutdownDone <- struct{}{} + }() + + const shutdownTimeout = 2 * time.Minute + + select { + case <-shutdownDone: + case <-time.After(shutdownTimeout): + log.Criticalf("Graceful shutdown timed out %s. Terminating...", shutdownTimeout) + } + log.Infof("Kaspad shutdown complete") + }() + kaspad.start() + if startedChan != nil { + startedChan <- struct{}{} + } + + // Wait until the interrupt signal is received from an OS signal or + // shutdown is requested through one of the subsystems such as the RPC + // server. + <-interrupt + return nil +} + +func removeDatabase() error { + dbPath := blockDbPath(cfg.DbType) + return os.RemoveAll(dbPath) +} + +// removeRegressionDB removes the existing regression test database if running +// in regression test mode and it already exists. +func removeRegressionDB(dbPath string) error { + // Don't do anything if not in regression test mode. + if !cfg.RegressionTest { + return nil + } + + // Remove the old regression test database if it already exists. + fi, err := os.Stat(dbPath) + if err == nil { + log.Infof("Removing regression test database from '%s'", dbPath) + if fi.IsDir() { + err := os.RemoveAll(dbPath) + if err != nil { + return err + } + } else { + err := os.Remove(dbPath) + if err != nil { + return err + } + } + } + + return nil +} + +// dbPath returns the path to the block database given a database type. +func blockDbPath(dbType string) string { + // The database name is based on the database type. + dbName := blockDbNamePrefix + "_" + dbType + if dbType == "sqlite" { + dbName = dbName + ".db" + } + dbPath := filepath.Join(cfg.DataDir, dbName) + return dbPath +} + +func openDB() error { + dbPath := filepath.Join(cfg.DataDir, "db") + log.Infof("Loading database from '%s'", dbPath) + err := dbaccess.Open(dbPath) + if err != nil { + return err + } + return nil +} + +func main() { + // Use all processor cores. + runtime.GOMAXPROCS(runtime.NumCPU()) + + // Up some limits. + if err := limits.SetLimits(); err != nil { + fmt.Fprintf(os.Stderr, "failed to set limits: %s\n", err) + os.Exit(1) + } + + // Call serviceMain on Windows to handle running as a service. When + // the return isService flag is true, exit now since we ran as a + // service. Otherwise, just fall through to normal operation. + if runtime.GOOS == "windows" { + isService, err := winServiceMain() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if isService { + os.Exit(0) + } + } + + // Work around defer not working after os.Exit() + if err := kaspadMain(nil); err != nil { + os.Exit(1) + } +} diff --git a/mempool/mempool.go b/mempool/mempool.go index ffcbda525..c742aa929 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -7,14 +7,14 @@ package mempool import ( "container/list" "fmt" - "github.com/kaspanet/kaspad/util/mstime" - "github.com/pkg/errors" "sync" "sync/atomic" "time" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/pkg/errors" + "github.com/kaspanet/kaspad/blockdag" - "github.com/kaspanet/kaspad/dagconfig" "github.com/kaspanet/kaspad/logger" "github.com/kaspanet/kaspad/mining" "github.com/kaspanet/kaspad/rpcmodel" @@ -54,15 +54,6 @@ type Config struct { // to policy. Policy Policy - // DAGParams identifies which DAG parameters the txpool is - // associated with. - DAGParams *dagconfig.Params - - // MedianTimePast defines the function to use in order to access the - // median time past calculated from the point-of-view of the current - // selected tip. - MedianTimePast func() mstime.Time - // CalcSequenceLockNoLock defines the function to use in order to generate // the current sequence lock for the given transaction using the passed // utxo set. @@ -825,7 +816,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, rejectDupOrphans bool) ([] } // Disallow non-native/coinbase subnetworks in networks that don't allow them - if !mp.cfg.DAGParams.EnableNonNativeSubnetworks { + if !mp.cfg.DAG.Params.EnableNonNativeSubnetworks { if !(tx.MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) || tx.MsgTx().SubnetworkID.IsEqual(subnetworkid.SubnetworkIDCoinbase)) { return nil, nil, txRuleError(wire.RejectInvalid, "non-native/coinbase subnetworks are not allowed") @@ -876,7 +867,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, rejectDupOrphans bool) ([] // the transaction as though it was mined on top of the current tips nextBlockBlueScore := mp.cfg.DAG.VirtualBlueScore() - medianTimePast := mp.cfg.MedianTimePast() + medianTimePast := mp.cfg.DAG.CalcPastMedianTime() // Don't allow non-standard transactions if the network parameters // forbid their acceptance. @@ -978,7 +969,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, rejectDupOrphans bool) ([] // Also returns the fees associated with the transaction which will be // used later. txFee, err := blockdag.CheckTransactionInputsAndCalulateFee(tx, nextBlockBlueScore, - mp.mpUTXOSet, mp.cfg.DAGParams, false) + mp.mpUTXOSet, mp.cfg.DAG.Params, false) if err != nil { var dagRuleErr blockdag.RuleError if ok := errors.As(err, &dagRuleErr); ok { diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 95b4678cd..0f3c5ba91 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -7,8 +7,6 @@ package mempool import ( "bytes" "fmt" - "github.com/kaspanet/kaspad/util/mstime" - "github.com/pkg/errors" "math" "reflect" "runtime" @@ -16,6 +14,9 @@ import ( "sync" "testing" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/pkg/errors" + "github.com/kaspanet/kaspad/util/subnetworkid" "github.com/kaspanet/kaspad/util/testtools" @@ -254,7 +255,7 @@ func (tc *testContext) mineTransactions(transactions []*util.Tx, numberOfBlocks if i == 0 { blockTxs = msgTxs } - block, err := mining.PrepareBlockForTest(tc.harness.txPool.cfg.DAG, tc.harness.txPool.cfg.DAGParams, tc.harness.txPool.cfg.DAG.TipHashes(), blockTxs, false) + block, err := mining.PrepareBlockForTest(tc.harness.txPool.cfg.DAG, tc.harness.txPool.cfg.DAG.TipHashes(), blockTxs, false) if err != nil { tc.t.Fatalf("PrepareBlockForTest: %s", err) } @@ -347,8 +348,6 @@ func newPoolHarness(t *testing.T, dagParams *dagconfig.Params, numOutputs uint32 MinRelayTxFee: 1000, // 1 sompi per byte MaxTxVersion: 1, }, - DAGParams: ¶ms, - MedianTimePast: fDAG.MedianTimePast, CalcSequenceLockNoLock: calcSequenceLock, SigCache: nil, }), diff --git a/mining/mining.go b/mining/mining.go index 79cb0f1a1..07ff55472 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -9,7 +9,6 @@ import ( "github.com/pkg/errors" "github.com/kaspanet/kaspad/blockdag" - "github.com/kaspanet/kaspad/dagconfig" "github.com/kaspanet/kaspad/txscript" "github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util/daghash" @@ -86,12 +85,10 @@ type BlockTemplate struct { // It also houses additional state required in order to ensure the templates // are built on top of the current DAG and adhere to the consensus rules. type BlkTmplGenerator struct { - policy *Policy - dagParams *dagconfig.Params - txSource TxSource - dag *blockdag.BlockDAG - timeSource blockdag.TimeSource - sigCache *txscript.SigCache + policy *Policy + txSource TxSource + dag *blockdag.BlockDAG + sigCache *txscript.SigCache } // NewBlkTmplGenerator returns a new block template generator for the given @@ -100,18 +97,15 @@ type BlkTmplGenerator struct { // The additional state-related fields are required in order to ensure the // templates are built on top of the current DAG and adhere to the // consensus rules. -func NewBlkTmplGenerator(policy *Policy, params *dagconfig.Params, +func NewBlkTmplGenerator(policy *Policy, txSource TxSource, dag *blockdag.BlockDAG, - timeSource blockdag.TimeSource, sigCache *txscript.SigCache) *BlkTmplGenerator { return &BlkTmplGenerator{ - policy: policy, - dagParams: params, - txSource: txSource, - dag: dag, - timeSource: timeSource, - sigCache: sigCache, + policy: policy, + txSource: txSource, + dag: dag, + sigCache: sigCache, } } diff --git a/mining/test_utils.go b/mining/test_utils.go index 6a166caac..06d96ce39 100644 --- a/mining/test_utils.go +++ b/mining/test_utils.go @@ -3,7 +3,6 @@ package mining // This file functions are not considered safe for regular use, and should be used for test purposes only. import ( - "github.com/kaspanet/kaspad/dagconfig" "github.com/kaspanet/kaspad/util/mstime" "github.com/pkg/errors" @@ -37,7 +36,9 @@ func (txs *fakeTxSource) HaveTransaction(txID *daghash.TxID) bool { } // PrepareBlockForTest generates a block with the proper merkle roots, coinbase transaction etc. This function is used for test purposes only -func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, parentHashes []*daghash.Hash, transactions []*wire.MsgTx, forceTransactions bool) (*wire.MsgBlock, error) { +func PrepareBlockForTest(dag *blockdag.BlockDAG, parentHashes []*daghash.Hash, transactions []*wire.MsgTx, forceTransactions bool, +) (*wire.MsgBlock, error) { + newVirtual, err := blockdag.GetVirtualFromParentsForTest(dag, parentHashes) if err != nil { return nil, err @@ -59,10 +60,9 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren } } - blockTemplateGenerator := NewBlkTmplGenerator(&policy, - params, txSource, dag, blockdag.NewTimeSource(), txscript.NewSigCache(100000)) + blockTemplateGenerator := NewBlkTmplGenerator(&policy, txSource, dag, txscript.NewSigCache(100000)) - OpTrueAddr, err := OpTrueAddress(params.Prefix) + OpTrueAddr, err := OpTrueAddress(dag.Params.Prefix) if err != nil { return nil, err } diff --git a/mining/txselection.go b/mining/txselection.go index 67bbf4721..2a9561205 100644 --- a/mining/txselection.go +++ b/mining/txselection.go @@ -1,12 +1,13 @@ package mining import ( - "github.com/kaspanet/kaspad/blockdag" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/subnetworkid" "math" "math/rand" "sort" + + "github.com/kaspanet/kaspad/blockdag" + "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/util/subnetworkid" ) const ( @@ -136,7 +137,7 @@ func (g *BlkTmplGenerator) collectCandidatesTxs(sourceTxs []*TxDesc) []*candidat // A block can't contain non-finalized transactions. if !blockdag.IsFinalizedTransaction(tx, nextBlockBlueScore, - g.timeSource.Now()) { + g.dag.Now()) { log.Debugf("Skipping non-finalized tx %s", tx.ID()) continue } diff --git a/server/log.go b/server/log.go deleted file mode 100644 index 38b666d2e..000000000 --- a/server/log.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package server - -import ( - "github.com/kaspanet/kaspad/logger" - "github.com/kaspanet/kaspad/util/panics" -) - -var ( - log, _ = logger.Get(logger.SubsystemTags.SRVR) - spawn = panics.GoroutineWrapperFunc(log) -) diff --git a/server/p2p/p2p.go b/server/p2p/p2p.go index 1ee6fb0b1..b71cb5d3e 100644 --- a/server/p2p/p2p.go +++ b/server/p2p/p2p.go @@ -9,7 +9,6 @@ import ( "crypto/rand" "encoding/binary" "fmt" - "github.com/kaspanet/kaspad/util/mstime" "math" "net" "runtime" @@ -217,8 +216,8 @@ type Server struct { DAGParams *dagconfig.Params AddrManager *addrmgr.AddrManager connManager *connmgr.ConnManager - SigCache *txscript.SigCache SyncManager *netsync.SyncManager + SigCache *txscript.SigCache DAG *blockdag.BlockDAG TxMemPool *mempool.TxPool @@ -1540,8 +1539,6 @@ func NewServer(listenAddrs []string, dagParams *dagconfig.Params, interrupt <-ch MinRelayTxFee: config.ActiveConfig().MinRelayTxFee, MaxTxVersion: 1, }, - DAGParams: dagParams, - MedianTimePast: func() mstime.Time { return s.DAG.CalcPastMedianTime() }, CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) { return s.DAG.CalcSequenceLockNoLock(tx, utxoSet, true) }, diff --git a/server/rpc/handle_get_block_template.go b/server/rpc/handle_get_block_template.go index e3962f59a..511b52f07 100644 --- a/server/rpc/handle_get_block_template.go +++ b/server/rpc/handle_get_block_template.go @@ -4,12 +4,13 @@ import ( "bytes" "encoding/hex" "fmt" - "github.com/kaspanet/kaspad/util/mstime" "strconv" "strings" "sync" "time" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/kaspanet/kaspad/blockdag" "github.com/kaspanet/kaspad/config" "github.com/kaspanet/kaspad/mining" @@ -61,16 +62,14 @@ type gbtWorkState struct { minTimestamp mstime.Time template *mining.BlockTemplate notifyMap map[string]map[int64]chan struct{} - timeSource blockdag.TimeSource payAddress util.Address } // newGbtWorkState returns a new instance of a gbtWorkState with all internal // fields initialized and ready to use. -func newGbtWorkState(timeSource blockdag.TimeSource) *gbtWorkState { +func newGbtWorkState() *gbtWorkState { return &gbtWorkState{ - notifyMap: make(map[string]map[int64]chan struct{}), - timeSource: timeSource, + notifyMap: make(map[string]map[int64]chan struct{}), } } @@ -636,7 +635,7 @@ func (state *gbtWorkState) blockTemplateResult(s *Server) (*rpcmodel.GetBlockTem template := state.template msgBlock := template.Block header := &msgBlock.Header - adjustedTime := state.timeSource.Now() + adjustedTime := dag.Now() maxTime := adjustedTime.Add(time.Millisecond * time.Duration(dag.TimestampDeviationTolerance)) if header.Timestamp.After(maxTime) { return nil, &rpcmodel.RPCError{ diff --git a/server/rpc/rpcserver.go b/server/rpc/rpcserver.go index 7e6193a81..d07214cc8 100644 --- a/server/rpc/rpcserver.go +++ b/server/rpc/rpcserver.go @@ -12,7 +12,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/kaspanet/kaspad/addrmgr" "io" "io/ioutil" "math/rand" @@ -23,6 +22,10 @@ import ( "sync/atomic" "time" + "github.com/kaspanet/kaspad/util/mstime" + + "github.com/kaspanet/kaspad/addrmgr" + "github.com/pkg/errors" "github.com/btcsuite/websocket" @@ -34,11 +37,11 @@ import ( "github.com/kaspanet/kaspad/mining" "github.com/kaspanet/kaspad/peer" "github.com/kaspanet/kaspad/rpcmodel" - "github.com/kaspanet/kaspad/server/p2p" "github.com/kaspanet/kaspad/server/serverutils" "github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util/daghash" "github.com/kaspanet/kaspad/util/fs" + "github.com/kaspanet/kaspad/util/network" "github.com/kaspanet/kaspad/wire" ) @@ -769,9 +772,8 @@ type rpcserverConfig struct { // These fields allow the RPC server to interface with the local block // DAG data and state. - TimeSource blockdag.TimeSource - DAG *blockdag.BlockDAG - DAGParams *dagconfig.Params + DAG *blockdag.BlockDAG + DAGParams *dagconfig.Params // TxMemPool defines the transaction memory pool to interact with. TxMemPool *mempool.TxPool @@ -821,7 +823,7 @@ func setupRPCListeners() ([]net.Listener, error) { } } - netAddrs, err := p2p.ParseListeners(config.ActiveConfig().RPCListeners) + netAddrs, err := network.ParseListeners(config.ActiveConfig().RPCListeners) if err != nil { return nil, err } @@ -841,10 +843,10 @@ func setupRPCListeners() ([]net.Listener, error) { // NewRPCServer returns a new instance of the rpcServer struct. func NewRPCServer( - startupTime int64, - p2pServer *p2p.Server, + dag *blockdag.BlockDAG, + txMempool *mempool.TxPool, + acceptanceIndex *indexers.AcceptanceIndex, blockTemplateGenerator *mining.BlkTmplGenerator, - ) (*Server, error) { // Setup listeners for the configured RPC listen addresses and // TLS settings. @@ -857,21 +859,17 @@ func NewRPCServer( } cfg := &rpcserverConfig{ Listeners: rpcListeners, - StartupTime: startupTime, - ConnMgr: &rpcConnManager{p2pServer}, - SyncMgr: &rpcSyncMgr{p2pServer, p2pServer.SyncManager}, - addressManager: p2pServer.AddrManager, - TimeSource: p2pServer.TimeSource, - DAGParams: p2pServer.DAGParams, - TxMemPool: p2pServer.TxMemPool, + StartupTime: mstime.Now().UnixMilliseconds(), + DAGParams: dag.Params, + TxMemPool: txMempool, Generator: blockTemplateGenerator, - AcceptanceIndex: p2pServer.AcceptanceIndex, - DAG: p2pServer.DAG, + AcceptanceIndex: acceptanceIndex, + DAG: dag, } rpc := Server{ cfg: *cfg, statusLines: make(map[int]string), - gbtWorkState: newGbtWorkState(cfg.TimeSource), + gbtWorkState: newGbtWorkState(), helpCacher: newHelpCacher(), requestProcessShutdown: make(chan struct{}), quit: make(chan int), diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 54984393d..000000000 --- a/server/server.go +++ /dev/null @@ -1,118 +0,0 @@ -package server - -import ( - "github.com/kaspanet/kaspad/util/mstime" - "sync/atomic" - - "github.com/kaspanet/kaspad/config" - "github.com/kaspanet/kaspad/dagconfig" - "github.com/kaspanet/kaspad/mempool" - "github.com/kaspanet/kaspad/mining" - "github.com/kaspanet/kaspad/server/p2p" - "github.com/kaspanet/kaspad/server/rpc" - "github.com/kaspanet/kaspad/signal" -) - -// Server is a wrapper for p2p server and rpc server -type Server struct { - rpcServer *rpc.Server - p2pServer *p2p.Server - startupTime int64 - - started, shutdown int32 -} - -// Start begins accepting connections from peers. -func (s *Server) Start() { - // Already started? - if atomic.AddInt32(&s.started, 1) != 1 { - return - } - - log.Trace("Starting server") - - // Server startup time. Used for the uptime command for uptime calculation. - s.startupTime = mstime.Now().UnixMilliseconds() - - s.p2pServer.Start() - - cfg := config.ActiveConfig() - - if !cfg.DisableRPC { - s.rpcServer.Start() - } -} - -// Stop gracefully shuts down the server by stopping and disconnecting all -// peers and the main listener. -func (s *Server) Stop() error { - // Make sure this only happens once. - if atomic.AddInt32(&s.shutdown, 1) != 1 { - log.Infof("Server is already in the process of shutting down") - return nil - } - - log.Warnf("Server shutting down") - - s.p2pServer.Stop() - - // Shutdown the RPC server if it's not disabled. - if !config.ActiveConfig().DisableRPC { - s.rpcServer.Stop() - } - - return nil -} - -// NewServer returns a new kaspad server configured to listen on addr for the -// kaspa network type specified by dagParams. Use start to begin accepting -// connections from peers. -func NewServer(listenAddrs []string, dagParams *dagconfig.Params, interrupt <-chan struct{}) (*Server, error) { - s := &Server{} - var err error - notifyNewTransactions := func(txns []*mempool.TxDesc) { - // Notify both websocket and getblocktemplate long poll clients of all - // newly accepted transactions. - if s.rpcServer != nil { - s.rpcServer.NotifyNewTransactions(txns) - } - } - s.p2pServer, err = p2p.NewServer(listenAddrs, dagParams, interrupt, notifyNewTransactions) - if err != nil { - return nil, err - } - - cfg := config.ActiveConfig() - - // Create the mining policy and block template generator based on the - // configuration options. - policy := mining.Policy{ - BlockMaxMass: cfg.BlockMaxMass, - } - blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, - s.p2pServer.DAGParams, s.p2pServer.TxMemPool, s.p2pServer.DAG, s.p2pServer.TimeSource, s.p2pServer.SigCache) - - if !cfg.DisableRPC { - s.rpcServer, err = rpc.NewRPCServer( - s.startupTime, - s.p2pServer, - blockTemplateGenerator, - ) - if err != nil { - return nil, err - } - - // Signal process shutdown when the RPC server requests it. - spawn(func() { - <-s.rpcServer.RequestedProcessShutdown() - signal.ShutdownRequestChannel <- struct{}{} - }) - } - - return s, nil -} - -// WaitForShutdown blocks until the main listener and peer handlers are stopped. -func (s *Server) WaitForShutdown() { - s.p2pServer.WaitForShutdown() -} diff --git a/service_windows.go b/service_windows.go index 7ea0fc7c3..bf22ff790 100644 --- a/service_windows.go +++ b/service_windows.go @@ -6,16 +6,16 @@ package main import ( "fmt" - "github.com/pkg/errors" "os" "path/filepath" "time" + "github.com/pkg/errors" + "github.com/btcsuite/winsvc/eventlog" "github.com/btcsuite/winsvc/mgr" "github.com/btcsuite/winsvc/svc" "github.com/kaspanet/kaspad/config" - "github.com/kaspanet/kaspad/server" "github.com/kaspanet/kaspad/signal" "github.com/kaspanet/kaspad/version" ) @@ -37,9 +37,9 @@ const ( // elog is used to send messages to the Windows event log. var elog *eventlog.Log -// logServiceStartOfDay logs information about kaspad when the main server has +// logServiceStart logs information about kaspad when the main server has // been started to the Windows event log. -func logServiceStartOfDay() { +func logServiceStart() { var message string message += fmt.Sprintf("Version %s\n", version.Version()) message += fmt.Sprintf("Configuration directory: %s\n", config.DefaultHomeDir) @@ -64,12 +64,12 @@ func (s *kaspadService) Execute(args []string, r <-chan svc.ChangeRequest, chang // Start kaspadMain in a separate goroutine so the service can start // quickly. Shutdown (along with a potential error) is reported via - // doneChan. serverChan is notified with the main server instance once - // it is started so it can be gracefully stopped. + // doneChan. startedChan is notified once kaspad is started so this can + // be properly logged doneChan := make(chan error) - serverChan := make(chan *server.Server) + startedChan := make(chan struct{}) spawn(func() { - err := kaspadMain(serverChan) + err := kaspadMain(startedChan) doneChan <- err }) @@ -96,8 +96,8 @@ loop: "request #%d.", c)) } - case <-serverChan: - logServiceStartOfDay() + case <-startedChan: + logServiceStart() case err := <-doneChan: if err != nil { diff --git a/util/network/parse_listeners.go b/util/network/parse_listeners.go new file mode 100644 index 000000000..ab798c396 --- /dev/null +++ b/util/network/parse_listeners.go @@ -0,0 +1,82 @@ +package network + +import ( + "net" + "runtime" + "strings" + + "github.com/pkg/errors" +) + +// simpleAddr implements the net.Addr interface with two struct fields +type simpleAddr struct { + net, addr string +} + +// String returns the address. +// +// This is part of the net.Addr interface. +func (a simpleAddr) String() string { + return a.addr +} + +// Network returns the network. +// +// This is part of the net.Addr interface. +func (a simpleAddr) Network() string { + return a.net +} + +// Ensure simpleAddr implements the net.Addr interface. +var _ net.Addr = simpleAddr{} + +// ParseListeners determines whether each listen address is IPv4 and IPv6 and +// returns a slice of appropriate net.Addrs to listen on with TCP. It also +// properly detects addresses which apply to "all interfaces" and adds the +// address as both IPv4 and IPv6. +func ParseListeners(addrs []string) ([]net.Addr, error) { + netAddrs := make([]net.Addr, 0, len(addrs)*2) + for _, addr := range addrs { + host, _, err := net.SplitHostPort(addr) + if err != nil { + // Shouldn't happen due to already being normalized. + return nil, err + } + + // Empty host or host of * on plan9 is both IPv4 and IPv6. + if host == "" || (host == "*" && runtime.GOOS == "plan9") { + netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr}) + netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr}) + continue + } + + // Strip IPv6 zone id if present since net.ParseIP does not + // handle it. + zoneIndex := strings.LastIndex(host, "%") + if zoneIndex > 0 { + host = host[:zoneIndex] + } + + // Parse the IP. + ip := net.ParseIP(host) + if ip == nil { + hostAddrs, err := net.LookupHost(host) + if err != nil { + return nil, err + } + ip = net.ParseIP(hostAddrs[0]) + if ip == nil { + return nil, errors.Errorf("Cannot resolve IP address for host '%s'", host) + } + } + + // To4 returns nil when the IP is not an IPv4 address, so use + // this determine the address type. + if ip.To4() == nil { + netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr}) + } else { + netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr}) + } + } + return netAddrs, nil +} diff --git a/util/testtools/testtools.go b/util/testtools/testtools.go index c75e8d155..df81a6f72 100644 --- a/util/testtools/testtools.go +++ b/util/testtools/testtools.go @@ -20,7 +20,7 @@ import ( // RegisterSubnetworkForTest is used to register network on DAG with specified gas limit func RegisterSubnetworkForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, gasLimit uint64) (*subnetworkid.SubnetworkID, error) { buildNextBlock := func(parentHashes []*daghash.Hash, txs []*wire.MsgTx) (*util.Block, error) { - msgBlock, err := mining.PrepareBlockForTest(dag, params, parentHashes, txs, false) + msgBlock, err := mining.PrepareBlockForTest(dag, parentHashes, txs, false) if err != nil { return nil, err } diff --git a/wire/msgtx_test.go b/wire/msgtx_test.go index 5c3056b87..82a899e75 100644 --- a/wire/msgtx_test.go +++ b/wire/msgtx_test.go @@ -192,7 +192,7 @@ func TestTxHashAndID(t *testing.T) { return } payload := []byte{1, 2, 3} - txIns := []*TxIn{&TxIn{ + txIns := []*TxIn{{ PreviousOutpoint: Outpoint{ Index: 0, TxID: daghash.TxID{1, 2, 3},