From cb9d7e313d5744f934cde103667a0a926f303835 Mon Sep 17 00:00:00 2001 From: Ori Newman <orinewman1@gmail.com> Date: Tue, 22 Dec 2020 17:38:54 +0200 Subject: [PATCH] Implement Clone and Equal for all model types (#1155) * [NOD-1575] Implement Clone and Equal for all model types * [NOD-1575] Add assertion for transaction ID equality * [NOD-1575] Use DomainTransaction.Equal to compare to expected coinbase transaction * [NOD-1575] Add TestDomainBlockHeader_Clone * [NOD-1575] Don't clone nil values * [NOD-1575] Add type assertions * [NOD-1575] Don't clone nil values * [NOD-1575] Add missing Equals * [NOD-1575] Add length checks * [NOD-1575] Update comment * [NOD-1575] Check length for TransactionAcceptanceData * [NOD-1575] Explicitly clone nils where needed * [NOD-1575] Clone tx id * [NOD-1575] Flip condition * Nod 1576 make coverage tests for equal clone inside model externalapi (#1177) * [NOD-1576] Make coverage tests for equal and clone inside model and externalapi * Some formatting and naming fixes * Made transactionToCompare type exported * Added some tests and made some changes to the tests code * No changes made * Some formatting and naming changes made * Made better test coverage for externalapi clone and equal functions * Changed expected result for two cases * Added equal and clone functions tests for ghostdag and utxodiff * Added tests * [NOD-1576] Implement reachabilitydata equal/clone unit tests * [NOD-1576] Full coverage of reachabilitydata equal/clone unit tests * Made changes and handling panic to transaction_equal_clone_test.go and formating of utxodiff_equal_clone_test.go * Added recoverForEqual2 for handling panic to transaction_equal_clone_test.go * [NOD-1576] Full coverage of transaction equal unit test * [NOD-1576] Add expects panic * [NOD-1576] Allow composites in go vet * [NOD-1576] Code review fixes (#1223) * [NOD-1576] Code review fixes * [NOD-1576] Code review fixes part 2 * [NOD-1576] Fix wrong name Co-authored-by: karim1king <karimkaspersky@yahoo.com> Co-authored-by: Ori Newman <orinewman1@gmail.com> Co-authored-by: Karim <karim1king@users.noreply.github.com> * Fix merge errors * Use Equal where possible * Use Equal where possible * Use Equal where possible Co-authored-by: andrey-hash <74914043+andrey-hash@users.noreply.github.com> Co-authored-by: karim1king <karimkaspersky@yahoo.com> Co-authored-by: Karim <karim1king@users.noreply.github.com> --- app/appmessage/p2p_msgblock.go | 2 +- app/appmessage/p2p_msgblock_test.go | 2 +- app/appmessage/p2p_msgrequestheaders_test.go | 2 +- app/appmessage/p2p_msgtx.go | 4 +- app/appmessage/p2p_msgtx_test.go | 10 +- app/protocol/flowcontext/orphans.go | 2 +- .../flows/blockrelay/handle_relay_invs.go | 2 +- app/protocol/flows/blockrelay/ibd.go | 2 +- .../flows/handshake/receiveversion.go | 2 +- build_and_test.sh | 2 +- domain/consensus/consensus.go | 2 +- domain/consensus/finality_test.go | 14 +- .../model/acceptancedata_equal_clone_test.go | 809 ++++++++++++ domain/consensus/model/blockrelations.go | 25 +- .../model/blockrelations_equal_clone_test.go | 115 ++ .../model/externalapi/acceptancedata.go | 78 +- domain/consensus/model/externalapi/block.go | 81 +- .../externalapi/block_equal_clone_test.go | 375 ++++++ .../consensus/model/externalapi/blockinfo.go | 9 + .../model/externalapi/blockinfo_clone_test.go | 45 + .../model/externalapi/blocklocator.go | 5 + .../externalapi/blocklocator_clone_test.go | 67 + .../model/externalapi/blockstatus.go | 9 + .../blockstatus_equal_clone_test.go | 87 ++ .../consensus/model/externalapi/coinbase.go | 14 + .../model/externalapi/coinbase_clone_test.go | 59 + .../consensus/model/externalapi/equal_test.go | 238 ++++ domain/consensus/model/externalapi/hash.go | 31 +- .../externalapi/hash_clone_equal_test.go | 119 ++ .../model/externalapi/subnetworkid.go | 17 +- .../subnetworkid_clone_equal_test.go | 99 ++ domain/consensus/model/externalapi/sync.go | 39 + .../externalapi/sync_equal_clone_test.go | 126 ++ .../model/externalapi/transaction.go | 175 ++- .../transaction_equal_clone_test.go | 1133 +++++++++++++++++ .../consensus/model/externalapi/utxoentry.go | 1 + domain/consensus/model/reachabilitydata.go | 100 +- .../reachabilitydata_equal_clone_test.go | 296 +++++ .../blockprocessor/validateandinsertblock.go | 4 +- .../validateandinsertpruningpoint.go | 2 +- .../blockvalidator/block_body_in_isolation.go | 2 +- .../blockvalidator/block_header_in_context.go | 2 +- .../block_header_in_isolation.go | 2 +- .../add_block_to_virtual.go | 8 +- .../calculate_past_utxo.go | 2 +- .../calculate_past_utxo_test.go | 2 +- .../check_finality_violation.go | 2 +- .../find_selected_parent_chain_changes.go | 2 +- ...find_selected_parent_chain_changes_test.go | 10 +- .../pick_virtual_parents.go | 2 +- .../resolve_block_status.go | 6 +- .../resolve_block_status_test.go | 2 +- .../update_pruning_utxo_set.go | 2 +- .../consensusstatemanager/update_virtual.go | 4 +- .../consensusstatemanager/utxo_diffs.go | 6 +- .../verify_and_build_utxo.go | 6 +- .../dagtopologymanager/dagtopologymanager.go | 8 +- .../selected_child_iterator.go | 2 +- .../finalitymanager/finality_manager.go | 4 +- .../processes/ghostdag2/ghostdagimpl.go | 6 +- .../ghostdagmanager/ghostdag_test.go | 4 +- .../processes/ghostdagmanager/mergeset.go | 2 +- .../headertipsmanager.go | 2 +- .../pruningmanager/pruningmanager.go | 2 +- .../reachability_external_test.go | 10 +- .../reachabilitymanager/reachability_test.go | 4 +- .../processes/reachabilitymanager/tree.go | 16 +- .../processes/syncmanager/syncinfo.go | 2 +- .../transaction_in_isolation.go | 4 +- domain/consensus/utils/hashes/to_big.go | 2 +- domain/consensus/utils/subnetworks/compare.go | 11 - domain/consensus/utils/utxo/utxo_entry.go | 38 +- .../consensus/utils/utxo/utxo_entry_test.go | 113 ++ domain/dagconfig/genesis_test.go | 8 +- domain/dagconfig/params_test.go | 2 +- domain/miningmanager/mempool/mempool.go | 4 +- domain/utxoindex/store.go | 4 +- domain/utxoindex/utxoindex.go | 4 +- testing/integration/basic_sync_test.go | 4 +- 79 files changed, 4346 insertions(+), 174 deletions(-) create mode 100644 domain/consensus/model/acceptancedata_equal_clone_test.go create mode 100644 domain/consensus/model/blockrelations_equal_clone_test.go create mode 100644 domain/consensus/model/externalapi/block_equal_clone_test.go create mode 100644 domain/consensus/model/externalapi/blockinfo_clone_test.go create mode 100644 domain/consensus/model/externalapi/blocklocator_clone_test.go create mode 100644 domain/consensus/model/externalapi/blockstatus_equal_clone_test.go create mode 100644 domain/consensus/model/externalapi/coinbase_clone_test.go create mode 100644 domain/consensus/model/externalapi/equal_test.go create mode 100644 domain/consensus/model/externalapi/hash_clone_equal_test.go create mode 100644 domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go create mode 100644 domain/consensus/model/externalapi/sync_equal_clone_test.go create mode 100644 domain/consensus/model/externalapi/transaction_equal_clone_test.go create mode 100644 domain/consensus/model/reachabilitydata_equal_clone_test.go create mode 100644 domain/consensus/utils/utxo/utxo_entry_test.go diff --git a/app/appmessage/p2p_msgblock.go b/app/appmessage/p2p_msgblock.go index 5d8d3de53..4436d40e8 100644 --- a/app/appmessage/p2p_msgblock.go +++ b/app/appmessage/p2p_msgblock.go @@ -71,7 +71,7 @@ func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 { // Note: this operation modifies the block in place. func (msg *MsgBlock) ConvertToPartial(subnetworkID *externalapi.DomainSubnetworkID) { for _, tx := range msg.Transactions { - if tx.SubnetworkID != *subnetworkID { + if !tx.SubnetworkID.Equal(subnetworkID) { tx.Payload = []byte{} } } diff --git a/app/appmessage/p2p_msgblock_test.go b/app/appmessage/p2p_msgblock_test.go index 39d02fca9..347b10556 100644 --- a/app/appmessage/p2p_msgblock_test.go +++ b/app/appmessage/p2p_msgblock_test.go @@ -110,7 +110,7 @@ func TestConvertToPartial(t *testing.T) { for _, testTransaction := range transactions { var subnetworkTx *MsgTx for _, blockTransaction := range block.Transactions { - if blockTransaction.SubnetworkID == *testTransaction.subnetworkID { + if blockTransaction.SubnetworkID.Equal(testTransaction.subnetworkID) { subnetworkTx = blockTransaction } } diff --git a/app/appmessage/p2p_msgrequestheaders_test.go b/app/appmessage/p2p_msgrequestheaders_test.go index 1d475b9d2..9d0a60cd3 100644 --- a/app/appmessage/p2p_msgrequestheaders_test.go +++ b/app/appmessage/p2p_msgrequestheaders_test.go @@ -26,7 +26,7 @@ func TestRequstIBDBlocks(t *testing.T) { // Ensure we get the same data back out. msg := NewMsgRequstHeaders(lowHash, highHash) - if *msg.HighHash != *highHash { + if !msg.HighHash.Equal(highHash) { t.Errorf("NewMsgRequstHeaders: wrong high hash - got %v, want %v", msg.HighHash, highHash) } diff --git a/app/appmessage/p2p_msgtx.go b/app/appmessage/p2p_msgtx.go index 94661b7cf..1e65d91ea 100644 --- a/app/appmessage/p2p_msgtx.go +++ b/app/appmessage/p2p_msgtx.go @@ -256,8 +256,8 @@ func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 { // 3. The transaction's subnetwork func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *externalapi.DomainSubnetworkID) bool { return subnetworkID == nil || - *subnetworkID == subnetworks.SubnetworkIDNative || - *subnetworkID == msg.SubnetworkID + subnetworkID.Equal(&subnetworks.SubnetworkIDNative) || + subnetworkID.Equal(&msg.SubnetworkID) } // newMsgTx returns a new tx message that conforms to the Message interface. diff --git a/app/appmessage/p2p_msgtx_test.go b/app/appmessage/p2p_msgtx_test.go index e85a50601..c1dba1885 100644 --- a/app/appmessage/p2p_msgtx_test.go +++ b/app/appmessage/p2p_msgtx_test.go @@ -53,7 +53,7 @@ func TestTx(t *testing.T) { // testing package functionality. prevOutIndex := uint32(1) prevOut := NewOutpoint(txID, prevOutIndex) - if prevOut.TxID != *txID { + if !prevOut.TxID.Equal(txID) { t.Errorf("NewOutpoint: wrong ID - got %v, want %v", spew.Sprint(&prevOut.TxID), spew.Sprint(txID)) } @@ -179,7 +179,7 @@ func TestTxHashAndID(t *testing.T) { // Ensure the TxID for coinbase transaction is the same as TxHash. tx1ID := tx1.TxID() - if *tx1ID != *wantTxID1 { + if !tx1ID.Equal(wantTxID1) { t.Errorf("TxID: wrong ID - got %v, want %v", spew.Sprint(tx1ID), spew.Sprint(wantTxID1)) } @@ -236,19 +236,19 @@ func TestTxHashAndID(t *testing.T) { // Ensure the hash produced is expected. tx2Hash := tx2.TxHash() - if *tx2Hash != *wantHash2 { + if !tx2Hash.Equal(wantHash2) { t.Errorf("TxHash: wrong hash - got %v, want %v", spew.Sprint(tx2Hash), spew.Sprint(wantHash2)) } // Ensure the TxID for coinbase transaction is the same as TxHash. tx2ID := tx2.TxID() - if *tx2ID != *wantID2 { + if !tx2ID.Equal(wantID2) { t.Errorf("TxID: wrong ID - got %v, want %v", spew.Sprint(tx2ID), spew.Sprint(wantID2)) } - if *tx2ID == (externalapi.DomainTransactionID)(*tx2Hash) { + if tx2ID.Equal((*externalapi.DomainTransactionID)(tx2Hash)) { t.Errorf("tx2ID and tx2Hash shouldn't be the same for non-coinbase transaction with signature and/or payload") } diff --git a/app/protocol/flowcontext/orphans.go b/app/protocol/flowcontext/orphans.go index c57afd95a..853462255 100644 --- a/app/protocol/flowcontext/orphans.go +++ b/app/protocol/flowcontext/orphans.go @@ -130,7 +130,7 @@ func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash) var childOrphans []externalapi.DomainHash for orphanHash, orphanBlock := range f.orphans { for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes { - if *orphanBlockParentHash == *blockHash { + if orphanBlockParentHash.Equal(blockHash) { childOrphans = append(childOrphans, orphanHash) break } diff --git a/app/protocol/flows/blockrelay/handle_relay_invs.go b/app/protocol/flows/blockrelay/handle_relay_invs.go index 26e335580..8b91c5788 100644 --- a/app/protocol/flows/blockrelay/handle_relay_invs.go +++ b/app/protocol/flows/blockrelay/handle_relay_invs.go @@ -171,7 +171,7 @@ func (flow *handleRelayInvsFlow) requestBlock(requestHash *externalapi.DomainHas block := appmessage.MsgBlockToDomainBlock(msgBlock) blockHash := consensushashing.BlockHash(block) - if *blockHash != *requestHash { + if !blockHash.Equal(requestHash) { return nil, false, protocolerrors.Errorf(true, "got unrequested block %s", blockHash) } diff --git a/app/protocol/flows/blockrelay/ibd.go b/app/protocol/flows/blockrelay/ibd.go index 41745bbab..a43c2e379 100644 --- a/app/protocol/flows/blockrelay/ibd.go +++ b/app/protocol/flows/blockrelay/ibd.go @@ -279,7 +279,7 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock) blockHash := consensushashing.BlockHash(block) - if *expectedHash != *blockHash { + if !expectedHash.Equal(blockHash) { return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash) } diff --git a/app/protocol/flows/handshake/receiveversion.go b/app/protocol/flows/handshake/receiveversion.go index b0191b9b7..f3357dab1 100644 --- a/app/protocol/flows/handshake/receiveversion.go +++ b/app/protocol/flows/handshake/receiveversion.go @@ -90,7 +90,7 @@ func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) { isRemoteNodeFull := msgVersion.SubnetworkID == nil isOutbound := flow.peer.Connection().IsOutbound() if (isLocalNodeFull && !isRemoteNodeFull && isOutbound) || - (!isLocalNodeFull && !isRemoteNodeFull && *msgVersion.SubnetworkID != *localSubnetworkID) { + (!isLocalNodeFull && !isRemoteNodeFull && !msgVersion.SubnetworkID.Equal(localSubnetworkID)) { return nil, protocolerrors.New(false, "incompatible subnetworks") } diff --git a/build_and_test.sh b/build_and_test.sh index 1f8fbc0a4..6fd9ac9ba 100755 --- a/build_and_test.sh +++ b/build_and_test.sh @@ -19,7 +19,7 @@ staticcheck -checks "\ SA4022,SA4023,SA5000,SA5002,SA5004,SA5005,SA5007,SA5008,SA5009,SA5010,SA5011,SA5012,SA6001,SA6002,SA9001,SA9002, \ SA9003,SA9004,SA9005,SA9006,ST1019" ./... -go vet $FLAGS ./... +go vet -composites=false $FLAGS ./... go build $FLAGS -o kaspad . diff --git a/domain/consensus/consensus.go b/domain/consensus/consensus.go index 6ad01c6a9..d4b01df9a 100644 --- a/domain/consensus/consensus.go +++ b/domain/consensus/consensus.go @@ -171,7 +171,7 @@ func (s *consensus) GetPruningPointUTXOSet(expectedPruningPointHash *externalapi return nil, err } - if *expectedPruningPointHash != *pruningPointHash { + if !expectedPruningPointHash.Equal(pruningPointHash) { return nil, errors.Wrapf(ruleerrors.ErrWrongPruningPointHash, "expected pruning point %s but got %s", expectedPruningPointHash, pruningPointHash) diff --git a/domain/consensus/finality_test.go b/domain/consensus/finality_test.go index 0c5aa3189..4fc06b148 100644 --- a/domain/consensus/finality_test.go +++ b/domain/consensus/finality_test.go @@ -107,7 +107,7 @@ func TestFinality(t *testing.T) { if err != nil { t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err) } - if *consensushashing.BlockHash(selectedTip) != *sideChainTipHash { + if !consensushashing.BlockHash(selectedTip).Equal(sideChainTipHash) { t.Fatalf("Overtaking block in side-chain is not selectedTip") } @@ -125,7 +125,7 @@ func TestFinality(t *testing.T) { t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err) } - if *virtualFinality == *params.GenesisHash { + if virtualFinality.Equal(params.GenesisHash) { t.Fatalf("virtual's finalityPoint is still genesis after adding finalityInterval + 1 blocks to the main chain") } @@ -309,7 +309,7 @@ func TestBoundedMergeDepth(t *testing.T) { // Make sure it's actually blue found := false for _, blue := range virtualGhotDagData.MergeSetBlues() { - if *blue == *kosherizingBlockHash { + if blue.Equal(kosherizingBlockHash) { found = true break } @@ -329,7 +329,7 @@ func TestBoundedMergeDepth(t *testing.T) { t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err) } - if *consensushashing.BlockHash(virtualSelectedParent) != *consensushashing.BlockHash(pointAtBlueKosherizing) { + if !consensushashing.BlockHash(virtualSelectedParent).Equal(consensushashing.BlockHash(pointAtBlueKosherizing)) { t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensushashing.BlockHash(pointAtBlueKosherizing), consensushashing.BlockHash(virtualSelectedParent)) } @@ -346,7 +346,7 @@ func TestBoundedMergeDepth(t *testing.T) { t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err) } - if *consensushashing.BlockHash(virtualSelectedParent) != *tip { + if !consensushashing.BlockHash(virtualSelectedParent).Equal(tip) { t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", tip, consensushashing.BlockHash(virtualSelectedParent)) } @@ -357,7 +357,7 @@ func TestBoundedMergeDepth(t *testing.T) { // Make sure it's actually blue found = false for _, blue := range virtualGhotDagData.MergeSetBlues() { - if *blue == *kosherizingBlockHash { + if blue.Equal(kosherizingBlockHash) { found = true break } @@ -382,7 +382,7 @@ func TestBoundedMergeDepth(t *testing.T) { t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err) } - if *consensushashing.BlockHash(virtualSelectedParent) != *consensushashing.BlockHash(transitiveBlueKosherizing) { + if !consensushashing.BlockHash(virtualSelectedParent).Equal(consensushashing.BlockHash(transitiveBlueKosherizing)) { t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensushashing.BlockHash(transitiveBlueKosherizing), consensushashing.BlockHash(virtualSelectedParent)) } diff --git a/domain/consensus/model/acceptancedata_equal_clone_test.go b/domain/consensus/model/acceptancedata_equal_clone_test.go new file mode 100644 index 000000000..73bc29c9a --- /dev/null +++ b/domain/consensus/model/acceptancedata_equal_clone_test.go @@ -0,0 +1,809 @@ +package model_test + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" + "reflect" + "testing" +) + +func initTestTransactionAcceptanceDataForClone() []*externalapi.TransactionAcceptanceData { + + tests := []*externalapi.TransactionAcceptanceData{ + { + &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }, + } + return tests +} + +type testTransactionAcceptanceDataToCompare struct { + transactionAcceptanceData *externalapi.TransactionAcceptanceData + expectedResult bool +} + +type testTransactionAcceptanceDataStruct struct { + baseTransactionAcceptanceData *externalapi.TransactionAcceptanceData + transactionAcceptanceDataToCompareTo []testTransactionAcceptanceDataToCompare +} + +func initTransactionAcceptanceDataForEqual() []testTransactionAcceptanceDataStruct { + var testTransactionAcceptanceDataBase = externalapi.TransactionAcceptanceData{ + + &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + } + + var testTransactionAcceptanceData1 = externalapi.TransactionAcceptanceData{ + &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + } + // test 2: different transactions + var testTransactionAcceptanceData2 = externalapi.TransactionAcceptanceData{ + &externalapi.DomainTransaction{ + Version: 2, + Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + } + //test 3: different Fee + var testTransactionAcceptanceData3 = externalapi.TransactionAcceptanceData{ + &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 2, + true, + } + //test 4: different isAccepted + var testTransactionAcceptanceData4 = externalapi.TransactionAcceptanceData{ + &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + false, + } + + tests := []testTransactionAcceptanceDataStruct{ + { + baseTransactionAcceptanceData: &testTransactionAcceptanceDataBase, + transactionAcceptanceDataToCompareTo: []testTransactionAcceptanceDataToCompare{ + { + transactionAcceptanceData: &testTransactionAcceptanceData1, + expectedResult: true, + }, { + transactionAcceptanceData: &testTransactionAcceptanceData2, + expectedResult: false, + }, { + transactionAcceptanceData: &testTransactionAcceptanceData3, + expectedResult: false, + }, { + transactionAcceptanceData: &testTransactionAcceptanceData4, + expectedResult: false, + }, { + transactionAcceptanceData: nil, + expectedResult: false, + }, + }, + }, { + baseTransactionAcceptanceData: nil, + transactionAcceptanceDataToCompareTo: []testTransactionAcceptanceDataToCompare{ + { + transactionAcceptanceData: &testTransactionAcceptanceData1, + expectedResult: false, + }, { + transactionAcceptanceData: nil, + expectedResult: true, + }, + }, + }, + } + return tests +} + +func TestTransactionAcceptanceData_Equal(t *testing.T) { + acceptanceData := initTransactionAcceptanceDataForEqual() + for i, test := range acceptanceData { + for j, subTest := range test.transactionAcceptanceDataToCompareTo { + result1 := test.baseTransactionAcceptanceData.Equal(subTest.transactionAcceptanceData) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.transactionAcceptanceData.Equal(test.baseTransactionAcceptanceData) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestTransactionAcceptanceData_Clone(t *testing.T) { + + testTransactionAcceptanceData := initTestTransactionAcceptanceDataForClone() + for i, transactionAcceptanceData := range testTransactionAcceptanceData { + transactionAcceptanceDataClone := transactionAcceptanceData.Clone() + if !transactionAcceptanceDataClone.Equal(transactionAcceptanceData) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(transactionAcceptanceData, transactionAcceptanceDataClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} + +func initTestBlockAcceptanceDataForClone() []*externalapi.BlockAcceptanceData { + + tests := []*externalapi.BlockAcceptanceData{{&externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{ + { + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}, + }, + } + return tests +} + +type testBlockAcceptanceDataToCompare struct { + blockAcceptanceData *externalapi.BlockAcceptanceData + expectedResult bool +} + +type testBlockAcceptanceDataStruct struct { + baseBlockAcceptanceData *externalapi.BlockAcceptanceData + blockAcceptanceDataToCompareTo []testBlockAcceptanceDataToCompare +} + +func iniBlockAcceptanceDataForEqual() []testBlockAcceptanceDataStruct { + var testBlockAcceptanceDataBase = externalapi.BlockAcceptanceData{ + &externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}} + //test 1: structs are equal + var testBlockAcceptanceData1 = externalapi.BlockAcceptanceData{ + &externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}} + // test 2: different size + var testBlockAcceptanceData2 = externalapi.BlockAcceptanceData{ + &externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }, {}}} + //test 3: different transactions, same size + var testBlockAcceptanceData3 = externalapi.BlockAcceptanceData{ + &externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + false, + }}} + + // test 4 - different block hash + var testBlockAcceptanceData4 = externalapi.BlockAcceptanceData{ + &externalapi.DomainHash{2}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}} + + tests := []testBlockAcceptanceDataStruct{ + { + baseBlockAcceptanceData: &testBlockAcceptanceDataBase, + blockAcceptanceDataToCompareTo: []testBlockAcceptanceDataToCompare{ + { + blockAcceptanceData: &testBlockAcceptanceData1, + expectedResult: true, + }, { + blockAcceptanceData: &testBlockAcceptanceData2, + expectedResult: false, + }, { + blockAcceptanceData: &testBlockAcceptanceData3, + expectedResult: false, + }, { + blockAcceptanceData: nil, + expectedResult: false, + }, + { + blockAcceptanceData: &testBlockAcceptanceData4, + expectedResult: false, + }, + }, + }, { + baseBlockAcceptanceData: nil, + blockAcceptanceDataToCompareTo: []testBlockAcceptanceDataToCompare{ + { + blockAcceptanceData: &testBlockAcceptanceData1, + expectedResult: false, + }, { + blockAcceptanceData: nil, + expectedResult: true, + }, + }, + }, + } + return tests +} + +func TestBlockAcceptanceData_Equal(t *testing.T) { + + blockAcceptances := iniBlockAcceptanceDataForEqual() + for i, test := range blockAcceptances { + for j, subTest := range test.blockAcceptanceDataToCompareTo { + result1 := test.baseBlockAcceptanceData.Equal(subTest.blockAcceptanceData) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.blockAcceptanceData.Equal(test.baseBlockAcceptanceData) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestBlockAcceptanceData_Clone(t *testing.T) { + + testBlockAcceptanceData := initTestBlockAcceptanceDataForClone() + for i, blockAcceptanceData := range testBlockAcceptanceData { + blockAcceptanceDataClone := blockAcceptanceData.Clone() + if !blockAcceptanceDataClone.Equal(blockAcceptanceData) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(blockAcceptanceData, blockAcceptanceDataClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} + +func initTestAcceptanceDataForClone() []externalapi.AcceptanceData { + + test1 := []*externalapi.BlockAcceptanceData{{ + &externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{ + { + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}, + }, + } + tests := []externalapi.AcceptanceData{test1, test1} + return tests +} + +type testAcceptanceDataToCompare struct { + acceptanceData externalapi.AcceptanceData + expectedResult bool +} + +type testAcceptanceDataStruct struct { + baseAcceptanceData externalapi.AcceptanceData + acceptanceDataToCompareTo []testAcceptanceDataToCompare +} + +func initAcceptanceDataForEqual() []testAcceptanceDataStruct { + var testAcceptanceDataBase = []*externalapi.BlockAcceptanceData{ + { + &externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}}} + //test 1: structs are equal + var testAcceptanceData1 = []*externalapi.BlockAcceptanceData{ + {&externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}}} + // test 2: different size + var testAcceptanceData2 = []*externalapi.BlockAcceptanceData{ + {&externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}}, {}} + //test 3: different transactions, same size + var testAcceptanceData3 = []*externalapi.BlockAcceptanceData{ + {&externalapi.DomainHash{1}, + []*externalapi.TransactionAcceptanceData{{ + &externalapi.DomainTransaction{ + 2, + []*externalapi.DomainTransactionInput{{ + externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + 1, + true, + }}}} + + tests := []testAcceptanceDataStruct{ + { + baseAcceptanceData: testAcceptanceDataBase, + acceptanceDataToCompareTo: []testAcceptanceDataToCompare{ + { + acceptanceData: testAcceptanceData1, + expectedResult: true, + }, { + acceptanceData: testAcceptanceData2, + expectedResult: false, + }, { + acceptanceData: testAcceptanceData3, + expectedResult: false, + }, + }, + }, + } + return tests +} + +func TestAcceptanceData_Equal(t *testing.T) { + + acceptances := initAcceptanceDataForEqual() + for i, test := range acceptances { + for j, subTest := range test.acceptanceDataToCompareTo { + result1 := test.baseAcceptanceData.Equal(subTest.acceptanceData) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.acceptanceData.Equal(test.baseAcceptanceData) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestAcceptanceData_Clone(t *testing.T) { + + testAcceptanceData := initTestAcceptanceDataForClone() + for i, acceptanceData := range testAcceptanceData { + acceptanceDataClone := acceptanceData.Clone() + if !acceptanceDataClone.Equal(acceptanceData) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(acceptanceData, acceptanceDataClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/blockrelations.go b/domain/consensus/model/blockrelations.go index 2ed402d46..face57b60 100644 --- a/domain/consensus/model/blockrelations.go +++ b/domain/consensus/model/blockrelations.go @@ -10,12 +10,29 @@ type BlockRelations struct { // Clone returns a clone of BlockRelations func (br *BlockRelations) Clone() *BlockRelations { - if br == nil { - return nil - } - return &BlockRelations{ Parents: externalapi.CloneHashes(br.Parents), Children: externalapi.CloneHashes(br.Children), } } + +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &BlockRelations{[]*externalapi.DomainHash{}, []*externalapi.DomainHash{}} + +// Equal returns whether br equals to other +func (br *BlockRelations) Equal(other *BlockRelations) bool { + if br == nil || other == nil { + return br == other + } + + if !externalapi.HashesEqual(br.Parents, other.Parents) { + return false + } + + if !externalapi.HashesEqual(br.Children, other.Children) { + return false + } + + return true +} diff --git a/domain/consensus/model/blockrelations_equal_clone_test.go b/domain/consensus/model/blockrelations_equal_clone_test.go new file mode 100644 index 000000000..39c05e8e3 --- /dev/null +++ b/domain/consensus/model/blockrelations_equal_clone_test.go @@ -0,0 +1,115 @@ +package model + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "reflect" + "testing" +) + +func initTestBlockRelationsForClone() []*BlockRelations { + + tests := []*BlockRelations{ + { + []*externalapi.DomainHash{{1}, {2}}, + []*externalapi.DomainHash{{3}, {4}}, + }, + } + return tests +} + +type testBlockRelationsToCompare struct { + blockRelations *BlockRelations + expectedResult bool +} + +type testBlockRelationsStruct struct { + baseBlockRelations *BlockRelations + blockRelationsToCompareTo []testBlockRelationsToCompare +} + +func initTestBlockRelationsForEqual() []testBlockRelationsStruct { + + var testBlockRelationsBase = BlockRelations{ + []*externalapi.DomainHash{{1}, {2}}, + []*externalapi.DomainHash{{3}, {4}}, + } + //First test: structs are equal + var testBlockRelations1 = BlockRelations{ + []*externalapi.DomainHash{{1}, {2}}, + []*externalapi.DomainHash{{3}, {4}}, + } + //Second test: children changed + var testBlockRelations2 = BlockRelations{ + []*externalapi.DomainHash{{1}, {2}}, + []*externalapi.DomainHash{{3}, {5}}, + } + //Third test: parents changed + var testBlockRelations3 = BlockRelations{ + []*externalapi.DomainHash{{6}, {2}}, + []*externalapi.DomainHash{{3}, {4}}, + } + + tests := []testBlockRelationsStruct{ + { + baseBlockRelations: &testBlockRelationsBase, + blockRelationsToCompareTo: []testBlockRelationsToCompare{ + { + blockRelations: &testBlockRelations1, + expectedResult: true, + }, { + blockRelations: &testBlockRelations2, + expectedResult: false, + }, { + blockRelations: &testBlockRelations3, + expectedResult: false, + }, { + blockRelations: nil, + expectedResult: false, + }, + }, + }, { + baseBlockRelations: nil, + blockRelationsToCompareTo: []testBlockRelationsToCompare{ + { + blockRelations: &testBlockRelations1, + expectedResult: false, + }, { + blockRelations: nil, + expectedResult: true, + }, + }, + }, + } + return tests +} + +func TestBlockRelationsData_Equal(t *testing.T) { + + blockRelationss := initTestBlockRelationsForEqual() + for i, test := range blockRelationss { + for j, subTest := range test.blockRelationsToCompareTo { + result1 := test.baseBlockRelations.Equal(subTest.blockRelations) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.blockRelations.Equal(test.baseBlockRelations) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestBlockRelations_Clone(t *testing.T) { + + testBlockRelations := initTestBlockRelationsForClone() + for i, blockRelations := range testBlockRelations { + blockRelationsClone := blockRelations.Clone() + if !blockRelationsClone.Equal(blockRelations) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(blockRelations, blockRelationsClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/acceptancedata.go b/domain/consensus/model/externalapi/acceptancedata.go index b3bde56b0..1cb4ae965 100644 --- a/domain/consensus/model/externalapi/acceptancedata.go +++ b/domain/consensus/model/externalapi/acceptancedata.go @@ -4,11 +4,27 @@ package externalapi // It's ordered in the same way as the block merge set blues. type AcceptanceData []*BlockAcceptanceData +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ AcceptanceData = []*BlockAcceptanceData{} + +// Equal returns whether ad equals to other +func (ad AcceptanceData) Equal(other AcceptanceData) bool { + if len(ad) != len(other) { + return false + } + + for i, blockAcceptanceData := range ad { + if !blockAcceptanceData.Equal(other[i]) { + return false + } + } + + return true +} + // Clone clones the AcceptanceData func (ad AcceptanceData) Clone() AcceptanceData { - if ad == nil { - return nil - } clone := make(AcceptanceData, len(ad)) for i, blockAcceptanceData := range ad { clone[i] = blockAcceptanceData.Clone() @@ -24,6 +40,33 @@ type BlockAcceptanceData struct { TransactionAcceptanceData []*TransactionAcceptanceData } +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &BlockAcceptanceData{&DomainHash{}, []*TransactionAcceptanceData{}} + +// Equal returns whether bad equals to other +func (bad *BlockAcceptanceData) Equal(other *BlockAcceptanceData) bool { + if bad == nil || other == nil { + return bad == other + } + + if !bad.BlockHash.Equal(other.BlockHash) { + return false + } + + if len(bad.TransactionAcceptanceData) != len(other.TransactionAcceptanceData) { + return false + } + + for i, acceptanceData := range bad.TransactionAcceptanceData { + if !acceptanceData.Equal(other.TransactionAcceptanceData[i]) { + return false + } + } + + return true +} + // Clone returns a clone of BlockAcceptanceData func (bad *BlockAcceptanceData) Clone() *BlockAcceptanceData { if bad == nil { @@ -49,12 +92,33 @@ type TransactionAcceptanceData struct { IsAccepted bool } -// Clone returns a clone of TransactionAcceptanceData -func (tad *TransactionAcceptanceData) Clone() *TransactionAcceptanceData { - if tad == nil { - return nil +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &TransactionAcceptanceData{&DomainTransaction{}, 0, false} + +// Equal returns whether tad equals to other +func (tad *TransactionAcceptanceData) Equal(other *TransactionAcceptanceData) bool { + if tad == nil || other == nil { + return tad == other } + if !tad.Transaction.Equal(other.Transaction) { + return false + } + + if tad.Fee != other.Fee { + return false + } + + if tad.IsAccepted != other.IsAccepted { + return false + } + + return true +} + +// Clone returns a clone of TransactionAcceptanceData +func (tad *TransactionAcceptanceData) Clone() *TransactionAcceptanceData { return &TransactionAcceptanceData{ Transaction: tad.Transaction.Clone(), Fee: tad.Fee, diff --git a/domain/consensus/model/externalapi/block.go b/domain/consensus/model/externalapi/block.go index da4990620..07d0d5cbb 100644 --- a/domain/consensus/model/externalapi/block.go +++ b/domain/consensus/model/externalapi/block.go @@ -8,10 +8,6 @@ type DomainBlock struct { // Clone returns a clone of DomainBlock func (block *DomainBlock) Clone() *DomainBlock { - if block == nil { - return nil - } - transactionClone := make([]*DomainTransaction, len(block.Transactions)) for i, tx := range block.Transactions { transactionClone[i] = tx.Clone() @@ -23,6 +19,33 @@ func (block *DomainBlock) Clone() *DomainBlock { } } +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = DomainBlock{&DomainBlockHeader{}, []*DomainTransaction{}} + +// Equal returns whether block equals to other +func (block *DomainBlock) Equal(other *DomainBlock) bool { + if block == nil || other == nil { + return block == other + } + + if len(block.Transactions) != len(other.Transactions) { + return false + } + + if !block.Header.Equal(other.Header) { + return false + } + + for i, tx := range block.Transactions { + if !tx.Equal(other.Transactions[i]) { + return false + } + } + + return true +} + // DomainBlockHeader represents the header part of a Kaspa block type DomainBlockHeader struct { Version int32 @@ -37,10 +60,6 @@ type DomainBlockHeader struct { // Clone returns a clone of DomainBlockHeader func (header *DomainBlockHeader) Clone() *DomainBlockHeader { - if header == nil { - return nil - } - return &DomainBlockHeader{ Version: header.Version, ParentHashes: CloneHashes(header.ParentHashes), @@ -52,3 +71,49 @@ func (header *DomainBlockHeader) Clone() *DomainBlockHeader { Nonce: header.Nonce, } } + +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &DomainBlockHeader{0, []*DomainHash{}, DomainHash{}, + DomainHash{}, DomainHash{}, 0, 0, 0} + +// Equal returns whether header equals to other +func (header *DomainBlockHeader) Equal(other *DomainBlockHeader) bool { + if header == nil || other == nil { + return header == other + } + + if header.Version != other.Version { + return false + } + + if !HashesEqual(header.ParentHashes, other.ParentHashes) { + return false + } + + if !header.HashMerkleRoot.Equal(&other.HashMerkleRoot) { + return false + } + + if !header.AcceptedIDMerkleRoot.Equal(&other.AcceptedIDMerkleRoot) { + return false + } + + if !header.UTXOCommitment.Equal(&other.UTXOCommitment) { + return false + } + + if header.TimeInMilliseconds != other.TimeInMilliseconds { + return false + } + + if header.Bits != other.Bits { + return false + } + + if header.Nonce != other.Nonce { + return false + } + + return true +} diff --git a/domain/consensus/model/externalapi/block_equal_clone_test.go b/domain/consensus/model/externalapi/block_equal_clone_test.go new file mode 100644 index 000000000..9a032d847 --- /dev/null +++ b/domain/consensus/model/externalapi/block_equal_clone_test.go @@ -0,0 +1,375 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +type blockToCompare struct { + block *DomainBlock + expectedResult bool +} + +type TestBlockStruct struct { + baseBlock *DomainBlock + blocksToCompareTo []blockToCompare +} + +func initTestBaseTransactions() []*DomainTransaction { + + testTx := []*DomainTransaction{{ + Version: 1, + Inputs: []*DomainTransactionInput{}, + Outputs: []*DomainTransactionOutput{}, + LockTime: 1, + SubnetworkID: DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }} + return testTx +} + +func initTestAnotherTransactions() []*DomainTransaction { + + testTx := []*DomainTransaction{{ + Version: 1, + Inputs: []*DomainTransactionInput{}, + Outputs: []*DomainTransactionOutput{}, + LockTime: 1, + SubnetworkID: DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }} + return testTx +} + +func initTestTwoTransactions() []*DomainTransaction { + + testTx := []*DomainTransaction{{ + Version: 1, + Inputs: []*DomainTransactionInput{}, + Outputs: []*DomainTransactionOutput{}, + LockTime: 1, + SubnetworkID: DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }, { + Version: 1, + Inputs: []*DomainTransactionInput{}, + Outputs: []*DomainTransactionOutput{}, + LockTime: 1, + SubnetworkID: DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }} + return testTx +} + +func initTestBlockStructsForClone() []*DomainBlock { + + tests := []*DomainBlock{ + { + &DomainBlockHeader{ + + 0, + []*DomainHash{{0}}, + DomainHash{1}, + DomainHash{2}, + DomainHash{3}, + 4, + 5, + 6, + }, + initTestBaseTransactions(), + }, { + &DomainBlockHeader{ + + 0, + []*DomainHash{}, + DomainHash{1}, + DomainHash{2}, + DomainHash{3}, + 4, + 5, + 6, + }, + initTestBaseTransactions(), + }, + } + + return tests +} + +func initTestBlockStructsForEqual() *[]TestBlockStruct { + tests := []TestBlockStruct{ + { + baseBlock: nil, + blocksToCompareTo: []blockToCompare{ + { + block: nil, + expectedResult: true, + }, + { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{0}}, + DomainHash{1}, + DomainHash{2}, + DomainHash{3}, + 4, + 5, + 6, + }, + initTestBaseTransactions()}, + expectedResult: false, + }, + }, + }, { + baseBlock: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + initTestBaseTransactions(), + }, + blocksToCompareTo: []blockToCompare{ + { + block: nil, + expectedResult: false, + }, + { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + initTestAnotherTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: true, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}, {2}}, // Changed + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{100}}, // Changed + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + initTestTwoTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{100}, // Changed + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{100}, // Changed + DomainHash{4}, + 5, + 6, + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{100}, // Changed + 5, + 6, + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 100, // Changed + 6, + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 100, // Changed + 7, + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, { + block: &DomainBlock{ + &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 100, // Changed + }, + initTestBaseTransactions(), + }, + expectedResult: false, + }, + }, + }, + } + + return &tests +} + +func TestDomainBlock_Equal(t *testing.T) { + + blockTests := initTestBlockStructsForEqual() + for i, test := range *blockTests { + for j, subTest := range test.blocksToCompareTo { + result1 := test.baseBlock.Equal(subTest.block) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.block.Equal(test.baseBlock) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } + +} + +func TestDomainBlock_Clone(t *testing.T) { + + blocks := initTestBlockStructsForClone() + for i, block := range blocks { + blockClone := block.Clone() + if !blockClone.Equal(block) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(block, blockClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/blockinfo.go b/domain/consensus/model/externalapi/blockinfo.go index dcd7128de..72cd0d8f9 100644 --- a/domain/consensus/model/externalapi/blockinfo.go +++ b/domain/consensus/model/externalapi/blockinfo.go @@ -6,3 +6,12 @@ type BlockInfo struct { BlockStatus BlockStatus BlueScore uint64 } + +// Clone returns a clone of BlockInfo +func (bi *BlockInfo) Clone() *BlockInfo { + return &BlockInfo{ + Exists: bi.Exists, + BlockStatus: bi.BlockStatus.Clone(), + BlueScore: bi.BlueScore, + } +} diff --git a/domain/consensus/model/externalapi/blockinfo_clone_test.go b/domain/consensus/model/externalapi/blockinfo_clone_test.go new file mode 100644 index 000000000..2ff7c2699 --- /dev/null +++ b/domain/consensus/model/externalapi/blockinfo_clone_test.go @@ -0,0 +1,45 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestBlockInfoStructsForClone() []*BlockInfo { + + tests := []*BlockInfo{ + { + true, + BlockStatus(0x01), + 0, + }, { + true, + BlockStatus(0x02), + 0, + }, { + true, + 1, + 1, + }, { + true, + 255, + 2, + }, { + true, + 0, + 3, + }, + } + return tests +} + +func TestBlockInfo_Clone(t *testing.T) { + + blockInfos := initTestBlockInfoStructsForClone() + for i, blockInfo := range blockInfos { + blockInfoClone := blockInfo.Clone() + if !reflect.DeepEqual(blockInfo, blockInfoClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/blocklocator.go b/domain/consensus/model/externalapi/blocklocator.go index 92503d6a4..a730a4df5 100644 --- a/domain/consensus/model/externalapi/blocklocator.go +++ b/domain/consensus/model/externalapi/blocklocator.go @@ -15,3 +15,8 @@ package externalapi // The block locator for block 17 would be the hashes of blocks: // [17 16 14 11 7 2 genesis] type BlockLocator []*DomainHash + +// Clone returns a clone of BlockLocator +func (locator BlockLocator) Clone() BlockLocator { + return CloneHashes(locator) +} diff --git a/domain/consensus/model/externalapi/blocklocator_clone_test.go b/domain/consensus/model/externalapi/blocklocator_clone_test.go new file mode 100644 index 000000000..149a51b2a --- /dev/null +++ b/domain/consensus/model/externalapi/blocklocator_clone_test.go @@ -0,0 +1,67 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestBlockLocatorForClone() []*BlockLocator { + + tests := []*BlockLocator{{ + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, + }, { + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2}, + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1}, + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1, 1}, + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2, 1}, + }, + } + return tests +} + +func TestBlockLocator_Clone(t *testing.T) { + + testBlockLocator := initTestBlockLocatorForClone() + for i, blockLocator := range testBlockLocator { + blockLocatorClone := blockLocator.Clone() + if !reflect.DeepEqual(blockLocator, &blockLocatorClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/blockstatus.go b/domain/consensus/model/externalapi/blockstatus.go index 2c2fd408a..7358bafdd 100644 --- a/domain/consensus/model/externalapi/blockstatus.go +++ b/domain/consensus/model/externalapi/blockstatus.go @@ -8,6 +8,15 @@ func (bs BlockStatus) Clone() BlockStatus { return bs } +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ BlockStatus = 0 + +// Equal returns whether bs equals to other +func (bs BlockStatus) Equal(other BlockStatus) bool { + return bs == other +} + const ( // StatusInvalid indicates that the block is invalid. StatusInvalid BlockStatus = iota diff --git a/domain/consensus/model/externalapi/blockstatus_equal_clone_test.go b/domain/consensus/model/externalapi/blockstatus_equal_clone_test.go new file mode 100644 index 000000000..7737296c1 --- /dev/null +++ b/domain/consensus/model/externalapi/blockstatus_equal_clone_test.go @@ -0,0 +1,87 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestBlockStatusForClone() []BlockStatus { + + tests := []BlockStatus{1, 2, 0xFF, 0} + + return tests +} + +type TestBlockStatusToCompare struct { + blockStatus BlockStatus + expectedResult bool +} + +type TestBlockStatusStruct struct { + baseBlockStatus BlockStatus + blockStatusesToCompareTo []TestBlockStatusToCompare +} + +func initTestBlockStatusForEqual() []TestBlockStatusStruct { + tests := []TestBlockStatusStruct{ + { + baseBlockStatus: 0, + blockStatusesToCompareTo: []TestBlockStatusToCompare{ + { + blockStatus: 1, + expectedResult: false, + }, + { + blockStatus: 0, + expectedResult: true, + }, + }, + }, { + baseBlockStatus: 255, + blockStatusesToCompareTo: []TestBlockStatusToCompare{ + { + blockStatus: 1, + expectedResult: false, + }, + { + blockStatus: 255, + expectedResult: true, + }, + }, + }, + } + return tests +} + +func TestBlockStatus_Equal(t *testing.T) { + + testBlockStatus := initTestBlockStatusForEqual() + + for i, test := range testBlockStatus { + for j, subTest := range test.blockStatusesToCompareTo { + result1 := test.baseBlockStatus.Equal(subTest.blockStatus) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + + result2 := subTest.blockStatus.Equal(test.baseBlockStatus) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestBlockStatus_Clone(t *testing.T) { + + testBlockStatus := initTestBlockStatusForClone() + for i, blockStatus := range testBlockStatus { + blockStatusClone := blockStatus.Clone() + if !blockStatusClone.Equal(blockStatus) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(blockStatus, blockStatusClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/coinbase.go b/domain/consensus/model/externalapi/coinbase.go index a0ce7fead..5770ed48a 100644 --- a/domain/consensus/model/externalapi/coinbase.go +++ b/domain/consensus/model/externalapi/coinbase.go @@ -6,3 +6,17 @@ type DomainCoinbaseData struct { ScriptPublicKey []byte ExtraData []byte } + +// Clone returns a clone of DomainCoinbaseData +func (dcd *DomainCoinbaseData) Clone() *DomainCoinbaseData { + scriptPubKeyClone := make([]byte, len(dcd.ScriptPublicKey)) + copy(scriptPubKeyClone, dcd.ScriptPublicKey) + + extraDataClone := make([]byte, len(dcd.ExtraData)) + copy(extraDataClone, dcd.ExtraData) + + return &DomainCoinbaseData{ + ScriptPublicKey: scriptPubKeyClone, + ExtraData: extraDataClone, + } +} diff --git a/domain/consensus/model/externalapi/coinbase_clone_test.go b/domain/consensus/model/externalapi/coinbase_clone_test.go new file mode 100644 index 000000000..b7c29bbc7 --- /dev/null +++ b/domain/consensus/model/externalapi/coinbase_clone_test.go @@ -0,0 +1,59 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestCoinbaseDataStructsForClone() []*DomainCoinbaseData { + + tests := []*DomainCoinbaseData{ + { + []byte{1, 2, 3, 4, 5, 6}, + []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + }, { + []byte{0, 0, 0, 0, 55}, + []byte{0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF}, + }, + } + return tests +} + +func TestDomainCoinbaseData_Clone(t *testing.T) { + + coinbaseData := initTestCoinbaseDataStructsForClone() + for i, coinbase := range coinbaseData { + coinbaseClone := coinbase.Clone() + if !reflect.DeepEqual(coinbase, coinbaseClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/equal_test.go b/domain/consensus/model/externalapi/equal_test.go new file mode 100644 index 000000000..4ccd24508 --- /dev/null +++ b/domain/consensus/model/externalapi/equal_test.go @@ -0,0 +1,238 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func TestDomainBlockHeader_Equal(t *testing.T) { + type headerToCompare struct { + header *DomainBlockHeader + expectedResult bool + } + tests := []struct { + baseHeader *DomainBlockHeader + headersToCompareTo []headerToCompare + }{ + { + baseHeader: nil, + headersToCompareTo: []headerToCompare{ + { + header: nil, + expectedResult: true, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{0}}, + DomainHash{1}, + DomainHash{2}, + DomainHash{3}, + 4, + 5, + 6, + }, + expectedResult: false, + }, + }, + }, + { + baseHeader: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + headersToCompareTo: []headerToCompare{ + { + header: nil, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + expectedResult: true, + }, + { + header: &DomainBlockHeader{ + 100, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}, {2}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{100}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{100}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{100}, + DomainHash{4}, + 5, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{100}, + 5, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 100, + 6, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 100, + 7, + }, + expectedResult: false, + }, + { + header: &DomainBlockHeader{ + 0, + []*DomainHash{{1}}, + DomainHash{2}, + DomainHash{3}, + DomainHash{4}, + 5, + 6, + 100, + }, + expectedResult: false, + }, + }, + }, + } + + for i, test := range tests { + for j, subTest := range test.headersToCompareTo { + result1 := test.baseHeader.Equal(subTest.header) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + + result2 := subTest.header.Equal(test.baseHeader) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainBlockHeader_Clone(t *testing.T) { + headers := []*DomainBlockHeader{ + { + 0, + []*DomainHash{{0}}, + DomainHash{1}, + DomainHash{2}, + DomainHash{3}, + 4, + 5, + 6, + }, + { + 0, + []*DomainHash{}, + DomainHash{1}, + DomainHash{2}, + DomainHash{3}, + 4, + 5, + 6, + }, + } + + for i, header := range headers { + clone := header.Clone() + if !clone.Equal(header) { + t.Fatalf("Test #%d: clone should be equal to the original", i) + } + + if !reflect.DeepEqual(header, clone) { + t.Fatalf("Test #%d: clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/hash.go b/domain/consensus/model/externalapi/hash.go index 40446d6d1..e7080e1bc 100644 --- a/domain/consensus/model/externalapi/hash.go +++ b/domain/consensus/model/externalapi/hash.go @@ -19,14 +19,37 @@ func (hash DomainHash) String() string { // Clone clones the hash func (hash *DomainHash) Clone() *DomainHash { - if hash == nil { - return nil - } - hashClone := *hash return &hashClone } +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ DomainHash = [DomainHashSize]byte{} + +// Equal returns whether hash equals to other +func (hash *DomainHash) Equal(other *DomainHash) bool { + if hash == nil || other == nil { + return hash == other + } + + return *hash == *other +} + +// HashesEqual returns whether the given hash slices are equal. +func HashesEqual(a, b []*DomainHash) bool { + if len(a) != len(b) { + return false + } + + for i, hash := range a { + if !hash.Equal(b[i]) { + return false + } + } + return true +} + // CloneHashes returns a clone of the given hashes slice func CloneHashes(hashes []*DomainHash) []*DomainHash { clone := make([]*DomainHash, len(hashes)) diff --git a/domain/consensus/model/externalapi/hash_clone_equal_test.go b/domain/consensus/model/externalapi/hash_clone_equal_test.go new file mode 100644 index 000000000..89e2c844e --- /dev/null +++ b/domain/consensus/model/externalapi/hash_clone_equal_test.go @@ -0,0 +1,119 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestDomainHashForClone() []*DomainHash { + + tests := []*DomainHash{ + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, + } + return tests +} + +type testHashToCompare struct { + hash *DomainHash + expectedResult bool +} + +type testHashStruct struct { + baseHash *DomainHash + hashesToCompareTo []testHashToCompare +} + +func initTestDomainHashForEqual() []*testHashStruct { + tests := []*testHashStruct{ + { + baseHash: nil, + hashesToCompareTo: []testHashToCompare{ + { + hash: nil, + expectedResult: true, + }, { + hash: &DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + expectedResult: false, + }, + }, + }, { + baseHash: &DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, + hashesToCompareTo: []testHashToCompare{ + { + hash: nil, + expectedResult: false, + }, { + hash: &DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + expectedResult: false, + }, { + hash: &DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, + expectedResult: true, + }, + }, + }, + } + return tests +} + +func TestDomainHash_Equal(t *testing.T) { + + hashTests := initTestDomainHashForEqual() + for i, test := range hashTests { + for j, subTest := range test.hashesToCompareTo { + result1 := test.baseHash.Equal(subTest.hash) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.hash.Equal(test.baseHash) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainHash_Clone(t *testing.T) { + + hashes := initTestDomainHashForClone() + for i, hash := range hashes { + hashClone := hash.Clone() + if !hashClone.Equal(hash) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(hash, hashClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/subnetworkid.go b/domain/consensus/model/externalapi/subnetworkid.go index 8a948dff0..16f978eaa 100644 --- a/domain/consensus/model/externalapi/subnetworkid.go +++ b/domain/consensus/model/externalapi/subnetworkid.go @@ -18,10 +18,19 @@ func (id DomainSubnetworkID) String() string { // Clone returns a clone of DomainSubnetworkID func (id *DomainSubnetworkID) Clone() *DomainSubnetworkID { - if id == nil { - return nil - } - idClone := *id return &idClone } + +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ DomainSubnetworkID = [DomainSubnetworkIDSize]byte{} + +// Equal returns whether id equals to other +func (id *DomainSubnetworkID) Equal(other *DomainSubnetworkID) bool { + if id == nil || other == nil { + return id == other + } + + return *id == *other +} diff --git a/domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go b/domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go new file mode 100644 index 000000000..dc6d7c5f0 --- /dev/null +++ b/domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go @@ -0,0 +1,99 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestDomainSubnetworkIDForClone() []*DomainSubnetworkID { + + tests := []*DomainSubnetworkID{{1, 0, 0xFF, 0}, {0, 1, 0xFF, 1}, + {0, 1, 0xFF, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}} + return tests +} + +type testDomainSubnetworkIDToCompare struct { + domainSubnetworkID *DomainSubnetworkID + expectedResult bool +} + +type testDomainSubnetworkIDStruct struct { + baseDomainSubnetworkID *DomainSubnetworkID + domainSubnetworkIDToCompareTo []testDomainSubnetworkIDToCompare +} + +func initTestDomainSubnetworkIDForEqual() []testDomainSubnetworkIDStruct { + tests := []testDomainSubnetworkIDStruct{ + { + baseDomainSubnetworkID: nil, + domainSubnetworkIDToCompareTo: []testDomainSubnetworkIDToCompare{ + { + domainSubnetworkID: &DomainSubnetworkID{255, 255, 0xFF, 0}, + expectedResult: false, + }, + { + domainSubnetworkID: nil, + expectedResult: true, + }, + }, + }, { + baseDomainSubnetworkID: &DomainSubnetworkID{0}, + domainSubnetworkIDToCompareTo: []testDomainSubnetworkIDToCompare{ + { + domainSubnetworkID: &DomainSubnetworkID{255, 254, 0xFF, 0}, + expectedResult: false, + }, + { + domainSubnetworkID: &DomainSubnetworkID{0}, + expectedResult: true, + }, + }, + }, { + baseDomainSubnetworkID: &DomainSubnetworkID{0, 1, 0xFF, 1, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + domainSubnetworkIDToCompareTo: []testDomainSubnetworkIDToCompare{ + { + domainSubnetworkID: &DomainSubnetworkID{0, 1, 0xFF, 1, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + expectedResult: true, + }, + { + domainSubnetworkID: &DomainSubnetworkID{0, 10, 0xFF, 0}, + expectedResult: false, + }, + }, + }, + } + return tests +} + +func TestDomainSubnetworkID_Equal(t *testing.T) { + + domainSubnetworkIDs := initTestDomainSubnetworkIDForEqual() + for i, test := range domainSubnetworkIDs { + for j, subTest := range test.domainSubnetworkIDToCompareTo { + result1 := test.baseDomainSubnetworkID.Equal(subTest.domainSubnetworkID) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.domainSubnetworkID.Equal(test.baseDomainSubnetworkID) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainSubnetworkID_Clone(t *testing.T) { + + domainSubnetworkIDs := initTestDomainSubnetworkIDForClone() + for i, domainSubnetworkID := range domainSubnetworkIDs { + domainSubnetworkIDClone := domainSubnetworkID.Clone() + if !domainSubnetworkIDClone.Equal(domainSubnetworkID) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(domainSubnetworkID, domainSubnetworkIDClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/sync.go b/domain/consensus/model/externalapi/sync.go index d5f11a499..d1b717857 100644 --- a/domain/consensus/model/externalapi/sync.go +++ b/domain/consensus/model/externalapi/sync.go @@ -7,3 +7,42 @@ type SyncInfo struct { HeaderCount uint64 BlockCount uint64 } + +// Clone returns a clone of SyncInfo +func (si *SyncInfo) Clone() *SyncInfo { + return &SyncInfo{ + IsAwaitingUTXOSet: si.IsAwaitingUTXOSet, + IBDRootUTXOBlockHash: si.IBDRootUTXOBlockHash.Clone(), + HeaderCount: si.HeaderCount, + BlockCount: si.BlockCount, + } +} + +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = SyncInfo{false, &DomainHash{}, 0, 0} + +// Equal returns whether si equals to other +func (si *SyncInfo) Equal(other *SyncInfo) bool { + if si == nil || other == nil { + return si == other + } + + if si.IsAwaitingUTXOSet != other.IsAwaitingUTXOSet { + return false + } + + if !si.IBDRootUTXOBlockHash.Equal(other.IBDRootUTXOBlockHash) { + return false + } + + if si.HeaderCount != other.HeaderCount { + return false + } + + if si.BlockCount != other.BlockCount { + return false + } + + return true +} diff --git a/domain/consensus/model/externalapi/sync_equal_clone_test.go b/domain/consensus/model/externalapi/sync_equal_clone_test.go new file mode 100644 index 000000000..4a216c564 --- /dev/null +++ b/domain/consensus/model/externalapi/sync_equal_clone_test.go @@ -0,0 +1,126 @@ +package externalapi + +import ( + "reflect" + "testing" +) + +func initTestSyncInfoForClone() []*SyncInfo { + + tests := []*SyncInfo{{ + false, + &DomainHash{1, 2}, + 0xF, + 0xF}} + return tests +} + +type testSyncInfoToCompare struct { + syncInfo *SyncInfo + expectedResult bool +} + +type testSyncInfoStruct struct { + baseSyncInfo *SyncInfo + syncInfoToCompareTo []testSyncInfoToCompare +} + +func initTestSyncInfoForEqual() []*testSyncInfoStruct { + tests := []*testSyncInfoStruct{ + { + baseSyncInfo: nil, + syncInfoToCompareTo: []testSyncInfoToCompare{ + { + syncInfo: &SyncInfo{ + false, + &DomainHash{1, 2}, + 0xF, + 0xF}, + expectedResult: false, + }, { + syncInfo: nil, + expectedResult: true, + }, + }}, { + baseSyncInfo: &SyncInfo{ + false, + &DomainHash{1, 2}, + 0xF, + 0xF}, + syncInfoToCompareTo: []testSyncInfoToCompare{ + { + syncInfo: &SyncInfo{ + false, + &DomainHash{1, 2}, + 0xF, + 0xF}, + expectedResult: true, + }, { + syncInfo: &SyncInfo{ + true, + &DomainHash{1, 2}, + 0xF, + 0xF}, + expectedResult: false, + }, + { + syncInfo: &SyncInfo{ + false, + &DomainHash{1, 3}, + 0xF, + 0xF}, + expectedResult: false, + }, + { + syncInfo: &SyncInfo{ + false, + &DomainHash{1, 2}, + 0xF1, + 0xF}, + expectedResult: false, + }, { + syncInfo: nil, + expectedResult: false, + }, { + syncInfo: &SyncInfo{ + false, + &DomainHash{1, 2}, + 0xF, + 0xF1}, + expectedResult: false}, + }, + }, + } + return tests +} + +func TestSyncInfo_Equal(t *testing.T) { + + testSyncState := initTestSyncInfoForEqual() + for i, test := range testSyncState { + for j, subTest := range test.syncInfoToCompareTo { + result1 := test.baseSyncInfo.Equal(subTest.syncInfo) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.syncInfo.Equal(test.baseSyncInfo) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestSyncInfo_Clone(t *testing.T) { + + testSyncInfo := initTestSyncInfoForClone() + for i, syncInfo := range testSyncInfo { + syncStateClone := syncInfo.Clone() + if !syncStateClone.Equal(syncInfo) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(syncInfo, syncStateClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/transaction.go b/domain/consensus/model/externalapi/transaction.go index 0ffdf0e48..d0410e9f3 100644 --- a/domain/consensus/model/externalapi/transaction.go +++ b/domain/consensus/model/externalapi/transaction.go @@ -1,7 +1,9 @@ package externalapi import ( + "bytes" "fmt" + "github.com/pkg/errors" ) // DomainTransaction represents a Kaspa transaction @@ -25,10 +27,6 @@ type DomainTransaction struct { // Clone returns a clone of DomainTransaction func (tx *DomainTransaction) Clone() *DomainTransaction { - if tx == nil { - return nil - } - payloadClone := make([]byte, len(tx.Payload)) copy(payloadClone, tx.Payload) @@ -42,6 +40,11 @@ func (tx *DomainTransaction) Clone() *DomainTransaction { outputsClone[i] = output.Clone() } + var idClone *DomainTransactionID + if tx.ID != nil { + idClone = tx.ID.Clone() + } + return &DomainTransaction{ Version: tx.Version, Inputs: inputsClone, @@ -53,9 +56,81 @@ func (tx *DomainTransaction) Clone() *DomainTransaction { Payload: payloadClone, Fee: tx.Fee, Mass: tx.Mass, + ID: idClone, } } +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = DomainTransaction{0, []*DomainTransactionInput{}, []*DomainTransactionOutput{}, 0, + DomainSubnetworkID{}, 0, DomainHash{}, []byte{}, 0, 0, + &DomainTransactionID{}} + +// Equal returns whether tx equals to other +func (tx *DomainTransaction) Equal(other *DomainTransaction) bool { + if tx == nil || other == nil { + return tx == other + } + + if tx.Version != other.Version { + return false + } + + if len(tx.Inputs) != len(other.Inputs) { + return false + } + + for i, input := range tx.Inputs { + if !input.Equal(other.Inputs[i]) { + return false + } + } + + if len(tx.Outputs) != len(other.Outputs) { + return false + } + + for i, output := range tx.Outputs { + if !output.Equal(other.Outputs[i]) { + return false + } + } + + if tx.LockTime != other.LockTime { + return false + } + + if !tx.SubnetworkID.Equal(&other.SubnetworkID) { + return false + } + + if tx.Gas != other.Gas { + return false + } + + if !tx.PayloadHash.Equal(&other.PayloadHash) { + return false + } + + if !bytes.Equal(tx.Payload, other.Payload) { + return false + } + + if tx.Fee != other.Fee { + return false + } + + if tx.Mass != other.Mass { + return false + } + + if tx.ID != nil && other.ID != nil && !tx.ID.Equal(other.ID) { + panic(errors.New("identical transactions should always have the same ID")) + } + + return true +} + // DomainTransactionInput represents a Kaspa transaction input type DomainTransactionInput struct { PreviousOutpoint DomainOutpoint @@ -65,12 +140,37 @@ type DomainTransactionInput struct { UTXOEntry UTXOEntry } -// Clone returns a clone of DomainTransactionInput -func (input *DomainTransactionInput) Clone() *DomainTransactionInput { - if input == nil { - return nil +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &DomainTransactionInput{DomainOutpoint{}, []byte{}, 0, nil} + +// Equal returns whether input equals to other +func (input *DomainTransactionInput) Equal(other *DomainTransactionInput) bool { + if input == nil || other == nil { + return input == other } + if !input.PreviousOutpoint.Equal(&other.PreviousOutpoint) { + return false + } + + if !bytes.Equal(input.SignatureScript, other.SignatureScript) { + return false + } + + if input.Sequence != other.Sequence { + return false + } + + if !input.UTXOEntry.Equal(other.UTXOEntry) { + return false + } + + return true +} + +// Clone returns a clone of DomainTransactionInput +func (input *DomainTransactionInput) Clone() *DomainTransactionInput { signatureScriptClone := make([]byte, len(input.SignatureScript)) copy(signatureScriptClone, input.SignatureScript) @@ -88,12 +188,21 @@ type DomainOutpoint struct { Index uint32 } -// Clone returns a clone of DomainOutpoint -func (op *DomainOutpoint) Clone() *DomainOutpoint { - if op == nil { - return nil +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = DomainOutpoint{DomainTransactionID{}, 0} + +// Equal returns whether op equals to other +func (op *DomainOutpoint) Equal(other *DomainOutpoint) bool { + if op == nil || other == nil { + return op == other } + return *op == *other +} + +// Clone returns a clone of DomainOutpoint +func (op *DomainOutpoint) Clone() *DomainOutpoint { return &DomainOutpoint{ TransactionID: *op.TransactionID.Clone(), Index: op.Index, @@ -119,12 +228,29 @@ type DomainTransactionOutput struct { ScriptPublicKey []byte } -// Clone returns a clone of DomainTransactionOutput -func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput { - if output == nil { - return nil +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = DomainTransactionOutput{0, []byte{}} + +// Equal returns whether output equals to other +func (output *DomainTransactionOutput) Equal(other *DomainTransactionOutput) bool { + if output == nil || other == nil { + return output == other } + if output.Value != other.Value { + return false + } + + if !bytes.Equal(output.ScriptPublicKey, other.ScriptPublicKey) { + return false + } + + return true +} + +// Clone returns a clone of DomainTransactionOutput +func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput { scriptPublicKeyClone := make([]byte, len(output.ScriptPublicKey)) copy(scriptPublicKeyClone, output.ScriptPublicKey) @@ -144,10 +270,19 @@ func (id DomainTransactionID) String() string { // Clone returns a clone of DomainTransactionID func (id *DomainTransactionID) Clone() *DomainTransactionID { - if id == nil { - return nil - } - idClone := *id return &idClone } + +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ DomainTransactionID = [DomainHashSize]byte{} + +// Equal returns whether id equals to other +func (id *DomainTransactionID) Equal(other *DomainTransactionID) bool { + if id == nil || other == nil { + return id == other + } + + return *id == *other +} diff --git a/domain/consensus/model/externalapi/transaction_equal_clone_test.go b/domain/consensus/model/externalapi/transaction_equal_clone_test.go new file mode 100644 index 000000000..9a6c6e7b8 --- /dev/null +++ b/domain/consensus/model/externalapi/transaction_equal_clone_test.go @@ -0,0 +1,1133 @@ +package externalapi_test + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" + "reflect" + "testing" +) + +// Changed fields of a test struct compared to a base test struct marked as "changed" and +// pointing in some cases name changed struct field + +type transactionToCompare struct { + tx *externalapi.DomainTransaction + expectedResult bool + expectsPanic bool +} + +type testDomainTransactionStruct struct { + baseTx *externalapi.DomainTransaction + transactionToCompareTo []*transactionToCompare +} + +type transactionInputToCompare struct { + tx *externalapi.DomainTransactionInput + expectedResult bool +} + +type testDomainTransactionInputStruct struct { + baseTx *externalapi.DomainTransactionInput + transactionInputToCompareTo []*transactionInputToCompare +} + +type transactionOutputToCompare struct { + tx *externalapi.DomainTransactionOutput + expectedResult bool +} + +type testDomainTransactionOutputStruct struct { + baseTx *externalapi.DomainTransactionOutput + transactionOutputToCompareTo []*transactionOutputToCompare +} + +type domainOutpointToCompare struct { + domainOutpoint *externalapi.DomainOutpoint + expectedResult bool +} + +type testDomainOutpointStruct struct { + baseDomainOutpoint *externalapi.DomainOutpoint + domainOutpointToCompareTo []*domainOutpointToCompare +} + +type domainTransactionIDToCompare struct { + domainTransactionID *externalapi.DomainTransactionID + expectedResult bool +} + +type testDomainTransactionIDStruct struct { + baseDomainTransactionID *externalapi.DomainTransactionID + domainTransactionIDToCompareTo []*domainTransactionIDToCompare +} + +func initTestBaseTransaction() *externalapi.DomainTransaction { + + testTx := &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + } + return testTx +} + +func initTestTransactionToCompare() []*transactionToCompare { + + testTx := []*transactionToCompare{{ + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, //Changed + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 3}}, //Changed + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01, 0x02}, //Changed + 1, + externalapi.DomainHash{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01, 0x02}, //Changed + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: true, + }, + { + // ID changed + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + }, + expectsPanic: true, + }, + { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, + {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 1000000000, //Changed + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 2, //Changed + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, //6 + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 2, //Changed + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }, //7 + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 2, //Changed + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}, + {externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}, {uint64(0xFFFFF), + []byte{1, 2, 3}}}, //changed Outputs + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + nil, //changed + }, + expectedResult: true, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3, 4}, true, 2)}}, + []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}, {uint64(0xFFFF), + []byte{1, 3}}}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, + } + return testTx +} + +func initTestDomainTransactionForClone() []*externalapi.DomainTransaction { + + tests := []*externalapi.DomainTransaction{ + { + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{ + {externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}, + }, + Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF), + []byte{1, 2}}}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + Payload: []byte{0x01}, + Fee: 5555555555, + Mass: 1, + ID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, { + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{}, + Outputs: []*externalapi.DomainTransactionOutput{}, + LockTime: 1, + SubnetworkID: externalapi.DomainSubnetworkID{0x01}, + Gas: 1, + PayloadHash: externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + Payload: []byte{0x01}, + Fee: 0, + Mass: 1, + ID: &externalapi.DomainTransactionID{}, + }, + } + return tests +} + +func initTestDomainTransactionForEqual() []testDomainTransactionStruct { + + tests := []testDomainTransactionStruct{ + { + baseTx: initTestBaseTransaction(), + transactionToCompareTo: initTestTransactionToCompare(), + }, + { + baseTx: nil, + transactionToCompareTo: []*transactionToCompare{{ + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{}, + []*externalapi.DomainTransactionOutput{}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + expectedResult: false, + }, { + tx: nil, + expectedResult: true}}, + }, { + baseTx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{}, + []*externalapi.DomainTransactionOutput{}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + }, + transactionToCompareTo: []*transactionToCompare{{ + tx: nil, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{}, + []*externalapi.DomainTransactionOutput{}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 0, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + nil, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransaction{ + 1, + []*externalapi.DomainTransactionInput{}, + []*externalapi.DomainTransactionOutput{}, + 1, + externalapi.DomainSubnetworkID{0x01}, + 1, + externalapi.DomainHash{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01}, + 0, + 1, + nil, + }, + expectedResult: true, + }}, + }, + } + return tests +} + +func initTestBaseDomainTransactionInput() *externalapi.DomainTransactionInput { + basetxInput := &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + } + return basetxInput +} + +func initTestDomainTxInputToCompare() []*transactionInputToCompare { + txInput := []*transactionInputToCompare{{ + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }, + expectedResult: true, + }, { + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, false, 2), // Changed + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFF0), // Changed + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3, 4}, // Changed + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01, 0x02}, 0xFFFF}, // Changed + []byte{1, 2, 3}, + uint64(0xFFFFFFF0), // Changed + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01, 0x02}, 0xFFFF}, // Changed + []byte{1, 2, 3}, + uint64(0xFFFFFFF0), // Changed + utxo.NewUTXOEntry(2 /* Changed */, []byte{0, 1, 2, 3}, true, 2), // Changed + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionInput{ + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01, 0x02}, 0xFFFF}, // Changed + []byte{1, 2, 3}, + uint64(0xFFFFFFF0), // Changed + utxo.NewUTXOEntry(3 /* Changed */, []byte{0, 1, 2, 3}, true, 3), // Changed + }, + expectedResult: false, + }, { + tx: nil, + expectedResult: false, + }} + return txInput + +} + +func initTestDomainTransactionInputForClone() []*externalapi.DomainTransactionInput { + txInput := []*externalapi.DomainTransactionInput{ + { + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }, { + + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFFF), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }, { + + externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF}, + []byte{1, 2, 3}, + uint64(0xFFFFFFF0), + utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2), + }} + return txInput +} + +func initTestBaseDomainTransactionOutput() *externalapi.DomainTransactionOutput { + basetxOutput := &externalapi.DomainTransactionOutput{ + 0xFFFFFFFF, + []byte{0xFF, 0xFF}, + } + return basetxOutput +} + +func initTestDomainTransactionOutputForClone() []*externalapi.DomainTransactionOutput { + txInput := []*externalapi.DomainTransactionOutput{ + { + 0xFFFFFFFF, + []byte{0xF0, 0xFF}, + }, { + 0xFFFFFFF1, + []byte{0xFF, 0xFF}, + }} + return txInput +} + +func initTestDomainTransactionOutputForEqual() []testDomainTransactionOutputStruct { + tests := []testDomainTransactionOutputStruct{ + { + baseTx: initTestBaseDomainTransactionOutput(), + transactionOutputToCompareTo: []*transactionOutputToCompare{{ + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFFF, + []byte{0xFF, 0xFF}}, + expectedResult: true, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFFF, + []byte{0xF0, 0xFF}, // Changed + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFF0, // Changed + []byte{0xFF, 0xFF}, + }, + expectedResult: false, + }, { + tx: nil, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFF0, // Changed + []byte{0xFF, 0xFF, 0x01}, // Changed + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFF0, // Changed + []byte{}, // Changed + }, + expectedResult: false, + }}, + }, + { + baseTx: nil, + transactionOutputToCompareTo: []*transactionOutputToCompare{{ + tx: nil, + expectedResult: true, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFFF, + []byte{0xFF, 0xFF}}, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFFF, + []byte{0xF0, 0xFF}, // Changed + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFF0, // Changed + []byte{0xFF, 0xFF}, + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFF0, + []byte{0xFF, 0xFF, 0x01}, // Changed + }, + expectedResult: false, + }, { + tx: &externalapi.DomainTransactionOutput{ + 0xFFFFFFF0, + []byte{}, // Changed + }, + expectedResult: false, + }}, + }, + } + return tests +} + +func initTestDomainTransactionInputForEqual() []testDomainTransactionInputStruct { + + tests := []testDomainTransactionInputStruct{ + { + baseTx: initTestBaseDomainTransactionInput(), + transactionInputToCompareTo: initTestDomainTxInputToCompare(), + }, + } + return tests +} + +func TestDomainTransaction_Equal(t *testing.T) { + + txTests := initTestDomainTransactionForEqual() + for i, test := range txTests { + for j, subTest := range test.transactionToCompareTo { + func() { + defer func() { + r := recover() + panicked := r != nil + if panicked != subTest.expectsPanic { + t.Fatalf("panicked expected to be %t but got %t", subTest.expectsPanic, panicked) + } + }() + result1 := test.baseTx.Equal(subTest.tx) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + }() + func() { + defer func() { + r := recover() + panicked := r != nil + if panicked != subTest.expectsPanic { + t.Fatalf("panicked expected to be %t but got %t", subTest.expectsPanic, panicked) + } + }() + result2 := subTest.tx.Equal(test.baseTx) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + }() + } + } +} + +func TestDomainTransaction_Clone(t *testing.T) { + + txs := initTestDomainTransactionForClone() + for i, tx := range txs { + txClone := tx.Clone() + if !txClone.Equal(tx) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(tx, txClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} + +func TestDomainTransactionInput_Equal(t *testing.T) { + + txTests := initTestDomainTransactionInputForEqual() + for i, test := range txTests { + for j, subTest := range test.transactionInputToCompareTo { + result1 := test.baseTx.Equal(subTest.tx) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.tx.Equal(test.baseTx) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainTransactionInput_Clone(t *testing.T) { + + txInputs := initTestDomainTransactionInputForClone() + for i, txInput := range txInputs { + txInputClone := txInput.Clone() + if !txInputClone.Equal(txInput) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(txInput, txInputClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} + +func TestDomainTransactionOutput_Equal(t *testing.T) { + + txTests := initTestDomainTransactionOutputForEqual() + for i, test := range txTests { + for j, subTest := range test.transactionOutputToCompareTo { + result1 := test.baseTx.Equal(subTest.tx) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.tx.Equal(test.baseTx) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainTransactionOutput_Clone(t *testing.T) { + + txInputs := initTestDomainTransactionOutputForClone() + for i, txOutput := range txInputs { + txOutputClone := txOutput.Clone() + if !txOutputClone.Equal(txOutput) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(txOutput, txOutputClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} + +func initTestDomainOutpointForClone() []*externalapi.DomainOutpoint { + outpoint := []*externalapi.DomainOutpoint{{ + externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + 1}, + } + return outpoint +} + +func initTestDomainOutpointForEqual() []testDomainOutpointStruct { + + var outpoint = []*domainOutpointToCompare{{ + domainOutpoint: &externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + 1}, + expectedResult: true, + }, { + domainOutpoint: &externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + 1}, + expectedResult: false, + }, { + domainOutpoint: &externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0}, + 2}, + expectedResult: false, + }} + tests := []testDomainOutpointStruct{ + { + baseDomainOutpoint: &externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + 1}, + domainOutpointToCompareTo: outpoint, + }, {baseDomainOutpoint: &externalapi.DomainOutpoint{ + externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + 1}, + domainOutpointToCompareTo: []*domainOutpointToCompare{{domainOutpoint: nil, expectedResult: false}}, + }, {baseDomainOutpoint: nil, + domainOutpointToCompareTo: []*domainOutpointToCompare{{domainOutpoint: nil, expectedResult: true}}, + }, + } + return tests +} + +func TestDomainOutpoint_Equal(t *testing.T) { + + domainOutpoints := initTestDomainOutpointForEqual() + for i, test := range domainOutpoints { + for j, subTest := range test.domainOutpointToCompareTo { + result1 := test.baseDomainOutpoint.Equal(subTest.domainOutpoint) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.domainOutpoint.Equal(test.baseDomainOutpoint) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainOutpoint_Clone(t *testing.T) { + + domainOutpoints := initTestDomainOutpointForClone() + for i, outpoint := range domainOutpoints { + outpointClone := outpoint.Clone() + if !outpointClone.Equal(outpoint) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(outpoint, outpointClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} + +func initTestDomainTransactionIDForClone() []*externalapi.DomainTransactionID { + outpoint := []*externalapi.DomainTransactionID{ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + } + + return outpoint +} + +func initTestDomainTransactionIDForEqual() []testDomainTransactionIDStruct { + + var outpoint = []*domainTransactionIDToCompare{{ + domainTransactionID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + expectedResult: true, + }, { + domainTransactionID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + expectedResult: false, + }, { + domainTransactionID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0}, + expectedResult: false, + }} + tests := []testDomainTransactionIDStruct{ + { + baseDomainTransactionID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + domainTransactionIDToCompareTo: outpoint, + }, { + baseDomainTransactionID: nil, + domainTransactionIDToCompareTo: []*domainTransactionIDToCompare{{ + domainTransactionID: &externalapi.DomainTransactionID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + expectedResult: false, + }}, + }, + } + return tests +} + +func TestDomainTransactionID_Equal(t *testing.T) { + + domainDomainTransactionIDs := initTestDomainTransactionIDForEqual() + for i, test := range domainDomainTransactionIDs { + for j, subTest := range test.domainTransactionIDToCompareTo { + result1 := test.baseDomainTransactionID.Equal(subTest.domainTransactionID) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.domainTransactionID.Equal(test.baseDomainTransactionID) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestDomainTransactionID_Clone(t *testing.T) { + + domainDomainTransactionIDs := initTestDomainTransactionIDForClone() + for i, domainTransactionID := range domainDomainTransactionIDs { + domainTransactionIDClone := domainTransactionID.Clone() + if !domainTransactionIDClone.Equal(domainTransactionID) { + t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i) + } + if !reflect.DeepEqual(domainTransactionID, domainTransactionIDClone) { + t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/model/externalapi/utxoentry.go b/domain/consensus/model/externalapi/utxoentry.go index 22f9498b8..efc803809 100644 --- a/domain/consensus/model/externalapi/utxoentry.go +++ b/domain/consensus/model/externalapi/utxoentry.go @@ -9,4 +9,5 @@ type UTXOEntry interface { ScriptPublicKey() []byte // The public key script for the output. BlockBlueScore() uint64 // Blue score of the block accepting the tx. IsCoinbase() bool + Equal(other UTXOEntry) bool } diff --git a/domain/consensus/model/reachabilitydata.go b/domain/consensus/model/reachabilitydata.go index 93ffd3b20..f90c42e5a 100644 --- a/domain/consensus/model/reachabilitydata.go +++ b/domain/consensus/model/reachabilitydata.go @@ -12,15 +12,32 @@ type ReachabilityData struct { FutureCoveringSet FutureCoveringTreeNodeSet } -// Clone returns a clone of ReachabilityData -func (rd *ReachabilityData) Clone() *ReachabilityData { - if rd == nil { - return nil +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &ReachabilityData{&ReachabilityTreeNode{}, FutureCoveringTreeNodeSet{}} + +// Equal returns whether rd equals to other +func (rd *ReachabilityData) Equal(other *ReachabilityData) bool { + if rd == nil || other == nil { + return rd == other } + if !rd.TreeNode.Equal(other.TreeNode) { + return false + } + + if !rd.FutureCoveringSet.Equal(other.FutureCoveringSet) { + return false + } + + return true +} + +// Clone returns a clone of ReachabilityData +func (rd *ReachabilityData) Clone() *ReachabilityData { return &ReachabilityData{ TreeNode: rd.TreeNode.Clone(), - FutureCoveringSet: externalapi.CloneHashes(rd.FutureCoveringSet), + FutureCoveringSet: rd.FutureCoveringSet.Clone(), } } @@ -48,15 +65,43 @@ type ReachabilityTreeNode struct { Interval *ReachabilityInterval } +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &ReachabilityTreeNode{[]*externalapi.DomainHash{}, &externalapi.DomainHash{}, + &ReachabilityInterval{}} + +// Equal returns whether rtn equals to other +func (rtn *ReachabilityTreeNode) Equal(other *ReachabilityTreeNode) bool { + if rtn == nil || other == nil { + return rtn == other + } + + if !externalapi.HashesEqual(rtn.Children, other.Children) { + return false + } + + if !rtn.Parent.Equal(other.Parent) { + return false + } + + if !rtn.Interval.Equal(other.Interval) { + return false + } + + return true +} + // Clone returns a clone of ReachabilityTreeNode func (rtn *ReachabilityTreeNode) Clone() *ReachabilityTreeNode { - if rtn == nil { - return nil + + var parentClone *externalapi.DomainHash + if rtn.Parent != nil { + parentClone = rtn.Parent.Clone() } return &ReachabilityTreeNode{ Children: externalapi.CloneHashes(rtn.Children), - Parent: rtn.Parent.Clone(), + Parent: parentClone, Interval: rtn.Interval.Clone(), } } @@ -69,12 +114,29 @@ type ReachabilityInterval struct { End uint64 } -// Clone returns a clone of ReachabilityInterval -func (ri *ReachabilityInterval) Clone() *ReachabilityInterval { - if ri == nil { - return nil +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ = &ReachabilityInterval{0, 0} + +// Equal returns whether ri equals to other +func (ri *ReachabilityInterval) Equal(other *ReachabilityInterval) bool { + if ri == nil || other == nil { + return ri == other } + if ri.Start != other.Start { + return false + } + + if ri.End != other.End { + return false + } + + return true +} + +// Clone returns a clone of ReachabilityInterval +func (ri *ReachabilityInterval) Clone() *ReachabilityInterval { return &ReachabilityInterval{ Start: ri.Start, End: ri.End, @@ -98,3 +160,17 @@ func (ri *ReachabilityInterval) String() string { // // See insertNode, hasAncestorOf, and isInPast for further details. type FutureCoveringTreeNodeSet []*externalapi.DomainHash + +// Clone returns a clone of FutureCoveringTreeNodeSet +func (fctns FutureCoveringTreeNodeSet) Clone() FutureCoveringTreeNodeSet { + return externalapi.CloneHashes(fctns) +} + +// If this doesn't compile, it means the type definition has been changed, so it's +// an indication to update Equal and Clone accordingly. +var _ FutureCoveringTreeNodeSet = []*externalapi.DomainHash{} + +// Equal returns whether fctns equals to other +func (fctns FutureCoveringTreeNodeSet) Equal(other FutureCoveringTreeNodeSet) bool { + return externalapi.HashesEqual(fctns, other) +} diff --git a/domain/consensus/model/reachabilitydata_equal_clone_test.go b/domain/consensus/model/reachabilitydata_equal_clone_test.go new file mode 100644 index 000000000..408981ec9 --- /dev/null +++ b/domain/consensus/model/reachabilitydata_equal_clone_test.go @@ -0,0 +1,296 @@ +package model + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "reflect" + "testing" +) + +func TestReachabilityData_Equal(t *testing.T) { + type dataToCompare struct { + data *ReachabilityData + expectedResult bool + } + tests := []struct { + baseData *ReachabilityData + dataToCompareTo []dataToCompare + }{ + // Test nil data + { + baseData: nil, + dataToCompareTo: nil, + }, + // Test empty data + { + baseData: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{}, + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{}, + }, + dataToCompareTo: []dataToCompare{ + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{}, + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{}, + }, + expectedResult: true, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}}, // Changed + &externalapi.DomainHash{}, + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{1}, // Changed + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{}, + &ReachabilityInterval{100, 0}, // Changed start + }, + FutureCoveringTreeNodeSet{}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{}, + &ReachabilityInterval{0, 100}, // Changed end + }, + FutureCoveringTreeNodeSet{}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{}, + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, // Changed + }, + expectedResult: false, + }, + }, + }, + // Test filled data + { + baseData: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}, {3}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + dataToCompareTo: []dataToCompare{ + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}, {3}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: true, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}, {3}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{}, // Changed + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}, {3}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{200, 200}, // Changed start + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}, {3}}, + &externalapi.DomainHash{1}, + nil, //Changed + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}, {3}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 100}, // Changed end + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, // Changed + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}}, + &externalapi.DomainHash{}, // Changed + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{}, // Changed + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{}, // Changed + }, + expectedResult: false, + }, + { + data: &ReachabilityData{ + nil, + FutureCoveringTreeNodeSet{}, + }, + expectedResult: false, + }, + { + data: nil, + expectedResult: false, + }, + }, + }, + } + + for i, test := range tests { + for j, subTest := range test.dataToCompareTo { + result1 := test.baseData.Equal(subTest.data) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + + result2 := subTest.data.Equal(test.baseData) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} + +func TestReachabilityData_Clone(t *testing.T) { + testData := []*ReachabilityData{ + { + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{}, + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{}, + }, + { + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + { + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + { + &ReachabilityTreeNode{ + []*externalapi.DomainHash{{1}, {2}}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + { + &ReachabilityTreeNode{ + []*externalapi.DomainHash{}, + &externalapi.DomainHash{1}, + &ReachabilityInterval{100, 200}, + }, + FutureCoveringTreeNodeSet{{1}, {2}}, + }, + } + + for i, data := range testData { + clone := data.Clone() + if !clone.Equal(data) { + t.Fatalf("Test #%d: clone should be equal to the original", i) + } + + if !reflect.DeepEqual(data, clone) { + t.Fatalf("Test #%d: clone should be equal to the original", i) + } + } +} diff --git a/domain/consensus/processes/blockprocessor/validateandinsertblock.go b/domain/consensus/processes/blockprocessor/validateandinsertblock.go index 10ed66614..049388553 100644 --- a/domain/consensus/processes/blockprocessor/validateandinsertblock.go +++ b/domain/consensus/processes/blockprocessor/validateandinsertblock.go @@ -74,7 +74,7 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock, } var oldHeadersSelectedTip *externalapi.DomainHash - isGenesis := *blockHash == *bp.genesisHash + isGenesis := blockHash.Equal(bp.genesisHash) if !isGenesis { var err error oldHeadersSelectedTip, err = bp.headersSelectedTipStore.HeadersSelectedTip(bp.databaseContext) @@ -156,7 +156,7 @@ func (bp *blockProcessor) updateReachabilityReindexRoot(oldHeadersSelectedTip *e return err } - if *headersSelectedTip == *oldHeadersSelectedTip { + if headersSelectedTip.Equal(oldHeadersSelectedTip) { return nil } diff --git a/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go b/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go index c2e60cc01..826b6632b 100644 --- a/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go +++ b/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go @@ -17,7 +17,7 @@ func (bp *blockProcessor) validateAndInsertPruningPoint(newPruningPoint *externa newPruningPointHash := consensushashing.BlockHash(newPruningPoint) - if *expectedNewPruningPointHash != *newPruningPointHash { + if !expectedNewPruningPointHash.Equal(newPruningPointHash) { return errors.Wrapf(ruleerrors.ErrUnexpectedPruningPoint, "expected pruning point %s but got %s", expectedNewPruningPointHash, newPruningPointHash) } diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go index 5b9367cf1..4daa95b9f 100644 --- a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go +++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go @@ -143,7 +143,7 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock) error { calculatedHashMerkleRoot := merkle.CalculateHashMerkleRoot(block.Transactions) - if block.Header.HashMerkleRoot != *calculatedHashMerkleRoot { + if !block.Header.HashMerkleRoot.Equal(calculatedHashMerkleRoot) { return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block hash merkle root is invalid - block "+ "header indicates %s, but calculated value is %s", block.Header.HashMerkleRoot, calculatedHashMerkleRoot) diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context.go b/domain/consensus/processes/blockvalidator/block_header_in_context.go index 19f92a2a1..63976e8bf 100644 --- a/domain/consensus/processes/blockvalidator/block_header_in_context.go +++ b/domain/consensus/processes/blockvalidator/block_header_in_context.go @@ -86,7 +86,7 @@ func (v *blockValidator) hasValidatedHeader(blockHash *externalapi.DomainHash) ( func (v *blockValidator) checkParentsIncest(header *externalapi.DomainBlockHeader) error { for _, parentA := range header.ParentHashes { for _, parentB := range header.ParentHashes { - if *parentA == *parentB { + if parentA.Equal(parentB) { continue } diff --git a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go index c6402e6f9..31ca02e4c 100644 --- a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go +++ b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go @@ -35,7 +35,7 @@ func (v *blockValidator) ValidateHeaderInIsolation(blockHash *externalapi.Domain func (v *blockValidator) checkParentsLimit(header *externalapi.DomainBlockHeader) error { hash := consensushashing.HeaderHash(header) - if len(header.ParentHashes) == 0 && *hash != *v.genesisHash { + if len(header.ParentHashes) == 0 && !hash.Equal(v.genesisHash) { return errors.Wrapf(ruleerrors.ErrNoParents, "block has no parents") } diff --git a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go index aab8210ae..01df31a28 100644 --- a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go +++ b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go @@ -68,7 +68,7 @@ func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(block log.Tracef("isCandidateToBeNextVirtualSelectedParent start for block %s", blockHash) defer log.Tracef("isCandidateToBeNextVirtualSelectedParent end for block %s", blockHash) - if *blockHash == *csm.genesisHash { + if blockHash.Equal(csm.genesisHash) { log.Tracef("Block %s is the genesis block, therefore it is "+ "the selected parent by definition", blockHash) return true, nil @@ -87,7 +87,7 @@ func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(block } log.Tracef("The next selected parent is: %s", nextVirtualSelectedParent) - return *blockHash == *nextVirtualSelectedParent, nil + return blockHash.Equal(nextVirtualSelectedParent), nil } func (csm *consensusStateManager) addTip(newTipHash *externalapi.DomainHash) (newTips []*externalapi.DomainHash, err error) { @@ -111,7 +111,7 @@ func (csm *consensusStateManager) calculateNewTips(newTipHash *externalapi.Domai log.Tracef("calculateNewTips start for new tip %s", newTipHash) defer log.Tracef("calculateNewTips end for new tip %s", newTipHash) - if *newTipHash == *csm.genesisHash { + if newTipHash.Equal(csm.genesisHash) { log.Tracef("The new tip is the genesis block, therefore it is the only tip by definition") return []*externalapi.DomainHash{newTipHash}, nil } @@ -133,7 +133,7 @@ func (csm *consensusStateManager) calculateNewTips(newTipHash *externalapi.Domai for _, currentTip := range currentTips { isCurrentTipInNewTipParents := false for _, newTipParent := range newTipParents { - if *currentTip == *newTipParent { + if currentTip.Equal(newTipParent) { isCurrentTipInNewTipParents = true break } diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go index 42d214bbf..3b6c3c419 100644 --- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go @@ -18,7 +18,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash * log.Tracef("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash) defer log.Tracef("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash) - if *blockHash == *csm.genesisHash { + if blockHash.Equal(csm.genesisHash) { log.Tracef("Block %s is the genesis. By definition, "+ "it has an empty UTXO diff, empty acceptance data, and a blank multiset", blockHash) return utxo.NewUTXODiff(), externalapi.AcceptanceData{}, multiset.New(), nil diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go index a608340e7..ed38040db 100644 --- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go +++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go @@ -154,7 +154,7 @@ func TestPastUTXOMultiset(t *testing.T) { secondMultisetHash := secondMultiset.Hash() // Make sure the multiset hasn't changed - if *firstMultisetHash != *secondMultisetHash { + if !firstMultisetHash.Equal(secondMultisetHash) { t.Fatalf("TestPastUTXOMultiSet: selectedParentMultiset appears to have changed!") } }) diff --git a/domain/consensus/processes/consensusstatemanager/check_finality_violation.go b/domain/consensus/processes/consensusstatemanager/check_finality_violation.go index 9774276fe..4451c5cee 100644 --- a/domain/consensus/processes/consensusstatemanager/check_finality_violation.go +++ b/domain/consensus/processes/consensusstatemanager/check_finality_violation.go @@ -8,7 +8,7 @@ func (csm *consensusStateManager) isViolatingFinality(blockHash *externalapi.Dom log.Tracef("isViolatingFinality start for block %s", blockHash) defer log.Tracef("isViolatingFinality end for block %s", blockHash) - if *blockHash == *csm.genesisHash { + if blockHash.Equal(csm.genesisHash) { log.Tracef("Block %s is the genesis block, "+ "and does not violate finality by definition", blockHash) return false, false, nil diff --git a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go index 965354283..d27b92e4c 100644 --- a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go +++ b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go @@ -51,7 +51,7 @@ func (csm *consensusStateManager) calculateSelectedParentChainChanges( // Walk down from the toBlockHash to the common ancestor var added []*externalapi.DomainHash current = toBlockHash - for *current != *commonAncestor { + for !current.Equal(commonAncestor) { added = append(added, current) currentGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, current) if err != nil { diff --git a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go index f2306e142..c90ef0680 100644 --- a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go +++ b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go @@ -35,7 +35,7 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) { t.Fatalf("The `added` slice contains an unexpected amount of items after inserting block A. "+ "Want: %d, got: %d", 1, len(blockASelectedParentChainChanges.Added)) } - if *blockASelectedParentChainChanges.Added[0] != *blockAHash { + if !blockASelectedParentChainChanges.Added[0].Equal(blockAHash) { t.Fatalf("The `added` slice contains an unexpected hash. Want: %s, got: %s", blockAHash, blockASelectedParentChainChanges.Added[0]) } @@ -53,7 +53,7 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) { } virtualSelectedParent := virtualGHOSTDAGData.SelectedParent() notVirtualSelectedParent := blockAHash - if *virtualSelectedParent == *blockAHash { + if virtualSelectedParent.Equal(blockAHash) { notVirtualSelectedParent = blockBHash } @@ -71,7 +71,7 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) { t.Fatalf("The `removed` slice contains an unexpected amount of items after inserting block C. "+ "Want: %d, got: %d", 1, len(blockCSelectedParentChainChanges.Removed)) } - if *blockCSelectedParentChainChanges.Removed[0] != *virtualSelectedParent { + if !blockCSelectedParentChainChanges.Removed[0].Equal(virtualSelectedParent) { t.Fatalf("The `removed` slice contains an unexpected hash. "+ "Want: %s, got: %s", virtualSelectedParent, blockCSelectedParentChainChanges.Removed[0]) } @@ -82,11 +82,11 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) { t.Fatalf("The `added` slice contains an unexpected amount of items after inserting block C. "+ "Want: %d, got: %d", 2, len(blockCSelectedParentChainChanges.Added)) } - if *blockCSelectedParentChainChanges.Added[0] != *notVirtualSelectedParent { + if !blockCSelectedParentChainChanges.Added[0].Equal(notVirtualSelectedParent) { t.Fatalf("The `added` slice contains an unexpected hash as the first item. "+ "Want: %s, got: %s", notVirtualSelectedParent, blockCSelectedParentChainChanges.Added[0]) } - if *blockCSelectedParentChainChanges.Added[1] != *blockCHash { + if !blockCSelectedParentChainChanges.Added[1].Equal(blockCHash) { t.Fatalf("The `added` slice contains an unexpected hash as the second item. "+ "Want: %s, got: %s", blockCHash, blockCSelectedParentChainChanges.Added[1]) } diff --git a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go index 103da8975..3746f2422 100644 --- a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go +++ b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go @@ -109,7 +109,7 @@ func (csm *consensusStateManager) selectVirtualSelectedParent( // remove virtual from parentChildren if it's there for i, parentChild := range parentChildren { - if *parentChild == *model.VirtualBlockHash { + if parentChild.Equal(model.VirtualBlockHash) { parentChildren = append(parentChildren[:i], parentChildren[i+1:]...) break } diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go index 6f28d7312..8123aa690 100644 --- a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go +++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go @@ -70,7 +70,7 @@ func (csm *consensusStateManager) findSelectedParentStatus(unverifiedBlocks []*e defer log.Tracef("findSelectedParentStatus end") lastUnverifiedBlock := unverifiedBlocks[len(unverifiedBlocks)-1] - if *lastUnverifiedBlock == *csm.genesisHash { + if lastUnverifiedBlock.Equal(csm.genesisHash) { log.Tracef("the most recent unverified block is the genesis block, "+ "which by definition has status: %s", externalapi.StatusUTXOValid) return externalapi.StatusUTXOValid, nil @@ -173,7 +173,7 @@ func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssign log.Tracef("removeAncestorsFromVirtualDiffParentsAndAssignDiffChild start for block %s", blockHash) defer log.Tracef("removeAncestorsFromVirtualDiffParentsAndAssignDiffChild end for block %s", blockHash) - if *blockHash == *csm.genesisHash { + if blockHash.Equal(csm.genesisHash) { log.Tracef("Genesis block doesn't have ancestors to remove from the virtual diff parents") return nil } @@ -184,7 +184,7 @@ func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssign } for _, virtualDiffParent := range virtualDiffParents { - if *virtualDiffParent == *blockHash { + if virtualDiffParent.Equal(blockHash) { log.Tracef("Skipping updating virtual diff parent %s "+ "because it was updated before.", virtualDiffParent) continue diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go index 52dc97612..77f038e27 100644 --- a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go +++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go @@ -56,7 +56,7 @@ func TestDoubleSpends(t *testing.T) { spendingTransaction2.Outputs[0].Value-- // tweak the value to create a different ID spendingTransaction1ID := consensushashing.TransactionID(spendingTransaction1) spendingTransaction2ID := consensushashing.TransactionID(spendingTransaction2) - if *spendingTransaction1ID == *spendingTransaction2ID { + if spendingTransaction1ID.Equal(spendingTransaction2ID) { t.Fatalf("spendingTransaction1 and spendingTransaction2 ids are equal") } diff --git a/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go b/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go index 87847d699..a01c05672 100644 --- a/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go +++ b/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go @@ -63,7 +63,7 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap log.Debugf("The UTXO commitment of the pruning point: %s", newPruningPointHeader.UTXOCommitment) - if newPruningPointHeader.UTXOCommitment != *utxoSetMultiSet.Hash() { + if !newPruningPointHeader.UTXOCommitment.Equal(utxoSetMultiSet.Hash()) { return errors.Wrapf(ruleerrors.ErrBadPruningPointUTXOSet, "the expected multiset hash of the pruning "+ "point UTXO set is %s but got %s", newPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash()) } diff --git a/domain/consensus/processes/consensusstatemanager/update_virtual.go b/domain/consensus/processes/consensusstatemanager/update_virtual.go index 1d80d2458..9d50dc8e5 100644 --- a/domain/consensus/processes/consensusstatemanager/update_virtual.go +++ b/domain/consensus/processes/consensusstatemanager/update_virtual.go @@ -13,7 +13,7 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain log.Tracef("Saving a reference to the GHOSTDAG data of the old virtual") var oldVirtualSelectedParent *externalapi.DomainHash - if *newBlockHash != *csm.genesisHash { + if !newBlockHash.Equal(csm.genesisHash) { oldVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, model.VirtualBlockHash) if err != nil { return nil, err @@ -65,7 +65,7 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain log.Tracef("Calculating selected parent chain changes") var selectedParentChainChanges *externalapi.SelectedParentChainChanges - if *newBlockHash != *csm.genesisHash { + if !newBlockHash.Equal(csm.genesisHash) { newVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, model.VirtualBlockHash) if err != nil { return nil, err diff --git a/domain/consensus/processes/consensusstatemanager/utxo_diffs.go b/domain/consensus/processes/consensusstatemanager/utxo_diffs.go index 564ca9fa2..4ba8d5325 100644 --- a/domain/consensus/processes/consensusstatemanager/utxo_diffs.go +++ b/domain/consensus/processes/consensusstatemanager/utxo_diffs.go @@ -29,7 +29,7 @@ func (csm *consensusStateManager) addToVirtualDiffParents(blockHash *externalapi defer log.Tracef("addToVirtualDiffParents end for block %s", blockHash) var oldVirtualDiffParents []*externalapi.DomainHash - if *blockHash != *csm.genesisHash { + if !blockHash.Equal(csm.genesisHash) { var err error oldVirtualDiffParents, err = csm.consensusStateStore.VirtualDiffParents(csm.databaseContext) if err != nil { @@ -39,7 +39,7 @@ func (csm *consensusStateManager) addToVirtualDiffParents(blockHash *externalapi isInVirtualDiffParents := false for _, diffParent := range oldVirtualDiffParents { - if *diffParent == *blockHash { + if diffParent.Equal(blockHash) { isInVirtualDiffParents = true break } @@ -67,7 +67,7 @@ func (csm *consensusStateManager) removeFromVirtualDiffParents(blockHash *extern newVirtualDiffParents := make([]*externalapi.DomainHash, 0, len(oldVirtualDiffParents)-1) for _, diffParent := range oldVirtualDiffParents { - if *diffParent != *blockHash { + if !diffParent.Equal(blockHash) { newVirtualDiffParents = append(newVirtualDiffParents, diffParent) } } diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go index c5a0eef2c..b4ecd098e 100644 --- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go @@ -103,7 +103,7 @@ func (csm *consensusStateManager) validateAcceptedIDMerkleRoot(block *externalap defer log.Tracef("validateAcceptedIDMerkleRoot end for block %s", blockHash) calculatedAcceptedIDMerkleRoot := calculateAcceptedIDMerkleRoot(acceptanceData) - if block.Header.AcceptedIDMerkleRoot != *calculatedAcceptedIDMerkleRoot { + if !block.Header.AcceptedIDMerkleRoot.Equal(calculatedAcceptedIDMerkleRoot) { return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block %s accepted ID merkle root is invalid - block "+ "header indicates %s, but calculated value is %s", blockHash, &block.Header.UTXOCommitment, calculatedAcceptedIDMerkleRoot) @@ -119,7 +119,7 @@ func (csm *consensusStateManager) validateUTXOCommitment( defer log.Tracef("validateUTXOCommitment end for block %s", blockHash) multisetHash := multiset.Hash() - if block.Header.UTXOCommitment != *multisetHash { + if !block.Header.UTXOCommitment.Equal(multisetHash) { return errors.Wrapf(ruleerrors.ErrBadUTXOCommitment, "block %s UTXO commitment is invalid - block "+ "header indicates %s, but calculated value is %s", blockHash, &block.Header.UTXOCommitment, multisetHash) } @@ -173,7 +173,7 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(blockHash *externa log.Tracef("given coinbase hash: %s, expected coinbase hash: %s", coinbaseTransactionHash, expectedCoinbaseTransactionHash) - if *coinbaseTransactionHash != *expectedCoinbaseTransactionHash { + if !coinbaseTransactionHash.Equal(expectedCoinbaseTransactionHash) { return errors.Wrap(ruleerrors.ErrBadCoinbaseTransaction, "coinbase transaction is not built as expected") } diff --git a/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go b/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go index 245fbfb13..e5c69cd6c 100644 --- a/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go +++ b/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go @@ -99,7 +99,7 @@ func (dtm *dagTopologyManager) IsInSelectedParentChainOf(blockHashA *externalapi func isHashInSlice(hash *externalapi.DomainHash, hashes []*externalapi.DomainHash) bool { for _, h := range hashes { - if *h == *hash { + if h.Equal(hash) { return true } } @@ -128,7 +128,7 @@ func (dtm *dagTopologyManager) SetParents(blockHash *externalapi.DomainHash, par return err } for i, parentChild := range parentRelations.Children { - if *parentChild == *blockHash { + if parentChild.Equal(blockHash) { parentRelations.Children = append(parentRelations.Children[:i], parentRelations.Children[i+1:]...) dtm.blockRelationStore.StageBlockRelation(currentParent, parentRelations) break @@ -145,7 +145,7 @@ func (dtm *dagTopologyManager) SetParents(blockHash *externalapi.DomainHash, par } isBlockAlreadyInChildren := false for _, parentChild := range parentRelations.Children { - if *parentChild == *blockHash { + if parentChild.Equal(blockHash) { isBlockAlreadyInChildren = true break } @@ -180,7 +180,7 @@ func (dtm *dagTopologyManager) ChildInSelectedParentChainOf( selectedParent := ghostdagData.SelectedParent() // In case where `blockHash` is an immediate parent of `highHash` - if *blockHash == *selectedParent { + if blockHash.Equal(selectedParent) { return highHash, nil } highHash = selectedParent diff --git a/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go b/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go index cdce9fd6a..9db1f12b3 100644 --- a/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go +++ b/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go @@ -20,7 +20,7 @@ func (s *selectedChildIterator) Next() bool { } for _, child := range children { - if *child == *model.VirtualBlockHash { + if child.Equal(model.VirtualBlockHash) { continue } diff --git a/domain/consensus/processes/finalitymanager/finality_manager.go b/domain/consensus/processes/finalitymanager/finality_manager.go index b5fb1ba29..67cf5b811 100644 --- a/domain/consensus/processes/finalitymanager/finality_manager.go +++ b/domain/consensus/processes/finalitymanager/finality_manager.go @@ -51,7 +51,7 @@ func (fm *finalityManager) VirtualFinalityPoint() (*externalapi.DomainHash, erro func (fm *finalityManager) FinalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) { log.Tracef("FinalityPoint start") defer log.Tracef("FinalityPoint end") - if *blockHash == *model.VirtualBlockHash { + if blockHash.Equal(model.VirtualBlockHash) { return fm.VirtualFinalityPoint() } finalityPoint, err := fm.finalityStore.FinalityPoint(fm.databaseContext, blockHash) @@ -88,7 +88,7 @@ func (fm *finalityManager) calculateFinalityPoint(blockHash *externalapi.DomainH } selectedParent := ghostdagData.SelectedParent() - if *selectedParent == *fm.genesisHash { + if selectedParent.Equal(fm.genesisHash) { return fm.genesisHash, nil } diff --git a/domain/consensus/processes/ghostdag2/ghostdagimpl.go b/domain/consensus/processes/ghostdag2/ghostdagimpl.go index 6a84f7b70..ec2ad1ef1 100644 --- a/domain/consensus/processes/ghostdag2/ghostdagimpl.go +++ b/domain/consensus/processes/ghostdag2/ghostdagimpl.go @@ -92,7 +92,7 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error } for _, mergeSetBlock := range mergeSetArr { - if *mergeSetBlock == *selectedParent { + if mergeSetBlock.Equal(selectedParent) { if !contains(selectedParent, mergeSetBlues) { mergeSetBlues = append(mergeSetBlues, selectedParent) blueSet = append(blueSet, selectedParent) @@ -252,7 +252,7 @@ func (gh *ghostdagHelper) validateKCluster(chain *externalapi.DomainHash, checke /*----------------contains-------------------------- */ func contains(item *externalapi.DomainHash, items []*externalapi.DomainHash) bool { for _, r := range items { - if *r == *item { + if r.Equal(item) { return true } } @@ -294,7 +294,7 @@ func (gh *ghostdagHelper) findMergeSet(parents []*externalapi.DomainHash, select for len(blockQueue) > 0 { block := blockQueue[0] blockQueue = blockQueue[1:] - if *selectedParent == *block { + if selectedParent.Equal(block) { if !contains(block, allMergeSet) { allMergeSet = append(allMergeSet, block) } diff --git a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go index a2d248cd1..08f64b32c 100644 --- a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go +++ b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go @@ -137,7 +137,7 @@ func TestGHOSTDAG(t *testing.T) { factory.implName, info.Name(), testBlockData.ID, testBlockData.Score, ghostdagData.BlueScore()) } - if *StringToByte(testBlockData.SelectedParent) != *ghostdagData.SelectedParent() { + if !StringToByte(testBlockData.SelectedParent).Equal(ghostdagData.SelectedParent()) { t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected selected parent %v but got %v.", factory.implName, info.Name(), testBlockData.ID, testBlockData.SelectedParent, string(ghostdagData.SelectedParent()[:])) } @@ -267,7 +267,7 @@ func (dt *DAGTopologyManagerImpl) IsAncestorOf(hashBlockA *externalapi.DomainHas } for _, parentOfB := range blockBParents { - if *parentOfB == *hashBlockA { + if parentOfB.Equal(hashBlockA) { return true, nil } } diff --git a/domain/consensus/processes/ghostdagmanager/mergeset.go b/domain/consensus/processes/ghostdagmanager/mergeset.go index ead66812f..4668eb127 100644 --- a/domain/consensus/processes/ghostdagmanager/mergeset.go +++ b/domain/consensus/processes/ghostdagmanager/mergeset.go @@ -14,7 +14,7 @@ func (gm *ghostdagManager) mergeSetWithoutSelectedParent(selecteParent *external queue := []*externalapi.DomainHash{} // Queueing all parents (other than the selected parent itself) for processing. for _, parent := range blockParents { - if *parent == *selecteParent { + if parent.Equal(selecteParent) { continue } mergeSetMap[*parent] = struct{}{} diff --git a/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go b/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go index 4ff750563..f4bb6f16b 100644 --- a/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go +++ b/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go @@ -46,7 +46,7 @@ func (h *headerTipsManager) AddHeaderTip(hash *externalapi.DomainHash) error { return err } - if *newHeadersSelectedTip != *headersSelectedTip { + if !newHeadersSelectedTip.Equal(headersSelectedTip) { h.headersSelectedTipStore.Stage(newHeadersSelectedTip) } } diff --git a/domain/consensus/processes/pruningmanager/pruningmanager.go b/domain/consensus/processes/pruningmanager/pruningmanager.go index 11a0e7431..1c87d7a81 100644 --- a/domain/consensus/processes/pruningmanager/pruningmanager.go +++ b/domain/consensus/processes/pruningmanager/pruningmanager.go @@ -124,7 +124,7 @@ func (pm *pruningManager) UpdatePruningPointByVirtual() error { return err } - if *newPruningPoint != *currentP { + if !newPruningPoint.Equal(currentP) { err = pm.savePruningPoint(newPruningPoint) if err != nil { return err diff --git a/domain/consensus/processes/reachabilitymanager/reachability_external_test.go b/domain/consensus/processes/reachabilitymanager/reachability_external_test.go index 8d2674955..01f0aaac6 100644 --- a/domain/consensus/processes/reachabilitymanager/reachability_external_test.go +++ b/domain/consensus/processes/reachabilitymanager/reachability_external_test.go @@ -26,7 +26,7 @@ func TestAddChildThatPointsDirectlyToTheSelectedParentChainBelowReindexRoot(t *t t.Fatalf("ReachabilityReindexRoot: %s", err) } - if *reindexRoot != *params.GenesisHash { + if !reindexRoot.Equal(params.GenesisHash) { t.Fatalf("reindex root is expected to initially be genesis") } @@ -52,7 +52,7 @@ func TestAddChildThatPointsDirectlyToTheSelectedParentChainBelowReindexRoot(t *t t.Fatalf("ReachabilityReindexRoot: %s", err) } - if *newReindexRoot == *reindexRoot { + if newReindexRoot.Equal(reindexRoot) { t.Fatalf("reindex root is expected to change") } @@ -114,7 +114,7 @@ func TestUpdateReindexRoot(t *testing.T) { t.Fatalf("ReachabilityReindexRoot: %s", err) } - if *reindexRoot != *params.GenesisHash { + if !reindexRoot.Equal(params.GenesisHash) { t.Fatalf("reindex root unexpectedly moved") } } @@ -131,7 +131,7 @@ func TestUpdateReindexRoot(t *testing.T) { t.Fatalf("ReachabilityReindexRoot: %s", err) } - if *reindexRoot != *chain1RootBlock { + if !reindexRoot.Equal(chain1RootBlock) { t.Fatalf("chain1RootBlock is not the reindex root after reindex") } @@ -207,7 +207,7 @@ func TestReindexIntervalsEarlierThanReindexRoot(t *testing.T) { t.Fatalf("ReachabilityReindexRoot: %s", err) } - if *reindexRoot != *centerBlock { + if !reindexRoot.Equal(centerBlock) { t.Fatalf("centerBlock is not the reindex root after reindex") } diff --git a/domain/consensus/processes/reachabilitymanager/reachability_test.go b/domain/consensus/processes/reachabilitymanager/reachability_test.go index 448ebb031..0e7f36af7 100644 --- a/domain/consensus/processes/reachabilitymanager/reachability_test.go +++ b/domain/consensus/processes/reachabilitymanager/reachability_test.go @@ -950,7 +950,7 @@ func BenchmarkReindexInterval(b *testing.B) { currentTreeNode = childTreeNode } - originalRemainingInterval := *helper.remainingIntervalAfter(root) + originalRemainingInterval := helper.remainingIntervalAfter(root).Clone() // After we added subTreeSize nodes, adding the next // node should lead to a reindex from root. fullReindexTriggeringNode := helper.newNode() @@ -961,7 +961,7 @@ func BenchmarkReindexInterval(b *testing.B) { b.Fatalf("addChild: %s", err) } - if *helper.remainingIntervalAfter(root) == originalRemainingInterval { + if helper.remainingIntervalAfter(root).Equal(originalRemainingInterval) { b.Fatal("Expected a reindex from root, but it didn't happen") } } diff --git a/domain/consensus/processes/reachabilitymanager/tree.go b/domain/consensus/processes/reachabilitymanager/tree.go index 054b3836e..2e2f3398c 100644 --- a/domain/consensus/processes/reachabilitymanager/tree.go +++ b/domain/consensus/processes/reachabilitymanager/tree.go @@ -317,7 +317,7 @@ func (rt *reachabilityManager) countSubtrees(node *externalapi.DomainHash, subTr // We reached a leaf or a pre-calculated subtree. // Push information up - for *current != *node { + for !current.Equal(node) { current, err = rt.parent(current) if err != nil { return err @@ -461,7 +461,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces return err } - if currentHasSlackIntervalBefore || *current == *reindexRoot { + if currentHasSlackIntervalBefore || current.Equal(reindexRoot) { break } @@ -471,7 +471,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces } } - if *current == *reindexRoot { + if current.Equal(reindexRoot) { // "Deallocate" an interval of slackReachabilityIntervalForReclaiming // from this node. This is the interval that we'll use for the new // node. @@ -505,7 +505,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces // current node with an interval that is smaller by // slackReachabilityIntervalForReclaiming. This is to make room // for the new node. - for *current != *commonAncestor { + for !current.Equal(commonAncestor) { currentInterval, err := rt.interval(current) if err != nil { return err @@ -583,7 +583,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces return err } - if currentHasSlackIntervalAfter || *current == *reindexRoot { + if currentHasSlackIntervalAfter || current.Equal(reindexRoot) { break } @@ -593,7 +593,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces } } - if *current == *reindexRoot { + if current.Equal(reindexRoot) { // "Deallocate" an interval of slackReachabilityIntervalForReclaiming // from this node. This is the interval that we'll use for the new // node. @@ -627,7 +627,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces // current node with an interval that is smaller by // slackReachabilityIntervalForReclaiming. This is to make room // for the new node. - for *current != *commonAncestor { + for !current.Equal(commonAncestor) { currentInterval, err := rt.interval(current) if err != nil { return err @@ -883,7 +883,7 @@ func (rt *reachabilityManager) splitChildrenAroundChild(node, child *externalapi } for i, candidateChild := range nodeChildren { - if *candidateChild == *child { + if candidateChild.Equal(child) { return nodeChildren[:i], nodeChildren[i+1:], nil } } diff --git a/domain/consensus/processes/syncmanager/syncinfo.go b/domain/consensus/processes/syncmanager/syncinfo.go index 90a5009d8..f62c10a96 100644 --- a/domain/consensus/processes/syncmanager/syncinfo.go +++ b/domain/consensus/processes/syncmanager/syncinfo.go @@ -36,7 +36,7 @@ func (sm *syncManager) isAwaitingUTXOSet() (isAwaitingUTXOSet bool, ibdRootUTXOB // If the pruning point by headers is different from the current point // it means we need to request the new pruning point UTXO set. - if *pruningPoint != *pruningPointByHeaders { + if !pruningPoint.Equal(pruningPointByHeaders) { return true, pruningPointByHeaders, nil } diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go b/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go index eee4d8168..9d0ecb65e 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go @@ -128,7 +128,7 @@ func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransac func (v *transactionValidator) checkTransactionPayloadHash(tx *externalapi.DomainTransaction) error { if tx.SubnetworkID != subnetworks.SubnetworkIDNative { payloadHash := hashes.PayloadHash(tx.Payload) - if tx.PayloadHash != *payloadHash { + if !tx.PayloadHash.Equal(payloadHash) { return errors.Wrapf(ruleerrors.ErrInvalidPayloadHash, "invalid payload hash") } } else if tx.PayloadHash != (externalapi.DomainHash{}) { @@ -177,7 +177,7 @@ func (v *transactionValidator) checkTransactionSubnetwork(tx *externalapi.Domain // If we are a partial node, only transactions on built in subnetworks // or our own subnetwork may have a payload isLocalNodeFull := localNodeSubnetworkID == nil - shouldTxBeFull := subnetworks.IsBuiltIn(tx.SubnetworkID) || subnetworks.IsEqual(&tx.SubnetworkID, localNodeSubnetworkID) + shouldTxBeFull := subnetworks.IsBuiltIn(tx.SubnetworkID) || tx.SubnetworkID.Equal(localNodeSubnetworkID) if !isLocalNodeFull && !shouldTxBeFull && len(tx.Payload) > 0 { return errors.Wrapf(ruleerrors.ErrInvalidPayload, "transaction that was expected to be partial has a payload "+ diff --git a/domain/consensus/utils/hashes/to_big.go b/domain/consensus/utils/hashes/to_big.go index 510181660..25077c69a 100644 --- a/domain/consensus/utils/hashes/to_big.go +++ b/domain/consensus/utils/hashes/to_big.go @@ -10,7 +10,7 @@ import ( func ToBig(hash *externalapi.DomainHash) *big.Int { // A Hash is in little-endian, but the big package wants the bytes in // big-endian, so reverse them. - buf := *hash + buf := hash.Clone() blen := len(buf) for i := 0; i < blen/2; i++ { buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i] diff --git a/domain/consensus/utils/subnetworks/compare.go b/domain/consensus/utils/subnetworks/compare.go index af1a06358..12cda20da 100644 --- a/domain/consensus/utils/subnetworks/compare.go +++ b/domain/consensus/utils/subnetworks/compare.go @@ -21,14 +21,3 @@ func cmp(a, b externalapi.DomainSubnetworkID) int { func Less(a, b externalapi.DomainSubnetworkID) bool { return cmp(a, b) < 0 } - -// IsEqual returns true if a and b are equal or both nil -func IsEqual(a, b *externalapi.DomainSubnetworkID) bool { - if a == nil && b == nil { - return true - } - if a == nil || b == nil { - return false - } - return *a == *b -} diff --git a/domain/consensus/utils/utxo/utxo_entry.go b/domain/consensus/utils/utxo/utxo_entry.go index 68b63793e..b6cae4bab 100644 --- a/domain/consensus/utils/utxo/utxo_entry.go +++ b/domain/consensus/utils/utxo/utxo_entry.go @@ -1,6 +1,9 @@ package utxo -import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" +import ( + "bytes" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" +) type utxoEntry struct { amount uint64 @@ -38,3 +41,36 @@ func (u *utxoEntry) BlockBlueScore() uint64 { func (u *utxoEntry) IsCoinbase() bool { return u.isCoinbase } + +// Equal returns whether entry equals to other +func (u *utxoEntry) Equal(other externalapi.UTXOEntry) bool { + if u == nil || other == nil { + return u == other + } + + // If only the underlying value of other is nil it'll + // make `other == nil` return false, so we check it + // explicitly. + downcastedOther := other.(*utxoEntry) + if u == nil || downcastedOther == nil { + return u == downcastedOther + } + + if u.Amount() != other.Amount() { + return false + } + + if !bytes.Equal(u.ScriptPublicKey(), other.ScriptPublicKey()) { + return false + } + + if u.BlockBlueScore() != other.BlockBlueScore() { + return false + } + + if u.IsCoinbase() != other.IsCoinbase() { + return false + } + + return true +} diff --git a/domain/consensus/utils/utxo/utxo_entry_test.go b/domain/consensus/utils/utxo/utxo_entry_test.go new file mode 100644 index 000000000..69b90277b --- /dev/null +++ b/domain/consensus/utils/utxo/utxo_entry_test.go @@ -0,0 +1,113 @@ +package utxo + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "testing" +) + +func TestUTXOEntry_Equal(t *testing.T) { + type testUTXOEntryToCompare struct { + utxoEntry *utxoEntry + expectedResult bool + } + + tests := []struct { + baseUTXOEntry *utxoEntry + UTXOEntryToCompareTo []testUTXOEntryToCompare + }{ + { + baseUTXOEntry: nil, + UTXOEntryToCompareTo: []testUTXOEntryToCompare{ + { + utxoEntry: nil, + expectedResult: true, + }, + { + utxoEntry: &utxoEntry{ + 0xFFFF, + []byte{0xA1, 0xA2, 0xA3}, + 0xFFFF, + false, + }, + expectedResult: false, + }, + }, + }, { + baseUTXOEntry: &utxoEntry{ + 0xFFFF, + []byte{0xA1, 0xA2, 0xA3}, + 0xFFFF, + true, + }, + UTXOEntryToCompareTo: []testUTXOEntryToCompare{ + { + utxoEntry: &utxoEntry{ + 0xFFFF, + []byte{0xA1, 0xA2, 0xA3}, + 0xFFFF, + true, + }, + expectedResult: true, + }, + { + utxoEntry: nil, + expectedResult: false, + }, + { + utxoEntry: &utxoEntry{ + 0xFFFF, + []byte{0xA1, 0xA0, 0xA3}, // Changed + 0xFFFF, + true, + }, + expectedResult: false, + }, + { + utxoEntry: &utxoEntry{ + 0xFFFF, + []byte{0xA1, 0xA2, 0xA3}, + 0xFFFF, + false, // Changed + }, + expectedResult: false, + }, + { + utxoEntry: &utxoEntry{ + 0xFFFF, + []byte{0xA1, 0xA2, 0xA3}, + 0xFFF0, // Changed + true, + }, + expectedResult: false, + }, + { + utxoEntry: nil, + expectedResult: false, + }, + { + utxoEntry: &utxoEntry{ + 0xFFF0, // Changed + []byte{0xA1, 0xA2, 0xA3}, + 0xFFFF, + true, + }, + expectedResult: false, + }, + }, + }, + } + + for i, test := range tests { + for j, subTest := range test.UTXOEntryToCompareTo { + var base externalapi.UTXOEntry = test.baseUTXOEntry + result1 := base.Equal(subTest.utxoEntry) + if result1 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1) + } + result2 := subTest.utxoEntry.Equal(base) + if result2 != subTest.expectedResult { + t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2) + } + } + } +} diff --git a/domain/dagconfig/genesis_test.go b/domain/dagconfig/genesis_test.go index dd6892757..580773546 100644 --- a/domain/dagconfig/genesis_test.go +++ b/domain/dagconfig/genesis_test.go @@ -15,7 +15,7 @@ import ( func TestGenesisBlock(t *testing.T) { // Check hash of the block against expected hash. hash := consensushashing.BlockHash(MainnetParams.GenesisBlock) - if *MainnetParams.GenesisHash != *hash { + if !MainnetParams.GenesisHash.Equal(hash) { t.Fatalf("TestGenesisBlock: Genesis block hash does "+ "not appear valid - got %v, want %v", hash, MainnetParams.GenesisHash) } @@ -26,7 +26,7 @@ func TestGenesisBlock(t *testing.T) { func TestTestnetGenesisBlock(t *testing.T) { // Check hash of the block against expected hash. hash := consensushashing.BlockHash(TestnetParams.GenesisBlock) - if *TestnetParams.GenesisHash != *hash { + if !TestnetParams.GenesisHash.Equal(hash) { t.Fatalf("TestTestnetGenesisBlock: Genesis block hash does "+ "not appear valid - got %v, want %v", hash, TestnetParams.GenesisHash) @@ -38,7 +38,7 @@ func TestTestnetGenesisBlock(t *testing.T) { func TestSimnetGenesisBlock(t *testing.T) { // Check hash of the block against expected hash. hash := consensushashing.BlockHash(SimnetParams.GenesisBlock) - if *SimnetParams.GenesisHash != *hash { + if !SimnetParams.GenesisHash.Equal(hash) { t.Fatalf("TestSimnetGenesisBlock: Genesis block hash does "+ "not appear valid - got %v, want %v", hash, SimnetParams.GenesisHash) @@ -50,7 +50,7 @@ func TestSimnetGenesisBlock(t *testing.T) { func TestDevnetGenesisBlock(t *testing.T) { // Check hash of the block against expected hash. hash := consensushashing.BlockHash(DevnetParams.GenesisBlock) - if *DevnetParams.GenesisHash != *hash { + if !DevnetParams.GenesisHash.Equal(hash) { t.Fatalf("TestDevnetGenesisBlock: Genesis block hash does "+ "not appear valid - got %v, want %v", hash, DevnetParams.GenesisHash) diff --git a/domain/dagconfig/params_test.go b/domain/dagconfig/params_test.go index ceb1219b8..3790ed75c 100644 --- a/domain/dagconfig/params_test.go +++ b/domain/dagconfig/params_test.go @@ -35,7 +35,7 @@ func TestNewHashFromStr(t *testing.T) { result := newHashFromStr(test.hexStr) - if *result != *test.expectedHash { + if !result.Equal(test.expectedHash) { t.Errorf("%s: Expected hash: %s, but got %s", test.hexStr, test.expectedHash, result) } }() diff --git a/domain/miningmanager/mempool/mempool.go b/domain/miningmanager/mempool/mempool.go index 0cba5cb1b..40dab8a9d 100644 --- a/domain/miningmanager/mempool/mempool.go +++ b/domain/miningmanager/mempool/mempool.go @@ -467,10 +467,10 @@ func (mp *mempool) removeChainTransaction(tx *consensusexternalapi.DomainTransac // // This function MUST be called with the mempool lock held (for writes). func (mp *mempool) removeDoubleSpends(tx *consensusexternalapi.DomainTransaction) error { - txID := *consensushashing.TransactionID(tx) + txID := consensushashing.TransactionID(tx) for _, txIn := range tx.Inputs { if txRedeemer, ok := mp.mempoolUTXOSet.poolTransactionBySpendingOutpoint(txIn.PreviousOutpoint); ok { - if !(*consensushashing.TransactionID(txRedeemer) == txID) { + if !consensushashing.TransactionID(txRedeemer).Equal(txID) { err := mp.removeTransactionAndItsChainedTransactions(txRedeemer) if err != nil { return err diff --git a/domain/utxoindex/store.go b/domain/utxoindex/store.go index e09a4b1be..67d985038 100644 --- a/domain/utxoindex/store.go +++ b/domain/utxoindex/store.go @@ -25,7 +25,7 @@ func newUTXOIndexStore(database database.Database) *utxoIndexStore { } } -func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry) error { +func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry) error { key := ConvertScriptPublicKeyToString(scriptPublicKey) log.Tracef("Adding outpoint %s:%d to scriptPublicKey %s", outpoint.TransactionID, outpoint.Index, key) @@ -52,7 +52,7 @@ func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.Dom return errors.Errorf("cannot add outpoint %s because it's being added already", outpoint) } - toAddPairsOfKey[*outpoint] = *utxoEntry + toAddPairsOfKey[*outpoint] = utxoEntry log.Tracef("Added outpoint %s:%d to scriptPublicKey %s", outpoint.TransactionID, outpoint.Index, key) diff --git a/domain/utxoindex/utxoindex.go b/domain/utxoindex/utxoindex.go index 8e2666fe5..defa4a6bb 100644 --- a/domain/utxoindex/utxoindex.go +++ b/domain/utxoindex/utxoindex.go @@ -126,7 +126,7 @@ func (ui *UTXOIndex) addTransaction(transaction *externalapi.DomainTransaction, log.Tracef("Adding outpoint %s:%d to UTXO index", transactionID, index) outpoint := externalapi.NewDomainOutpoint(transactionID, uint32(index)) utxoEntry := utxo.NewUTXOEntry(transactionOutput.Value, transactionOutput.ScriptPublicKey, isCoinbase, blockBlueScore) - err := ui.store.add(transactionOutput.ScriptPublicKey, outpoint, &utxoEntry) + err := ui.store.add(transactionOutput.ScriptPublicKey, outpoint, utxoEntry) if err != nil { return err } @@ -148,7 +148,7 @@ func (ui *UTXOIndex) removeTransaction(transaction *externalapi.DomainTransactio for _, transactionInput := range transaction.Inputs { log.Tracef("Adding outpoint %s:%d to UTXO index", transactionInput.PreviousOutpoint.TransactionID, transactionInput.PreviousOutpoint.Index) - err := ui.store.add(transactionInput.UTXOEntry.ScriptPublicKey(), &transactionInput.PreviousOutpoint, &transactionInput.UTXOEntry) + err := ui.store.add(transactionInput.UTXOEntry.ScriptPublicKey(), &transactionInput.PreviousOutpoint, transactionInput.UTXOEntry) if err != nil { return err } diff --git a/testing/integration/basic_sync_test.go b/testing/integration/basic_sync_test.go index 0050ebd8c..60eaecfe6 100644 --- a/testing/integration/basic_sync_test.go +++ b/testing/integration/basic_sync_test.go @@ -38,7 +38,7 @@ func TestIntegrationBasicSync(t *testing.T) { } blockHash := consensushashing.BlockHash(block) - if *header.BlockHash() != *blockHash { + if !header.BlockHash().Equal(blockHash) { t.Errorf("Expected block with hash '%s', but got '%s'", blockHash, header.BlockHash()) } @@ -49,7 +49,7 @@ func TestIntegrationBasicSync(t *testing.T) { } blockHash = consensushashing.BlockHash(block) - if *header.BlockHash() != *blockHash { + if !header.BlockHash().Equal(blockHash) { t.Errorf("Expected block with hash '%s', but got '%s'", blockHash, header.BlockHash()) } }