From 0415d0ef88095d2b4598f54eb2308166c45e0cad Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Tue, 23 Apr 2024 16:55:27 -0500 Subject: [PATCH 01/34] docs: Update test specification document for AccountCreateTransaction to use as a model going forward. Create utility.md file to hold miscellaneous JSON RPC functions. Move tests and test specifictions documents to folders for the specific Hedera service they are for Signed-off-by: Rob Walworth --- .../accountCreateTransaction.md | 132 -------- .../accountBalanceQuery.md | 0 .../cryptoService/accountCreateTransaction.md | 155 +++++++++ .../accountDeleteTransaction.md | 0 .../accountUpdateTransaction.md | 0 .../testSpecificationsTemplate.md | 2 +- test-specifications/utility.md | 293 ++++++++++++++++++ .../test_AccountInfo.js | 0 .../test_CreateAccount.js | 4 +- .../test_DeleteAccount.js | 0 .../test_UpdateAccountKey.js | 0 .../test_UpdateAccountMemo.js | 0 12 files changed, 451 insertions(+), 135 deletions(-) delete mode 100644 test-specifications/accountCreateTransaction.md rename test-specifications/{ => cryptoService}/accountBalanceQuery.md (100%) create mode 100644 test-specifications/cryptoService/accountCreateTransaction.md rename test-specifications/{ => cryptoService}/accountDeleteTransaction.md (100%) rename test-specifications/{ => cryptoService}/accountUpdateTransaction.md (100%) create mode 100644 test-specifications/utility.md rename test/{account => cryptoService}/test_AccountInfo.js (100%) rename test/{account => cryptoService}/test_CreateAccount.js (97%) rename test/{account => cryptoService}/test_DeleteAccount.js (100%) rename test/{account => cryptoService}/test_UpdateAccountKey.js (100%) rename test/{account => cryptoService}/test_UpdateAccountMemo.js (100%) diff --git a/test-specifications/accountCreateTransaction.md b/test-specifications/accountCreateTransaction.md deleted file mode 100644 index bf9ab1f..0000000 --- a/test-specifications/accountCreateTransaction.md +++ /dev/null @@ -1,132 +0,0 @@ -# AccountCreateTransaction - Test specification - -## Description: -This test specification for the AccountCreateTransaction is to be one of many for testing the functionality of the Hedera SDKs. The SDK under test will use the language specific JSON-RPC server return responses back to the test driver. - -## Design: -Each test within the test specification is linked to one of the properties within AccountCreateTransaction. Each property is tested with a mix of boundaries. The inputs for each test are a range of valid, minimum, maximum, negative and invalid values for the method. The expected response of a passed test can be a correct error or a results of node queries. Success on the consensus node can be obtained by a queries such as AccountInfoQuery or AccountBalanceQuery, and on the mirror node through the rest API. Error codes are obtained from the response code proto files. - -**Transaction properties:** - -https://docs.hedera.com/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-an-account - -**CryptoCreate protobufs:** - -https://github.com/hashgraph/hedera-protobufs/blob/main/services/crypto_create.proto - -**Response codes:** - -https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.proto - -## Initialisation: - -```jsx -new AccountCreateTransaction() - -//example - -const transaction = new AccountCreateTransaction() - .setKey(privateKey.publicKey) - .setInitialBalance(new Hbar(1000)) -``` - -## Properties - -### **Key:** - -- The PublicKey for the new account - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|----------------------------------------------|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Simple account creation | A public key with all other fields at default values | The account creation succeeds. The account can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create an account with no public key | All fields default with no public key | The account creation fails with a response of KEY_NOT_PROVIDED | | -| 3 | Create an account with an invalid public key | An invalid public key with all other fields at default values | The account creation fails with an error response of : | | - -### **Initial Balance:** - -- The initial number of tinybars to put into the account - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account and set the initial balance | A public key and the initial balance set to 100 hbar | The account creation succeeds with a balance of 100 Hbar. The account can be queried on the consensus node with AccountBalanceQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Set the initial balance of the new account to a negative number | A public key and the initial balance set to -1 Hbar | The account creation should fail and return error response - INVALID_INITIAL_BALANCE | | -| 3 | Set the initial balance to more than the operator account balance | A public key and the initial balance set to a balance higher than the operator account | The account creation should fail and return error response - INSUFFICIENT_PAYER_BALANCE | | - -### **Receiver Signature Required:** - -- If true, this account's key must sign any transaction depositing into this account - (in addition to all withdrawals) - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Require a receiving signature when creating a new account transaction | A public key, an valid initial balance of 100 Hbars and set Receiver Signature Required to “True” | The account creation succeeds. The accounts isReceiverSignatureRequired should equal true, can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create a new account transaction that doesn’t require a signature | A public key, an valid initial balance of 100 Hbars and set Receiver Signature Required to “False” or none | The account creation succeeds. The accounts isReceiverSignatureRequired should equal false, can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | - -### **Max Automatic Token Associations:** - -- The maximum number of tokens that an Account can be implicitly associated with. Defaults to 0 - and up to a maximum value of 1000. - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|---------------------------------------------------------------------|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account with a default max token association | A public key with all other fields at default values | The account creation succeeds. The accounts maxAutomaticTokenAssociations can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create an account with a max token association set to maximum value | A public key and a max token association of 1000 | The account creation succeeds. The accounts maxAutomaticTokenAssociations can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 3 | Create an account with a token association over the maximum value | A public key and a max token association of 1001 | The account creation should fail and return error response - REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT | | - - -### **Staked ID:** - -- ID of the account to which this account is staking - - OR -- ID of the node this account is staked to. - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|-----------------------------------------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account and set the staked account ID to the operators account ID | A public key and the operators account ID | The account creation succeeds. The accounts stakingInfo.stakedAccountID should equal the operator account. It can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create an account and set the staked node ID a node ID | A public key and the node ID | The account creation succeeds. The accounts stakingInfo.stakedNodeID should equal the node ID. It can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 3 | Create an account and set the staked account ID to an invalid ID | A public key and a staked account ID | The account creation should fail and return error response - INVALID_STAKING_ID | | -| 4 | Create an account and set the staked node ID to an invalid node | A public key and a staked node ID | The account creation should fail and return error response - INVALID_STAKING_ID | | -| 5 | Create an account and set the staked account ID with no input | A public key and an staked account ID with an empty value | The account creation should fail and return an error response | | -| 6 | Create an account and set the staked node ID with no input | A public key and an staked node ID with an empty value | The account creation should fail and return an error response | | -| 7 | Create an account and set both a staking account ID and Node ID | A public key, a stoked account Id and Node ID | The account creation should fail and return an error response | | - -### **Decline rewards:** - -- If true, the account declines receiving a staking reward. The default value is false. - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|------------------------------------------------------------------|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account and set the account to decline staking rewards | A public key and decline rewards set to “true” | The account creation succeeds. The accounts stakingInfo.declineStakingReward should equal “true” can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create an account and leave decline rewards at default value | A public key with all other fields at default values | The account creation succeeds. The accounts stakingInfo.declineStakingInfo should equal false. It can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | - -### **Memo:** - -- The memo associated with the account (UTF-8 encoding max 100 bytes) - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|-----------------------------------------------------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account with a memo | A public key and a memo value of “testmemo” | The account creation succeeds. The accounts memo should equal “testmemo”. It can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create an account with a memo that exceeds 100 characters | A public key and a memo > 100 characters | The account creation should fail and return error response - MEMO_TOO_LONG | | - -### **Auto Renew Period:** - -- The account is charged to extend its expiration date every ‘this many’ seconds. If it doesn't - have enough balance, it extends as long as possible. If it is empty when it expires, then it - is deleted. - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|--------------------------------------------------------------------------------|---------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account and set the auto renew period to 30 days (2,592,000 seconds) | A public key and 2592000 as the auto renew period value | The account creation succeeds. The accounts auto renew period should equal 2,592,000. It can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | -| 2 | Create an account and set the auto renew period to -1 | A public key and -1 as the auto renew period value | The account creation should fail and return error response - INVALID_RENEWAL_PERIOD | | -| 3 | Create an account and set the auto renew period to 10 days (864000 seconds) | A public key and 864000 as the auto renew period value | The account creation should fail and return error response -AUTORENEW_DURATION_NOT_IN_RANGE | | - -### **Alias:** - -- The bytes to be used as the account's alias. The bytes will be 1 of 2 options. It will be the serialization of a protobuf Key message for an ED25519/ECDSA_SECP256K1 primitive key type. If the account is ECDSA_SECP256K1 based it may also be the public address, calculated as the last 20 bytes of the keccak-256 hash of the ECDSA_SECP256K1 primitive key. Currently only primitive key bytes are supported as the key for an account with an alias. ThresholdKey, KeyList, ContractID, and delegatable_contract_id are not supported. -- A given alias can map to at most one account on the network at a time. This uniqueness will be enforced relative to aliases currently on the network at alias assignment. -- If a transaction creates an account using an alias, any further crypto transfers to that alias will simply be deposited in that account, without creating anything, and with no creation fee being charged - -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|----------------------------------------|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Create an account via an alias account | An operator account id, an alias account id and an initial balance | The account creation succeeds. . It can be queried on the consensus node with AccountInfoQuery, and on mirror node using the REST API, within 10 seconds | | - diff --git a/test-specifications/accountBalanceQuery.md b/test-specifications/cryptoService/accountBalanceQuery.md similarity index 100% rename from test-specifications/accountBalanceQuery.md rename to test-specifications/cryptoService/accountBalanceQuery.md diff --git a/test-specifications/cryptoService/accountCreateTransaction.md b/test-specifications/cryptoService/accountCreateTransaction.md new file mode 100644 index 0000000..627c7cf --- /dev/null +++ b/test-specifications/cryptoService/accountCreateTransaction.md @@ -0,0 +1,155 @@ +# AccountCreateTransaction - Test specification + +## Description: +This test specification for AccountCreateTransaction is to be one of many for testing the functionality of the Hedera SDKs. The SDK under test will use the language specific JSON-RPC server return responses back to the test driver. + +## Design: +Each test within the test specification is linked to one of the properties within AccountCreateTransaction. Each property is tested with a mix of boundaries. The inputs for each test are a range of valid, minimum, maximum, negative and invalid values for the method. The expected response of a passed test can be a correct error response code or seen as the result of node queries. Success on the consensus node can be obtained by a queries such as AccountInfoQuery or AccountBalanceQuery, and on the mirror node through the rest API. Error codes are obtained from the response code proto files. + +**Transaction properties:** + +https://docs.hedera.com/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-an-account + +**CryptoCreate protobufs:** + +https://github.com/hashgraph/hedera-protobufs/blob/main/services/crypto_create.proto + +**Response codes:** + +https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.proto + +**Mirror Node APIs:** + +https://docs.hedera.com/hedera/sdks-and-apis/rest-api + +## Initialisation + +```jsx +new AccountCreateTransaction() + +//example + +const transaction = new AccountCreateTransaction() + .setKey(privateKey.publicKey) + .setInitialBalance(new Hbar(1000)) + ... +``` + +## JSON-RPC API Endpoint Documentation + +### Method Name + +`createAccount` + +### Parameters + +| Parameter Name | Type | Required/Optional | Description/Notes | +|---------------------------|--------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| key | string | optional | DER-encoded hex string representation for private or public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes. | +| initialBalance | int64 | optional | Units of tinybars | +| receiverSignatureRequired | bool | optional | | +| autoRenewPeriod | int64 | optional | Units of seconds | +| memo | string | optional | | +| maxAutoTokenAssociations | int32 | optional | | +| stakedAccountId | string | optional | | +| stakedNodeId | int64 | optional | | +| declineStakingReward | bool | optional | | +| alias | string | optional | Hex string representation of a serialized protobuf Key of an ED25519 or ECDSAsecp256k1 public key type. | + +## Property Tests + +### **Key:** + +- The key for the new account. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|-----------------------------------------------------------------------------------------------|-----------------------------------------|--------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a valid ED25519 public key | key= | The account creation succeeds. | N | +| 2 | Creates an account with a valid ECDSAsecp256k1 public key | key= | The account creation succeeds. | N | +| 3 | Creates an account with a valid ED25519 private key | key= | The account creation succeeds. | N | +| 4 | Creates an account with a valid ECDSAsecp256k1 private key | key= | The account creation succeeds. | N | +| 5 | Creates an account with a valid KeyList of ED25519 and ECDSAsecp256k1 private and public keys | key= | The account creation succeeds. | N | +| 6 | Creates an account with a valid KeyList of nested Keylists (three levels) | key= | The account creation succeeds. | N | +| 7 | Creates an account with no key | | The account creation fails with a KEY_REQUIRED response code from the network. | N | +| 8 | Creates an account with an invalid key | key= | The account creation fails with an SDK internal error. | N | + +### **Initial Balance:** + +- The initial number of tinybars to put into the account. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|-------------------------------------------------------------------------------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with an initial balance | key=, initialBalance=100 | The account creation succeeds and the account contains 100 tinybar. | N | +| 2 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | N | +| 3 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=1,000,000,000,000 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | N | + +### **Receiver Signature Required:** + +- If true, this account's key must sign any transaction depositing into this account (in addition to all withdrawals).. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|--------------------------------------------------------------------------------------------|---------------------------------------------------------|--------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account that requires a receiving signature | key=, receiverSignatureRequired=true | The account creation succeeds and the account requires a receiving signature. | N | +| 2 | Creates an account that requires a receiving signature but isn't signed by the account key | key=, receiverSignatureRequired=true | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | + +### **Auto Renew Period:** + +- The account is charged to extend its expiration date every ‘this many’ seconds. If it doesn't have enough balance, it extends as long as possible. If it is empty when it expires, then it is deleted. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|---------------------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with an auto renew period set to 30 days (2,592,000 seconds) | key=, autoRenewPeriod=2592000 | The account creation succeeds and the account's auto renew period should equal 2,592,000 seconds. | N | +| 2 | Creates an account and set the auto renew period to -1 seconds | key=, autoRenewPeriod=-1 | The account creation fails with an INVALID_RENEWAL_PERIOD response code from the network. | N | +| 3 | Creates an account and set the auto renew period to 10 days (864,000 seconds) | key=, autoRenewPeriod=864000 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | N | + +### **Memo:** + +- The memo associated with the account (UTF-8 encoding max 100 bytes). + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a memo | key=, memo="testmemo" | The account creation succeeds and the account's memo equals “testmemo”. | N | +| 2 | Creates an account with a memo that exceeds 100 characters | key=, memo="this is a really long memo that definitely exceeds 100 characters and it should without a doubt fail the test" | The account creation fails with a MEMO_TOO_LONG response code from the network. | N | + +### **Max Automatic Token Associations:** + +- The maximum number of tokens with which an account can be implicitly associated. Defaults to 0 and up to a maximum value of 1000. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|----------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a max token association set to maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | +| 2 | Creates an account with a max token association set over the maximum value | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | + +### **Staked ID:** + +- ID of the account to which this account is staked. + - OR +- ID of the node to which this account is staked. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|---------------------------------------------------------------------------------------|--------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with the staked account ID set to the operators account ID | key=, stakedAccountId= | The account creation succeeds and the account has a staking account ID equal to the input account ID. | N | +| 2 | Creates an account with the staked node ID set to a valid node ID | key=, stakedNodeId= | The account creation succeeds and the account has a staking node ID equal to the input node ID. | N | +| 3 | Creates an account with the staked account ID set to an account ID that doesn't exist | key=, stakedAccountId=123.456.789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | N | +| 6 | Creates an account with the staked account ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | + +### **Decline rewards:** + +- If true, the account declines receiving a staking reward. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|--------------------------------------------------|---------------------------------------------|-------------------------------------------------------------------------|-------------------| +| 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | N | + +### **Alias:** + +- The bytes to be used as the account's alias. The bytes must be the serialization of a protobuf Key message for an ED25519/ECDSA_SECP256K1 primitive key type or, if the account is ECDSA_SECP256K1 based it may also be the public address, calculated as the last 20 bytes of the keccak-256 hash of the ECDSA_SECP256K1 primitive key. + +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with an ED25519 public key alias | key=, alias= | The account creation succeeds and the account has the ED25519 public key alias. | N | +| 2 | Creates an account with an ECDSAsecp256k1 public key alias | key=, alias= | The account creation succeeds and the account has the ECDSAsecp256k1 public key alias. | N | +| 2 | Creates an account with an invalid public key alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | N | + diff --git a/test-specifications/accountDeleteTransaction.md b/test-specifications/cryptoService/accountDeleteTransaction.md similarity index 100% rename from test-specifications/accountDeleteTransaction.md rename to test-specifications/cryptoService/accountDeleteTransaction.md diff --git a/test-specifications/accountUpdateTransaction.md b/test-specifications/cryptoService/accountUpdateTransaction.md similarity index 100% rename from test-specifications/accountUpdateTransaction.md rename to test-specifications/cryptoService/accountUpdateTransaction.md diff --git a/test-specifications/testSpecificationsTemplate.md b/test-specifications/testSpecificationsTemplate.md index 8d25e05..a11dac2 100644 --- a/test-specifications/testSpecificationsTemplate.md +++ b/test-specifications/testSpecificationsTemplate.md @@ -1,4 +1,4 @@ -# TestName - Test specification +# RequestName - Test specification ## Description: diff --git a/test-specifications/utility.md b/test-specifications/utility.md new file mode 100644 index 0000000..c2a5d9e --- /dev/null +++ b/test-specifications/utility.md @@ -0,0 +1,293 @@ +# Utility JSON RPC Methods + +## Description +The JSON RPC methods mentioned in this file describe additional methods that should be implemented by a TCK server that provide a utility value of some sort that is not specific to one Hedera request type. These methods can involve, but are not limited to, setting up or tearing down a test environment or using the SDK to generate a key pair to be used by the TCK driver. + +## Methods + +### `setup` + +#### Description + +Method used to establish communication and initialize a TCK server with fee-payer information, as well as optional network information depending on the network setup being used to test. + +#### Parameters + +| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | +|--------------------|--------|-------------------|--------------|-----------------------------------------------------------------------------------| +| operatorAccountId | string | required | Input | The ID of the account to pay for all requests | +| operatorPrivateKey | string | required | Input | The private key of the fee-payer account in DER-encoded hex string representation | +| nodeIp | string | optional | Input | Required for a custom network. The IP of the local consensus node | +| nodeAccountId | string | optional | Input | Required for a custom network. The account ID for the local node | +| mirrorNetworkIp | string | optional | Input | Required for a custom network. The IP for the local mirror node | +| message | string | required | Output | Informational message about the execution of the method | +| status | string | required | Output | The status/result of the execution | + +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 763543, + "method": "setup", + "params": { + "operatorAccountId": "0.0.47762334", + "operatorPrivateKey": "302e020100300506032b65700422042091f37373fe8b38bd4495e489ae7cb50c28909970231b906b6322a984e582f6af", + "nodeIp": "127.0.0.1:50211", + "nodeAccountId": "0.0.3", + "mirrorNetworkIp": "127.0.0.1:5600" + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 763543, + "result": { + "message": "Successfully setup custom client.", + "status": "SUCCESS" + } +} +``` + +--- + +### `reset` + +#### Description + +Method used to close the TCK network connections. Network connections can be reestablished after with another `setup` call. + +#### Parameters + +| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | +|----------------|--------|-------------------|--------------|---------------------------------------------------------| +| message | string | required | Output | Informational message about the execution of the method | +| status | string | required | Output | The status/result of the execution | + +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "reset" +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "message": "Successfully reset client.", + "status": "SUCCESS" + } +} +``` + +--- + +### `generateKey` + +#### Description + +Method used to generate a Hedera Key. + +#### Parameters + +| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | +|----------------|--------|-------------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | string | optional | Input | The type of Key to generate. If provided, it MUST be one of `ed25519PrivateKey`, `ed25519PublicKey`, `ecdsaSecp256k1PrivateKey`, `ecdsaSecp256k1PublicKey`, `keyList`, or `thresholdKey`. If not provided, the returned key will be of type `ed25519PrivateKey`, `ed25519PublicKey`, `ecdsaSecp256k1PrivateKey`, or `ecdsaSecp256k1PublicKey` | +| privateKey | string | optional | Input | The DER-encoded hex string private key from which to generate a public key. This should only be provided for types `ed25519PublicKey` and `ecdsaSecp256k1PublicKey` if the public keys would like to be generated from a specific private key, but still not required if a random public key is desired. | +| protobufBytes | bool | optional | Input | For `ed25519PublicKey` and `ecdsaSecp256k1PublicKey` types, `true` if instead of the DER-encoded hex string of the generated key, the serialized Key protobuf bytes are desired. Useful for generating aliases. | +| threshold | int | optional | Input | Required for `thresholdKey` types. The number of keys that must sign for a threshold key. | +| keys | list | optional | Input | Required for `keyList` and `thresholdKey` types. Specify the types of keys to be generated and put in the `keyList` or `thresholdKey`. All keys should contain the same parameters as this `generateKey` method (see examples below), if required | +| key | string | required | Output | The DER-encoded hex string of the generated ECDSA or ED25519 private or public key (compressed if ECDSAsecp256k1 public key). If the type was `keyList` or `thresholdKey`, the hex string of the respective serialized protobuf | + +#### JSON Request Examples + +*Generates a random ED25519 or ECDSAsecp256k1 private or public key* +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "generateKey" +} +``` + +*Generates an ED25519 private key* +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "generateKey", + "params": { + "type": "ed25519PrivateKey" + } +} +``` + +*Generates an ED25519 public key* +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "generateKey", + "params": { + "type": "ed25519PublicKey" + } +} +``` + +*Generates the serialized protobuf ECDSAsecp256k1 public key that is paired with the input ECDSAsecp256k1 private key* +```json +{ + "jsonrpc": "2.0", + "id": 4, + "method": "generateKey", + "params": { + "type": "ecdsaSecp256k1PublicKey", + "privateKey": "3030020100300706052b8104000a04220420e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", + "protobufBytes": true + } +} +``` + +*Generates a threshold key that requires two keys to sign, and contains a random key, an ED25519 private key, and an ECDSAsecp256k1 public key that is paired with the input ECDSAsecp256k1 private key* +```json +{ + "jsonrpc": "2.0", + "id": 5, + "method": "generateKey", + "params": { + "type": "thresholdKey", + "threshold": 2, + "keys": [ + {}, + { + "type": "ed25519PrivateKey" + }, + { + "type": "ecdsaSecp256k1PublicKey", + "privateKey": "3030020100300706052b8104000a04220420e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35" + } + ] + } +} +``` + +*Generate a key list that contains two key lists. The first key list contains an ED25519 private key, a random key, and an ECDSAsecp256k1 public key that is paired with the input ECDSAsecp256k1 private key. The second key list contains an ECDSAsecp256k1 private key, a threshold key, and a random key. The threshold key requires two keys to sign, and contains two random keys and an ED25519 public key that is paired with the input ED25519 private key.* +```json +{ + "jsonrpc": "2.0", + "id": 6, + "method": "generateKey", + "params": { + "type": "keyList", + "keys": [ + { + "type": "keyList", + "keys": [ + { + "type": "ed25519PrivateKey" + }, + {}, + { + "type": "ecdsaSecp256k1PublicKey", + "privateKey": "3030020100300706052b8104000a04220420e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35" + } + ] + }, + { + "type": "keyList", + "keys": [ + { + "type": "ecdsaSecp256k1PrivateKey" + }, + { + "type": "thresholdKey", + "threshold": 2, + "keys": [ + {}, + {}, + { + "type": "ed25519PublicKey", + "privateKey": "302e020100300506032b657004220420c036915d924e5b517fae86ce34d8c76005cb5099798a37a137831ff5e3dc0622" + } + ] + }, + {} + ] + } + ] + } +} +``` + +#### JSON Response Examples + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "key": "302D300706052B8104000A0322000345B82F32CA13D777FC9474AD8785045A4EC8C55B15B339CE8DFE00B9AC62ED0A" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "key": "302E020100300506032B65700422042002986CE0E075C595C8F092D4144F24925C38A4C4ADEE25E3AA0ABED5C6F309BF" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "key": "302A300506032B657003210025FCF76794560FAB2E0E795E14AB12E88C853F09BDFA7DBF7FAC7A2F6B31E403" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 4, + "result": { + "key": "3A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C2" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 5, + "result": { + "key": "2A710802126D0A2212209181CA10AA3166E56755D5F5A7D0A80DABB17A5699B1185F6E85B0F7F450669E0A2212205C7F51463FF9444951E878EEB7161EE7250691684DE0DFE048B68BDF1602D05F0A233A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C2" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 6, + "result": { + "key": "328D020A6F326D0A22122056E73F6802877166FD611D421F8AAC8D24527E0A657AFFF28DC7167CAB838B690A221220202D875F498B407BBBF5B5358A1F73D326419B4BA5AF7A3A8C2FA03F39C9628B0A233A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C20A99013296010A233A2103936FE869DB7187AC18E19E002B5EAE47EA6A02F51E9F02E190D1EF8F0B5DB2A50A6F326D0A233A210220C3866F3C42DFB0C530351A35274FE2BD9531B89A770D6336638D88C18150950A22122077F49D8D6F82DA4BC0AC9BD55B36571022E85812A25AEAF80B7AF4502F1CC3E70A22122008530EA4B75F639032EDA3C18F41A296CF631D1828697E4F052297553139F347" + } +} +``` diff --git a/test/account/test_AccountInfo.js b/test/cryptoService/test_AccountInfo.js similarity index 100% rename from test/account/test_AccountInfo.js rename to test/cryptoService/test_AccountInfo.js diff --git a/test/account/test_CreateAccount.js b/test/cryptoService/test_CreateAccount.js similarity index 97% rename from test/account/test_CreateAccount.js rename to test/cryptoService/test_CreateAccount.js index 60859fa..24b1ab8 100644 --- a/test/account/test_CreateAccount.js +++ b/test/cryptoService/test_CreateAccount.js @@ -140,7 +140,7 @@ describe("#createAccount()", function () { * The payer account has insufficient cryptocurrency to pay the transaction fee * INSUFFICIENT_PAYER_BALANCE = 10; **/ - // set initial bal to 5 Hbar ( 500000000 Tinybar at ratio 1: 100,000,000 ) + // set initial bal to 5 Hbar ( 500000000 Tinybar at ratio 1: 100,000,000 ) const initialBalance = 500000000; // set payer (funding account) bal to 5 Hbar + 1 Tinybar ( 500000001 Tinybar ) const payerBalance = 500000001; @@ -581,4 +581,4 @@ describe("#createAccount()", function () { }); return Promise.resolve(); -}); +}); \ No newline at end of file diff --git a/test/account/test_DeleteAccount.js b/test/cryptoService/test_DeleteAccount.js similarity index 100% rename from test/account/test_DeleteAccount.js rename to test/cryptoService/test_DeleteAccount.js diff --git a/test/account/test_UpdateAccountKey.js b/test/cryptoService/test_UpdateAccountKey.js similarity index 100% rename from test/account/test_UpdateAccountKey.js rename to test/cryptoService/test_UpdateAccountKey.js diff --git a/test/account/test_UpdateAccountMemo.js b/test/cryptoService/test_UpdateAccountMemo.js similarity index 100% rename from test/account/test_UpdateAccountMemo.js rename to test/cryptoService/test_UpdateAccountMemo.js From 4bfc6e9233f80b8466f3e5188521247c0f76bd8a Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Tue, 23 Apr 2024 16:59:34 -0500 Subject: [PATCH 02/34] revert: unintended changes in test_CreateAccount.js Signed-off-by: Rob Walworth --- test/cryptoService/test_CreateAccount.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cryptoService/test_CreateAccount.js b/test/cryptoService/test_CreateAccount.js index 24b1ab8..60859fa 100644 --- a/test/cryptoService/test_CreateAccount.js +++ b/test/cryptoService/test_CreateAccount.js @@ -140,7 +140,7 @@ describe("#createAccount()", function () { * The payer account has insufficient cryptocurrency to pay the transaction fee * INSUFFICIENT_PAYER_BALANCE = 10; **/ - // set initial bal to 5 Hbar ( 500000000 Tinybar at ratio 1: 100,000,000 ) + // set initial bal to 5 Hbar ( 500000000 Tinybar at ratio 1: 100,000,000 ) const initialBalance = 500000000; // set payer (funding account) bal to 5 Hbar + 1 Tinybar ( 500000001 Tinybar ) const payerBalance = 500000001; @@ -581,4 +581,4 @@ describe("#createAccount()", function () { }); return Promise.resolve(); -}); \ No newline at end of file +}); From 6bf2191f915b31c0c0f858e1e2c4962603a70bcf Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Tue, 23 Apr 2024 17:08:31 -0500 Subject: [PATCH 03/34] docs: Update test specifications template markdown file to be correct Signed-off-by: Rob Walworth --- .../testSpecificationsTemplate.md | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/test-specifications/testSpecificationsTemplate.md b/test-specifications/testSpecificationsTemplate.md index a11dac2..686d50a 100644 --- a/test-specifications/testSpecificationsTemplate.md +++ b/test-specifications/testSpecificationsTemplate.md @@ -4,13 +4,18 @@ ## Design: -**Transaction properties:** - -**Crypto create protobufs:** +**Request properties:** +** protobufs:** **Response codes:** +https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.proto + +**Mirror Node APIs:** + +https://docs.hedera.com/hedera/sdks-and-apis/rest-api + ## Initialisation: @@ -18,26 +23,37 @@ ``` -## Properties +## JSON-RPC API Endpoint Documentation -### **PropertyName:** +### Method Name -- Description +`` + +### Parameters -| Test no | Name | Input | Expected response | Implemented (Y/N) -| ------- | -------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |--------------------- -| 1 | | | | -| 2 | | | | -| 3 | | | | +| Parameter Name | Type | Required/Optional | Description/Notes | +|--------------------|--------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------| + + +## Property/Function Tests + +### **Property/Function Name:** + +- Description +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------|-------|-------------------|-------------------| +| 1 | | | | | +| 2 | | | | | +| 3 | | | | | -### **PropertyName:** +### **Property/Function Name:** - Description -| Test no | Name | Input | Expected response | Implemented (Y/N) -| ------- | -------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |--------------------- -| 1 | | | | -| 2 | | | | -| 3 | | | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------|-------|-------------------|-------------------| +| 1 | | | | | +| 2 | | | | | +| 3 | | | | | From f58de7567f5d48e015cf2b473df351a82114cfdf Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 24 Apr 2024 13:14:18 -0500 Subject: [PATCH 04/34] docs: Remove JSONRPC API document for account create as that information is now contained in accountCreateTransaction.md Signed-off-by: Rob Walworth --- JSONRPC_API_doc_createAccount.md | 678 ------------------------------- 1 file changed, 678 deletions(-) delete mode 100644 JSONRPC_API_doc_createAccount.md diff --git a/JSONRPC_API_doc_createAccount.md b/JSONRPC_API_doc_createAccount.md deleted file mode 100644 index a36880b..0000000 --- a/JSONRPC_API_doc_createAccount.md +++ /dev/null @@ -1,678 +0,0 @@ -# JSON-RPC API documentation - Create account - -# Description: - -API documentation for the JSON-RPC server - creating an account. - -For account creation, the createAccount method handles setting all the required and optional parameters for calling the SDKs AccountCreateTransaction() - -AccountCreateTransaction() parameters: - -```javascipt -* @param publicKey required -* @param initialBalance optional -* @param receiverSignatureRequired optional -* @param maxAutomaticTokenAssociations optional -* @param stakedAccountId optional -* @param stakedNodeId optional -* @param declineStakingReward optional -* @param accountMemo optional -* @param autoRenewPeriod optional -* @param privateKey optional (used for signing) -``` - -# Methods: - ---- - -### Setting up/break down environment - -### [setup (testnet)](#setup1) - -Parameters - -| Name | Type | Description | -| ------------------------- | ------ | ----------------------------------- | -| params | object | | -| params.OperatorAccountID | string | Account Id of the operator | -| params.OperatorPrivateKey | string | Private key of the operator account | - -### [setup (local node)](#setup2) - -Parameters - -| Name | Type | Description | -| ------------------------- | ------ | ----------------------------------- | -| params | object | | -| params.OperatorAccountID | string | Account Id of the operator | -| params.OperatorPrivateKey | string | Private key of the operator account | -| params.nodeIP | string | IP of the local node | -| params.nodeAccountID | string | Account ID for the local node | -| params.mirrorNetworkIP | string | IP for the mirror network | - -### [reset](#reset) - -Parameters - -| Name | Type | Description | -| ------ | ------ | ---------------------------------- | -| method | string | “reset” method to reset sdk client | - -**Result/error parameters** - -Result - -| Name | Type | Description | -| -------------- | ------ | ---------------------------------------- | -| result | object | | -| result.message | string | Details on the testnet mirror node setup | -| result.status | string | Status of the network setup/teardown | - -Errors - -| Code | Message | Description | -| ---- | --------------------------- | ------------------------------- | -| 0 | failed to parse entity id | Provided operator ID is invalid | -| 0 | invalid private key length: | No value given to operator ID | - ---- - -### Account creation - -Calls for create account. The JSON-RPC server returns a string representation of a transaction receipt back to the test driver - -- Method: createAccount - **Parameters:** - - [publicKey](#publickey) - - [initialBalance](#initialkalance) - - [maxAutomaticTokenAssociations](#maxautomatictokenassociations) - - [stakedAccountId](#stakedaccountid) - - [stakedNodeId](#stakednodeid) - - [declineStakingReward](#declinestakingreward) - - [accountMemo](#accountmemo) - - [autoRenewPeriod](#autorenewperiod) - -params - -| Name | Type | Description | | -| ------------------------------------ | ------- | ------------------------------------------------------------------------------- | -------- | -| params | object | | | -| params.publicKey | string | The accounts public key | Required | -| params.initialBalance | number | Initial balance for the new account | Optional | -| params.receiverSignatureRequired | boolean | True/false if the receiver signature is required | Optional | -| params.maxAutomaticTokenAssociations | number | Max token associations of the account | Optional | -| params.stakedAccountID | string | Id of the account the new account is staking to. Can stake to a node OR account | Optional | -| params.stakedNodeId | string | Id of the node the new account is staking to. Can stake to a node OR account | Optional | -| params.declineStakingReward | boolean | True/false if declining to receive staking rewards | Optional | -| params.accountMemo | string | Account memo for the new account | Optional | - -result - -| Name | Type | Description | -| ---------------- | ------ | ------------------------------ | -| result | object | | -| result.accountId | string | The ID of the new account | -| result.status | string | Status of the account creation | - -error - -| Name | Type | Description | -| ------------------ | ------ | --------------------------- | -| error.data | object | | -| error.data.status | string | The hedera error code value | -| error.data.message | string | Full error message | - ---- - -### setup1 - -- This method sets up the environment for connecting to Testnet - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 567, - "method": "setup", - "params": { - "operatorAccountId": "0.0.47762334", - "operatorPrivateKey": "302e020100300506032b65700422042091f37373fe8b38bd4495e489ae7cb50c28909970231b906b6322a984e582f6af" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 567, - "result": { - "message": "Successfully setup testnet client.", - "status": "SUCCESS" - } -} -``` - ---- - -### setup2 - -- This method sets up the environment for connecting to the Local Node - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 763543, - "method": "setup", - "params": { - "operatorAccountId": "0.0.47762334", - "operatorPrivateKey": "302e020100300506032b65700422042091f37373fe8b38bd4495e489ae7cb50c28909970231b906b6322a984e582f6af" - "nodeIp": "127.0.0.1:50211", - "nodeAccountId": "3", - "mirrorNetworkIp": "127.0.0.1:5600" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 763543, - "result": { - "message": "Successfully setup custom client.", - "status": "SUCCESS" - } -} -``` - ---- - -### reset - -- This method resets the SDK client - -**Examples** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 99232, - "method": "reset" -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 99232, - "result": true -} -``` - ---- - -### publicKey - -- This parameter sets the public key for account creation - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "error": { - "code": -32603, - "message": "Internal error", - "data": { - "message": "invalid public key length: 42 bytes" - } - } -} -``` - ---- - -### initialBalance - -- This parameter sets the initial balance of the account when creating an account - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "initialBalance": 5000 - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "INVALID_INITIAL_BALANCE", - "message": "transaction 0.0.47762334@1665620301.783615877 failed precheck with status INVALID_INITIAL_BALANCE" - } - } -} -``` - ---- - -### receiverSignatureRequired - -- This parameter sets the receiverSignatureRequired property when creating an account. If true, a private key must be supplied to sign the transaction - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "privateKey": "302e020100300506032b65700422042091f37373fe8b38bd4495e489ae7cb50c28909970231b906b6322a984e582f6af", - "initialBalance": 5000, - "receiverSignatureRequired": true - } -} -``` - -Response - -```yaml -{ - 'jsonrpc': '2.0', - 'id': 648, - 'result': { 'accountId': '0.0.48601829', 'status': 'SUCCESS' }, -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "INVALID_SIGNATURE", - "message": "receipt for transaction 0.0.47762334@1665621371.487172994 contained error status INVALID_SIGNATURE" - } - } -} -``` - ---- - -### maxAutomaticTokenAssociations - -- This parameter sets the maxAutomaticTokenAssociations property when creating an account. - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "maxAutomaticTokenAssociations": 2 - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT", - "message": "transaction 0.0.47762334@1665622075.473792774 failed precheck with status REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT" - } - } -} -``` - ---- - -### stakedAccountId - -- This parameter sets the stakedAccountId property when creating an account. Note an account can stake to another account ID OR a node ID - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "stakedAccountId": "0.0.47762334" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "INVALID_STAKING_ID", - "message": "transaction 0.0.47762334@1665623150.206800943 failed precheck with status INVALID_STAKING_ID" - } - } -} -``` - ---- - -### stakedNodeId - -- This parameter sets the stakedNodeId property when creating an account. Note an account can stake to another account ID OR a node ID - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "stakedNodeId": "0.0.47762334" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "INVALID_STAKING_ID", - "message": "transaction 0.0.47762334@1665623150.206800943 failed precheck with status INVALID_STAKING_ID" - } - } -} -``` - ---- - -### declineStakingReward - -- This parameter sets the declineStakingReward property when creating an account. - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 649, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "declineStakingReward": true - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```yaml -None -``` - ---- - -### accountMemo - -This parameter sets the accountMemo property when creating an account. - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 649, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "accountMemo": "Hello I am an account memo" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 648, - "result": { - "accountId": "0.0.48601829", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 649, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "MEMO_TOO_LONG", - "message": "transaction 0.0.47762334@1665624763.603280522 failed precheck with status MEMO_TOO_LONG" - } - } -} -``` - ---- - -### autoRenewPeriod - -This parameter sets the autoRenewPeriod property when creating an account. - -**Example:** - -Request - -```json -{ - "jsonrpc": "2.0", - "id": 649, - "method": "createAccount", - "params": { - "publicKey": "302a300506032b6570032100eb42aa1eabdb60bfd1d6ac3c9f226f0ff7d5a53335f67a851e446e015290f213", - "autoRenewPeriod": "2592000" - } -} -``` - -Response - -```json -{ - "jsonrpc": "2.0", - "id": 649, - "result": { - "accountId": "0.0.48628115", - "status": "SUCCESS" - } -} -``` - -Error - -```json -{ - "jsonrpc": "2.0", - "id": 649, - "error": { - "code": -32001, - "message": "Hedera Error", - "data": { - "status": "AUTORENEW_DURATION_NOT_IN_RANGE", - "message": "transaction 0.0.47762334@1665882197.620188556 failed precheck with status AUTORENEW_DURATION_NOT_IN_RANGE" - } - } -} -``` From 5a46641e16102277f19808d3b5fb9dca94820656 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 24 Apr 2024 14:28:01 -0500 Subject: [PATCH 05/34] docs: Update language used to determine if a request is successful. Fix alias only being able to be an ECDSAsecp256k1 key. Signed-off-by: Rob Walworth --- .../cryptoService/accountCreateTransaction.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test-specifications/cryptoService/accountCreateTransaction.md b/test-specifications/cryptoService/accountCreateTransaction.md index 627c7cf..e15528a 100644 --- a/test-specifications/cryptoService/accountCreateTransaction.md +++ b/test-specifications/cryptoService/accountCreateTransaction.md @@ -4,7 +4,7 @@ This test specification for AccountCreateTransaction is to be one of many for testing the functionality of the Hedera SDKs. The SDK under test will use the language specific JSON-RPC server return responses back to the test driver. ## Design: -Each test within the test specification is linked to one of the properties within AccountCreateTransaction. Each property is tested with a mix of boundaries. The inputs for each test are a range of valid, minimum, maximum, negative and invalid values for the method. The expected response of a passed test can be a correct error response code or seen as the result of node queries. Success on the consensus node can be obtained by a queries such as AccountInfoQuery or AccountBalanceQuery, and on the mirror node through the rest API. Error codes are obtained from the response code proto files. +Each test within the test specification is linked to one of the properties within AccountCreateTransaction. Each property is tested with a mix of boundaries. The inputs for each test are a range of valid, minimum, maximum, negative and invalid values for the method. The expected response of a passed test can be a correct error response code or seen as the result of node queries. A successful transaction (the transaction reached consensus and was applied to state) can be determined by getting a `TransactionReceipt` or `TransactionRecord`, or can be determined by using queries such as `AccountInfoQuery` or `AccountBalanceQuery` and investigating for the required changes (creations, updates, etc.). The mirror node can also be used to determine if a transaction was successful via its rest API. Error codes are obtained from the response code proto files. **Transaction properties:** @@ -54,7 +54,8 @@ const transaction = new AccountCreateTransaction() | stakedAccountId | string | optional | | | stakedNodeId | int64 | optional | | | declineStakingReward | bool | optional | | -| alias | string | optional | Hex string representation of a serialized protobuf Key of an ED25519 or ECDSAsecp256k1 public key type. | +| alias | string | optional | Hex string representation of the keccak-256 hash of an ECDSAsecp256k1 public key type. | +| signerKey | string | optional | DER-encoded hex string representation of an additional private key required to sign. | ## Property Tests @@ -145,11 +146,11 @@ const transaction = new AccountCreateTransaction() ### **Alias:** -- The bytes to be used as the account's alias. The bytes must be the serialization of a protobuf Key message for an ED25519/ECDSA_SECP256K1 primitive key type or, if the account is ECDSA_SECP256K1 based it may also be the public address, calculated as the last 20 bytes of the keccak-256 hash of the ECDSA_SECP256K1 primitive key. +- The bytes to be used as the account's alias. The bytes must be formatted as the calculated last 20 bytes of the keccak-256 hash of an ECDSA primitive key. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|------------------------------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with an ED25519 public key alias | key=, alias= | The account creation succeeds and the account has the ED25519 public key alias. | N | -| 2 | Creates an account with an ECDSAsecp256k1 public key alias | key=, alias= | The account creation succeeds and the account has the ECDSAsecp256k1 public key alias. | N | -| 2 | Creates an account with an invalid public key alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key | key=, alias=, signerKey= | The account creation succeeds and the account has the keccak-256 hash of the ECDSAsecp256k1 public key as its alias. | N | +| 2 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature | key=, alias= | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | +| 3 | Creates an account with an invalid alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | N | From 0853ffc7323ed613adf66e79be0f84f72f724438 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 25 Apr 2024 14:09:16 -0500 Subject: [PATCH 06/34] docs: address PR comments Signed-off-by: Rob Walworth --- .../cryptoService/accountCreateTransaction.md | 355 ++++++++++++++++-- test-specifications/utility.md | 104 +++-- 2 files changed, 366 insertions(+), 93 deletions(-) diff --git a/test-specifications/cryptoService/accountCreateTransaction.md b/test-specifications/cryptoService/accountCreateTransaction.md index e15528a..0529347 100644 --- a/test-specifications/cryptoService/accountCreateTransaction.md +++ b/test-specifications/cryptoService/accountCreateTransaction.md @@ -43,19 +43,21 @@ const transaction = new AccountCreateTransaction() ### Parameters -| Parameter Name | Type | Required/Optional | Description/Notes | -|---------------------------|--------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------| -| key | string | optional | DER-encoded hex string representation for private or public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes. | -| initialBalance | int64 | optional | Units of tinybars | -| receiverSignatureRequired | bool | optional | | -| autoRenewPeriod | int64 | optional | Units of seconds | -| memo | string | optional | | -| maxAutoTokenAssociations | int32 | optional | | -| stakedAccountId | string | optional | | -| stakedNodeId | int64 | optional | | -| declineStakingReward | bool | optional | | -| alias | string | optional | Hex string representation of the keccak-256 hash of an ECDSAsecp256k1 public key type. | -| signerKey | string | optional | DER-encoded hex string representation of an additional private key required to sign. | +| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | +|---------------------------|--------|-------------------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| key | string | optional | Input | DER-encoded hex string representation for private or public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes. | +| initialBalance | int64 | optional | Input | Units of tinybars | +| receiverSignatureRequired | bool | optional | Input | | +| autoRenewPeriod | int64 | optional | Input | Units of seconds | +| memo | string | optional | Input | | +| maxAutoTokenAssociations | int32 | optional | Input | | +| stakedAccountId | string | optional | Input | | +| stakedNodeId | int64 | optional | Input | | +| declineStakingReward | bool | optional | Input | | +| alias | string | optional | Input | Hex string representation of the keccak-256 hash of an ECDSAsecp256k1 public key type. | +| signerKey | string | optional | Input | DER-encoded hex string representation of an additional private key required to sign. | +| accountId | string | required | Output | The ID of the created account. | +| status | string | required | Output | The status of the submitted `AccountCreateTransaction` (from a `TransactionReceipt`). | ## Property Tests @@ -74,52 +76,219 @@ const transaction = new AccountCreateTransaction() | 7 | Creates an account with no key | | The account creation fails with a KEY_REQUIRED response code from the network. | N | | 8 | Creates an account with an invalid key | key= | The account creation fails with an SDK internal error. | N | +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "3030020100300706052b8104000a04220420e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35" + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` + ### **Initial Balance:** - The initial number of tinybars to put into the account. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|-------------------------------------------------------------------------------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with an initial balance | key=, initialBalance=100 | The account creation succeeds and the account contains 100 tinybar. | N | -| 2 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | N | -| 3 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=1,000,000,000,000 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|-------------------------------------------------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with an initial balance | key=, initialBalance=100 | The account creation succeeds and the account contains 100 tinybar. | N | +| 2 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | N | +| 3 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=+1 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | N | + +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "3030020100300706052b8104000a04220420e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", + "initialBalance": 100 + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` ### **Receiver Signature Required:** -- If true, this account's key must sign any transaction depositing into this account (in addition to all withdrawals).. +- If true, this account's key must sign any transaction depositing into this account (in addition to all withdrawals). | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|--------------------------------------------------------------------------------------------|---------------------------------------------------------|--------------------------------------------------------------------------------------|-------------------| | 1 | Creates an account that requires a receiving signature | key=, receiverSignatureRequired=true | The account creation succeeds and the account requires a receiving signature. | N | | 2 | Creates an account that requires a receiving signature but isn't signed by the account key | key=, receiverSignatureRequired=true | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "receiverSignatureRequired": true + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` + ### **Auto Renew Period:** - The account is charged to extend its expiration date every ‘this many’ seconds. If it doesn't have enough balance, it extends as long as possible. If it is empty when it expires, then it is deleted. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|---------------------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with an auto renew period set to 30 days (2,592,000 seconds) | key=, autoRenewPeriod=2592000 | The account creation succeeds and the account's auto renew period should equal 2,592,000 seconds. | N | -| 2 | Creates an account and set the auto renew period to -1 seconds | key=, autoRenewPeriod=-1 | The account creation fails with an INVALID_RENEWAL_PERIOD response code from the network. | N | -| 3 | Creates an account and set the auto renew period to 10 days (864,000 seconds) | key=, autoRenewPeriod=864000 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with an auto renew period set to 60 days (5,184,000 seconds) | key=, autoRenewPeriod=5184000 | The account creation succeeds and the account's auto renew period should equal 5,184,000 seconds. | N | +| 2 | Creates an account with an auto renew period set to -1 seconds | key=, autoRenewPeriod=-1 | The account creation fails with an INVALID_RENEWAL_PERIOD response code from the network. | N | +| 3 | Creates an account with an auto renew period set to the minimum period of 30 days (2,592,000 seconds) | key=, autoRenewPeriod=2592000 | The account creation succeeds and the account's auto renew period should equal 2,592,000 seconds. | N | +| 4 | Creates an account with an auto renew period set to the minimum period of 30 days minus one second (2,591,999 seconds) | key=, autoRenewPeriod=2591999 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | N | +| 5 | Creates an account with an auto renew period set to the maximum period of 8,000,001 seconds | key=, autoRenewPeriod=8000001 | The account creation succeeds and the account's auto renew period should equal 8,000,001 seconds. | N | +| 6 | Creates an account with an auto renew period set to the maximum period plus one seconds (8,000,002 seconds) | key=, autoRenewPeriod=8000002 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | N | + +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "autoRenewPeriod": 5184000 + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` ### **Memo:** - The memo associated with the account (UTF-8 encoding max 100 bytes). -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a memo | key=, memo="testmemo" | The account creation succeeds and the account's memo equals “testmemo”. | N | -| 2 | Creates an account with a memo that exceeds 100 characters | key=, memo="this is a really long memo that definitely exceeds 100 characters and it should without a doubt fail the test" | The account creation fails with a MEMO_TOO_LONG response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a valid memo | key=, memo="testmemo" | The account creation succeeds and the account's memo equals “testmemo”. | N | +| 2 | Creates an account with an empty memo | key=, memo="" | The account creation succeeds and the account's memo is empty. | N | +| 3 | Creates an account with a memo that is 100 characters | key=, memo="This is a really long memo but it is still valid because it is 100 characters exactly on the money!!" | The account creation succeeds and the account's memo equals "This is a really long memo but it is still valid because it is 100 characters exactly on the money!!". | N | +| 4 | Creates an account with a memo that exceeds 100 characters | key=, memo="This is a long memo that is not valid because it exceeds 100 characters and it should fail the test!!" | The account creation fails with a MEMO_TOO_LONG response code from the network. | N | + +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "memo": "testmemo" + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` ### **Max Automatic Token Associations:** - The maximum number of tokens with which an account can be implicitly associated. Defaults to 0 and up to a maximum value of 1000. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|----------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a max token association set to maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | -| 2 | Creates an account with a max token association set over the maximum value | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a max token association set to 0 | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | +| 2 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | +| 3 | Creates an account with a max token association that is the maximum value plus one | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | + +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "maxAutoTokenAssociations": 100 + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` ### **Staked ID:** @@ -127,14 +296,65 @@ const transaction = new AccountCreateTransaction() - OR - ID of the node to which this account is staked. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|---------------------------------------------------------------------------------------|--------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with the staked account ID set to the operators account ID | key=, stakedAccountId= | The account creation succeeds and the account has a staking account ID equal to the input account ID. | N | -| 2 | Creates an account with the staked node ID set to a valid node ID | key=, stakedNodeId= | The account creation succeeds and the account has a staking node ID equal to the input node ID. | N | -| 3 | Creates an account with the staked account ID set to an account ID that doesn't exist | key=, stakedAccountId=123.456.789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | -| 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | -| 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | N | -| 6 | Creates an account with the staked account ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|---------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with the staked account ID set to the operators account ID | key=, stakedAccountId= | The account creation succeeds and the account has a staking account ID equal to the input account ID. | N | +| 2 | Creates an account with the staked node ID set to a valid node ID | key=, stakedNodeId= | The account creation succeeds and the account has a staking node ID equal to the input node ID. | N | +| 3 | Creates an account with the staked account ID set to an account ID that doesn't exist | key=, stakedAccountId="123.456.789" | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | N | +| 6 | Creates an account with the staked account ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 7 | Creates an account with a staked account ID and a staked node ID | key=, stakedAccountId=, stakedNodeId= | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | + +#### JSON Request Examples + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "stakedAccountId": "0.0.3" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 99233, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "stakedNodeId": 10 + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 99233, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` ### **Decline rewards:** @@ -144,6 +364,33 @@ const transaction = new AccountCreateTransaction() |---------|--------------------------------------------------|---------------------------------------------|-------------------------------------------------------------------------|-------------------| | 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | N | +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "declineStakingRewards": true + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` + ### **Alias:** - The bytes to be used as the account's alias. The bytes must be formatted as the calculated last 20 bytes of the keccak-256 hash of an ECDSA primitive key. @@ -154,3 +401,31 @@ const transaction = new AccountCreateTransaction() | 2 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature | key=, alias= | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | | 3 | Creates an account with an invalid alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | N | +#### JSON Request Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "method": "createAccount", + "params": { + "key": "302e020100300506032b65700422042031f8eb3e77a04ebe599c51570976053009e619414f26bdd39676a5d3b2782a1d", + "alias": "990a3f6573669cc6266c00983dc24359bd4b223b", + "signerKey": "30540201010420c5f9d140822511e581228feb2bde5a9706ee4c4377822e7cf4755fec529f0bcfa00706052b8104000aa124032200038064ccfe93ce1492ada790da7204edd8e3fd004ee68e4fae7641e00db20527c5" + } +} +``` + +#### JSON Response Example + +```json +{ + "jsonrpc": "2.0", + "id": 99232, + "result": { + "accountId": "0.0.12345", + "status": "SUCCESS" + } +} +``` + diff --git a/test-specifications/utility.md b/test-specifications/utility.md index c2a5d9e..4fb47eb 100644 --- a/test-specifications/utility.md +++ b/test-specifications/utility.md @@ -110,7 +110,7 @@ Method used to generate a Hedera Key. | keys | list | optional | Input | Required for `keyList` and `thresholdKey` types. Specify the types of keys to be generated and put in the `keyList` or `thresholdKey`. All keys should contain the same parameters as this `generateKey` method (see examples below), if required | | key | string | required | Output | The DER-encoded hex string of the generated ECDSA or ED25519 private or public key (compressed if ECDSAsecp256k1 public key). If the type was `keyList` or `thresholdKey`, the hex string of the respective serialized protobuf | -#### JSON Request Examples +#### JSON Request/Response Examples *Generates a random ED25519 or ECDSAsecp256k1 private or public key* ```json @@ -121,6 +121,16 @@ Method used to generate a Hedera Key. } ``` +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "key": "302D300706052B8104000A0322000345B82F32CA13D777FC9474AD8785045A4EC8C55B15B339CE8DFE00B9AC62ED0A" + } +} +``` + *Generates an ED25519 private key* ```json { @@ -133,6 +143,16 @@ Method used to generate a Hedera Key. } ``` +```json +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "key": "302E020100300506032B65700422042002986CE0E075C595C8F092D4144F24925C38A4C4ADEE25E3AA0ABED5C6F309BF" + } +} +``` + *Generates an ED25519 public key* ```json { @@ -145,6 +165,16 @@ Method used to generate a Hedera Key. } ``` +```json +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "key": "302A300506032B657003210025FCF76794560FAB2E0E795E14AB12E88C853F09BDFA7DBF7FAC7A2F6B31E403" + } +} +``` + *Generates the serialized protobuf ECDSAsecp256k1 public key that is paired with the input ECDSAsecp256k1 private key* ```json { @@ -159,6 +189,16 @@ Method used to generate a Hedera Key. } ``` +```json +{ + "jsonrpc": "2.0", + "id": 4, + "result": { + "key": "3A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C2" + } +} +``` + *Generates a threshold key that requires two keys to sign, and contains a random key, an ED25519 private key, and an ECDSAsecp256k1 public key that is paired with the input ECDSAsecp256k1 private key* ```json { @@ -182,6 +222,16 @@ Method used to generate a Hedera Key. } ``` +```json +{ + "jsonrpc": "2.0", + "id": 5, + "result": { + "key": "2A710802126D0A2212209181CA10AA3166E56755D5F5A7D0A80DABB17A5699B1185F6E85B0F7F450669E0A2212205C7F51463FF9444951E878EEB7161EE7250691684DE0DFE048B68BDF1602D05F0A233A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C2" + } +} +``` + *Generate a key list that contains two key lists. The first key list contains an ED25519 private key, a random key, and an ECDSAsecp256k1 public key that is paired with the input ECDSAsecp256k1 private key. The second key list contains an ECDSAsecp256k1 private key, a threshold key, and a random key. The threshold key requires two keys to sign, and contains two random keys and an ED25519 public key that is paired with the input ED25519 private key.* ```json { @@ -230,58 +280,6 @@ Method used to generate a Hedera Key. } ``` -#### JSON Response Examples - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "key": "302D300706052B8104000A0322000345B82F32CA13D777FC9474AD8785045A4EC8C55B15B339CE8DFE00B9AC62ED0A" - } -} -``` - -```json -{ - "jsonrpc": "2.0", - "id": 2, - "result": { - "key": "302E020100300506032B65700422042002986CE0E075C595C8F092D4144F24925C38A4C4ADEE25E3AA0ABED5C6F309BF" - } -} -``` - -```json -{ - "jsonrpc": "2.0", - "id": 3, - "result": { - "key": "302A300506032B657003210025FCF76794560FAB2E0E795E14AB12E88C853F09BDFA7DBF7FAC7A2F6B31E403" - } -} -``` - -```json -{ - "jsonrpc": "2.0", - "id": 4, - "result": { - "key": "3A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C2" - } -} -``` - -```json -{ - "jsonrpc": "2.0", - "id": 5, - "result": { - "key": "2A710802126D0A2212209181CA10AA3166E56755D5F5A7D0A80DABB17A5699B1185F6E85B0F7F450669E0A2212205C7F51463FF9444951E878EEB7161EE7250691684DE0DFE048B68BDF1602D05F0A233A210339A36013301597DAEF41FBE593A02CC513D0B55527EC2DF1050E2E8FF49C85C2" - } -} -``` - ```json { "jsonrpc": "2.0", From f5e113cd9afc04b8998b44a0b63d5e600f01188e Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Fri, 26 Apr 2024 09:52:37 -0500 Subject: [PATCH 07/34] docs: Address PR comments. Keep folder names kebab-case Signed-off-by: Rob Walworth --- .../accountBalanceQuery.md | 0 .../accountCreateTransaction.md | 60 +++++++++++-------- .../accountDeleteTransaction.md | 0 .../accountUpdateTransaction.md | 0 test-specifications/utility.md | 60 +++++++++++-------- .../test_AccountInfo.js | 0 .../test_CreateAccount.js | 0 .../test_DeleteAccount.js | 0 .../test_UpdateAccountKey.js | 0 .../test_UpdateAccountMemo.js | 0 10 files changed, 69 insertions(+), 51 deletions(-) rename test-specifications/{cryptoService => crypto-service}/accountBalanceQuery.md (100%) rename test-specifications/{cryptoService => crypto-service}/accountCreateTransaction.md (83%) rename test-specifications/{cryptoService => crypto-service}/accountDeleteTransaction.md (100%) rename test-specifications/{cryptoService => crypto-service}/accountUpdateTransaction.md (100%) rename test/{cryptoService => crypto-service}/test_AccountInfo.js (100%) rename test/{cryptoService => crypto-service}/test_CreateAccount.js (100%) rename test/{cryptoService => crypto-service}/test_DeleteAccount.js (100%) rename test/{cryptoService => crypto-service}/test_UpdateAccountKey.js (100%) rename test/{cryptoService => crypto-service}/test_UpdateAccountMemo.js (100%) diff --git a/test-specifications/cryptoService/accountBalanceQuery.md b/test-specifications/crypto-service/accountBalanceQuery.md similarity index 100% rename from test-specifications/cryptoService/accountBalanceQuery.md rename to test-specifications/crypto-service/accountBalanceQuery.md diff --git a/test-specifications/cryptoService/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md similarity index 83% rename from test-specifications/cryptoService/accountCreateTransaction.md rename to test-specifications/crypto-service/accountCreateTransaction.md index 0529347..aa1cb7f 100644 --- a/test-specifications/cryptoService/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -41,23 +41,28 @@ const transaction = new AccountCreateTransaction() `createAccount` -### Parameters - -| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | -|---------------------------|--------|-------------------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------| -| key | string | optional | Input | DER-encoded hex string representation for private or public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes. | -| initialBalance | int64 | optional | Input | Units of tinybars | -| receiverSignatureRequired | bool | optional | Input | | -| autoRenewPeriod | int64 | optional | Input | Units of seconds | -| memo | string | optional | Input | | -| maxAutoTokenAssociations | int32 | optional | Input | | -| stakedAccountId | string | optional | Input | | -| stakedNodeId | int64 | optional | Input | | -| declineStakingReward | bool | optional | Input | | -| alias | string | optional | Input | Hex string representation of the keccak-256 hash of an ECDSAsecp256k1 public key type. | -| signerKey | string | optional | Input | DER-encoded hex string representation of an additional private key required to sign. | -| accountId | string | required | Output | The ID of the created account. | -| status | string | required | Output | The status of the submitted `AccountCreateTransaction` (from a `TransactionReceipt`). | +### Input Parameters + +| Parameter Name | Type | Required/Optional | Description/Notes | +|---------------------------|--------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| key | string | optional | DER-encoded hex string representation for private or public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes. | +| initialBalance | int64 | optional | Units of tinybars | +| receiverSignatureRequired | bool | optional | | +| autoRenewPeriod | int64 | optional | Units of seconds | +| memo | string | optional | | +| maxAutoTokenAssociations | int32 | optional | | +| stakedAccountId | string | optional | | +| stakedNodeId | int64 | optional | | +| declineStakingReward | bool | optional | | +| alias | string | optional | Hex string representation of the keccak-256 hash of an ECDSAsecp256k1 public key type. | +| signerKey | string | optional | DER-encoded hex string representation of an additional private key required to sign. | + +### Output Parameters + +| Parameter Name | Type | Required/Optional | Description/Notes | +|----------------|--------|-------------------|---------------------------------------------------------------------------------------| +| accountId | string | required | The ID of the created account. | +| status | string | required | The status of the submitted `AccountCreateTransaction` (from a `TransactionReceipt`). | ## Property Tests @@ -109,8 +114,9 @@ const transaction = new AccountCreateTransaction() | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|-------------------------------------------------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------| | 1 | Creates an account with an initial balance | key=, initialBalance=100 | The account creation succeeds and the account contains 100 tinybar. | N | -| 2 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | N | -| 3 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=+1 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | N | +| 2 | Creates an account with no initial balance | key=, initialBalance=0 | The account creation succeeds and the account contains 0 tinybar. | N | +| 3 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | N | +| 4 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=+1 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | N | #### JSON Request Example @@ -143,10 +149,11 @@ const transaction = new AccountCreateTransaction() - If true, this account's key must sign any transaction depositing into this account (in addition to all withdrawals). -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|--------------------------------------------------------------------------------------------|---------------------------------------------------------|--------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account that requires a receiving signature | key=, receiverSignatureRequired=true | The account creation succeeds and the account requires a receiving signature. | N | -| 2 | Creates an account that requires a receiving signature but isn't signed by the account key | key=, receiverSignatureRequired=true | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|--------------------------------------------------------------------------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account that requires a receiving signature | key=, receiverSignatureRequired=true | The account creation succeeds and the account requires a receiving signature. | N | +| 1 | Creates an account that doesn't require a receiving signature | key=, receiverSignatureRequired=false | The account creation succeeds and the account doesn't require a receiving signature. | N | +| 2 | Creates an account that requires a receiving signature but isn't signed by the account key | key=, receiverSignatureRequired=true | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | #### JSON Request Example @@ -360,9 +367,10 @@ const transaction = new AccountCreateTransaction() - If true, the account declines receiving a staking reward. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|--------------------------------------------------|---------------------------------------------|-------------------------------------------------------------------------|-------------------| -| 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|---------------------------------------------------------|----------------------------------------------|--------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | N | +| 1 | Creates an account that doesn't decline staking rewards | key=, declineStakingRewards=false | The account creation succeeds and the account doesn't decline staking rewards. | N | #### JSON Request Example diff --git a/test-specifications/cryptoService/accountDeleteTransaction.md b/test-specifications/crypto-service/accountDeleteTransaction.md similarity index 100% rename from test-specifications/cryptoService/accountDeleteTransaction.md rename to test-specifications/crypto-service/accountDeleteTransaction.md diff --git a/test-specifications/cryptoService/accountUpdateTransaction.md b/test-specifications/crypto-service/accountUpdateTransaction.md similarity index 100% rename from test-specifications/cryptoService/accountUpdateTransaction.md rename to test-specifications/crypto-service/accountUpdateTransaction.md diff --git a/test-specifications/utility.md b/test-specifications/utility.md index 4fb47eb..429d0e2 100644 --- a/test-specifications/utility.md +++ b/test-specifications/utility.md @@ -9,19 +9,24 @@ The JSON RPC methods mentioned in this file describe additional methods that sho #### Description -Method used to establish communication and initialize a TCK server with fee-payer information, as well as optional network information depending on the network setup being used to test. +Method used to establish communication and initialize a TCK server with fee-payer information, as well as optional network information depending on the network setup being used to test. If the TCK server only receives `operatorAccountId` and `operatorPrivateKey` parameters, it will assume that a testnet connection should be established. Other network parameters imply a custom/local network setup. -#### Parameters +#### Input Parameters -| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | -|--------------------|--------|-------------------|--------------|-----------------------------------------------------------------------------------| -| operatorAccountId | string | required | Input | The ID of the account to pay for all requests | -| operatorPrivateKey | string | required | Input | The private key of the fee-payer account in DER-encoded hex string representation | -| nodeIp | string | optional | Input | Required for a custom network. The IP of the local consensus node | -| nodeAccountId | string | optional | Input | Required for a custom network. The account ID for the local node | -| mirrorNetworkIp | string | optional | Input | Required for a custom network. The IP for the local mirror node | -| message | string | required | Output | Informational message about the execution of the method | -| status | string | required | Output | The status/result of the execution | +| Parameter Name | Type | Required/Optional | Description/Notes | +|--------------------|--------|-------------------|-----------------------------------------------------------------------------------| +| operatorAccountId | string | required | The ID of the account to pay for all requests | +| operatorPrivateKey | string | required | The private key of the fee-payer account in DER-encoded hex string representation | +| nodeIp | string | optional | Required for a custom network. The IP of the local consensus node | +| nodeAccountId | string | optional | Required for a custom network. The account ID for the local node | +| mirrorNetworkIp | string | optional | Required for a custom network. The IP for the local mirror node | + +#### Output Parameters + +| Parameter Name | Type | Required/Optional | Description/Notes | +|----------------|--------|-------------------|---------------------------------------------------------| +| message | string | required | Informational message about the execution of the method | +| status | string | required | The status/result of the execution | #### JSON Request Example @@ -61,12 +66,12 @@ Method used to establish communication and initialize a TCK server with fee-paye Method used to close the TCK network connections. Network connections can be reestablished after with another `setup` call. -#### Parameters +#### Output Parameters -| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | -|----------------|--------|-------------------|--------------|---------------------------------------------------------| -| message | string | required | Output | Informational message about the execution of the method | -| status | string | required | Output | The status/result of the execution | +| Parameter Name | Type | Required/Optional | Description/Notes | +|----------------|--------|-------------------|---------------------------------------------------------| +| message | string | required | Informational message about the execution of the method | +| status | string | required | The status/result of the execution | #### JSON Request Example @@ -99,16 +104,21 @@ Method used to close the TCK network connections. Network connections can be ree Method used to generate a Hedera Key. -#### Parameters +#### Input Parameters + +| Parameter Name | Type | Required/Optional | Description/Notes | +|----------------|--------|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | string | optional | The type of Key to generate. If provided, it MUST be one of `ed25519PrivateKey`, `ed25519PublicKey`, `ecdsaSecp256k1PrivateKey`, `ecdsaSecp256k1PublicKey`, `keyList`, or `thresholdKey`. If not provided, the returned key will be of type `ed25519PrivateKey`, `ed25519PublicKey`, `ecdsaSecp256k1PrivateKey`, or `ecdsaSecp256k1PublicKey`. | +| privateKey | string | optional | The DER-encoded hex string private key from which to generate a public key. This should only be provided for types `ed25519PublicKey` and `ecdsaSecp256k1PublicKey` if the public keys would like to be generated from a specific private key, but still not required if a random public key is desired. | +| protobufBytes | bool | optional | For `ed25519PublicKey` and `ecdsaSecp256k1PublicKey` types, `true` if instead of the DER-encoded hex string of the generated key, the serialized Key protobuf bytes are desired. Useful for generating aliases. | +| threshold | int | optional | Required for `thresholdKey` types. The number of keys that must sign for a threshold key. | +| keys | list | optional | Required for `keyList` and `thresholdKey` types. Specify the types of keys to be generated and put in the `keyList` or `thresholdKey`. All keys should contain the same parameters as this `generateKey` method (see examples below), if required. | + +#### Output Parameters -| Parameter Name | Type | Required/Optional | Input/Output | Description/Notes | -|----------------|--------|-------------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| type | string | optional | Input | The type of Key to generate. If provided, it MUST be one of `ed25519PrivateKey`, `ed25519PublicKey`, `ecdsaSecp256k1PrivateKey`, `ecdsaSecp256k1PublicKey`, `keyList`, or `thresholdKey`. If not provided, the returned key will be of type `ed25519PrivateKey`, `ed25519PublicKey`, `ecdsaSecp256k1PrivateKey`, or `ecdsaSecp256k1PublicKey` | -| privateKey | string | optional | Input | The DER-encoded hex string private key from which to generate a public key. This should only be provided for types `ed25519PublicKey` and `ecdsaSecp256k1PublicKey` if the public keys would like to be generated from a specific private key, but still not required if a random public key is desired. | -| protobufBytes | bool | optional | Input | For `ed25519PublicKey` and `ecdsaSecp256k1PublicKey` types, `true` if instead of the DER-encoded hex string of the generated key, the serialized Key protobuf bytes are desired. Useful for generating aliases. | -| threshold | int | optional | Input | Required for `thresholdKey` types. The number of keys that must sign for a threshold key. | -| keys | list | optional | Input | Required for `keyList` and `thresholdKey` types. Specify the types of keys to be generated and put in the `keyList` or `thresholdKey`. All keys should contain the same parameters as this `generateKey` method (see examples below), if required | -| key | string | required | Output | The DER-encoded hex string of the generated ECDSA or ED25519 private or public key (compressed if ECDSAsecp256k1 public key). If the type was `keyList` or `thresholdKey`, the hex string of the respective serialized protobuf | +| Parameter Name | Type | Required/Optional | Description/Notes | +|----------------|--------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| key | string | required | The DER-encoded hex string of the generated ECDSA or ED25519 private or public key (compressed if ECDSAsecp256k1 public key). If the type was `keyList` or `thresholdKey`, the hex string of the respective serialized protobuf. | #### JSON Request/Response Examples diff --git a/test/cryptoService/test_AccountInfo.js b/test/crypto-service/test_AccountInfo.js similarity index 100% rename from test/cryptoService/test_AccountInfo.js rename to test/crypto-service/test_AccountInfo.js diff --git a/test/cryptoService/test_CreateAccount.js b/test/crypto-service/test_CreateAccount.js similarity index 100% rename from test/cryptoService/test_CreateAccount.js rename to test/crypto-service/test_CreateAccount.js diff --git a/test/cryptoService/test_DeleteAccount.js b/test/crypto-service/test_DeleteAccount.js similarity index 100% rename from test/cryptoService/test_DeleteAccount.js rename to test/crypto-service/test_DeleteAccount.js diff --git a/test/cryptoService/test_UpdateAccountKey.js b/test/crypto-service/test_UpdateAccountKey.js similarity index 100% rename from test/cryptoService/test_UpdateAccountKey.js rename to test/crypto-service/test_UpdateAccountKey.js diff --git a/test/cryptoService/test_UpdateAccountMemo.js b/test/crypto-service/test_UpdateAccountMemo.js similarity index 100% rename from test/cryptoService/test_UpdateAccountMemo.js rename to test/crypto-service/test_UpdateAccountMemo.js From d665531575fa9feb6862dda440a22cba703e0c7e Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 29 Apr 2024 13:24:17 -0500 Subject: [PATCH 08/34] docs: address PR comments Signed-off-by: Rob Walworth --- .../accountCreateTransaction.md | 23 ++++--------------- .../testSpecificationsTemplate.md | 7 ------ 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index aa1cb7f..59f40b4 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -22,19 +22,6 @@ https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.p https://docs.hedera.com/hedera/sdks-and-apis/rest-api -## Initialisation - -```jsx -new AccountCreateTransaction() - -//example - -const transaction = new AccountCreateTransaction() - .setKey(privateKey.publicKey) - .setInitialBalance(new Hbar(1000)) - ... -``` - ## JSON-RPC API Endpoint Documentation ### Method Name @@ -59,10 +46,10 @@ const transaction = new AccountCreateTransaction() ### Output Parameters -| Parameter Name | Type | Required/Optional | Description/Notes | -|----------------|--------|-------------------|---------------------------------------------------------------------------------------| -| accountId | string | required | The ID of the created account. | -| status | string | required | The status of the submitted `AccountCreateTransaction` (from a `TransactionReceipt`). | +| Parameter Name | Type | Description/Notes | +|----------------|--------|---------------------------------------------------------------------------------------| +| accountId | string | The ID of the created account. | +| status | string | The status of the submitted `AccountCreateTransaction` (from a `TransactionReceipt`). | ## Property Tests @@ -310,7 +297,7 @@ const transaction = new AccountCreateTransaction() | 3 | Creates an account with the staked account ID set to an account ID that doesn't exist | key=, stakedAccountId="123.456.789" | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | | 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | | 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | N | -| 6 | Creates an account with the staked account ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 6 | Creates an account with the staked node ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | | 7 | Creates an account with a staked account ID and a staked node ID | key=, stakedAccountId=, stakedNodeId= | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | #### JSON Request Examples diff --git a/test-specifications/testSpecificationsTemplate.md b/test-specifications/testSpecificationsTemplate.md index 686d50a..0b7ac8d 100644 --- a/test-specifications/testSpecificationsTemplate.md +++ b/test-specifications/testSpecificationsTemplate.md @@ -16,13 +16,6 @@ https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.p https://docs.hedera.com/hedera/sdks-and-apis/rest-api - -## Initialisation: - -```jsx - -``` - ## JSON-RPC API Endpoint Documentation ### Method Name From a1bc8b352212163a3226004225273929a1fcfa3b Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 6 May 2024 09:24:21 -0500 Subject: [PATCH 09/34] docs: Add valid value test for max automatic token associations Signed-off-by: Rob Walworth --- .../crypto-service/accountCreateTransaction.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index 59f40b4..e32db4b 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -254,8 +254,9 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|------------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| | 1 | Creates an account with a max token association set to 0 | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | -| 2 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | -| 3 | Creates an account with a max token association that is the maximum value plus one | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | +| 2 | Creates an account with a max token association set to 100 | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 0 automatic token associations. | N | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | +| 4 | Creates an account with a max token association that is the maximum value plus one | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | #### JSON Request Example From ff81c5a5935ce7f95c61ea794b0f1c1448d142ff Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 6 May 2024 15:35:21 -0500 Subject: [PATCH 10/34] docs: fix typo Signed-off-by: Rob Walworth --- .../crypto-service/accountCreateTransaction.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index e32db4b..1b793eb 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -251,11 +251,11 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api - The maximum number of tokens with which an account can be implicitly associated. Defaults to 0 and up to a maximum value of 1000. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|------------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a max token association set to 0 | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | -| 2 | Creates an account with a max token association set to 100 | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 0 automatic token associations. | N | -| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------------------------------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a max token association set to 0 | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | +| 2 | Creates an account with a max token association set to 100 | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | N | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | | 4 | Creates an account with a max token association that is the maximum value plus one | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | #### JSON Request Example From 01ea46877c7d122d3f9669a9d0f8154d4ba58a8e Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 6 May 2024 15:35:41 -0500 Subject: [PATCH 11/34] docs: fix table formatting Signed-off-by: Rob Walworth --- .../crypto-service/accountCreateTransaction.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index 1b793eb..b7f9cdd 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -251,11 +251,11 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api - The maximum number of tokens with which an account can be implicitly associated. Defaults to 0 and up to a maximum value of 1000. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|------------------------------------------------------------------------------------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a max token association set to 0 | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | -| 2 | Creates an account with a max token association set to 100 | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | N | -| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|------------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a max token association set to 0 | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | +| 2 | Creates an account with a max token association set to 100 | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | N | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | | 4 | Creates an account with a max token association that is the maximum value plus one | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | #### JSON Request Example From 0bf5231f99e7a7ff2e20ab152a70e2e54cab5d0b Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 6 May 2024 15:44:30 -0500 Subject: [PATCH 12/34] docs: remove required/optional columns from output parameter tables Signed-off-by: Rob Walworth --- test-specifications/utility.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test-specifications/utility.md b/test-specifications/utility.md index 429d0e2..9620e5b 100644 --- a/test-specifications/utility.md +++ b/test-specifications/utility.md @@ -23,10 +23,10 @@ Method used to establish communication and initialize a TCK server with fee-paye #### Output Parameters -| Parameter Name | Type | Required/Optional | Description/Notes | -|----------------|--------|-------------------|---------------------------------------------------------| -| message | string | required | Informational message about the execution of the method | -| status | string | required | The status/result of the execution | +| Parameter Name | Type | Description/Notes | +|----------------|--------|---------------------------------------------------------| +| message | string | Informational message about the execution of the method | +| status | string | The status/result of the execution | #### JSON Request Example @@ -68,10 +68,10 @@ Method used to close the TCK network connections. Network connections can be ree #### Output Parameters -| Parameter Name | Type | Required/Optional | Description/Notes | -|----------------|--------|-------------------|---------------------------------------------------------| -| message | string | required | Informational message about the execution of the method | -| status | string | required | The status/result of the execution | +| Parameter Name | Type | Description/Notes | +|----------------|--------|---------------------------------------------------------| +| message | string | Informational message about the execution of the method | +| status | string | The status/result of the execution | #### JSON Request Example @@ -116,9 +116,9 @@ Method used to generate a Hedera Key. #### Output Parameters -| Parameter Name | Type | Required/Optional | Description/Notes | -|----------------|--------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| key | string | required | The DER-encoded hex string of the generated ECDSA or ED25519 private or public key (compressed if ECDSAsecp256k1 public key). If the type was `keyList` or `thresholdKey`, the hex string of the respective serialized protobuf. | +| Parameter Name | Type | Description/Notes | +|----------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| key | string | The DER-encoded hex string of the generated ECDSA or ED25519 private or public key (compressed if ECDSAsecp256k1 public key). If the type was `keyList` or `thresholdKey`, the hex string of the respective serialized protobuf. | #### JSON Request/Response Examples From f0a8c76de5493b7394cc421f25ded0e3152fd3a6 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Tue, 7 May 2024 16:43:26 -0500 Subject: [PATCH 13/34] Update account create transaction tests for key and initial balance Signed-off-by: Rob Walworth --- README.md | 2 +- ...nt.js => test_accountCreateTransaction.js} | 316 ++++++++++++------ 2 files changed, 216 insertions(+), 102 deletions(-) rename test/crypto-service/{test_CreateAccount.js => test_accountCreateTransaction.js} (68%) diff --git a/README.md b/README.md index a59e9f9..6fffda7 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Install packages with npm Run specific test file - npm run test test/account/test_CreateAccount.js + npm run test test/account/test_accountCreateTransaction.js Run all tests diff --git a/test/crypto-service/test_CreateAccount.js b/test/crypto-service/test_accountCreateTransaction.js similarity index 68% rename from test/crypto-service/test_CreateAccount.js rename to test/crypto-service/test_accountCreateTransaction.js index 60859fa..86d6bea 100644 --- a/test/crypto-service/test_CreateAccount.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -5,176 +5,290 @@ import { generateAccountKeys, setOperator, getNodeType } from "../../setup_Tests import { PrivateKey } from "@hashgraph/sdk"; import crypto from "crypto"; import { assert, expect } from "chai"; +import { JSONRPC } from "json-rpc-2.0"; /** - * Test Create account and compare results with js SDK + * Tests for AccountCreateTransaction */ -describe("#createAccount()", function () { +describe("AccountCreateTransaction", function () { + async function verifyOnlyAccountCreation(accountId) { + // Query for the account via the consensus node. + const accountIdFromConsensusNode = await consensusInfoClient.getAccountInfo(accountId).accountId.toString(); + + // Query for the account via the mirror node. + const accountIdFromMirrorNode = await mirrorNodeClient.getAccountData(accountIdFromConsensusNode).accounts[0].account; + + // If the account was created successfully, the queried account IDs should be equal. + expect(accountId).to.equal(accountIdFromConsensusNode); + expect(accountId).to.equal(accountIdFromMirrorNode); + } + + async function verifyAccountCreationWithInitialBalance(accountId, initialBalance) { + // Query for the account's initial balance via the consensus node. + const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(accountId); + const accountBalanceFromConsensusNode = accountInfoFromConsensusNode.balance._valueInTinybar; + + // Query for the account's initial balance via the mirror node. + const accountBalanceFromMirrorNode = await mirrorNodeClient.getBalanceData(accountInfoFromConsensusNode.accountId.toString()).balances[0].balance; + + // If the account was created successfully, the queried account balances should be equal. + expect(initialBalance).to.equal(Number(accountBalanceFromConsensusNode)); + expect(initialBalance).to.equal(Number(accountBalanceFromMirrorNode)); + } + + // Tests should not take longer than 30 seconds to fully execute. this.timeout(30000); - let publicKey, privateKey, local; + // Each test should first establish the network to use, and then teardown the network when complete. beforeEach(async function () { - local = await getNodeType(process.env.NODE_TYPE); - ({ publicKey, privateKey } = await generateAccountKeys()); await setOperator(process.env.OPERATOR_ACCOUNT_ID, process.env.OPERATOR_ACCOUNT_PRIVATE_KEY); }); afterEach(async function () { await JSONRPCRequest("reset"); }); - //----------- Key is needed to sign each transfer ----------- - describe("Key signature for each transfer", function () { - it("Creates an account with a public key", async function () { - // initiate request for JSON-RPC server to create a new account + describe("Key", function () { + it("(#1) Creates an account with a valid ED25519 public key", async function () { + // Generate an ED25519 public key for the account. + const ed25519PublicKey = await JSONRPCRequest("generateKey", { + type: "ed25519PublicKey" + }); + if (ed25519PublicKey.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, + key: ed25519PublicKey.key, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - // query account via consensus node to verify creation - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const accountIDFromConsensusNode = accountInfoFromConsensusNode.accountId.toString(); + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); - // query account via mirror node to confirm availability after creation - const accountInfoFromMirrorNode = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const accountIDFromMirrorNode = accountInfoFromMirrorNode.accounts[0].account; + it("(#2) Creates an account with a valid ECDSAsecp256k1 public key", async function () { + // Generate an ECDSAsecp256k1 public key for the account. + const ecdsaSecp256k1PublicKey = await JSONRPCRequest("generateKey", { + type: "ecdsaSecp256k1PublicKey" + }); + if (ecdsaSecp256k1PublicKey.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account. + const response = await JSONRPCRequest("createAccount", { + key: ecdsaSecp256k1PublicKey.key, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); - // confirm pass status with assertion testing for account creation - expect(newAccountId).to.equal(accountIDFromConsensusNode); - expect(newAccountId).to.equal(accountIDFromMirrorNode); + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); }); - // Create an account with no public key - it("Creates an account with no public key", async function () { - /** - * Key not provided in the transaction body - * KEY_REQUIRED = 26; - **/ - try { - // request JSON-RPC server to create a new account without providing public key - // Try to create account with the JSON-RPC without providing a PublicKey + it("(#3) Creates an account with a valid ED25519 private key", async function () { + // Generate an ED25519 private key for the account. + const ed25519PrivateKey = await JSONRPCRequest("generateKey", { + type: "ed25519PrivateKey" + }); + if (ed25519PrivateKey.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account. + const response = await JSONRPCRequest("createAccount", { + key: ed25519PrivateKey.key, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); + + it("(#4) Creates an account with a valid ECDSAsecp256k1 private key", async function () { + // Generate an ECDSAsecp256k1 private key for the account. + const ecdsaSecp256k1PrivateKey = await JSONRPCRequest("generateKey", { + type: "ecdsaSecp256k1PrivateKey" + }); + if (ecdsaSecp256k1PrivateKey.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account. + const response = await JSONRPCRequest("createAccount", { + key: ecdsaSecp256k1PrivateKey.key, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); + + it("(#5) Creates an account with a valid KeyList of ED25519 and ECDSAsecp256k1 private and public keys", async function () { + // Generate a KeyList of ED25519 and ECDSAsecp256k1 private and public keys for the account. + const keyList = await JSONRPCRequest("generateKey", { + keys: [ + {}, + {}, + {} + ] + }); + if (keyList.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account. + const response = await JSONRPCRequest("createAccount", { + key: keyList.key, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); + + it("(#6) Creates an account with a valid KeyList of nested Keylists (three levels)", async function () { + // Generate a KeyList of nested KeyLists of ED25519 and ECDSAsecp256k1 private and public keys for the account. + const nestedKeyList = await JSONRPCRequest("generateKey", { + keys: [ + { + keys: [ + {}, + {} + ] + }, + { + keys: [ + {}, + {} + ] + }, + { + keys: [ + {}, + {} + ] + } + ] + }); + if (nestedKeyList.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account. + const response = await JSONRPCRequest("createAccount", { + key: nestedKeyList.key, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); + + it("(#7) Creates an account with no key", async function () { + try { + // Attempt to create an account without providing a key. The network should respond with a KEY_REQUIRED status. const response = await JSONRPCRequest("createAccount", {}); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - // confirm error thrown for creation attempt without provision of public key assert.equal(err.data.status, "KEY_REQUIRED"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); - // Create an account with an invalid public key - it("Creates an account with an invalid public key", async function () { + + it("(#8) Creates an account with an invalid key", async function () { try { - // generate a random key value (where 88b is equal to byte length of valid public key) + // Generate a random key value (88 is equal to the byte length of a valid public key). const invalidPublicKey = crypto.randomBytes(88).toString(); - // set initial balance of 10,000,000,000 tinybars (100 Hbar) - const initialBal = 10000000000; - // request JSON-RPC server to create a new account with invalid public key + + // Attempt to create an account with the invalid key. The SDK should throw an internal error. const response = await JSONRPCRequest("createAccount", { - publicKey: invalidPublicKey, - initialBalance: initialBal, + publicKey: invalidPublicKey }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - // confirm error thrown for creation attempt with an invalid public key assert.equal(err.code, -32603, "Internal error"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); }); - //----------- Create an account with an initial balance ----------- - describe("Create an account with an initial balance", function () { - it("Sets initial balance to 100 HBar", async function () { - // set initial balance of 10,000,000,000 tinybars (100 Hbar) - const initialBal = 10000000000; - // initiate request for JSON-RPC server to create a new account + describe("Initial Balance", function () { + it("(#1) Creates an account with an initial balance", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with an initial balance of 100 tinybars. + const initialBalance = 100; const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: initialBal, + key: key.key, + initialBalance: initialBalance, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - - // query account balance via consensus node to check amount set - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const accountIDFromConsensusNode = accountInfoFromConsensusNode.accountId.toString(); - const accountBalConsensus = accountInfoFromConsensusNode.balance; - const accountBalanceConsensusNode = accountBalConsensus._valueInTinybar; - // query account balance via mirror node to check amount set - const accountInfoFromMirrorNode = await mirrorNodeClient.getBalanceData(accountIDFromConsensusNode); - const accountBalanceMirrorNode = accountInfoFromMirrorNode.balances[0].balance; + // Verify the account was created with 100 tinybars. + verifyAccountCreationWithInitialBalance(response.accountId, initialBalance); + }); + + it("(#2) Creates an account with no initial balance", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with an initial balance of 0 tinybars. + const initialBalance = 0; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + initialBalance: 0, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); - expect(initialBal).to.equal(Number(accountBalanceConsensusNode)); - expect(initialBal).to.equal(Number(accountBalanceMirrorNode)); + // Verify the account was created with 0 tinybars. + verifyAccountCreationWithInitialBalance(response.accountId, initialBalance); }); - // Set initial balance to -1 HBAR - it("Sets initial balance to -1 HBar", async function () { - /** - * Attempt to set negative initial balance - * INVALID_INITIAL_BALANCE = 85; - **/ + + it("(#3) Creates an account with a negative initial balance", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + try { - // set a negative initial balance of minus 1 HBar - let initialBalance = -1; - // convert Hbar to Tinybar at ratio 1: 100,000,000 - let negativeInitialBalance = (initialBalance *= 100000000); + // Attempt to create an account with an initial balance of -1. The network should respond with an INVALID_INITIAL_BALANCE status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: negativeInitialBalance, + key: key.key, + initialBalance: -1, }); if (response.status == "NOT_IMPLEMENTED") this.skip(); } catch (err) { - // confirm error thrown for using a negative initial balance amount assert.equal(err.data.status, "INVALID_INITIAL_BALANCE"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); - // Set the initial balance to more than operator balance - it("Sets initial balance to more than operator balance", async function () { - /** - * The payer account has insufficient cryptocurrency to pay the transaction fee - * INSUFFICIENT_PAYER_BALANCE = 10; - **/ - // set initial bal to 5 Hbar ( 500000000 Tinybar at ratio 1: 100,000,000 ) - const initialBalance = 500000000; - // set payer (funding account) bal to 5 Hbar + 1 Tinybar ( 500000001 Tinybar ) - const payerBalance = 500000001; - // create a first test account that will be used as the funding account for a - // second account. Allocate an initial balance of 5 HBAr to the funding account - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: initialBalance, - }); - const firstAccountId = response.accountId; - // set operator Id temporarily to firstAccountId - await setOperator(firstAccountId, privateKey); + + it("(#4) Creates an account with an initial balance higher than the operator account balance", async function () { + // Get the operator account balance. + const operatorAccountBalance = await mirrorNodeClient.getBalanceData(process.env.OPERATOR_ACCOUNT_ID).balances[0].balance; + + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); - // generate fresh keys for second account - ({ publicKey, privateKey } = await generateAccountKeys()); try { + // Attempt to create an account with an initial balance of the operator account balance + 1. The network should respond with an INSUFFICIENT_PAYER_BALANCE status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: payerBalance, + key: key.key, + initialBalance: operatorAccountBalance + 1, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - // confirm error thrown for creation attempt where initial balance is more - // than the balance held in the funding account balance assert.equal(err.data.status, "INSUFFICIENT_PAYER_BALANCE"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); }); - //----------- Account key signs transactions depositing into account ----------- - // Require a receiving signature when creating account transaction - describe("Account key signatures to deposit into account", function () { + describe("Receiver Signature Required", function () { it("Creates account that always requires Receiver signature", async function () { // Creates new account that always requires transactions to have receiving signature const response = await JSONRPCRequest("createAccount", { @@ -302,7 +416,7 @@ describe("#createAccount()", function () { }); // Create an account and set staked node ID and a node ID it("Creates an account and sets staked node ID to a node ID", async function () { - if (local) this.skip(); + if (await getNodeType(process.env.NODE_TYPE)) this.skip(); // select a staked node id between 0 and 6 for the test const randomNodeId = Math.floor(Math.random() * 6) + 1; From 0893d5bdce19f689844c11ced3cad1dab98ab480 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 8 May 2024 14:08:18 -0500 Subject: [PATCH 14/34] feat(accountCreateTransaction): Update account create transaction tests for receiver signature required, auto renew period, and memo Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 402 +++++++++++------- 1 file changed, 259 insertions(+), 143 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 86d6bea..3ddaee3 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -10,32 +10,7 @@ import { JSONRPC } from "json-rpc-2.0"; /** * Tests for AccountCreateTransaction */ -describe("AccountCreateTransaction", function () { - async function verifyOnlyAccountCreation(accountId) { - // Query for the account via the consensus node. - const accountIdFromConsensusNode = await consensusInfoClient.getAccountInfo(accountId).accountId.toString(); - - // Query for the account via the mirror node. - const accountIdFromMirrorNode = await mirrorNodeClient.getAccountData(accountIdFromConsensusNode).accounts[0].account; - - // If the account was created successfully, the queried account IDs should be equal. - expect(accountId).to.equal(accountIdFromConsensusNode); - expect(accountId).to.equal(accountIdFromMirrorNode); - } - - async function verifyAccountCreationWithInitialBalance(accountId, initialBalance) { - // Query for the account's initial balance via the consensus node. - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(accountId); - const accountBalanceFromConsensusNode = accountInfoFromConsensusNode.balance._valueInTinybar; - - // Query for the account's initial balance via the mirror node. - const accountBalanceFromMirrorNode = await mirrorNodeClient.getBalanceData(accountInfoFromConsensusNode.accountId.toString()).balances[0].balance; - - // If the account was created successfully, the queried account balances should be equal. - expect(initialBalance).to.equal(Number(accountBalanceFromConsensusNode)); - expect(initialBalance).to.equal(Number(accountBalanceFromMirrorNode)); - } - +describe("AccountCreateTransaction", function () { // Tests should not take longer than 30 seconds to fully execute. this.timeout(30000); @@ -48,6 +23,12 @@ describe("AccountCreateTransaction", function () { }); describe("Key", function () { + async function verifyOnlyAccountCreation(accountId) { + // If the account was created successfully, the queried account IDs should be equal. + expect(accountId).to.equal(await consensusInfoClient.getAccountInfo(accountId).accountId.toString()); + expect(accountId).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].account); + } + it("(#1) Creates an account with a valid ED25519 public key", async function () { // Generate an ED25519 public key for the account. const ed25519PublicKey = await JSONRPCRequest("generateKey", { @@ -208,6 +189,12 @@ describe("AccountCreateTransaction", function () { }); describe("Initial Balance", function () { + async function verifyAccountCreationWithInitialBalance(accountId, initialBalance) { + // If the account was created successfully, the queried account balances should be equal. + expect(initialBalance).to.equal(Number(await consensusInfoClient.getAccountInfo(accountId).balance._valueInTinybar)); + expect(initialBalance).to.equal(Number(await mirrorNodeClient.getBalanceData(accountId).balances[0].balance)); + } + it("(#1) Creates an account with an initial balance", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", {}); @@ -289,54 +276,265 @@ describe("AccountCreateTransaction", function () { }); describe("Receiver Signature Required", function () { - it("Creates account that always requires Receiver signature", async function () { - // Creates new account that always requires transactions to have receiving signature + async function verifyAccountCreationWithReceiverSignatureRequired(accountId, receiverSignatureRequired) { + // If the account was created successfully, the queried account receiver signature required policies should be equal. + expect(receiverSignatureRequired).to.equal(await consensusInfoClient.getAccountInfo(accountId).isReceiverSignatureRequired); + expect(receiverSignatureRequired).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].receiver_sig_required); + } + + it("(#1) Creates an account that requires a receiving signature", async function () { + // Generate a valid private key for the account. + const key = await JSONRPCRequest("generateKey", { + type: "privateKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account that requires a signature when receiving. + const receiverSignatureRequired = true; const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - privateKey: privateKey, - initialBalance: 1, - receiverSignatureRequired: true, + key: key.key, + receiverSignatureRequired: receiverSignatureRequired, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - // query account via consensus node to verify creation - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const accountIDFromConsensusNode = accountInfoFromConsensusNode.accountId.toString(); - const recvdSignatureStatusFromConsensusNode = accountInfoFromConsensusNode.isReceiverSignatureRequired; + verifyAccountCreationWithReceiverSignatureRequired(response.accountId, receiverSignatureRequired); + }); - // query account via mirror node to confirm availability after creation - const respJSON = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const recvdSignatureStatusFromMirrorNode = respJSON.accounts[0].receiver_sig_required; + it("(#2) Creates an account that doesn't require a receiving signature", async function () { + // Generate a valid private key for the account. + const key = await JSONRPCRequest("generateKey", { + type: "privateKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); - // confirm pass status for account creation with signature required - expect(recvdSignatureStatusFromConsensusNode).to.equal(true); - expect(recvdSignatureStatusFromMirrorNode).to.equal(true); + // Attempt to create an account that doesn't require a signature when receiving. + const receiverSignatureRequired = false; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + receiverSignatureRequired: receiverSignatureRequired, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithReceiverSignatureRequired(response.accountId, receiverSignatureRequired); }); - // Creates new account that doesn't require transactions to have receiving signature - it("Creates account without receiver signature required", async function () { - // Creates new account that always requires transactions to have receiving signature + + it("(#3) Creates an account that requires a receiving signature but isn't signed by the account key", async function () { + // Generate a valid public key for the account. + const key = await JSONRPCRequest("generateKey", { + type: "publicKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account that requires a signature when receiving but can't be signed. The network should respond with an INVALID_SIGNATURE status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + receiverSignatureRequired: true, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_SIGNATURE"); + return; + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); + }); + }); + + describe("Auto Renew Period", function () { + async function verifyAccountCreationWithAutoRenewPeriod(accountId, autoRenewPeriodSeconds) { + // If the account was created successfully, the queried account auto renew periods should be equal. + expect(autoRenewPeriodSeconds).to.equal(await consensusInfoClient.getAccountInfo(accountId).autoRenewPeriod); + expect(autoRenewPeriodSeconds).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].auto_renew_period); + } + + it("(#1) Creates an account with an auto renew period set to 60 days (5,184,000 seconds)", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with an auto-renew period set to 60 days. + const autoRenewPeriodSeconds = 5184000; const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - privateKey: privateKey, - initialBalance: 1, - receiverSignatureRequired: false, + key: key.key, + autoRenewPeriod: autoRenewPeriodSeconds, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - // query account via consensus node to verify creation - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const accountIDFromConsensusNode = accountInfoFromConsensusNode.accountId.toString(); - const recvdSignatureStatusFromConsensusNode = accountInfoFromConsensusNode.isReceiverSignatureRequired; + verifyAccountCreationWithAutoRenewPeriod(response.accountId, autoRenewPeriodSeconds); + }); - // query account via mirror node to confirm availability after creation - const respJSON = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const recvdSignatureStatusFromMirrorNode = respJSON.accounts[0].receiver_sig_required; + it("(#2) Creates an account with an auto renew period set to -1 seconds", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with an auto-renew period set to -1 seconds. The network should respond with an INVALID_RENEWAL_PERIOD status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + autoRenewPeriod: -1, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_RENEWAL_PERIOD"); + return; + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); + }); + + it("(#3) Creates an account with an auto renew period set to the minimum period of 30 days (2,592,000 seconds)", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with an auto-renew period set to 30 days. + const autoRenewPeriodSeconds = 2592000; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + autoRenewPeriod: autoRenewPeriodSeconds, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithAutoRenewPeriod(response.accountId, autoRenewPeriodSeconds); + }); + + it("(#4) Creates an account with an auto renew period set to the minimum period of 30 days minus one second (2,591,999 seconds)", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with an auto-renew period set to 2,591,999 seconds. The network should respond with an AUTORENEW_DURATION_NOT_IN_RANGE status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + autoRenewPeriod: 2591999, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "AUTORENEW_DURATION_NOT_IN_RANGE"); + return; + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); + }); + + it("(#5) Creates an account with an auto renew period set to the maximum period of 8,000,001 seconds", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with an auto-renew period set to 90ish days. + const autoRenewPeriodSeconds = 8000001; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + autoRenewPeriod: autoRenewPeriodSeconds, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithAutoRenewPeriod(response.accountId, autoRenewPeriodSeconds); + }); + + it("(#6) Creates an account with an auto renew period set to the maximum period plus one seconds (8,000,002 seconds)", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with an auto-renew period set to 8,000,002 seconds. The network should respond with an AUTORENEW_DURATION_NOT_IN_RANGE status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + autoRenewPeriod: 8000002, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "AUTORENEW_DURATION_NOT_IN_RANGE"); + return; + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); + }); + }); - // confirm pass for account creation with requirement for signature set to true - expect(recvdSignatureStatusFromConsensusNode).to.equal(false); - expect(recvdSignatureStatusFromMirrorNode).to.equal(false); + describe("Memo", async function () { + async function verifyAccountCreationWithMemo(accountId, memo) { + // If the account was created successfully, the queried account memos should be equal. + expect(memo).to.equal(await consensusInfoClient.getAccountInfo(accountId).memo); + expect(memo).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].memo); + } + + it("(#1) Creates an account with a valid memo", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with a memo set to "testmemo". + const memo = "testmemo"; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + memo: memo, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithMemo(response.accountId, memo); + }); + + it("(#2) Creates an account with an empty memo", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with an empty memo. + const memo = ""; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + memo: memo, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithMemo(response.accountId, memo); + }); + + it("(#3) Creates an account with a memo that is 100 characters", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with a memo set to the maximum length. + const memo = "This is a really long memo but it is still valid because it is 100 characters exactly on the money!!"; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + memo: memo, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithMemo(response.accountId, memo); + }); + + it("(#4) Creates an account with a memo that exceeds 100 characters", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with a memo over the maximum length. The network should respond with an MEMO_TOO_LONG status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + memo: "This is a long memo that is not valid because it exceeds 100 characters and it should fail the test!!", + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "MEMO_TOO_LONG"); + return; + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); }); }); @@ -575,88 +773,6 @@ describe("AccountCreateTransaction", function () { }); }); - describe("Create accounts with a memo", async function () { - // Create an account with a memo - it("Creates an account with a memo", async function () { - const testMemo = "testMemo"; - // Create account with the JSON-RPC that includes a memo in the memo field - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - accountMemo: testMemo, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - - const newAccountID = response.accountId; - // First query consensus node - const cNodeQuery = await consensusInfoClient.getAccountInfo(newAccountID); - const cNodeRes = cNodeQuery.accountMemo; - - // Query the mirror node - const mNodeQuery = await mirrorNodeClient.getAccountData(newAccountID); - const mNodeRes = mNodeQuery.accounts[0].memo; - - expect(cNodeRes).to.equal(testMemo); - expect(mNodeRes).to.equal(testMemo); - }); - // Create an account with a memo that exceeds 100 characters - it("Creates an account with a memo exceeding 100 characters", async function () { - // put 101 characters in memo - const testMemo = "testMemo12testMemo12testMemo12testMemo12testMemo12testMemo12testMemo12testMemo12testMemo12testMemo123"; - try { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - accountMemo: testMemo, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "MEMO_TOO_LONG"); - return; - } - assert.fail("Should throw an error"); - }); - }); - - //----------- Set auto renew periods ----------- - describe("Create account with specific auto renew period", async function () { - // Create an account and set auto renew period to 2,592,000 seconds - it("should set account auto renew period to 2,592,000 seconds", async function () { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - autoRenewPeriod: BigInt(2592000).toString(), - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - let newAccountId = response.accountId; - // consensus node account - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const autoRenewConsensus = accountInfoFromConsensusNode.autoRenewPeriod; - assert.equal(autoRenewConsensus.seconds.toString(), BigInt(2592000).toString()); - }); - // Create an account and set auto renew period to -1 - it("should set account auto renew period to -1 seconds - returns 'AUTORENEW_DURATION_NOT_IN_RANGE'", async function () { - try { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - autoRenewPeriod: BigInt(-1).toString(), - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "AUTORENEW_DURATION_NOT_IN_RANGE"); - } - }); - // Create an account and set the auto renew period to 10 days (864000 seconds) - it("should set account auto renew period to 864000 seconds returns 'AUTORENEW_DURATION_NOT_IN_RANGE'", async function () { - try { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - autoRenewPeriod: BigInt(864000).toString(), - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "AUTORENEW_DURATION_NOT_IN_RANGE"); - } - }); - }); - ///----------- Set account alias ----------- describe("Create account with alias", async function () { // Create an account by using an alias From 7cfaa8719a01de96cc2dca954c75d4ca5e97d2f6 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 9 May 2024 09:21:51 -0500 Subject: [PATCH 15/34] feat(accountCreateTransaction): add remaining tests Signed-off-by: Rob Walworth --- .../accountCreateTransaction.md | 2 +- .../test_accountCreateTransaction.js | 436 ++++++++++-------- 2 files changed, 255 insertions(+), 183 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index b7f9cdd..17dab60 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -351,7 +351,7 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api } ``` -### **Decline rewards:** +### **Decline Reward:** - If true, the account declines receiving a staking reward. diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 3ddaee3..b9c662d 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -2,7 +2,7 @@ import { JSONRPCRequest } from "../../client.js"; import mirrorNodeClient from "../../mirrorNodeClient.js"; import consensusInfoClient from "../../consensusInfoClient.js"; import { generateAccountKeys, setOperator, getNodeType } from "../../setup_Tests.js"; -import { PrivateKey } from "@hashgraph/sdk"; +import { EvmAddress } from "@hashgraph/sdk"; import crypto from "crypto"; import { assert, expect } from "chai"; import { JSONRPC } from "json-rpc-2.0"; @@ -24,7 +24,7 @@ describe("AccountCreateTransaction", function () { describe("Key", function () { async function verifyOnlyAccountCreation(accountId) { - // If the account was created successfully, the queried account IDs should be equal. + // If the account was created successfully, the queried account's IDs should be equal. expect(accountId).to.equal(await consensusInfoClient.getAccountInfo(accountId).accountId.toString()); expect(accountId).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].account); } @@ -169,13 +169,10 @@ describe("AccountCreateTransaction", function () { }); it("(#8) Creates an account with an invalid key", async function () { - try { - // Generate a random key value (88 is equal to the byte length of a valid public key). - const invalidPublicKey = crypto.randomBytes(88).toString(); - - // Attempt to create an account with the invalid key. The SDK should throw an internal error. + try { + // Attempt to create an account with an invalid key (random 88 bytes, which is equal to the byte length of a valid public key). The SDK should throw an internal error. const response = await JSONRPCRequest("createAccount", { - publicKey: invalidPublicKey + key: crypto.randomBytes(88).toString() }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { @@ -190,7 +187,7 @@ describe("AccountCreateTransaction", function () { describe("Initial Balance", function () { async function verifyAccountCreationWithInitialBalance(accountId, initialBalance) { - // If the account was created successfully, the queried account balances should be equal. + // If the account was created successfully, the queried account's balances should be equal. expect(initialBalance).to.equal(Number(await consensusInfoClient.getAccountInfo(accountId).balance._valueInTinybar)); expect(initialBalance).to.equal(Number(await mirrorNodeClient.getBalanceData(accountId).balances[0].balance)); } @@ -277,7 +274,7 @@ describe("AccountCreateTransaction", function () { describe("Receiver Signature Required", function () { async function verifyAccountCreationWithReceiverSignatureRequired(accountId, receiverSignatureRequired) { - // If the account was created successfully, the queried account receiver signature required policies should be equal. + // If the account was created successfully, the queried account's receiver signature required policies should be equal. expect(receiverSignatureRequired).to.equal(await consensusInfoClient.getAccountInfo(accountId).isReceiverSignatureRequired); expect(receiverSignatureRequired).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].receiver_sig_required); } @@ -344,7 +341,7 @@ describe("AccountCreateTransaction", function () { describe("Auto Renew Period", function () { async function verifyAccountCreationWithAutoRenewPeriod(accountId, autoRenewPeriodSeconds) { - // If the account was created successfully, the queried account auto renew periods should be equal. + // If the account was created successfully, the queried account's auto renew periods should be equal. expect(autoRenewPeriodSeconds).to.equal(await consensusInfoClient.getAccountInfo(accountId).autoRenewPeriod); expect(autoRenewPeriodSeconds).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].auto_renew_period); } @@ -463,7 +460,7 @@ describe("AccountCreateTransaction", function () { describe("Memo", async function () { async function verifyAccountCreationWithMemo(accountId, memo) { - // If the account was created successfully, the queried account memos should be equal. + // If the account was created successfully, the queried account's memos should be equal. expect(memo).to.equal(await consensusInfoClient.getAccountInfo(accountId).memo); expect(memo).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].memo); } @@ -538,117 +535,140 @@ describe("AccountCreateTransaction", function () { }); }); - //----------- Maximum number of tokens that an Account be associated with ----------- - describe.only("Max Token Association", function () { - // Creates an account with a default max token association - // maxAutomaticTokenAssociations can be queried via consensus node with AccountInfoQuery - it("Default max token association", async function () { + describe("Max Automatic Token Associations", async function () { + async function verifyAccountCreationWithMaxAutoTokenAssociations(accountId, maxAutomaticTokenAssociations) { + // If the account was created successfully, the queried account's max automatic token associations should be equal. + expect(maxAutomaticTokenAssociations).to.equal(await consensusInfoClient.getAccountInfo(accountId).maxAutomaticTokenAssociations); + expect(maxAutomaticTokenAssociations).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].max_automatic_token_associations); + } + + it("(#1) Creates an account with a max token association set to 0", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the max automatic token associations set to 0. + const maxAutomaticTokenAssociations = 0; const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: 0, - maxAutomaticTokenAssociations: 0, + key: key.key, + maxAutomaticTokenAssociations: maxAutomaticTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - // consensus node account - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const acctMaxTokenConsensus = accountInfoFromConsensusNode.maxAutomaticTokenAssociations.low; - assert.equal(acctMaxTokenConsensus, 0); + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutomaticTokenAssociations) }); - // Creates an account with max token set to the maximum - it("Max token set to the maximum", async function () { + + it("(#2) Creates an account with a max token association set to 100", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the max automatic token associations set to 100. + const maxAutomaticTokenAssociations = 100; const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: 0, - maxAutomaticTokenAssociations: 5000, + key: key.key, + maxAutomaticTokenAssociations: maxAutomaticTokenAssociations, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutomaticTokenAssociations) + }); + + it("(#3) Creates an account with a max token association that is the maximum value", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the max automatic token associations set to the maximum value. + const maxAutomaticTokenAssociations = 1000; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + maxAutomaticTokenAssociations: maxAutomaticTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - // consensus node account - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const acctMaxTokenConsensus = accountInfoFromConsensusNode.maxAutomaticTokenAssociations.low; - assert.equal(acctMaxTokenConsensus, 5000); + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutomaticTokenAssociations) }); - // Create an account with token association over the max - it("Max token association over the maximum - should have status REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT", async function () { + + it("(#4) Creates an account with a max token association that is the maximum value plus one", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + try { + // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: 0, - maxAutomaticTokenAssociations: 5001, + key: key.key, + maxAutomaticTokenAssociations: 1001, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - assert.fail("Expected an error but none was thrown."); } catch (err) { assert.equal(err.data.status, "REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT"); } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); }); }); - //----------- Staked ID - ID of the account to which is staking -------------------- - describe("Staked ID, ID of account or node to which is staking", async function () { - // Create an account and set staked account ID to operator account ID - it("Creates an account and sets staked account ID to operator account ID", async function () { - // Create account with the JSON-RPC that includes a staked account Id + describe("Staked ID", async function () { + async function verifyAccountCreationWithStakedAccountId(accountId, stakedAccountId) { + // If the account was created successfully, the queried account's staked account IDs should be equal. + expect(stakedAccountId).to.equal(await consensusInfoClient.getAccountInfo(accountId).stakedAccountId); + expect(stakedAccountId).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].staked_account_id); + } + + async function verifyAccountCreationWithStakedAccountId(accountId, stakedNodeId) { + // If the account was created successfully, the queried account's staked node IDs should be equal. + expect(stakedNodeId).to.equal(await consensusInfoClient.getAccountInfo(accountId).stakedNodeId); + expect(stakedNodeId).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].staked_node_id); + } + + it("(#1) Creates an account with the staked account ID set to the operators account ID", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the staked account ID set to the operator's account ID. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, + key: key.key, stakedAccountId: process.env.OPERATOR_ACCOUNT_ID, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - // query account via consensus node to verify creation - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const accountID = accountInfoFromConsensusNode.accountId.toString(); - const stakedIDFromConsensusNode = accountInfoFromConsensusNode.stakingInfo.stakedAccountId.toString(); - - // query account via mirror node to confirm availability after creation - const respJSON = await mirrorNodeClient.getAccountData(accountID); - const stakedIDFromMirrorNode = respJSON.accounts[0].staked_account_id; - - // confirm pass for account creation with a set staked account ID - expect(stakedIDFromConsensusNode).to.equal(process.env.OPERATOR_ACCOUNT_ID); - expect(stakedIDFromMirrorNode).to.equal(process.env.OPERATOR_ACCOUNT_ID); + verifyAccountCreationWithStakedAccountId(response.accountId, process.env.OPERATOR_ACCOUNT_ID); }); - // Create an account and set staked node ID and a node ID - it("Creates an account and sets staked node ID to a node ID", async function () { + + it("(#2) Creates an account with the staked node ID set to a valid node ID", async function () { + // Only run this test in a local setup. if (await getNodeType(process.env.NODE_TYPE)) this.skip(); - // select a staked node id between 0 and 6 for the test - const randomNodeId = Math.floor(Math.random() * 6) + 1; + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the staked node ID set to the operator's account ID. + // TODO: figure out from where to grab the correct node ID. .env file?? For now, using random node ID. + const stakedNodeId = Math.floor(Math.random() * 6) + 1; const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedNodeId: randomNodeId, + key: key.key, + stakedNodeId: stakedNodeId, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountId = response.accountId; - - // query account via consensus node to verify creation - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(newAccountId); - const accountID = accountInfoFromConsensusNode.accountId.toString(); - const stakedNodeIDFromConsensusNode = accountInfoFromConsensusNode.stakingInfo.stakedNodeId.low.toString(); - - // query account via mirror node to confirm availability after creation - const respJSON = await mirrorNodeClient.getAccountData(accountID); - const stakedNodeIDFromMirrorNode = respJSON.accounts[0].staked_node_id; - - // confirm pass for account creation with a set staked node ID - expect(Number(stakedNodeIDFromConsensusNode)).to.equal(randomNodeId); - expect(Number(stakedNodeIDFromMirrorNode)).to.equal(randomNodeId); - }); - // Create an account and set the staked account ID to an invalid ID - it("Creates an account and sets the staked account ID to an invalid ID", async function () { - /** - * The staking account id or staking node id given is invalid or does not exist. - * INVALID_STAKING_ID = 322; - **/ + + verifyAccountCreationWithStakedAccountId(response.accountId, stakedNodeId); + }); + + it("(#3) Creates an account with the staked account ID set to an account ID that doesn't exist", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + try { - const invalidStakedId = "9.9.999999"; + // Attempt to create an account with a staked account ID that doesn't exist. The network should respond with an INVALID_STAKING_ID status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedAccountId: invalidStakedId, + key: key.key, + stakedAccountId: "123.456.789", }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { @@ -657,71 +677,81 @@ describe("AccountCreateTransaction", function () { } assert.fail("Should throw an error"); }); - // Create an account and set the staked node ID to an invalid node - it("Creates an account and sets the staked node ID to an invalid node", async function () { - /** - * The staking account id or staking node id given is invalid or does not exist. - * INVALID_STAKING_ID = 322; - **/ + + it("(#4) Creates an account with the staked node ID set to a node ID that doesn't exist", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + try { - // select a staked node id greater than 6 for the test - const invalidNodeId = 10; + // Attempt to create an account with a staked node ID that doesn't exist. The network should respond with an INVALID_STAKING_ID status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedNodeId: invalidNodeId, + key: key.key, + stakedNodeId: 123456789, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { assert.equal(err.data.status, "INVALID_STAKING_ID"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); - // Create an account and set staked account ID with no input - it("Creates an account and sets staked account ID with no input", async function () { + + it("(#5) Creates an account with the staked account ID set to an empty account ID", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + try { - // set a staked node Id with no input - const noInputStakedAccountId = ""; + // Attempt to create an account with a staked node ID that doesn't exist. The SDK should throw an internal error. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedAccountId: noInputStakedAccountId, + key: key.key, + stakedAccountId: "", }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - // confirm error thrown for create with no input for staked account ID } catch (err) { + assert.equal(err.code, -32603, "Internal error"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); - // Create an account and set staked account ID with no input - it("Creates an account and sets staked node ID with no input", async function () { + + it("(#6) Creates an account with the staked node ID set to an invalid node ID", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + try { - // set a staked node Id with no input - const noInputStakedNodeId = ""; + // Attempt to create an account with an invalid staked node ID. The network should respond with an INVALID_STAKING_ID status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedNodeId: noInputStakedNodeId, + key: key.key, + stakedNodeId: -100, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - // confirm error thrown for create with no input for staked account ID } catch (err) { + assert.equal(err.data.status, "INVALID_STAKING_ID"); return; } + + // This shouldn't happen, the JSONRPCRequest should throw. assert.fail("Should throw an error"); }); - // Create an account and set both a staking account ID and node ID - it("Creates an account and sets both a staking account ID and node ID", async function () { - try { - // set staked account ID to operator account ID - const stakedAccountId = process.env.OPERATOR_ACCOUNT_ID; - // select a staked node id betwen 0 and 6 for the test - const stakedNodeId = Math.floor(Math.random() * 6) + 1; - // request JSON-RPC create account with both StakedAccountId and StakedNodeId + it("(#7) Creates an account with a staked account ID and a staked node ID", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with a staked account ID and a staked node ID. The network should respond with an INVALID_STAKING_ID status. const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedAccountId: stakedAccountId, - stakedNodeId: stakedNodeId, + key: key.key, + stakedAccountId: process.env.OPERATOR_ACCOUNT_ID, + stakedNodeId: Math.floor(Math.random() * 6) + 1, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { @@ -731,82 +761,124 @@ describe("AccountCreateTransaction", function () { }); }); - //----------- If true - account declines receiving a staking reward ----------- - describe("Account declines receiving a staking reward", async function () { - // Create an account and set the account to decline staking rewards - it("Creates an account and set the account to decline staking rewards", async function () { - // Create acount with the JSON-RPC that declines rewards for staking + describe("Decline Rewards", async function () { + async function verifyAccountCreationWithDeclineRewards(accountId, declineRewards) { + // If the account was created successfully, the queried account's decline rewards policy should be equal. + expect(declineRewards).to.equal(await consensusInfoClient.getAccountInfo(accountId).stakingInfo.declineStakingReward); + expect(declineRewards).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].decline_reward); + } + + it("(#1) Creates an account that declines staking rewards", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with that declines staking rewards. + const declineStakingReward = true const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - declineStakingReward: true, + key: key.key, + declineStakingReward: declineStakingReward, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountID = response.accountId; - // Query the consensus node - const cNodeQuery = await consensusInfoClient.getAccountInfo(newAccountID); - const cNodeRes = cNodeQuery.stakingInfo.declineStakingReward; - - // Query the mirror node - const mNodeQuery = await mirrorNodeClient.getAccountData(newAccountID); - const mNodeRes = mNodeQuery.accounts[0].decline_reward; - expect(cNodeRes).to.be.true; - expect(mNodeRes).to.be.true; + verifyAccountCreationWithDeclineRewards(response.accountId, declineStakingReward); }); - // Create an account and leave decline rewards at default value - it("Creates an account and leave staking rewards at default value", async function () { + + it("(#2) Creates an account that doesn't decline staking rewards", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with that doesn't decline staking rewards. + const declineStakingReward = false const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, + key: key.key, + declineStakingReward: declineStakingReward, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - const newAccountID = response.accountId; - // first query consensus node - const cNodeQuery = await consensusInfoClient.getAccountInfo(newAccountID); - const cNodeRes = cNodeQuery.stakingInfo.declineStakingReward; - - // Query the mirror node - const mNodeQuery = await mirrorNodeClient.getAccountData(newAccountID); - const mNodeRes = mNodeQuery.accounts[0].decline_reward; - expect(cNodeRes).to.be.false; - expect(mNodeRes).to.be.false; + verifyAccountCreationWithDeclineRewards(response.accountId, declineStakingReward); }); }); - ///----------- Set account alias ----------- - describe("Create account with alias", async function () { - // Create an account by using an alias - it("should create an account using an 'alias'", async function () { - // set initial balance to 100 Hbar - const initialBalance = 100; + describe("Alias", async function () { + async function verifyAccountCreationWithAlias(accountId, alias) { + // If the account was created successfully, the queried account's aliases should be equal. + expect(alias).to.equal(await consensusInfoClient.getAccountInfo(accountId).aliasKey); + expect(alias).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].alias); + } - // create public/private key pair then generate alias consisting of .. - const aliasPublicKey = PrivateKey.generateED25519().publicKey; - const aliasID = aliasPublicKey.toAccountId(0, 0); - const aliasIdStr = JSON.stringify(aliasID.toString()); + it("(#1) Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); - // initiate request for JSON-RPC server to transfer into alias account to initiate account creation - // Create alias account with the JSON-RPC - const response = await JSONRPCRequest("createAccountFromAlias", { - operator_id: process.env.OPERATOR_ACCOUNT_ID, - aliasAccountId: aliasIdStr, - initialBalance: initialBalance, + // Generate the ECDSAsecp256k1 private key of the alias for the account. + const ecdsaSecp256k1PrivateKey = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); + + // Generate the ECDSAsecp256k1 public key associated with the private key, which will then be used as the alias for the account. + const ecdsaSecp256k1PublicKey = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey", + "privateKey": ecdsaSecp256k1PrivateKey.key + }); + + // Attempt to create an account with the alias. + const alias = EvmAddress.fromString(ecdsaSecp256k1PublicKey.key).toString(); + const response = await JSONRPCRequest("createAccount", { + key: key.key, + alias: alias, + signerKey: ecdsaSecp256k1PrivateKey.key }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - // query account via consensus node to verify creation - const accountInfoFromConsensusNode = await consensusInfoClient.getAccountInfo(aliasID); - const accountIDFromConsensusNode = accountInfoFromConsensusNode.accountId.toString(); + verifyAccountCreationWithAlias(response.accountId, alias); + }); - // query account via mirror node to confirm availability after creation - const accountInfoFromMirrorNode = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const accountAliasFromMirrorNode = accountInfoFromMirrorNode.accounts[0].alias; - const accountMemoFromMirrorNode = accountInfoFromMirrorNode.accounts[0].memo; + it("(#2) Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); - expect(accountInfoFromConsensusNode.aliasKey.toString()).to.equal(aliasPublicKey.toString()); - expect(accountInfoFromConsensusNode.accountMemo).to.equal("auto-created account"); - expect(accountAliasFromMirrorNode).to.not.be.null; - expect(accountMemoFromMirrorNode).to.equal("auto-created account"); + // Generate the ECDSAsecp256k1 public key to be used as the alias for the account. + const ecdsaSecp256k1PublicKey = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); + + try { + // Attempt to create an account with the alias without signing with the associated ECDSAsecp256k1 private key. The network should respond with an INVALID_SIGNATURE status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + alias: EvmAddress.fromString(ecdsaSecp256k1PublicKey.key).toString(), + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_SIGNATURE"); + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); + }); + + it("(#3) Creates an account with an invalid alias", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", {}); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with an invalid alias. The network should respond with an INVALID_ALIAS_KEY status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + alias: crypto.randomBytes(20).toString(), + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_ALIAS_KEY"); + } + + // This shouldn't happen, the JSONRPCRequest should throw. + assert.fail("Should throw an error"); }); }); From e3b51bf71edad835ecdae6b118b8bba9f6f1d2d2 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 9 May 2024 14:16:49 -0500 Subject: [PATCH 16/34] feat(accountCreateTransaction): remove unnecessary includes and mark tests as implemented Signed-off-by: Rob Walworth --- .../accountCreateTransaction.md | 82 +++++++++---------- .../test_accountCreateTransaction.js | 3 +- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index 5c59598..d9f0aec 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -59,14 +59,14 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|-----------------------------------------------------------------------------------------------|-----------------------------------------|--------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a valid ED25519 public key | key= | The account creation succeeds. | N | -| 2 | Creates an account with a valid ECDSAsecp256k1 public key | key= | The account creation succeeds. | N | -| 3 | Creates an account with a valid ED25519 private key | key= | The account creation succeeds. | N | -| 4 | Creates an account with a valid ECDSAsecp256k1 private key | key= | The account creation succeeds. | N | -| 5 | Creates an account with a valid KeyList of ED25519 and ECDSAsecp256k1 private and public keys | key= | The account creation succeeds. | N | -| 6 | Creates an account with a valid KeyList of nested Keylists (three levels) | key= | The account creation succeeds. | N | -| 7 | Creates an account with no key | | The account creation fails with a KEY_REQUIRED response code from the network. | N | -| 8 | Creates an account with an invalid key | key= | The account creation fails with an SDK internal error. | N | +| 1 | Creates an account with a valid ED25519 public key | key= | The account creation succeeds. | Y | +| 2 | Creates an account with a valid ECDSAsecp256k1 public key | key= | The account creation succeeds. | Y | +| 3 | Creates an account with a valid ED25519 private key | key= | The account creation succeeds. | Y | +| 4 | Creates an account with a valid ECDSAsecp256k1 private key | key= | The account creation succeeds. | Y | +| 5 | Creates an account with a valid KeyList of ED25519 and ECDSAsecp256k1 private and public keys | key= | The account creation succeeds. | Y | +| 6 | Creates an account with a valid KeyList of nested Keylists (three levels) | key= | The account creation succeeds. | Y | +| 7 | Creates an account with no key | | The account creation fails with a KEY_REQUIRED response code from the network. | Y | +| 8 | Creates an account with an invalid key | key= | The account creation fails with an SDK internal error. | Y | #### JSON Request Example @@ -100,10 +100,10 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|-------------------------------------------------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with an initial balance | key=, initialBalance=100 | The account creation succeeds and the account contains 100 tinybar. | N | -| 2 | Creates an account with no initial balance | key=, initialBalance=0 | The account creation succeeds and the account contains 0 tinybar. | N | -| 3 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | N | -| 4 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=+1 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | N | +| 1 | Creates an account with an initial balance | key=, initialBalance=100 | The account creation succeeds and the account contains 100 tinybar. | Y | +| 2 | Creates an account with no initial balance | key=, initialBalance=0 | The account creation succeeds and the account contains 0 tinybar. | Y | +| 3 | Creates an account with a negative initial balance | key=, initialBalance=-1 | The account creation fails with an INVALID_INITIAL_BALANCE response code from the network. | Y | +| 4 | Creates an account with an initial balance higher than the operator account balance | key=, initialBalance=+1 | The account creation fails with an INSUFFICIENT_PAYER_BALANCE response code from the network. | Y | #### JSON Request Example @@ -138,9 +138,9 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|--------------------------------------------------------------------------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account that requires a receiving signature | key=, receiverSignatureRequired=true | The account creation succeeds and the account requires a receiving signature. | N | -| 2 | Creates an account that doesn't require a receiving signature | key=, receiverSignatureRequired=false | The account creation succeeds and the account doesn't require a receiving signature. | N | -| 3 | Creates an account that requires a receiving signature but isn't signed by the account key | key=, receiverSignatureRequired=true | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | +| 1 | Creates an account that requires a receiving signature | key=, receiverSignatureRequired=true | The account creation succeeds and the account requires a receiving signature. | Y | +| 2 | Creates an account that doesn't require a receiving signature | key=, receiverSignatureRequired=false | The account creation succeeds and the account doesn't require a receiving signature. | Y | +| 3 | Creates an account that requires a receiving signature but isn't signed by the account key | key=, receiverSignatureRequired=true | The account creation fails with an INVALID_SIGNATURE response code from the network. | Y | #### JSON Request Example @@ -175,12 +175,12 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|------------------------------------------------------------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with an auto renew period set to 60 days (5,184,000 seconds) | key=, autoRenewPeriod=5184000 | The account creation succeeds and the account's auto renew period should equal 5,184,000 seconds. | N | -| 2 | Creates an account with an auto renew period set to -1 seconds | key=, autoRenewPeriod=-1 | The account creation fails with an INVALID_RENEWAL_PERIOD response code from the network. | N | -| 3 | Creates an account with an auto renew period set to the minimum period of 30 days (2,592,000 seconds) | key=, autoRenewPeriod=2592000 | The account creation succeeds and the account's auto renew period should equal 2,592,000 seconds. | N | -| 4 | Creates an account with an auto renew period set to the minimum period of 30 days minus one second (2,591,999 seconds) | key=, autoRenewPeriod=2591999 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | N | -| 5 | Creates an account with an auto renew period set to the maximum period of 8,000,001 seconds | key=, autoRenewPeriod=8000001 | The account creation succeeds and the account's auto renew period should equal 8,000,001 seconds. | N | -| 6 | Creates an account with an auto renew period set to the maximum period plus one seconds (8,000,002 seconds) | key=, autoRenewPeriod=8000002 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | N | +| 1 | Creates an account with an auto renew period set to 60 days (5,184,000 seconds) | key=, autoRenewPeriod=5184000 | The account creation succeeds and the account's auto renew period should equal 5,184,000 seconds. | Y | +| 2 | Creates an account with an auto renew period set to -1 seconds | key=, autoRenewPeriod=-1 | The account creation fails with an INVALID_RENEWAL_PERIOD response code from the network. | Y | +| 3 | Creates an account with an auto renew period set to the minimum period of 30 days (2,592,000 seconds) | key=, autoRenewPeriod=2592000 | The account creation succeeds and the account's auto renew period should equal 2,592,000 seconds. | Y | +| 4 | Creates an account with an auto renew period set to the minimum period of 30 days minus one second (2,591,999 seconds) | key=, autoRenewPeriod=2591999 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | Y | +| 5 | Creates an account with an auto renew period set to the maximum period of 8,000,001 seconds | key=, autoRenewPeriod=8000001 | The account creation succeeds and the account's auto renew period should equal 8,000,001 seconds. | Y | +| 6 | Creates an account with an auto renew period set to the maximum period plus one seconds (8,000,002 seconds) | key=, autoRenewPeriod=8000002 | The account creation fails with an AUTORENEW_DURATION_NOT_IN_RANGE response code from the network. | Y | #### JSON Request Example @@ -215,10 +215,10 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a memo that is a valid length | key=, memo="testmemo" | The account creation succeeds and the account's memo equals “testmemo”. | N | -| 2 | Creates an account with a memo that is the minimum length | key=, memo="" | The account creation succeeds and the account's memo is empty. | N | -| 3 | Creates an account with a memo that is the maximum length | key=, memo="This is a really long memo but it is still valid because it is 100 characters exactly on the money!!" | The account creation succeeds and the account's memo equals "This is a really long memo but it is still valid because it is 100 characters exactly on the money!!". | N | -| 4 | Creates an account with a memo that exceeds the maximum length | key=, memo="This is a long memo that is not valid because it exceeds 100 characters and it should fail the test!!" | The account creation fails with a MEMO_TOO_LONG response code from the network. | N | +| 1 | Creates an account with a memo that is a valid length | key=, memo="testmemo" | The account creation succeeds and the account's memo equals “testmemo”. | Y | +| 2 | Creates an account with a memo that is the minimum length | key=, memo="" | The account creation succeeds and the account's memo is empty. | Y | +| 3 | Creates an account with a memo that is the maximum length | key=, memo="This is a really long memo but it is still valid because it is 100 characters exactly on the money!!" | The account creation succeeds and the account's memo equals "This is a really long memo but it is still valid because it is 100 characters exactly on the money!!". | Y | +| 4 | Creates an account with a memo that exceeds the maximum length | key=, memo="This is a long memo that is not valid because it exceeds 100 characters and it should fail the test!!" | The account creation fails with a MEMO_TOO_LONG response code from the network. | Y | #### JSON Request Example @@ -253,10 +253,10 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|--------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | N | -| 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | N | -| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | N | -| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | N | +| 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | Y | +| 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | Y | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | Y | +| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | Y | #### JSON Request Example @@ -293,13 +293,13 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|---------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with the staked account ID set to the operators account ID | key=, stakedAccountId= | The account creation succeeds and the account has a staking account ID equal to the input account ID. | N | -| 2 | Creates an account with the staked node ID set to a valid node ID | key=, stakedNodeId= | The account creation succeeds and the account has a staking node ID equal to the input node ID. | N | -| 3 | Creates an account with the staked account ID set to an account ID that doesn't exist | key=, stakedAccountId="123.456.789" | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | -| 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | -| 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | N | -| 6 | Creates an account with the staked node ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | -| 7 | Creates an account with a staked account ID and a staked node ID | key=, stakedAccountId=, stakedNodeId= | The account creation fails with an INVALID_STAKING_ID response code from the network. | N | +| 1 | Creates an account with the staked account ID set to the operators account ID | key=, stakedAccountId= | The account creation succeeds and the account has a staking account ID equal to the input account ID. | Y | +| 2 | Creates an account with the staked node ID set to a valid node ID | key=, stakedNodeId= | The account creation succeeds and the account has a staking node ID equal to the input node ID. | Y | +| 3 | Creates an account with the staked account ID set to an account ID that doesn't exist | key=, stakedAccountId="123.456.789" | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | +| 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | +| 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | Y | +| 6 | Creates an account with the staked node ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | +| 7 | Creates an account with a staked account ID and a staked node ID | key=, stakedAccountId=, stakedNodeId= | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | #### JSON Request Examples @@ -357,8 +357,8 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|---------------------------------------------------------|----------------------------------------------|--------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | N | -| 1 | Creates an account that doesn't decline staking rewards | key=, declineStakingRewards=false | The account creation succeeds and the account doesn't decline staking rewards. | N | +| 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | Y | +| 1 | Creates an account that doesn't decline staking rewards | key=, declineStakingRewards=false | The account creation succeeds and the account doesn't decline staking rewards. | Y | #### JSON Request Example @@ -393,9 +393,9 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key | key=, alias=, signerKey= | The account creation succeeds and the account has the keccak-256 hash of the ECDSAsecp256k1 public key as its alias. | N | -| 2 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature | key=, alias= | The account creation fails with an INVALID_SIGNATURE response code from the network. | N | -| 3 | Creates an account with an invalid alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | N | +| 1 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key | key=, alias=, signerKey= | The account creation succeeds and the account has the keccak-256 hash of the ECDSAsecp256k1 public key as its alias. | Y | +| 2 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature | key=, alias= | The account creation fails with an INVALID_SIGNATURE response code from the network. | Y | +| 3 | Creates an account with an invalid alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | Y | #### JSON Request Example diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index b9c662d..43d1d05 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -1,11 +1,10 @@ import { JSONRPCRequest } from "../../client.js"; import mirrorNodeClient from "../../mirrorNodeClient.js"; import consensusInfoClient from "../../consensusInfoClient.js"; -import { generateAccountKeys, setOperator, getNodeType } from "../../setup_Tests.js"; +import { setOperator, getNodeType } from "../../setup_Tests.js"; import { EvmAddress } from "@hashgraph/sdk"; import crypto from "crypto"; import { assert, expect } from "chai"; -import { JSONRPC } from "json-rpc-2.0"; /** * Tests for AccountCreateTransaction From 1f6b04f89aa7bcc66fc8e0663ceb0d1cdcd3f9b0 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 15 May 2024 12:30:13 -0500 Subject: [PATCH 17/34] fix: node account ID should contain the entire ID. Numbering issue in tests for declineStakingRewards. Some generateKey calls didn't have a type when they should. Signed-off-by: Rob Walworth --- .env.custom_node | 2 +- .../crypto-service/accountCreateTransaction.md | 2 +- test/crypto-service/test_accountCreateTransaction.js | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.env.custom_node b/.env.custom_node index 9d33b2f..560c8e3 100644 --- a/.env.custom_node +++ b/.env.custom_node @@ -3,7 +3,7 @@ OPERATOR_ACCOUNT_ID=0.0.2 OPERATOR_ACCOUNT_PRIVATE_KEY=302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137 NODE_TYPE=local NODE_IP=127.0.0.1:50211 -NODE_ACCOUNT_ID=3 +NODE_ACCOUNT_ID=0.0.3 NODE_TIMEOUT=30000 # Time after which the tests would fail if the mirror node does not have the data MIRROR_NETWORK=127.0.0.1:5600 MIRROR_NODE_REST_URL=http://127.0.0.1:5551 diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index d9f0aec..add50bd 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -358,7 +358,7 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | Test no | Name | Input | Expected response | Implemented (Y/N) | |---------|---------------------------------------------------------|----------------------------------------------|--------------------------------------------------------------------------------|-------------------| | 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines staking rewards. | Y | -| 1 | Creates an account that doesn't decline staking rewards | key=, declineStakingRewards=false | The account creation succeeds and the account doesn't decline staking rewards. | Y | +| 2 | Creates an account that doesn't decline staking rewards | key=, declineStakingRewards=false | The account creation succeeds and the account doesn't decline staking rewards. | Y | #### JSON Request Example diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 43d1d05..a91c795 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -99,6 +99,7 @@ describe("AccountCreateTransaction", function () { it("(#5) Creates an account with a valid KeyList of ED25519 and ECDSAsecp256k1 private and public keys", async function () { // Generate a KeyList of ED25519 and ECDSAsecp256k1 private and public keys for the account. const keyList = await JSONRPCRequest("generateKey", { + type: "keyList", keys: [ {}, {}, @@ -120,6 +121,7 @@ describe("AccountCreateTransaction", function () { it("(#6) Creates an account with a valid KeyList of nested Keylists (three levels)", async function () { // Generate a KeyList of nested KeyLists of ED25519 and ECDSAsecp256k1 private and public keys for the account. const nestedKeyList = await JSONRPCRequest("generateKey", { + type: "keyList", keys: [ { keys: [ @@ -248,7 +250,8 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with an initial balance higher than the operator account balance", async function () { // Get the operator account balance. - const operatorAccountBalance = await mirrorNodeClient.getBalanceData(process.env.OPERATOR_ACCOUNT_ID).balances[0].balance; + const operatorBalanceData = await mirrorNodeClient.getBalanceData(process.env.OPERATOR_ACCOUNT_ID); + const operatorAccountBalance = Number(operatorBalanceData.balances[0].balance); // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", {}); From add1435fd3d475ee1be444a3bb51df4612cf7d85 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 15 May 2024 14:45:34 -0500 Subject: [PATCH 18/34] fix: use signerKeys to explicitly sign transactions. Update to correct variable name for maxAutoTokenAssociations. Use correct bounds for maxAutoTokenAssociations Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index a91c795..576ad04 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -283,16 +283,26 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account that requires a receiving signature", async function () { // Generate a valid private key for the account. - const key = await JSONRPCRequest("generateKey", { + const privateKey = await JSONRPCRequest("generateKey", { type: "privateKey" }); - if (key.status === "NOT_IMPLEMENTED") this.skip(); + if (privateKey.status === "NOT_IMPLEMENTED") this.skip(); + + // Generate a valid public key from the generated private key. + const publicKey = await JSONRPCRequest("generateKey", { + type: "publicKey", + privateKey: privateKey.key + }); + if (publicKey.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account that requires a signature when receiving. const receiverSignatureRequired = true; const response = await JSONRPCRequest("createAccount", { - key: key.key, + key: publicKey.key, receiverSignatureRequired: receiverSignatureRequired, + signerKeys: [ + privateKey.key + ] }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -550,14 +560,14 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the max automatic token associations set to 0. - const maxAutomaticTokenAssociations = 0; + const maxAutoTokenAssociations = 0; const response = await JSONRPCRequest("createAccount", { key: key.key, - maxAutomaticTokenAssociations: maxAutomaticTokenAssociations, + maxAutoTokenAssociations: maxAutoTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutomaticTokenAssociations) + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) }); it("(#2) Creates an account with a max token association set to 100", async function () { @@ -566,14 +576,14 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the max automatic token associations set to 100. - const maxAutomaticTokenAssociations = 100; + const maxAutoTokenAssociations = 100; const response = await JSONRPCRequest("createAccount", { key: key.key, - maxAutomaticTokenAssociations: maxAutomaticTokenAssociations, + maxAutoTokenAssociations: maxAutoTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutomaticTokenAssociations) + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) }); it("(#3) Creates an account with a max token association that is the maximum value", async function () { @@ -582,14 +592,14 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the max automatic token associations set to the maximum value. - const maxAutomaticTokenAssociations = 1000; + const maxAutoTokenAssociations = 5000; const response = await JSONRPCRequest("createAccount", { key: key.key, - maxAutomaticTokenAssociations: maxAutomaticTokenAssociations, + maxAutoTokenAssociations: maxAutoTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); - verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutomaticTokenAssociations) + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) }); it("(#4) Creates an account with a max token association that is the maximum value plus one", async function () { @@ -601,7 +611,7 @@ describe("AccountCreateTransaction", function () { // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT status. const response = await JSONRPCRequest("createAccount", { key: key.key, - maxAutomaticTokenAssociations: 1001, + maxAutoTokenAssociations: 5001, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { From c5990eefcdb6b02e977bd5a810a49a713e4117b1 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 16 May 2024 13:43:58 -0500 Subject: [PATCH 19/34] fix(accountCreateTransaction): fix remaining tests Signed-off-by: Rob Walworth --- setup_Tests.js | 12 ----- .../accountCreateTransaction.md | 2 +- .../test_accountCreateTransaction.js | 46 +++++++++---------- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/setup_Tests.js b/setup_Tests.js index 0b4160f..f903f51 100644 --- a/setup_Tests.js +++ b/setup_Tests.js @@ -1,17 +1,5 @@ import { JSONRPCRequest } from './client.js' -export async function generateAccountKeys() { - // Generate new private & public key - let privateKey = await JSONRPCRequest('generatePrivateKey', {}) - let publicKey = await JSONRPCRequest('generatePublicKey', { - privateKey: privateKey, - }) - return { - publicKey: publicKey, - privateKey: privateKey, - } -} - export async function setOperator(accountId, privateKey) { // sets funding and fee-paying account for CRUD ops await JSONRPCRequest('setup', { diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index add50bd..b4980ee 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -395,7 +395,7 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api |---------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|-------------------| | 1 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key | key=, alias=, signerKey= | The account creation succeeds and the account has the keccak-256 hash of the ECDSAsecp256k1 public key as its alias. | Y | | 2 | Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature | key=, alias= | The account creation fails with an INVALID_SIGNATURE response code from the network. | Y | -| 3 | Creates an account with an invalid alias | key=, alias= | The account creation fails with an INVALID_ALIAS_KEY response code from the network. | Y | +| 3 | Creates an account with an invalid alias | key=, alias= | The account creation fails with an INVALID_SIGNATURE response code from the network. | Y | #### JSON Request Example diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 576ad04..38a4505 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -2,7 +2,6 @@ import { JSONRPCRequest } from "../../client.js"; import mirrorNodeClient from "../../mirrorNodeClient.js"; import consensusInfoClient from "../../consensusInfoClient.js"; import { setOperator, getNodeType } from "../../setup_Tests.js"; -import { EvmAddress } from "@hashgraph/sdk"; import crypto from "crypto"; import { assert, expect } from "chai"; @@ -173,7 +172,7 @@ describe("AccountCreateTransaction", function () { try { // Attempt to create an account with an invalid key (random 88 bytes, which is equal to the byte length of a valid public key). The SDK should throw an internal error. const response = await JSONRPCRequest("createAccount", { - key: crypto.randomBytes(88).toString() + key: crypto.randomBytes(88).toString('hex') }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { @@ -291,7 +290,7 @@ describe("AccountCreateTransaction", function () { // Generate a valid public key from the generated private key. const publicKey = await JSONRPCRequest("generateKey", { type: "publicKey", - privateKey: privateKey.key + fromKey: privateKey.key }); if (publicKey.status === "NOT_IMPLEMENTED") this.skip(); @@ -616,6 +615,7 @@ describe("AccountCreateTransaction", function () { if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { assert.equal(err.data.status, "REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT"); + return; } // This shouldn't happen, the JSONRPCRequest should throw. @@ -652,16 +652,13 @@ describe("AccountCreateTransaction", function () { }); it("(#2) Creates an account with the staked node ID set to a valid node ID", async function () { - // Only run this test in a local setup. - if (await getNodeType(process.env.NODE_TYPE)) this.skip(); - // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); - // Attempt to create an account with the staked node ID set to the operator's account ID. - // TODO: figure out from where to grab the correct node ID. .env file?? For now, using random node ID. - const stakedNodeId = Math.floor(Math.random() * 6) + 1; + // Attempt to create an account with the staked node ID set to the node's node ID. + // TODO: figure out from where to grab the correct node ID. .env file?? For now, 0 is what works. + const stakedNodeId = 0; const response = await JSONRPCRequest("createAccount", { key: key.key, stakedNodeId: stakedNodeId, @@ -830,18 +827,19 @@ describe("AccountCreateTransaction", function () { "type": "ecdsaSecp256k1PrivateKey" }); - // Generate the ECDSAsecp256k1 public key associated with the private key, which will then be used as the alias for the account. - const ecdsaSecp256k1PublicKey = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey", - "privateKey": ecdsaSecp256k1PrivateKey.key + // Generate the EVM address associated with the private key, which will then be used as the alias for the account. + const alias = await JSONRPCRequest("generateKey", { + "type": "evmAddress", + "fromKey": ecdsaSecp256k1PrivateKey.key }); // Attempt to create an account with the alias. - const alias = EvmAddress.fromString(ecdsaSecp256k1PublicKey.key).toString(); const response = await JSONRPCRequest("createAccount", { key: key.key, - alias: alias, - signerKey: ecdsaSecp256k1PrivateKey.key + alias: alias.key, + signerKeys: [ + ecdsaSecp256k1PrivateKey.key + ] }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -853,20 +851,21 @@ describe("AccountCreateTransaction", function () { const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); - // Generate the ECDSAsecp256k1 public key to be used as the alias for the account. - const ecdsaSecp256k1PublicKey = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + // Generate the EVM address to be used as the alias for the account. + const alias = await JSONRPCRequest("generateKey", { + "type": "evmAddress" }); try { // Attempt to create an account with the alias without signing with the associated ECDSAsecp256k1 private key. The network should respond with an INVALID_SIGNATURE status. const response = await JSONRPCRequest("createAccount", { key: key.key, - alias: EvmAddress.fromString(ecdsaSecp256k1PublicKey.key).toString(), + alias: alias.key }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { assert.equal(err.data.status, "INVALID_SIGNATURE"); + return; } // This shouldn't happen, the JSONRPCRequest should throw. @@ -879,14 +878,15 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); try { - // Attempt to create an account with an invalid alias. The network should respond with an INVALID_ALIAS_KEY status. + // Attempt to create an account with an invalid alias. The network should respond with an INVALID_SIGNATURE status. const response = await JSONRPCRequest("createAccount", { key: key.key, - alias: crypto.randomBytes(20).toString(), + alias: crypto.randomBytes(20).toString('hex').toUpperCase() }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - assert.equal(err.data.status, "INVALID_ALIAS_KEY"); + assert.equal(err.data.status, "INVALID_SIGNATURE"); + return; } // This shouldn't happen, the JSONRPCRequest should throw. From b8dd150efa68d85d0e641fd7689ad6b404a207dd Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 20 May 2024 10:00:52 -0500 Subject: [PATCH 20/34] fix: add keyList type to tests as they're necessary according to the spec Signed-off-by: Rob Walworth --- test/crypto-service/test_accountCreateTransaction.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 38a4505..fa04954 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -123,18 +123,21 @@ describe("AccountCreateTransaction", function () { type: "keyList", keys: [ { + type: "keyList", keys: [ {}, {} ] }, { + type: "keyList", keys: [ {}, {} ] }, { + type: "keyList", keys: [ {}, {} From d54fbed8c36a5c1542badefc69575d66be32b5d6 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 20 May 2024 15:23:37 -0500 Subject: [PATCH 21/34] refactor: add comments, keep consistent between tests Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index fa04954..e6d6846 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -167,7 +167,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -183,7 +183,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); @@ -246,7 +246,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -271,7 +271,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); @@ -308,14 +308,13 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with a receiver signature required. verifyAccountCreationWithReceiverSignatureRequired(response.accountId, receiverSignatureRequired); }); it("(#2) Creates an account that doesn't require a receiving signature", async function () { // Generate a valid private key for the account. - const key = await JSONRPCRequest("generateKey", { - type: "privateKey" - }); + const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account that doesn't require a signature when receiving. @@ -326,14 +325,13 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with a receiver signature not required. verifyAccountCreationWithReceiverSignatureRequired(response.accountId, receiverSignatureRequired); }); it("(#3) Creates an account that requires a receiving signature but isn't signed by the account key", async function () { // Generate a valid public key for the account. - const key = await JSONRPCRequest("generateKey", { - type: "publicKey" - }); + const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -348,7 +346,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); @@ -373,6 +371,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with an auto-renew period set to 60 days. verifyAccountCreationWithAutoRenewPeriod(response.accountId, autoRenewPeriodSeconds); }); @@ -393,7 +392,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -410,6 +409,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with an auto-renew period set to 30 days. verifyAccountCreationWithAutoRenewPeriod(response.accountId, autoRenewPeriodSeconds); }); @@ -430,7 +430,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -447,6 +447,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with an auto-renew period set to 90ish days. verifyAccountCreationWithAutoRenewPeriod(response.accountId, autoRenewPeriodSeconds); }); @@ -467,7 +468,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); @@ -492,6 +493,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the memo set to "testmemo". verifyAccountCreationWithMemo(response.accountId, memo); }); @@ -508,6 +510,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with an empty memo. verifyAccountCreationWithMemo(response.accountId, memo); }); @@ -524,6 +527,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the memo set to "This is a really long memo but it is still valid because it is 100 characters exactly on the money!!". verifyAccountCreationWithMemo(response.accountId, memo); }); @@ -544,7 +548,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); @@ -556,35 +560,37 @@ describe("AccountCreateTransaction", function () { expect(maxAutomaticTokenAssociations).to.equal(await mirrorNodeClient.getAccountData(accountId).accounts[0].max_automatic_token_associations); } - it("(#1) Creates an account with a max token association set to 0", async function () { + it("(#1) Creates an account with a max token association set to 100", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); - // Attempt to create an account with the max automatic token associations set to 0. - const maxAutoTokenAssociations = 0; + // Attempt to create an account with the max automatic token associations set to 100. + const maxAutoTokenAssociations = 100; const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: maxAutoTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the max automatic token associations set to 100. verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) }); - it("(#2) Creates an account with a max token association set to 100", async function () { + it("(#2) Creates an account with a max token association set to 0", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); - // Attempt to create an account with the max automatic token associations set to 100. - const maxAutoTokenAssociations = 100; + // Attempt to create an account with the max automatic token associations set to 0. + const maxAutoTokenAssociations = 0; const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: maxAutoTokenAssociations, }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the max automatic token associations set to 0. verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) }); @@ -601,6 +607,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the max automatic token associations set to 5000. verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) }); @@ -621,7 +628,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); @@ -651,6 +658,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the staked account ID equal to the operator account ID. verifyAccountCreationWithStakedAccountId(response.accountId, process.env.OPERATOR_ACCOUNT_ID); }); @@ -668,6 +676,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the staked node ID equal to 0. verifyAccountCreationWithStakedAccountId(response.accountId, stakedNodeId); }); @@ -687,6 +696,8 @@ describe("AccountCreateTransaction", function () { assert.equal(err.data.status, "INVALID_STAKING_ID"); return; } + + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -707,7 +718,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -728,7 +739,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -749,7 +760,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -770,6 +781,9 @@ describe("AccountCreateTransaction", function () { assert.equal(err.data.status, "INVALID_STAKING_ID"); return; } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); }); }); @@ -793,6 +807,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with decline staking rewards. verifyAccountCreationWithDeclineRewards(response.accountId, declineStakingReward); }); @@ -809,6 +824,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created without declining staking rewards. verifyAccountCreationWithDeclineRewards(response.accountId, declineStakingReward); }); }); @@ -846,6 +862,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); + // Verify the account was created with the generated alias. verifyAccountCreationWithAlias(response.accountId, alias); }); @@ -871,7 +888,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); @@ -892,7 +909,7 @@ describe("AccountCreateTransaction", function () { return; } - // This shouldn't happen, the JSONRPCRequest should throw. + // The test failed, no error was thrown. assert.fail("Should throw an error"); }); }); From a6bd6b5f6fa3e3ae88f6a1ad1307a837811eab9d Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 20 May 2024 15:24:22 -0500 Subject: [PATCH 22/34] docs: update max value for max auto token associations Signed-off-by: Rob Walworth --- .../crypto-service/accountCreateTransaction.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index b4980ee..3883f1a 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -255,8 +255,8 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api |---------|--------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| | 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | Y | | 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | Y | -| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=1000 | The account creation succeeds and the account has 1000 automatic token associations. | Y | -| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=1001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | Y | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=5000 | The account creation succeeds and the account has 5000 automatic token associations. | Y | +| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=5001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | Y | #### JSON Request Example From 70982a6f7779e0898a2096c125d6f871ffe04c03 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 22 May 2024 15:23:59 -0500 Subject: [PATCH 23/34] fix: update staking account ID and staking node ID test to passing Signed-off-by: Rob Walworth --- .../accountCreateTransaction.md | 2 +- .../test_accountCreateTransaction.js | 27 ++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index 3883f1a..0a242b5 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -299,7 +299,7 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | 4 | Creates an account with the staked node ID set to a node ID that doesn't exist | key=, stakedNodeId=123456789 | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | | 5 | Creates an account with the staked account ID set to an empty account ID | key=, stakedAccountId="" | The account creation fails with and SDK internal error. | Y | | 6 | Creates an account with the staked node ID set to an invalid node ID | key=, stakedNodeId=-100 | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | -| 7 | Creates an account with a staked account ID and a staked node ID | key=, stakedAccountId=, stakedNodeId= | The account creation fails with an INVALID_STAKING_ID response code from the network. | Y | +| 7 | Creates an account with a staked account ID and a staked node ID | key=, stakedAccountId=, stakedNodeId= | The account creation succeeds and the account has a staking node ID equal to the input node ID. | Y | #### JSON Request Examples diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index e6d6846..24431c3 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -668,7 +668,6 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the staked node ID set to the node's node ID. - // TODO: figure out from where to grab the correct node ID. .env file?? For now, 0 is what works. const stakedNodeId = 0; const response = await JSONRPCRequest("createAccount", { key: key.key, @@ -769,21 +768,17 @@ describe("AccountCreateTransaction", function () { const key = await JSONRPCRequest("generateKey", {}); if (key.status === "NOT_IMPLEMENTED") this.skip(); - try { - // Attempt to create an account with a staked account ID and a staked node ID. The network should respond with an INVALID_STAKING_ID status. - const response = await JSONRPCRequest("createAccount", { - key: key.key, - stakedAccountId: process.env.OPERATOR_ACCOUNT_ID, - stakedNodeId: Math.floor(Math.random() * 6) + 1, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "INVALID_STAKING_ID"); - return; - } - - // The test failed, no error was thrown. - assert.fail("Should throw an error"); + // Attempt to create an account with a staked account ID and a staked node ID. + const stakedNodeId = Math.floor(Math.random() * 6) + 1; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + stakedAccountId: process.env.OPERATOR_ACCOUNT_ID, + stakedNodeId: stakedNodeId + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created with the staked node ID equal to stakedNodeId. + verifyAccountCreationWithStakedAccountId(response.accountId, stakedNodeId); }); }); From cbb3f3292187ef8928710351e2aeb19e31be1484 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 22 May 2024 15:33:33 -0500 Subject: [PATCH 24/34] fix: update staked node ID to ID that will work for both testnet and local node testing Signed-off-by: Rob Walworth --- test/crypto-service/test_accountCreateTransaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 24431c3..48ac880 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -769,7 +769,7 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with a staked account ID and a staked node ID. - const stakedNodeId = Math.floor(Math.random() * 6) + 1; + const stakedNodeId = 0; const response = await JSONRPCRequest("createAccount", { key: key.key, stakedAccountId: process.env.OPERATOR_ACCOUNT_ID, From d42950673716f773c6d0544086c51fc066bb3135 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 30 May 2024 15:42:19 -0500 Subject: [PATCH 25/34] fix: update to use commonTransactionParams Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 48ac880..1a0f928 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -302,9 +302,11 @@ describe("AccountCreateTransaction", function () { const response = await JSONRPCRequest("createAccount", { key: publicKey.key, receiverSignatureRequired: receiverSignatureRequired, - signerKeys: [ - privateKey.key - ] + commonTransactionParams: { + signers: [ + privateKey.key + ] + } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -838,22 +840,24 @@ describe("AccountCreateTransaction", function () { // Generate the ECDSAsecp256k1 private key of the alias for the account. const ecdsaSecp256k1PrivateKey = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); // Generate the EVM address associated with the private key, which will then be used as the alias for the account. const alias = await JSONRPCRequest("generateKey", { - "type": "evmAddress", - "fromKey": ecdsaSecp256k1PrivateKey.key + type: "evmAddress", + fromKey: ecdsaSecp256k1PrivateKey.key }); // Attempt to create an account with the alias. const response = await JSONRPCRequest("createAccount", { key: key.key, alias: alias.key, - signerKeys: [ - ecdsaSecp256k1PrivateKey.key - ] + commonTransactionParams: { + signers: [ + ecdsaSecp256k1PrivateKey.key + ] + } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -868,7 +872,7 @@ describe("AccountCreateTransaction", function () { // Generate the EVM address to be used as the alias for the account. const alias = await JSONRPCRequest("generateKey", { - "type": "evmAddress" + type: "evmAddress" }); try { From d90e545b357aea914ec305ff17c645c82c9d55df Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Fri, 31 May 2024 11:51:57 -0500 Subject: [PATCH 26/34] fix: no longer allowing random types for generateKey Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 172 +++++++++++++----- 1 file changed, 127 insertions(+), 45 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 1a0f928..a9ece46 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -100,9 +100,15 @@ describe("AccountCreateTransaction", function () { const keyList = await JSONRPCRequest("generateKey", { type: "keyList", keys: [ - {}, - {}, - {} + { + "type": "ed25519PrivateKey" + }, + { + "type": "ecdsaSecp256k1PublicKey" + }, + { + "type": "ed25519PublicKey" + } ] }); if (keyList.status === "NOT_IMPLEMENTED") this.skip(); @@ -125,22 +131,34 @@ describe("AccountCreateTransaction", function () { { type: "keyList", keys: [ - {}, - {} + { + "type": "ecdsaSecp256k1PublicKey" + }, + { + "type": "ecdsaSecp256k1PrivateKey" + } ] }, { type: "keyList", keys: [ - {}, - {} + { + "type": "ecdsaSecp256k1PublicKey" + }, + { + "type": "ed25519PublicKey" + } ] }, { type: "keyList", keys: [ - {}, - {} + { + "type": "ed25519PrivateKey" + }, + { + "type": "ecdsaSecp256k1PublicKey" + } ] } ] @@ -197,7 +215,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with an initial balance", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with an initial balance of 100 tinybars. @@ -214,7 +234,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with no initial balance", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with an initial balance of 0 tinybars. @@ -231,7 +253,9 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with a negative initial balance", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -256,7 +280,9 @@ describe("AccountCreateTransaction", function () { const operatorAccountBalance = Number(operatorBalanceData.balances[0].balance); // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -286,13 +312,13 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account that requires a receiving signature", async function () { // Generate a valid private key for the account. const privateKey = await JSONRPCRequest("generateKey", { - type: "privateKey" + type: "ed25519PrivateKey" }); if (privateKey.status === "NOT_IMPLEMENTED") this.skip(); // Generate a valid public key from the generated private key. const publicKey = await JSONRPCRequest("generateKey", { - type: "publicKey", + type: "ed25519PublicKey", fromKey: privateKey.key }); if (publicKey.status === "NOT_IMPLEMENTED") this.skip(); @@ -315,8 +341,10 @@ describe("AccountCreateTransaction", function () { }); it("(#2) Creates an account that doesn't require a receiving signature", async function () { - // Generate a valid private key for the account. - const key = await JSONRPCRequest("generateKey", {}); + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account that doesn't require a signature when receiving. @@ -332,8 +360,10 @@ describe("AccountCreateTransaction", function () { }); it("(#3) Creates an account that requires a receiving signature but isn't signed by the account key", async function () { - // Generate a valid public key for the account. - const key = await JSONRPCRequest("generateKey", {}); + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -362,7 +392,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with an auto renew period set to 60 days (5,184,000 seconds)", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with an auto-renew period set to 60 days. @@ -379,7 +411,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with an auto renew period set to -1 seconds", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -400,7 +434,9 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with an auto renew period set to the minimum period of 30 days (2,592,000 seconds)", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with an auto-renew period set to 30 days. @@ -417,7 +453,9 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with an auto renew period set to the minimum period of 30 days minus one second (2,591,999 seconds)", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -438,7 +476,9 @@ describe("AccountCreateTransaction", function () { it("(#5) Creates an account with an auto renew period set to the maximum period of 8,000,001 seconds", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with an auto-renew period set to 90ish days. @@ -455,7 +495,9 @@ describe("AccountCreateTransaction", function () { it("(#6) Creates an account with an auto renew period set to the maximum period plus one seconds (8,000,002 seconds)", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -484,7 +526,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with a valid memo", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with a memo set to "testmemo". @@ -501,7 +545,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with an empty memo", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with an empty memo. @@ -518,7 +564,9 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with a memo that is 100 characters", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with a memo set to the maximum length. @@ -535,7 +583,9 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with a memo that exceeds 100 characters", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -564,7 +614,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with a max token association set to 100", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the max automatic token associations set to 100. @@ -581,7 +633,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with a max token association set to 0", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the max automatic token associations set to 0. @@ -598,7 +652,9 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with a max token association that is the maximum value", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the max automatic token associations set to the maximum value. @@ -615,7 +671,9 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with a max token association that is the maximum value plus one", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -650,7 +708,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with the staked account ID set to the operators account ID", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the staked account ID set to the operator's account ID. @@ -666,7 +726,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with the staked node ID set to a valid node ID", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with the staked node ID set to the node's node ID. @@ -683,7 +745,9 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with the staked account ID set to an account ID that doesn't exist", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -704,7 +768,9 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with the staked node ID set to a node ID that doesn't exist", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -725,7 +791,9 @@ describe("AccountCreateTransaction", function () { it("(#5) Creates an account with the staked account ID set to an empty account ID", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -746,7 +814,9 @@ describe("AccountCreateTransaction", function () { it("(#6) Creates an account with the staked node ID set to an invalid node ID", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { @@ -767,7 +837,9 @@ describe("AccountCreateTransaction", function () { it("(#7) Creates an account with a staked account ID and a staked node ID", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with a staked account ID and a staked node ID. @@ -793,7 +865,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account that declines staking rewards", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with that declines staking rewards. @@ -810,7 +884,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account that doesn't decline staking rewards", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Attempt to create an account with that doesn't decline staking rewards. @@ -835,7 +911,9 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ecdsaSecp256k1PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Generate the ECDSAsecp256k1 private key of the alias for the account. @@ -867,7 +945,9 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PrivateKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); // Generate the EVM address to be used as the alias for the account. @@ -893,7 +973,9 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with an invalid alias", async function () { // Generate a valid key for the account. - const key = await JSONRPCRequest("generateKey", {}); + const key = await JSONRPCRequest("generateKey", { + "type": "ed25519PublicKey" + }); if (key.status === "NOT_IMPLEMENTED") this.skip(); try { From 22c74ab89de87cf561cc3a86f8516dc822923d2c Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Fri, 31 May 2024 14:44:55 -0500 Subject: [PATCH 27/34] fix: add explicity key types for all generateKey calls Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index a9ece46..43426da 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -101,13 +101,13 @@ describe("AccountCreateTransaction", function () { type: "keyList", keys: [ { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }, { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }, { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" } ] }); @@ -132,10 +132,10 @@ describe("AccountCreateTransaction", function () { type: "keyList", keys: [ { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }, { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" } ] }, @@ -143,10 +143,10 @@ describe("AccountCreateTransaction", function () { type: "keyList", keys: [ { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }, { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" } ] }, @@ -154,10 +154,10 @@ describe("AccountCreateTransaction", function () { type: "keyList", keys: [ { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }, { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" } ] } @@ -216,7 +216,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with an initial balance", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -235,7 +235,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with no initial balance", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -254,7 +254,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with a negative initial balance", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -281,7 +281,7 @@ describe("AccountCreateTransaction", function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -343,7 +343,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account that doesn't require a receiving signature", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -362,7 +362,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account that requires a receiving signature but isn't signed by the account key", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -393,7 +393,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with an auto renew period set to 60 days (5,184,000 seconds)", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -412,7 +412,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with an auto renew period set to -1 seconds", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -435,7 +435,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with an auto renew period set to the minimum period of 30 days (2,592,000 seconds)", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -454,7 +454,7 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with an auto renew period set to the minimum period of 30 days minus one second (2,591,999 seconds)", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -477,7 +477,7 @@ describe("AccountCreateTransaction", function () { it("(#5) Creates an account with an auto renew period set to the maximum period of 8,000,001 seconds", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -496,7 +496,7 @@ describe("AccountCreateTransaction", function () { it("(#6) Creates an account with an auto renew period set to the maximum period plus one seconds (8,000,002 seconds)", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -527,7 +527,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with a valid memo", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -546,7 +546,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with an empty memo", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -565,7 +565,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with a memo that is 100 characters", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -584,7 +584,7 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with a memo that exceeds 100 characters", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -615,7 +615,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with a max token association set to 100", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -634,7 +634,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with a max token association set to 0", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -653,7 +653,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with a max token association that is the maximum value", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -672,7 +672,7 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with a max token association that is the maximum value plus one", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -709,7 +709,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with the staked account ID set to the operators account ID", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -727,7 +727,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with the staked node ID set to a valid node ID", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -746,7 +746,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with the staked account ID set to an account ID that doesn't exist", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -769,7 +769,7 @@ describe("AccountCreateTransaction", function () { it("(#4) Creates an account with the staked node ID set to a node ID that doesn't exist", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -792,7 +792,7 @@ describe("AccountCreateTransaction", function () { it("(#5) Creates an account with the staked account ID set to an empty account ID", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -815,7 +815,7 @@ describe("AccountCreateTransaction", function () { it("(#6) Creates an account with the staked node ID set to an invalid node ID", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -838,7 +838,7 @@ describe("AccountCreateTransaction", function () { it("(#7) Creates an account with a staked account ID and a staked node ID", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -866,7 +866,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account that declines staking rewards", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -885,7 +885,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account that doesn't decline staking rewards", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PublicKey" + type: "ecdsaSecp256k1PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -912,7 +912,7 @@ describe("AccountCreateTransaction", function () { it("(#1) Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ecdsaSecp256k1PrivateKey" + type: "ecdsaSecp256k1PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -946,7 +946,7 @@ describe("AccountCreateTransaction", function () { it("(#2) Creates an account with the keccak-256 hash of an ECDSAsecp256k1 public key without a signature", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PrivateKey" + type: "ed25519PrivateKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); @@ -974,7 +974,7 @@ describe("AccountCreateTransaction", function () { it("(#3) Creates an account with an invalid alias", async function () { // Generate a valid key for the account. const key = await JSONRPCRequest("generateKey", { - "type": "ed25519PublicKey" + type: "ed25519PublicKey" }); if (key.status === "NOT_IMPLEMENTED") this.skip(); From fcf33a7222d7decd2542e50e436145580365ccab Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 5 Jun 2024 14:38:51 -0500 Subject: [PATCH 28/34] fix: use maxTransactionFee to allow for an account to be created with maximum auto token associations The functionality for this will need to be removed when HIP-904 is implemented, as that will make the sender pay at transaction time for the association of a token to a receiver, as opposed to the receiver paying up front at account creation time for all its token assocations. Signed-off-by: Rob Walworth --- test/crypto-service/test_accountCreateTransaction.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 43426da..b23eddd 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -662,6 +662,9 @@ describe("AccountCreateTransaction", function () { const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: maxAutoTokenAssociations, + commonTransactionParams: { + maxTransactionFee: 10000000000 + } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -681,6 +684,9 @@ describe("AccountCreateTransaction", function () { const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: 5001, + commonTransactionParams: { + maxTransactionFee: 10000000000 + } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { From df4596e7c0ed7a8c1d4e3dd98d9b8547873348d5 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Tue, 18 Jun 2024 15:35:50 -0500 Subject: [PATCH 29/34] fix: maxTransactionFee wasn't large enough and not documented Signed-off-by: Rob Walworth --- .../crypto-service/accountCreateTransaction.md | 12 ++++++------ test/crypto-service/test_accountCreateTransaction.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index 40fc93a..194d5f0 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -256,12 +256,12 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api - The maximum number of tokens with which an account can be implicitly associated. Defaults to 0 and up to a maximum value of 1000. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|--------------------------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | Y | -| 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | Y | -| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=5000 | The account creation succeeds and the account has 5000 automatic token associations. | Y | -| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=5001 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | Y | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | Y | +| 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | Y | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=5000, commonTransactionParams.maxTransactionFee=100000000000 | The account creation succeeds and the account has 5000 automatic token associations. | Y | +| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=5001, commonTransactionParams.maxTransactionFee=100000000000 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | Y | #### JSON Request Example diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index b23eddd..477009b 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -663,7 +663,7 @@ describe("AccountCreateTransaction", function () { key: key.key, maxAutoTokenAssociations: maxAutoTokenAssociations, commonTransactionParams: { - maxTransactionFee: 10000000000 + maxTransactionFee: 100000000000 } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -685,7 +685,7 @@ describe("AccountCreateTransaction", function () { key: key.key, maxAutoTokenAssociations: 5001, commonTransactionParams: { - maxTransactionFee: 10000000000 + maxTransactionFee: 100000000000 } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); From 555f8af37b0d191c632e9dce827885fbe1e00345 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 11 Jul 2024 07:54:49 -0500 Subject: [PATCH 30/34] feat(memo): add test for invalid memo checking Signed-off-by: Rob Walworth --- .../accountCreateTransaction.md | 1 + .../test_accountCreateTransaction.js | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index 194d5f0..d8f2fc4 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -224,6 +224,7 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api | 2 | Creates an account with a memo that is the minimum length | key=, memo="" | The account creation succeeds and the account's memo is empty. | Y | | 3 | Creates an account with a memo that is the maximum length | key=, memo="This is a really long memo but it is still valid because it is 100 characters exactly on the money!!" | The account creation succeeds and the account's memo equals "This is a really long memo but it is still valid because it is 100 characters exactly on the money!!". | Y | | 4 | Creates an account with a memo that exceeds the maximum length | key=, memo="This is a long memo that is not valid because it exceeds 100 characters and it should fail the test!!" | The account creation fails with a MEMO_TOO_LONG response code from the network. | Y | +| 5 | Creates an account with an invalid memo | key=, memo="This is an invalid memo!\0" | The account creation fails with a INVALID_ZERO_BYTE_IN_STRING response code from the network. | Y | #### JSON Request Example diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 477009b..0039d9c 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -603,6 +603,29 @@ describe("AccountCreateTransaction", function () { // The test failed, no error was thrown. assert.fail("Should throw an error"); }); + + it("(#5) Creates an account with an invalid memo", async function () { + // Generate a valid key for the account. + const key = await JSONRPCRequest("generateKey", { + type: "ed25519PrivateKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // Attempt to create an account with an invalid memo. The network should respond with an INVALID_ZERO_BYTE_IN_STRING status. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + memo: "This is an invalid memo!\0", + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_ZERO_BYTE_IN_STRING"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); }); describe("Max Automatic Token Associations", async function () { From b47d9c65dd37cd591a6e116476809aebeb9f5efe Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Wed, 17 Jul 2024 12:34:15 -0500 Subject: [PATCH 31/34] fix: update response code for max auto association tests for account create Signed-off-by: Rob Walworth --- .../crypto-service/accountCreateTransaction.md | 12 ++++++------ test/crypto-service/test_accountCreateTransaction.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test-specifications/crypto-service/accountCreateTransaction.md b/test-specifications/crypto-service/accountCreateTransaction.md index d8f2fc4..12567fb 100644 --- a/test-specifications/crypto-service/accountCreateTransaction.md +++ b/test-specifications/crypto-service/accountCreateTransaction.md @@ -257,12 +257,12 @@ https://docs.hedera.com/hedera/sdks-and-apis/rest-api - The maximum number of tokens with which an account can be implicitly associated. Defaults to 0 and up to a maximum value of 1000. -| Test no | Name | Input | Expected response | Implemented (Y/N) | -|---------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------| -| 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | Y | -| 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | Y | -| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=5000, commonTransactionParams.maxTransactionFee=100000000000 | The account creation succeeds and the account has 5000 automatic token associations. | Y | -| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=5001, commonTransactionParams.maxTransactionFee=100000000000 | The account creation fails with a REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT response code from the network. | Y | +| Test no | Name | Input | Expected response | Implemented (Y/N) | +|---------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|-------------------| +| 1 | Creates an account with a max token association that is a valid amount | key=, maxAutoTokenAssociations=100 | The account creation succeeds and the account has 100 automatic token associations. | Y | +| 2 | Creates an account with a max token association that is the minimum value | key=, maxAutoTokenAssociations=0 | The account creation succeeds and the account has 0 automatic token associations. | Y | +| 3 | Creates an account with a max token association that is the maximum value | key=, maxAutoTokenAssociations=5000, commonTransactionParams.maxTransactionFee=100000000000 | The account creation succeeds and the account has 5000 automatic token associations. | Y | +| 4 | Creates an account with a max token association that exceeds the maximum value | key=, maxAutoTokenAssociations=5001, commonTransactionParams.maxTransactionFee=100000000000 | The account creation fails with a INVALID_MAX_AUTO_ASSOCIATIONS response code from the network. | Y | #### JSON Request Example diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 0039d9c..0aa5bd2 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -703,7 +703,7 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); try { - // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT status. + // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an INVALID_MAX_AUTO_ASSOCIATIONS status. const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: 5001, @@ -713,7 +713,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - assert.equal(err.data.status, "REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT"); + assert.equal(err.data.status, "INVALID_MAX_AUTO_ASSOCIATIONS"); return; } From 466cc7c14234f281aed825184b9ea492eb6a14bb Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Tue, 23 Jul 2024 09:13:25 -0500 Subject: [PATCH 32/34] refactor: remove getNodeType Signed-off-by: Rob Walworth --- test/crypto-service/test_accountCreateTransaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 0aa5bd2..41e14ed 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -1,7 +1,7 @@ import { JSONRPCRequest } from "../../client.js"; import mirrorNodeClient from "../../mirrorNodeClient.js"; import consensusInfoClient from "../../consensusInfoClient.js"; -import { setOperator, getNodeType } from "../../setup_Tests.js"; +import { setOperator } from "../../setup_Tests.js"; import crypto from "crypto"; import { assert, expect } from "chai"; From 73138e8d316fd162b7c2359a770230d070a522db Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Mon, 29 Jul 2024 14:31:50 -0500 Subject: [PATCH 33/34] fix: add '0x' to alias bytes and revert unlimited max auto token association changes as that was moved to v0.53 Signed-off-by: Rob Walworth --- test/crypto-service/test_accountCreateTransaction.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index 41e14ed..df3f6b2 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -647,6 +647,9 @@ describe("AccountCreateTransaction", function () { const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: maxAutoTokenAssociations, + commonTransactionParams: { + maxTransactionFee: 100000000000 + } }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -703,7 +706,7 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); try { - // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an INVALID_MAX_AUTO_ASSOCIATIONS status. + // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT status. const response = await JSONRPCRequest("createAccount", { key: key.key, maxAutoTokenAssociations: 5001, @@ -713,7 +716,7 @@ describe("AccountCreateTransaction", function () { }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - assert.equal(err.data.status, "INVALID_MAX_AUTO_ASSOCIATIONS"); + assert.equal(err.data.status, "REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT"); return; } @@ -1011,7 +1014,7 @@ describe("AccountCreateTransaction", function () { // Attempt to create an account with an invalid alias. The network should respond with an INVALID_SIGNATURE status. const response = await JSONRPCRequest("createAccount", { key: key.key, - alias: crypto.randomBytes(20).toString('hex').toUpperCase() + alias: "0x" + crypto.randomBytes(20).toString('hex').toUpperCase() }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { From 2d0f6846df60e0da5f0c2cab8a027e5b19de19b6 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Thu, 1 Aug 2024 11:40:20 -0500 Subject: [PATCH 34/34] revert: undo max auto token associations changes Signed-off-by: Rob Walworth --- .../test_accountCreateTransaction.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js index df3f6b2..b5dc4a7 100644 --- a/test/crypto-service/test_accountCreateTransaction.js +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -687,10 +687,7 @@ describe("AccountCreateTransaction", function () { const maxAutoTokenAssociations = 5000; const response = await JSONRPCRequest("createAccount", { key: key.key, - maxAutoTokenAssociations: maxAutoTokenAssociations, - commonTransactionParams: { - maxTransactionFee: 100000000000 - } + maxAutoTokenAssociations: maxAutoTokenAssociations }); if (response.status === "NOT_IMPLEMENTED") this.skip(); @@ -706,17 +703,14 @@ describe("AccountCreateTransaction", function () { if (key.status === "NOT_IMPLEMENTED") this.skip(); try { - // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT status. + // Attempt to create an account with the max automatic token associations over the maximum value. The network should respond with an INVALID_MAX_AUTO_ASSOCIATIONS status. const response = await JSONRPCRequest("createAccount", { key: key.key, - maxAutoTokenAssociations: 5001, - commonTransactionParams: { - maxTransactionFee: 100000000000 - } + maxAutoTokenAssociations: 5001 }); if (response.status === "NOT_IMPLEMENTED") this.skip(); } catch (err) { - assert.equal(err.data.status, "REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT"); + assert.equal(err.data.status, "INVALID_MAX_AUTO_ASSOCIATIONS"); return; }