diff --git a/.env.custom_node b/.env.custom_node index f3e4ff1..69ff87c 100644 --- a/.env.custom_node +++ b/.env.custom_node @@ -3,7 +3,7 @@ OPERATOR_ACCOUNT_ID=0.0.1022 OPERATOR_ACCOUNT_PRIVATE_KEY=302e020100300506032b657004220420a608e2130a0a3cb34f86e757303c862bee353d9ab77ba4387ec084f881d420d4 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/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/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 86f42cd..12567fb 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, commonTransactionParams.signers=[] | 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, commonTransactionParams.signers=[] | 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 @@ -180,12 +180,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 @@ -220,10 +220,11 @@ 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 | +| 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 @@ -256,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. | 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 | +| 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 @@ -298,13 +299,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 succeeds and the account has a staking node ID equal to the input node ID. | Y | #### JSON Request Examples @@ -362,8 +363,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 | -| 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. | N | +| 1 | Creates an account that declines staking rewards | key=, declineStakingRewards=true | The account creation succeeds and the account declines 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 @@ -398,9 +399,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=, commonTransactionParams.signers=[] | 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=, commonTransactionParams.signers=[] | 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_CreateAccount.js b/test/crypto-service/test_CreateAccount.js deleted file mode 100644 index 60859fa..0000000 --- a/test/crypto-service/test_CreateAccount.js +++ /dev/null @@ -1,584 +0,0 @@ -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 crypto from "crypto"; -import { assert, expect } from "chai"; - -/** - * Test Create account and compare results with js SDK - */ -describe("#createAccount()", function () { - this.timeout(30000); - let publicKey, privateKey, local; - - 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - }); - 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(); - - // query account via mirror node to confirm availability after creation - const accountInfoFromMirrorNode = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const accountIDFromMirrorNode = accountInfoFromMirrorNode.accounts[0].account; - - // confirm pass status with assertion testing for account creation - expect(newAccountId).to.equal(accountIDFromConsensusNode); - expect(newAccountId).to.equal(accountIDFromMirrorNode); - }); - // 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 - - 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; - } - 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 () { - try { - // generate a random key value (where 88b is equal to byte length of 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: invalidPublicKey, - initialBalance: initialBal, - }); - 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; - } - 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: initialBal, - }); - 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; - - expect(initialBal).to.equal(Number(accountBalanceConsensusNode)); - expect(initialBal).to.equal(Number(accountBalanceMirrorNode)); - }); - // 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; - **/ - 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); - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: negativeInitialBalance, - }); - 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; - } - 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); - - // generate fresh keys for second account - ({ publicKey, privateKey } = await generateAccountKeys()); - try { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: payerBalance, - }); - 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; - } - 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 () { - 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", { - publicKey: publicKey, - privateKey: privateKey, - initialBalance: 1, - receiverSignatureRequired: true, - }); - 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; - - // query account via mirror node to confirm availability after creation - const respJSON = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const recvdSignatureStatusFromMirrorNode = respJSON.accounts[0].receiver_sig_required; - - // confirm pass status for account creation with signature required - expect(recvdSignatureStatusFromConsensusNode).to.equal(true); - expect(recvdSignatureStatusFromMirrorNode).to.equal(true); - }); - // 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - privateKey: privateKey, - initialBalance: 1, - receiverSignatureRequired: false, - }); - 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; - - // query account via mirror node to confirm availability after creation - const respJSON = await mirrorNodeClient.getAccountData(accountIDFromConsensusNode); - const recvdSignatureStatusFromMirrorNode = respJSON.accounts[0].receiver_sig_required; - - // confirm pass for account creation with requirement for signature set to true - expect(recvdSignatureStatusFromConsensusNode).to.equal(false); - expect(recvdSignatureStatusFromMirrorNode).to.equal(false); - }); - }); - - //----------- 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 () { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: 0, - maxAutomaticTokenAssociations: 0, - }); - 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); - }); - // Creates an account with max token set to the maximum - it("Max token set to the maximum", async function () { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: 0, - maxAutomaticTokenAssociations: 5000, - }); - 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); - }); - // 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 () { - try { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - initialBalance: 0, - maxAutomaticTokenAssociations: 5001, - }); - 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"); - } - }); - }); - - //----------- 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - 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); - }); - // 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(); - - // select a staked node id between 0 and 6 for the test - const randomNodeId = Math.floor(Math.random() * 6) + 1; - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedNodeId: randomNodeId, - }); - 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; - **/ - try { - const invalidStakedId = "9.9.999999"; - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedAccountId: invalidStakedId, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "INVALID_STAKING_ID"); - return; - } - 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; - **/ - try { - // select a staked node id greater than 6 for the test - const invalidNodeId = 10; - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedNodeId: invalidNodeId, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "INVALID_STAKING_ID"); - return; - } - 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 () { - try { - // set a staked node Id with no input - const noInputStakedAccountId = ""; - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedAccountId: noInputStakedAccountId, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - // confirm error thrown for create with no input for staked account ID - } catch (err) { - return; - } - 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 () { - try { - // set a staked node Id with no input - const noInputStakedNodeId = ""; - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedNodeId: noInputStakedNodeId, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - // confirm error thrown for create with no input for staked account ID - } catch (err) { - return; - } - 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - stakedAccountId: stakedAccountId, - stakedNodeId: stakedNodeId, - }); - if (response.status === "NOT_IMPLEMENTED") this.skip(); - } catch (err) { - assert.equal(err.data.status, "INVALID_STAKING_ID"); - return; - } - }); - }); - - //----------- 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 - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - declineStakingReward: true, - }); - 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; - }); - // Create an account and leave decline rewards at default value - it("Creates an account and leave staking rewards at default value", async function () { - const response = await JSONRPCRequest("createAccount", { - publicKey: publicKey, - }); - 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; - }); - }); - - 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 - it("should create an account using an 'alias'", async function () { - // set initial balance to 100 Hbar - const initialBalance = 100; - - // 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()); - - // 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, - }); - 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(); - - // 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; - - 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"); - }); - }); - - return Promise.resolve(); -}); diff --git a/test/crypto-service/test_accountCreateTransaction.js b/test/crypto-service/test_accountCreateTransaction.js new file mode 100644 index 0000000..b5dc4a7 --- /dev/null +++ b/test/crypto-service/test_accountCreateTransaction.js @@ -0,0 +1,1025 @@ +import { JSONRPCRequest } from "../../client.js"; +import mirrorNodeClient from "../../mirrorNodeClient.js"; +import consensusInfoClient from "../../consensusInfoClient.js"; +import { setOperator } from "../../setup_Tests.js"; +import crypto from "crypto"; +import { assert, expect } from "chai"; + +/** + * Tests for AccountCreateTransaction + */ +describe("AccountCreateTransaction", function () { + // Tests should not take longer than 30 seconds to fully execute. + this.timeout(30000); + + // Each test should first establish the network to use, and then teardown the network when complete. + beforeEach(async function () { + await setOperator(process.env.OPERATOR_ACCOUNT_ID, process.env.OPERATOR_ACCOUNT_PRIVATE_KEY); + }); + afterEach(async function () { + await JSONRPCRequest("reset"); + }); + + describe("Key", function () { + async function verifyOnlyAccountCreation(accountId) { + // 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); + } + + 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", { + key: ed25519PublicKey.key, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); + + 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(); + + // Verify the account was created. + verifyOnlyAccountCreation(response.accountId); + }); + + 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", { + type: "keyList", + keys: [ + { + type: "ed25519PrivateKey" + }, + { + type: "ecdsaSecp256k1PublicKey" + }, + { + type: "ed25519PublicKey" + } + ] + }); + 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", { + type: "keyList", + keys: [ + { + type: "keyList", + keys: [ + { + type: "ecdsaSecp256k1PublicKey" + }, + { + type: "ecdsaSecp256k1PrivateKey" + } + ] + }, + { + type: "keyList", + keys: [ + { + type: "ecdsaSecp256k1PublicKey" + }, + { + type: "ed25519PublicKey" + } + ] + }, + { + type: "keyList", + keys: [ + { + type: "ed25519PrivateKey" + }, + { + type: "ecdsaSecp256k1PublicKey" + } + ] + } + ] + }); + 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) { + assert.equal(err.data.status, "KEY_REQUIRED"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + + it("(#8) Creates an account with an invalid key", async 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('hex') + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.code, -32603, "Internal error"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + }); + + describe("Initial Balance", function () { + async function verifyAccountCreationWithInitialBalance(accountId, initialBalance) { + // 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)); + } + + 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" + }); + 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", { + key: key.key, + initialBalance: initialBalance, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // 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", { + type: "ecdsaSecp256k1PublicKey" + }); + 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(); + + // Verify the account was created with 0 tinybars. + verifyAccountCreationWithInitialBalance(response.accountId, initialBalance); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // 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", { + key: key.key, + initialBalance: -1, + }); + if (response.status == "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_INITIAL_BALANCE"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + + it("(#4) Creates an account with an initial balance higher than the operator account balance", async function () { + // Get the operator account 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", { + type: "ecdsaSecp256k1PrivateKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + 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", { + key: key.key, + initialBalance: operatorAccountBalance + 1, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INSUFFICIENT_PAYER_BALANCE"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + }); + + describe("Receiver Signature Required", function () { + async function verifyAccountCreationWithReceiverSignatureRequired(accountId, receiverSignatureRequired) { + // 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); + } + + 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: "ed25519PrivateKey" + }); + if (privateKey.status === "NOT_IMPLEMENTED") this.skip(); + + // Generate a valid public key from the generated private key. + const publicKey = await JSONRPCRequest("generateKey", { + type: "ed25519PublicKey", + fromKey: 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: publicKey.key, + receiverSignatureRequired: receiverSignatureRequired, + commonTransactionParams: { + signers: [ + privateKey.key + ] + } + }); + 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 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. + const receiverSignatureRequired = false; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + receiverSignatureRequired: receiverSignatureRequired, + }); + 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 key for the account. + const key = await JSONRPCRequest("generateKey", { + type: "ed25519PublicKey" + }); + 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; + } + + // The test failed, no error was thrown. + 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'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); + } + + 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" + }); + 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", { + key: key.key, + autoRenewPeriod: autoRenewPeriodSeconds, + }); + 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); + }); + + 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" + }); + 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; + } + + // The test failed, no error was thrown. + 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", { + type: "ecdsaSecp256k1PublicKey" + }); + 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(); + + // Verify the account was created with an auto-renew period set to 30 days. + 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", { + type: "ed25519PublicKey" + }); + 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; + } + + // The test failed, no error was thrown. + 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", { + type: "ecdsaSecp256k1PrivateKey" + }); + 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(); + + // Verify the account was created with an auto-renew period set to 90ish days. + 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", { + type: "ed25519PrivateKey" + }); + 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; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + }); + + describe("Memo", async function () { + async function verifyAccountCreationWithMemo(accountId, memo) { + // 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); + } + + 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" + }); + 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(); + + // Verify the account was created with the memo set to "testmemo". + 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", { + type: "ed25519PublicKey" + }); + 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(); + + // Verify the account was created with an empty memo. + 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", { + type: "ecdsaSecp256k1PrivateKey" + }); + 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(); + + // 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); + }); + + 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" + }); + 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; + } + + // 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 () { + 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 100", async function () { + // 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 with the max automatic token associations set to 100. + const maxAutoTokenAssociations = 100; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + maxAutoTokenAssociations: maxAutoTokenAssociations, + commonTransactionParams: { + maxTransactionFee: 100000000000 + } + }); + 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 0", async function () { + // Generate a valid key for the account. + 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. + 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) + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the max automatic token associations set to the maximum value. + const maxAutoTokenAssociations = 5000; + 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 5000. + verifyAccountCreationWithMaxAutoTokenAssociations(response.accountId, maxAutoTokenAssociations) + }); + + 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" + }); + 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. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + maxAutoTokenAssociations: 5001 + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_MAX_AUTO_ASSOCIATIONS"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + }); + + 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", { + 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. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + stakedAccountId: process.env.OPERATOR_ACCOUNT_ID, + }); + 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); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with the staked node ID set to the node's node ID. + const stakedNodeId = 0; + const response = await JSONRPCRequest("createAccount", { + key: key.key, + stakedNodeId: stakedNodeId, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created with the staked node ID equal to 0. + 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", { + type: "ed25519PrivateKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // 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", { + key: key.key, + stakedAccountId: "123.456.789", + }); + 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"); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // 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", { + key: key.key, + stakedNodeId: 123456789, + }); + 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"); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // 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", { + key: key.key, + stakedAccountId: "", + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.code, -32603, "Internal error"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // 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", { + key: key.key, + stakedNodeId: -100, + }); + 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"); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Attempt to create an account with a staked account ID and a staked node ID. + const stakedNodeId = 0; + 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); + }); + }); + + 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", { + type: "ed25519PublicKey" + }); + 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", { + key: key.key, + declineStakingReward: declineStakingReward, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created with decline staking rewards. + verifyAccountCreationWithDeclineRewards(response.accountId, declineStakingReward); + }); + + 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" + }); + 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", { + key: key.key, + declineStakingReward: declineStakingReward, + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created without declining staking rewards. + verifyAccountCreationWithDeclineRewards(response.accountId, declineStakingReward); + }); + }); + + 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); + } + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // Generate the ECDSAsecp256k1 private key of the alias for the account. + const ecdsaSecp256k1PrivateKey = await JSONRPCRequest("generateKey", { + 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 + }); + + // Attempt to create an account with the alias. + const response = await JSONRPCRequest("createAccount", { + key: key.key, + alias: alias.key, + commonTransactionParams: { + signers: [ + ecdsaSecp256k1PrivateKey.key + ] + } + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + + // Verify the account was created with the generated alias. + verifyAccountCreationWithAlias(response.accountId, alias); + }); + + 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" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + // 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: alias.key + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_SIGNATURE"); + return; + } + + // The test failed, no error was thrown. + 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", { + type: "ed25519PublicKey" + }); + if (key.status === "NOT_IMPLEMENTED") this.skip(); + + try { + // 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: "0x" + crypto.randomBytes(20).toString('hex').toUpperCase() + }); + if (response.status === "NOT_IMPLEMENTED") this.skip(); + } catch (err) { + assert.equal(err.data.status, "INVALID_SIGNATURE"); + return; + } + + // The test failed, no error was thrown. + assert.fail("Should throw an error"); + }); + }); + + return Promise.resolve(); +});