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 }