github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/transaction_record_query_unit_test.go (about)

     1  //go:build all || unit
     2  // +build all unit
     3  
     4  package hedera
     5  
     6  /*-
     7   *
     8   * Hedera Go SDK
     9   *
    10   * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC
    11   *
    12   * Licensed under the Apache License, Version 2.0 (the "License");
    13   * you may not use this file except in compliance with the License.
    14   * You may obtain a copy of the License at
    15   *
    16   *      http://www.apache.org/licenses/LICENSE-2.0
    17   *
    18   * Unless required by applicable law or agreed to in writing, software
    19   * distributed under the License is distributed on an "AS IS" BASIS,
    20   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    21   * See the License for the specific language governing permissions and
    22   * limitations under the License.
    23   *
    24   */
    25  
    26  import (
    27  	"encoding/hex"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/hashgraph/hedera-protobufs-go/services"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestUnitTransactionRecordQueryValidate(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	client, err := _NewMockClient()
    39  	client.SetLedgerID(*NewLedgerIDTestnet())
    40  	require.NoError(t, err)
    41  	client.SetAutoValidateChecksums(true)
    42  	accountID, err := AccountIDFromString("0.0.123-esxsf")
    43  	transactionID := TransactionIDGenerate(accountID)
    44  	require.NoError(t, err)
    45  
    46  	recordQuery := NewTransactionRecordQuery().
    47  		SetTransactionID(transactionID)
    48  
    49  	err = recordQuery.validateNetworkOnIDs(client)
    50  	require.NoError(t, err)
    51  }
    52  
    53  func TestUnitTransactionRecordQueryValidateWrong(t *testing.T) {
    54  	t.Parallel()
    55  
    56  	client, err := _NewMockClient()
    57  	client.SetLedgerID(*NewLedgerIDTestnet())
    58  	require.NoError(t, err)
    59  	client.SetAutoValidateChecksums(true)
    60  	accountID, err := AccountIDFromString("0.0.123-rmkykd")
    61  	transactionID := TransactionIDGenerate(accountID)
    62  	require.NoError(t, err)
    63  
    64  	recordQuery := NewTransactionRecordQuery().
    65  		SetTransactionID(transactionID)
    66  
    67  	err = recordQuery.validateNetworkOnIDs(client)
    68  	require.Error(t, err)
    69  	if err != nil {
    70  		require.Equal(t, "network mismatch or wrong checksum given, given checksum: rmkykd, correct checksum esxsf, network: testnet", err.Error())
    71  	}
    72  }
    73  
    74  func TestUnitTransactionRecordQueryGet(t *testing.T) {
    75  	t.Parallel()
    76  
    77  	txID := TransactionIDGenerate(AccountID{Account: 7})
    78  	deadline := time.Duration(time.Minute)
    79  	accountId := AccountID{Account: 123}
    80  	transactionID := TransactionIDGenerate(accountId)
    81  	query := NewTransactionRecordQuery().
    82  		SetTransactionID(txID).
    83  		SetIncludeDuplicates(true).
    84  		SetIncludeChildren(true).
    85  		SetQueryPayment(NewHbar(2)).
    86  		SetMaxQueryPayment(NewHbar(1)).
    87  		SetQueryPayment(HbarFromTinybar(25)).
    88  		SetNodeAccountIDs([]AccountID{{Account: 10}, {Account: 11}, {Account: 12}}).
    89  		SetMaxRetry(3).
    90  		SetMinBackoff(300 * time.Millisecond).
    91  		SetMaxBackoff(10 * time.Second).
    92  		SetPaymentTransactionID(transactionID).
    93  		SetMaxQueryPayment(NewHbar(500)).
    94  		SetGrpcDeadline(&deadline)
    95  
    96  	require.Equal(t, txID, query.GetTransactionID())
    97  	require.True(t, query.GetIncludeChildren())
    98  	require.True(t, query.GetIncludeDuplicates())
    99  	require.Equal(t, []AccountID{{Account: 10}, {Account: 11}, {Account: 12}}, query.GetNodeAccountIDs())
   100  	require.Equal(t, 300*time.Millisecond, query.GetMinBackoff())
   101  	require.Equal(t, 10*time.Second, query.GetMaxBackoff())
   102  	require.Equal(t, 3, query.GetMaxRetryCount())
   103  	require.Equal(t, transactionID, query.GetPaymentTransactionID())
   104  	require.Equal(t, HbarFromTinybar(25), query.GetQueryPayment())
   105  	require.Equal(t, NewHbar(500), query.GetMaxQueryPayment())
   106  	require.Equal(t, &deadline, query.GetGrpcDeadline())
   107  }
   108  
   109  func TestUnitTransactionRecordQueryNothingSet(t *testing.T) {
   110  	t.Parallel()
   111  
   112  	query := NewTransactionRecordQuery()
   113  
   114  	require.Equal(t, TransactionID{}, query.GetTransactionID())
   115  	require.False(t, query.GetIncludeChildren())
   116  	require.False(t, query.GetIncludeDuplicates())
   117  	require.Empty(t, query.GetNodeAccountIDs())
   118  	require.Equal(t, 250*time.Millisecond, query.GetMinBackoff())
   119  	require.Equal(t, 8*time.Second, query.GetMaxBackoff())
   120  	require.Equal(t, 10, query.GetMaxRetryCount())
   121  	require.Equal(t, TransactionID{}, query.GetPaymentTransactionID())
   122  	require.Equal(t, Hbar{}, query.GetQueryPayment())
   123  	require.Equal(t, Hbar{}, query.GetMaxQueryPayment())
   124  }
   125  
   126  func TestUnitTransactionRecordPlatformNotActiveGracefulHandling(t *testing.T) {
   127  	t.Parallel()
   128  
   129  	responses := [][]interface{}{{
   130  		&services.TransactionResponse{
   131  			NodeTransactionPrecheckCode: services.ResponseCodeEnum_OK,
   132  		},
   133  		&services.Response{
   134  			Response: &services.Response_TransactionGetReceipt{
   135  				TransactionGetReceipt: &services.TransactionGetReceiptResponse{
   136  					Header: &services.ResponseHeader{
   137  						Cost:         0,
   138  						ResponseType: services.ResponseType_ANSWER_ONLY,
   139  					},
   140  					Receipt: &services.TransactionReceipt{
   141  						Status: services.ResponseCodeEnum_SUCCESS,
   142  					},
   143  				},
   144  			},
   145  		},
   146  		&services.Response{
   147  			Response: &services.Response_TransactionGetRecord{
   148  				TransactionGetRecord: &services.TransactionGetRecordResponse{
   149  					Header: &services.ResponseHeader{
   150  						Cost:         0,
   151  						ResponseType: services.ResponseType_ANSWER_ONLY,
   152  					},
   153  					TransactionRecord: &services.TransactionRecord{
   154  						Receipt: &services.TransactionReceipt{
   155  							Status: services.ResponseCodeEnum_PLATFORM_NOT_ACTIVE,
   156  						},
   157  					},
   158  				},
   159  			},
   160  		},
   161  		&services.Response{
   162  			Response: &services.Response_TransactionGetRecord{
   163  				TransactionGetRecord: &services.TransactionGetRecordResponse{
   164  					Header: &services.ResponseHeader{
   165  						Cost:         0,
   166  						ResponseType: services.ResponseType_ANSWER_ONLY,
   167  					},
   168  					TransactionRecord: &services.TransactionRecord{
   169  						Receipt: &services.TransactionReceipt{
   170  							Status: services.ResponseCodeEnum_SUCCESS,
   171  						},
   172  					},
   173  				},
   174  			},
   175  		},
   176  		&services.Response{
   177  			Response: &services.Response_TransactionGetRecord{
   178  				TransactionGetRecord: &services.TransactionGetRecordResponse{
   179  					Header: &services.ResponseHeader{
   180  						Cost:         0,
   181  						ResponseType: services.ResponseType_ANSWER_ONLY,
   182  					},
   183  					TransactionRecord: &services.TransactionRecord{
   184  						Receipt: &services.TransactionReceipt{
   185  							Status: services.ResponseCodeEnum_PLATFORM_NOT_ACTIVE,
   186  						},
   187  					},
   188  				},
   189  			},
   190  		},
   191  		&services.Response{
   192  			Response: &services.Response_TransactionGetRecord{
   193  				TransactionGetRecord: &services.TransactionGetRecordResponse{
   194  					Header: &services.ResponseHeader{
   195  						Cost:         0,
   196  						ResponseType: services.ResponseType_ANSWER_ONLY,
   197  					},
   198  					TransactionRecord: &services.TransactionRecord{
   199  						Receipt: &services.TransactionReceipt{
   200  							Status: services.ResponseCodeEnum_SUCCESS,
   201  						},
   202  					},
   203  				},
   204  			},
   205  		},
   206  	}}
   207  
   208  	client, server := NewMockClientAndServer(responses)
   209  	defer server.Close()
   210  	tx, err := NewTransferTransaction().
   211  		SetNodeAccountIDs([]AccountID{{Account: 3}}).
   212  		AddHbarTransfer(AccountID{Account: 2}, HbarFromTinybar(-1)).
   213  		AddHbarTransfer(AccountID{Account: 3}, HbarFromTinybar(1)).
   214  		Execute(client)
   215  	client.SetMaxAttempts(2)
   216  	require.NoError(t, err)
   217  	_, err = tx.SetValidateStatus(true).GetRecord(client)
   218  	require.NoError(t, err)
   219  }
   220  
   221  func TestUnitTransactionRecordReceiptNotFound(t *testing.T) {
   222  	t.Parallel()
   223  
   224  	responses := [][]interface{}{{
   225  		&services.TransactionResponse{
   226  			NodeTransactionPrecheckCode: services.ResponseCodeEnum_OK,
   227  		},
   228  		&services.Response{
   229  			Response: &services.Response_TransactionGetReceipt{
   230  				TransactionGetReceipt: &services.TransactionGetReceiptResponse{
   231  					Header: &services.ResponseHeader{
   232  						Cost:         0,
   233  						ResponseType: services.ResponseType_ANSWER_ONLY,
   234  					},
   235  					Receipt: &services.TransactionReceipt{
   236  						Status: services.ResponseCodeEnum_RECEIPT_NOT_FOUND,
   237  					},
   238  				},
   239  			},
   240  		},
   241  		&services.Response{
   242  			Response: &services.Response_TransactionGetReceipt{
   243  				TransactionGetReceipt: &services.TransactionGetReceiptResponse{
   244  					Header: &services.ResponseHeader{
   245  						Cost:         0,
   246  						ResponseType: services.ResponseType_ANSWER_ONLY,
   247  					},
   248  					Receipt: &services.TransactionReceipt{
   249  						Status: services.ResponseCodeEnum_RECEIPT_NOT_FOUND,
   250  					},
   251  				},
   252  			},
   253  		},
   254  	}}
   255  	client, server := NewMockClientAndServer(responses)
   256  	defer server.Close()
   257  	tx, err := NewTransferTransaction().
   258  		SetNodeAccountIDs([]AccountID{{Account: 3}}).
   259  		AddHbarTransfer(AccountID{Account: 2}, HbarFromTinybar(-1)).
   260  		AddHbarTransfer(AccountID{Account: 3}, HbarFromTinybar(1)).
   261  		Execute(client)
   262  	client.SetMaxAttempts(2)
   263  	require.NoError(t, err)
   264  	record, err := tx.SetValidateStatus(true).GetRecord(client)
   265  	require.Error(t, err)
   266  	require.Equal(t, "exceptional precheck status RECEIPT_NOT_FOUND", err.Error())
   267  	require.Equal(t, StatusReceiptNotFound, record.Receipt.Status)
   268  }
   269  
   270  func TestUnitTransactionRecordQueryMarshalJSON(t *testing.T) {
   271  	t.Parallel()
   272  	hexRecord, err := hex.DecodeString(`1afe010a26081612070800100018de092a130a110801100c1a0b0880ae99a4ffffffffff013800420058001230cac44f2db045ba441f3fbc295217f2eb0f956293d28b3401578f6160e66f4e47ea87952d91c4b1cb5bda6447823b979a1a0c08f3fcb495061083d9be900322190a0c08e8fcb495061098f09cf20112070800100018850918002a0030bee8f013526c0a0f0a0608001000180510d0df820118000a0f0a0608001000186210f08dff1e18000a100a070800100018a00610def1ef0318000a100a070800100018a10610def1ef0318000a110a070800100018850910fbf8b7e10718000a110a070800100018de091080a8d6b90718008a0100aa0100`)
   273  	require.NoError(t, err)
   274  	record, err := TransactionRecordFromBytes([]byte(hexRecord))
   275  	require.NoError(t, err)
   276  	accID, err := AccountIDFromString("0.0.1246")
   277  	require.NoError(t, err)
   278  	tokenID, err := TokenIDFromString("0.0.123")
   279  	require.NoError(t, err)
   280  	contractID, err := ContractIDFromString("0.0.3")
   281  	require.NoError(t, err)
   282  	record.Receipt.ContractID = &contractID
   283  	record.Receipt.NodeID = 1
   284  
   285  	tokenTransfer := TokenTransfer{
   286  		AccountID:  accID,
   287  		Amount:     789,
   288  		IsApproved: true,
   289  	}
   290  	tokenTransferList := map[TokenID][]TokenTransfer{}
   291  	tokenTransferList[tokenID] = []TokenTransfer{tokenTransfer}
   292  
   293  	tokenNftTransfer := _TokenNftTransfer{
   294  		SenderAccountID:   accID,
   295  		ReceiverAccountID: accID,
   296  		SerialNumber:      123,
   297  		IsApproved:        true,
   298  	}
   299  	tokenNftTransferList := map[TokenID][]_TokenNftTransfer{}
   300  	tokenNftTransferList[tokenID] = []_TokenNftTransfer{tokenNftTransfer}
   301  
   302  	assessedCustomFee := AssessedCustomFee{
   303  		FeeCollectorAccountId: &accID,
   304  		Amount:                789,
   305  		TokenID:               &tokenID,
   306  		PayerAccountIDs:       []*AccountID{&accID},
   307  	}
   308  
   309  	tokenAssociation := TokenAssociation{
   310  		AccountID: &accID,
   311  		TokenID:   &tokenID,
   312  	}
   313  
   314  	plaidStaking := map[AccountID]Hbar{}
   315  	for _, transfer := range record.Transfers {
   316  		plaidStaking[transfer.AccountID] = transfer.Amount
   317  	}
   318  
   319  	pk, err := PublicKeyFromString("302a300506032b6570032100d7366c45e4d2f1a6c1d9af054f5ef8edc0b8d3875ba5d08a7f2e81ee8876e9e8")
   320  	require.NoError(t, err)
   321  
   322  	prngNumber := int32(123)
   323  	evmAddressBytes, err := hex.DecodeString("deadbeef")
   324  	require.NoError(t, err)
   325  
   326  	record.TransactionMemo = "test"
   327  	record.TokenTransfers = tokenTransferList
   328  	record.NftTransfers = tokenNftTransferList
   329  	record.ParentConsensusTimestamp = record.ConsensusTimestamp
   330  	record.AliasKey = &pk
   331  	record.EthereumHash = []byte{1, 2, 3, 4}
   332  	record.PaidStakingRewards = plaidStaking
   333  	record.PrngBytes = []byte{1, 2, 3, 4}
   334  	record.PrngNumber = &prngNumber
   335  	record.EvmAddress = evmAddressBytes
   336  	record.AssessedCustomFees = []AssessedCustomFee{assessedCustomFee}
   337  	record.AutomaticTokenAssociations = []TokenAssociation{tokenAssociation}
   338  	record.PendingAirdropRecords = []PendingAirdropRecord{{pendingAirdropId: PendingAirdropId{&accID, &accID, &tokenID, nil}, pendingAirdropAmount: 789}}
   339  	result, err := record.MarshalJSON()
   340  	require.NoError(t, err)
   341  	expected := `{
   342          "aliasKey":"302a300506032b6570032100d7366c45e4d2f1a6c1d9af054f5ef8edc0b8d3875ba5d08a7f2e81ee8876e9e8",
   343          "assessedCustomFees":[{"feeCollectorAccountId":"0.0.1246","tokenId":"0.0.123","amount":"789","payerAccountIds":["0.0.1246"]}],
   344          "automaticTokenAssociations":[{"tokenId":"0.0.123","accountId":"0.0.1246"}],
   345          "callResultIsCreate":true,
   346          "children":[],
   347          "consensusTimestamp":"2022-06-18T02:54:43.839Z",
   348          "duplicates":[],
   349          "ethereumHash":"01020304",
   350          "evmAddress":"deadbeef",
   351          "expectedDecimals":null,
   352          "nftTransfers":{"0.0.123":[{"sender":"0.0.1246","recipient":"0.0.1246","isApproved":true,"serial":123}]},
   353          "paidStakingRewards":[
   354              {"accountId":"0.0.1157","amount":"-1041694270","isApproved":false},
   355              {"accountId":"0.0.1246","amount":"1000000000","isApproved":false},
   356              {"accountId":"0.0.5","amount":"1071080","isApproved":false},
   357              {"accountId":"0.0.800","amount":"4062319","isApproved":false},
   358              {"accountId":"0.0.801","amount":"4062319","isApproved":false},
   359              {"accountId":"0.0.98","amount":"32498552","isApproved":false}
   360          ],
   361          "parentConsensusTimestamp":"2022-06-18T02:54:43.839Z",
   362          "pendingAirdropRecords":[
   363              {
   364                  "pendingAirdropAmount":"789",
   365                  "pendingAirdropId":{
   366                      "nftId":"",
   367                      "receiver":"0.0.1246",
   368                      "sender":"0.0.1246",
   369                      "tokenId":"0.0.123"
   370                  }
   371              }
   372          ],
   373          "prngBytes":"01020304",
   374          "prngNumber":123,
   375          "receipt":{
   376              "accountId":"0.0.1246",
   377              "children":[],
   378              "contractId":"0.0.3",
   379              "duplicates":[],
   380              "exchangeRate":{"cents":12,"expirationTime":"1963-11-25T17:31:44.000Z","hbars":1},
   381              "fileId":null,
   382              "nodeId":1,
   383              "scheduleId":null,
   384              "scheduledTransactionId":null,
   385              "serialNumbers":null,
   386              "status":"SUCCESS",
   387              "tokenId":null,
   388              "topicId":null,
   389              "topicRunningHash":"",
   390              "topicRunningHashVersion":0,
   391              "topicSequenceNumber":0,
   392              "totalSupply":0
   393          },
   394          "tokenTransfers":{"0.0.123":{"0.0.1246":"789"}},
   395          "transactionFee":"41694270",
   396          "transactionHash":"cac44f2db045ba441f3fbc295217f2eb0f956293d28b3401578f6160e66f4e47ea87952d91c4b1cb5bda6447823b979a",
   397          "transactionId":"0.0.1157@1655520872.507983896",
   398          "transactionMemo":"test",
   399          "transfers":[
   400              {"accountId":"0.0.5","amount":"1071080","isApproved":false},
   401              {"accountId":"0.0.98","amount":"32498552","isApproved":false},
   402              {"accountId":"0.0.800","amount":"4062319","isApproved":false},
   403              {"accountId":"0.0.801","amount":"4062319","isApproved":false},
   404              {"accountId":"0.0.1157","amount":"-1041694270","isApproved":false},
   405              {"accountId":"0.0.1246","amount":"1000000000","isApproved":false}
   406          ]
   407      }`
   408  	require.JSONEqf(t, expected, string(result), "json should be equal")
   409  }