Repository: kaspanet/kaspad
Pull request #1140 (2020-11-23 14:01:25)

[NOD-1560] Add TestValidateTransactionInIsolation
Based on this test https://sourcegraph.com/github.com/kaspanet/kaspad@v0.7.3-dev/-/blob/domain/blockdag/validate_test.go#L563:6 but without the non native subnetwork transactions


Patch:
diff --git a/domain/consensus/utils/transactionhelper/new.go b/domain/consensus/utils/transactionhelper/new.go
index 2c42bd03..927d3c03 100644
--- a/domain/consensus/utils/transactionhelper/new.go
+++ b/domain/consensus/utils/transactionhelper/new.go
@@ -3,6 +3,7 @@ package transactionhelper
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
 )
 
 // NewSubnetworkTransaction returns a new trsnactions in the specified subnetwork with specified gas and payload
@@ -24,3 +25,19 @@ func NewSubnetworkTransaction(version int32, inputs []*externalapi.DomainTransac
 		Mass:         0,
 	}
 }
+
+// NewNativeTransaction returns a new native transaction
+func NewNativeTransaction(version int32, inputs []*externalapi.DomainTransactionInput,
+	outputs []*externalapi.DomainTransactionOutput) *externalapi.DomainTransaction {
+	return &externalapi.DomainTransaction{
+		Version:      version,
+		Inputs:       inputs,
+		Outputs:      outputs,
+		LockTime:     0,
+		SubnetworkID: subnetworks.SubnetworkIDNative,
+		Gas:          0,
+		Payload:      []byte{},
+		Fee:          0,
+		Mass:         0,
+	}
+}


