2022-11-16 23:48:05 +02:00

159 lines
4.5 KiB
Go

package mine
import (
"math/rand"
"path/filepath"
"strings"
"time"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/mining"
"github.com/kaspanet/kaspad/stability-tests/common/rpc"
"github.com/pkg/errors"
)
// FromFile mines all blocks as described by `jsonFile`
func FromFile(jsonFile string, consensusConfig *consensus.Config, rpcClient *rpc.Client, dataDir string) error {
log.Infof("Mining blocks from JSON file %s from data directory %s", jsonFile, dataDir)
blockChan, err := readBlocks(jsonFile)
if err != nil {
return err
}
return mineBlocks(consensusConfig, rpcClient, blockChan, dataDir)
}
func mineBlocks(consensusConfig *consensus.Config, rpcClient *rpc.Client, blockChan <-chan JSONBlock, dataDir string) error {
mdb, err := newMiningDB(dataDir)
if err != nil {
return err
}
dbPath := filepath.Join(dataDir, "db")
factory := consensus.NewFactory()
factory.SetTestDataDir(dbPath)
testConsensus, tearDownFunc, err := factory.NewTestConsensus(consensusConfig, "minejson")
if err != nil {
return err
}
defer tearDownFunc(true)
info, err := testConsensus.GetSyncInfo()
if err != nil {
return err
}
log.Infof("Starting with data directory with %d headers and %d blocks", info.HeaderCount, info.BlockCount)
err = mdb.putID("0", consensusConfig.GenesisHash)
if err != nil {
return err
}
totalBlocksSubmitted := 0
lastLogTime := time.Now()
rpcWaitInInterval := 0 * time.Second
for blockData := range blockChan {
if hash := mdb.hashByID(blockData.ID); hash != nil {
_, err := rpcClient.GetBlock(hash.String(), false)
if err == nil {
continue
}
if !strings.Contains(err.Error(), "not found") {
return err
}
}
block, err := mineOrFetchBlock(blockData, mdb, testConsensus)
if err != nil {
return err
}
beforeSubmitBlockTime := time.Now()
rejectReason, err := rpcClient.SubmitBlockAlsoIfNonDAA(block)
if err != nil {
return errors.Wrap(err, "error in SubmitBlock")
}
if rejectReason != appmessage.RejectReasonNone {
return errors.Errorf("block rejected in SubmitBlock")
}
rpcWaitInInterval += time.Since(beforeSubmitBlockTime)
totalBlocksSubmitted++
const logInterval = 1000
if totalBlocksSubmitted%logInterval == 0 {
intervalDuration := time.Since(lastLogTime)
blocksPerSecond := logInterval / intervalDuration.Seconds()
log.Infof("It took %s to submit %d blocks (%f blocks/sec) while %s of it it waited for RPC response"+
" (total blocks sent %d)", intervalDuration, logInterval, blocksPerSecond, rpcWaitInInterval,
totalBlocksSubmitted)
rpcWaitInInterval = 0
lastLogTime = time.Now()
}
blockHash := consensushashing.BlockHash(block)
log.Tracef("Submitted block %s with hash %s", blockData.ID, blockHash)
}
return nil
}
func mineOrFetchBlock(blockData JSONBlock, mdb *miningDB, testConsensus testapi.TestConsensus) (*externalapi.DomainBlock, error) {
hash := mdb.hashByID(blockData.ID)
if mdb.hashByID(blockData.ID) != nil {
block, found, err := testConsensus.GetBlock(hash)
if err != nil {
return nil, err
}
if !found {
return nil, errors.Errorf("block %s is missing", hash)
}
return block, nil
}
parentHashes := make([]*externalapi.DomainHash, len(blockData.Parents))
for i, parentID := range blockData.Parents {
parentHashes[i] = mdb.hashByID(parentID)
}
block, _, err := testConsensus.BuildBlockWithParents(parentHashes,
&externalapi.DomainCoinbaseData{ScriptPublicKey: &externalapi.ScriptPublicKey{}}, []*externalapi.DomainTransaction{})
if err != nil {
return nil, errors.Wrap(err, "error in BuildBlockWithParents")
}
if !testConsensus.DAGParams().SkipProofOfWork {
SolveBlock(block)
}
err = testConsensus.ValidateAndInsertBlock(block, true)
if err != nil {
return nil, errors.Wrap(err, "error in ValidateAndInsertBlock")
}
blockHash := consensushashing.BlockHash(block)
err = mdb.putID(blockData.ID, blockHash)
if err != nil {
return nil, err
}
err = mdb.updateLastMinedBlock(blockData.ID)
if err != nil {
return nil, err
}
return block, nil
}
var random = rand.New(rand.NewSource(time.Now().UnixNano()))
// SolveBlock increments the given block's nonce until it matches the difficulty requirements in its bits field
func SolveBlock(block *externalapi.DomainBlock) {
mining.SolveBlock(block, random)
}