kaspad/mining/mining_test.go
Dan Aharoni ea6f7a28c2 [NOD-420] Process delayed blocks (#529)
* [NOD-420] Delay blocks with valid timestamp (non-delayed) that point to a delayed block.

* [NOD-420] Mark block as requested when setting as delayed.

* [NOD-420] Merge master; Use dag.timeSource.AdjustedTime() instead of time.Now;

* [NOD-420] Return nil when not expecting an error

* [NOD-420] Initialise delyaed blocks mapping

* [NOD-420] Trigger delayed blocks processing every time we process a block.

* [NOD-420] Hold the read lock in processDelayedBlocks

* [NOD-420] Add delayed blocks heap sorted by their process time so we could process them in order.

* [NOD-420] Update debug log

* [NOD-420] Fix process blocks loop

* [NOD-420] Add comment

* [NOD-420] Log error message

* [NOD-420] Implement peek method for delayed block heap. extract delayed block processing to another  function.

* [NOD-420] Trigger process delayed blocks only in process block

* [NOD-420] Move delayed block addition to process block

* [NOD-420] Use process block to make sure we fully process the delayed block and deal with orphans.

* [NOD-420] Unexport functions when not needed; Return isDelayed boolean from ProcessBlock instead of the delay duration

* [NOd-420] Remove redundant delayedBlocksLock

* [NOD-420] Resolve merge conflict; Return delay 0 instead of boolean

* [NOD-420] Do not treat delayed block as orphan

* [NOD-420] Make sure block is not processed if we have already sa delayed.

* [NOD-420] Process delayed block if parent is delayed to make sure it would not be treated as orphan.

* [NOD-420] Rename variable

* [NOD-420] Rename function. Move maxDelayOfParents to process.go

* [NOD-420] Fix typo

* [NOD-420] Handle errors from processDelayedBlocks properly

* [NOD-420] Return default values if err != nil from dag.addDelayedBlock

* [NOD-420] Return default values if err != nil from dag.addDelayedBlock in another place

Co-authored-by: Svarog <feanorr@gmail.com>
2020-01-08 15:28:52 +02:00

240 lines
6.6 KiB
Go

// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package mining
import (
"github.com/pkg/errors"
"testing"
"github.com/kaspanet/kaspad/util/subnetworkid"
"bou.ke/monkey"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
"github.com/kaspanet/kaspad/util"
)
func TestNewBlockTemplate(t *testing.T) {
params := dagconfig.SimNetParams
params.BlockCoinbaseMaturity = 0
dag, teardownFunc, err := blockdag.DAGSetup("TestNewBlockTemplate", blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
scriptPubKey, err := txscript.NewScriptBuilder().AddOp(txscript.OpTrue).Script()
if err != nil {
t.Fatalf("Failed to create scriptPubKey: %v", err)
}
policy := Policy{
BlockMaxMass: 50000,
}
// First we create a block to have coinbase funds for the rest of the test.
txSource := &fakeTxSource{
txDescs: []*TxDesc{},
}
blockTemplateGenerator := NewBlkTmplGenerator(&policy,
&params, txSource, dag, blockdag.NewMedianTime(), txscript.NewSigCache(100000))
OpTrueAddr, err := OpTrueAddress(params.Prefix)
if err != nil {
t.Fatalf("OpTrueAddress: %s", err)
}
template1, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
if err != nil {
t.Fatalf("NewBlockTemplate: %v", err)
}
isOrphan, isDelayed, err := dag.ProcessBlock(util.NewBlock(template1.Block), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if isDelayed {
t.Fatalf("ProcessBlock: template1 " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock: template1 got unexpectedly orphan")
}
// We create another 4 blocks to in order to create more funds for tests.
cbTxs := []*wire.MsgTx{template1.Block.Transactions[util.CoinbaseTransactionIndex]}
for i := 0; i < 4; i++ {
template, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
if err != nil {
t.Fatalf("NewBlockTemplate: %v", err)
}
isOrphan, isDelayed, err = dag.ProcessBlock(util.NewBlock(template.Block), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if isDelayed {
t.Fatalf("ProcessBlock: template " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock: template got unexpectedly orphan")
}
cbTxs = append(cbTxs, template.Block.Transactions[util.CoinbaseTransactionIndex])
}
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
t.Fatalf("Error creating signature script: %s", err)
}
// tx is a regular transaction, and should not be filtered by the miner
txIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *cbTxs[0].TxID(),
Index: 0,
},
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
txOut := &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: 1,
}
tx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut})
// We want to check that the miner filters non finalized transactions
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *cbTxs[1].TxID(),
Index: 0,
},
Sequence: 0,
SignatureScript: signatureScript,
}
txOut = &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: 1,
}
nonFinalizedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut})
nonFinalizedTx.LockTime = dag.ChainHeight() + 2
existingSubnetwork := &subnetworkid.SubnetworkID{0xff}
nonExistingSubnetwork := &subnetworkid.SubnetworkID{0xfe}
// We want to check that the miner filters transactions with non-existing subnetwork id. (It should first push it to the priority queue, and then ignore it)
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *cbTxs[2].TxID(),
Index: 0,
},
Sequence: 0,
SignatureScript: signatureScript,
}
txOut = &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: 1,
}
nonExistingSubnetworkTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut},
nonExistingSubnetwork, 1, []byte{})
// We want to check that the miner doesn't filters transactions that do not exceed the subnetwork gas limit
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *cbTxs[3].TxID(),
Index: 0,
},
Sequence: 0,
SignatureScript: signatureScript,
}
txOut = &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: 1,
}
subnetworkTx1 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut}, existingSubnetwork, 1, []byte{})
// We want to check that the miner filters transactions that exceed the subnetwork gas limit. (It should first push it to the priority queue, and then ignore it)
txIn = &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *cbTxs[4].TxID(),
},
Sequence: 0,
SignatureScript: signatureScript,
}
txOut = &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: 1,
}
subnetworkTx2 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut}, existingSubnetwork,
100, // Subnetwork gas limit is 90
[]byte{})
txSource.txDescs = []*TxDesc{
{
Tx: util.NewTx(tx),
Fee: 1,
},
{
Tx: util.NewTx(nonFinalizedTx),
Fee: 1,
},
{
Tx: util.NewTx(subnetworkTx1),
Fee: 1,
},
{
Tx: util.NewTx(subnetworkTx2),
Fee: 1,
},
{
Tx: util.NewTx(nonExistingSubnetworkTx),
Fee: 1,
},
}
// Here we define nonExistingSubnetwork to be non-exist, and existingSubnetwork to have a gas limit of 90
gasLimitPatch := monkey.Patch((*blockdag.SubnetworkStore).GasLimit, func(_ *blockdag.SubnetworkStore, subnetworkID *subnetworkid.SubnetworkID) (uint64, error) {
if subnetworkID.IsEqual(nonExistingSubnetwork) {
return 0, errors.New("not found")
}
return 90, nil
})
defer gasLimitPatch.Unpatch()
template3, err := blockTemplateGenerator.NewBlockTemplate(OpTrueAddr)
gasLimitPatch.Unpatch()
if err != nil {
t.Errorf("NewBlockTemplate: unexpected error: %v", err)
}
expectedTxs := map[daghash.TxID]bool{
*tx.TxID(): false,
*subnetworkTx1.TxID(): false,
}
for _, tx := range template3.Block.Transactions[util.CoinbaseTransactionIndex+1:] {
id := *tx.TxID()
if _, ok := expectedTxs[id]; !ok {
t.Errorf("Unexpected tx %v in template3's candidate block", id)
}
expectedTxs[id] = true
}
for id, exists := range expectedTxs {
if !exists {
t.Errorf("tx %v was expected to be in template3's candidate block, but wasn't", id)
}
}
}