Test patch:
diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go b/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go
new file mode 100644
index 00000000..3c2051c9
--- /dev/null
+++ b/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go
@@ -0,0 +1,162 @@
+package transactionvalidator_test
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
+	"github.com/kaspanet/kaspad/domain/dagconfig"
+	"github.com/kaspanet/kaspad/util"
+	"github.com/pkg/errors"
+	"testing"
+)
+
+type txSubnetworkData struct {
+	subnetworkID externalapi.DomainSubnetworkID
+	gas          uint64
+	payload      []byte
+}
+
+func TestValidateTransactionInIsolation(t *testing.T) {
+	testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
+		factory := consensus.NewFactory()
+		tc, teardown, err := factory.NewTestConsensus(params, "TestValidateTransactionInIsolation")
+		if err != nil {
+			t.Fatalf("Error setting up consensus: %+v", err)
+		}
+		defer teardown()
+
+		tests := []struct {
+			name                   string
+			numInputs              uint32
+			numOutputs             uint32
+			outputValue            uint64
+			nodeSubnetworkID       externalapi.DomainSubnetworkID
+			txSubnetworkData       *txSubnetworkData
+			extraModificationsFunc func(*externalapi.DomainTransaction)
+			expectedErr            error
+		}{
+			{"good one", 1, 1, 1, subnetworks.SubnetworkIDNative, nil, nil, nil},
+			{"no inputs", 0, 1, 1, subnetworks.SubnetworkIDNative, nil, nil, ruleerrors.ErrNoTxInputs},
+			{"no outputs", 1, 0, 1, subnetworks.SubnetworkIDNative, nil, nil, nil},
+			{"too much sompi in one output", 1, 1, util.MaxSompi + 1,
+				subnetworks.SubnetworkIDNative,
+				nil,
+				nil,
+				ruleerrors.ErrBadTxOutValue},
+			{"too much sompi in total outputs", 1, 2, util.MaxSompi - 1,
+				subnetworks.SubnetworkIDNative,
+				nil,
+				nil,
+				ruleerrors.ErrBadTxOutValue},
+			{"duplicate inputs", 2, 1, 1,
+				subnetworks.SubnetworkIDNative,
+				nil,
+				func(tx *externalapi.DomainTransaction) { tx.Inputs[1].PreviousOutpoint.Index = 0 },
+				ruleerrors.ErrDuplicateTxInputs},
+			{"1 input coinbase",
+				1,
+				1,
+				1,
+				subnetworks.SubnetworkIDNative,
+				&txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil},
+				nil,
+				nil},
+			{"no inputs coinbase",
+				0,
+				1,
+				1,
+				subnetworks.SubnetworkIDNative,
+				&txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil},
+				nil,
+				nil},
+			{"too long payload coinbase",
+				1,
+				1,
+				1,
+				subnetworks.SubnetworkIDNative,
+				&txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, make([]byte, constants.MaxCoinbasePayloadLength+1)},
+				nil,
+				ruleerrors.ErrBadCoinbasePayloadLen},
+			{"non-zero gas in Kaspa", 1, 1, 0,
+				subnetworks.SubnetworkIDNative,
+				nil,
+				func(tx *externalapi.DomainTransaction) {
+					tx.Gas = 1
+				},
+				ruleerrors.ErrInvalidGas},
+			{"non-zero gas in subnetwork registry", 1, 1, 0,
+				subnetworks.SubnetworkIDRegistry,
+				&txSubnetworkData{subnetworks.SubnetworkIDRegistry, 1, []byte{}},
+				nil,
+				ruleerrors.ErrInvalidGas},
+			{"non-zero payload in Kaspa", 1, 1, 0,
+				subnetworks.SubnetworkIDNative,
+				nil,
+				func(tx *externalapi.DomainTransaction) {
+					tx.Payload = []byte{1}
+				},
+				ruleerrors.ErrInvalidPayload},
+			{"invalid payload hash", 1, 1, 0,
+				externalapi.DomainSubnetworkID{123},
+				&txSubnetworkData{externalapi.DomainSubnetworkID{123}, 0, []byte{1}},
+				func(tx *externalapi.DomainTransaction) {
+					tx.PayloadHash = externalapi.DomainHash{}
+				},
+				ruleerrors.ErrInvalidPayloadHash},
+			{"invalid payload hash in native subnetwork", 1, 1, 0,
+				subnetworks.SubnetworkIDNative,
+				nil,
+				func(tx *externalapi.DomainTransaction) {
+					tx.PayloadHash = *hashes.HashData(tx.Payload)
+				},
+				ruleerrors.ErrInvalidPayloadHash},
+		}
+
+		for _, test := range tests {
+			tx := createTxForTest(test.numInputs, test.numOutputs, test.outputValue, test.txSubnetworkData)
+
+			if test.extraModificationsFunc != nil {
+				test.extraModificationsFunc(tx)
+			}
+
+			err := tc.TransactionValidator().ValidateTransactionInIsolation(tx)
+			if !errors.Is(err, test.expectedErr) {
+				t.Errorf("TestValidateTransactionInIsolation: '%s': unexpected error %+v", test.name, err)
+			}
+		}
+	})
+}
+
+func createTxForTest(numInputs uint32, numOutputs uint32, outputValue uint64, subnetworkData *txSubnetworkData) *externalapi.DomainTransaction {
+	txIns := []*externalapi.DomainTransactionInput{}
+	txOuts := []*externalapi.DomainTransactionOutput{}
+
+	for i := uint32(0); i < numInputs; i++ {
+		txIns = append(txIns, &externalapi.DomainTransactionInput{
+			PreviousOutpoint: externalapi.DomainOutpoint{
+				TransactionID: externalapi.DomainTransactionID{},
+				Index:         i,
+			},
+			SignatureScript: []byte{},
+			Sequence:        constants.MaxTxInSequenceNum,
+		})
+	}
+
+	for i := uint32(0); i < numOutputs; i++ {
+		txOuts = append(txOuts, &externalapi.DomainTransactionOutput{
+			ScriptPublicKey: []byte{},
+			Value:           outputValue,
+		})
+	}
+
+	if subnetworkData != nil {
+		return transactionhelper.NewSubnetworkTransaction(constants.TransactionVersion, txIns, txOuts, &subnetworkData.subnetworkID, subnetworkData.gas, subnetworkData.payload)
+	}
+
+	return transactionhelper.NewNativeTransaction(constants.TransactionVersion, txIns, txOuts)
+}
diff --git a/domain/consensus/utils/testutils/for_all_nets.go b/domain/consensus/utils/testutils/for_all_nets.go
index ad73ad9f..689cf405 100644
--- a/domain/consensus/utils/testutils/for_all_nets.go
+++ b/domain/consensus/utils/testutils/for_all_nets.go
@@ -17,8 +17,9 @@ func ForAllNets(t *testing.T, skipPow bool, testFunc func(*testing.T, *dagconfig
 	}
 
 	for _, params := range allParams {
-		params.SkipProofOfWork = skipPow
+		paramsCopy := params
+		paramsCopy.SkipProofOfWork = skipPow
 		t.Logf("Running test for %s", params.Name)
-		testFunc(t, &params)
+		testFunc(t, &paramsCopy)
 	}
 }