Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTS precompile token key management tests #464

Merged
merged 30 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cb1b198
test: add placeholder tests for key management
Ivo-Yankov Aug 23, 2022
74f6382
chore: revert unnecessary changes
Ivo-Yankov Aug 23, 2022
f6ec88f
chore: revert unnecessary changes
Ivo-Yankov Aug 23, 2022
22915ff
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Aug 23, 2022
2023a21
chore: update comment
Ivo-Yankov Aug 24, 2022
8acf39f
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Sep 5, 2022
ebcc0a7
test: enable pending tests
Ivo-Yankov Sep 5, 2022
974dea8
Enable tests
Nana-EC Sep 7, 2022
9ba280d
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Sep 12, 2022
e78fbd7
wip: enable and refactor token management tests
Ivo-Yankov Sep 12, 2022
dc34cf5
test: hts precompile updateTokenKeys
Ivo-Yankov Sep 13, 2022
86b2314
fix: trying to fix failing tests
Ivo-Yankov Sep 14, 2022
6a9a5c6
nit: reduce hbar usage
Ivo-Yankov Sep 14, 2022
09d905f
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Sep 14, 2022
cd5fefe
resolve conflicts
Ivo-Yankov Sep 14, 2022
fb4e87e
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Sep 15, 2022
018d077
chore: revert accidental changes
Ivo-Yankov Sep 15, 2022
35b8a1f
chore: revert accidental changes
Ivo-Yankov Sep 15, 2022
e6dd590
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Sep 16, 2022
86fa2e8
chore: resolve conflicts
Ivo-Yankov Sep 16, 2022
66e4368
Merge branch 'main' into 409-hts-precompile-token-key-tests
Ivo-Yankov Sep 18, 2022
76c93f3
chore: resolve conflicts
Ivo-Yankov Sep 18, 2022
b82733b
test: enable pending tests
Ivo-Yankov Sep 19, 2022
4cee6b4
test: create a new hts precompile test suite
Ivo-Yankov Sep 19, 2022
4dc9abe
test: remove duplicated tests
Ivo-Yankov Sep 19, 2022
f57f0b8
fix: add npm script for new test suite
Ivo-Yankov Sep 19, 2022
fcb283e
chore: refactor hts precompile test structure
Ivo-Yankov Sep 20, 2022
90cb10f
fix: CI acceptance filters
Ivo-Yankov Sep 20, 2022
a937d19
nit: test titles
Ivo-Yankov Sep 20, 2022
3e5cbef
chore: rearrange functions in contracts
Ivo-Yankov Sep 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 61 additions & 6 deletions packages/server/tests/acceptance/htsPrecompile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ describe('@htsprecompile Acceptance Tests', async function () {
let HTSTokenWithCustomFeesContractAddress;

this.beforeAll(async () => {
accounts[0] = await servicesNode.createAliasAccount(170, relay.provider);
accounts[0] = await servicesNode.createAliasAccount(200, relay.provider);
accounts[1] = await servicesNode.createAliasAccount(30, relay.provider);
accounts[2] = await servicesNode.createAliasAccount(30, relay.provider);

// alow mirror node a 2 full record stream write windows (2 sec) and a buffer to persist setup details
// allow mirror node a 2 full record stream write windows (2 sec) and a buffer to persist setup details
await new Promise(r => setTimeout(r, 5000));
await mirrorNode.get(`/accounts/${accounts[0].accountId}`);
await mirrorNode.get(`/accounts/${accounts[1].accountId}`);
await mirrorNode.get(`/accounts/${accounts[2].accountId}`);

BaseHTSContractAddress = await deployBaseHTSContract();
HTSTokenContractAddress = await createHTSToken();
Expand Down Expand Up @@ -499,7 +502,7 @@ describe('@htsprecompile Acceptance Tests', async function () {
{
const tx = await baseHTSContract.wipeTokenAccountNFTPublic(NftHTSTokenContractAddress, accounts[1].wallet.address, serials, { gasLimit: 1_000_000 });
const { responseCode } = (await tx.wait()).events.filter(e => e.event === 'ResponseCode')[0].args;
console.log(`Wipe response: ${responseCode}`);
expect(responseCode).to.equal(TX_SUCCESS_CODE);
}

// Get token info after
Expand Down Expand Up @@ -660,7 +663,8 @@ describe('@htsprecompile Acceptance Tests', async function () {
second: AUTO_RENEW_SECOND,
autoRenewAccount: BaseHTSContractAddress,
autoRenewPeriod: NEW_AUTO_RENEW_PERIOD
}
};

const updateTokenExpiryInfoTx = (await baseHTSContract.updateTokenExpiryInfoPublic(NftHTSTokenContractAddress, expiryInfo, { gasLimit: 1_000_000 }));
const updateExpiryInfoResponseCode = (await updateTokenExpiryInfoTx.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode;

Expand Down Expand Up @@ -843,7 +847,7 @@ describe('@htsprecompile Acceptance Tests', async function () {
}
});

it('should fail to swap approved non-fungible tokens', async function() {
it('should fail to swap approved non-fungible tokens', async function() {
const txApprove1 = await baseHTSContract.setApprovalForAllPublic(NftHTSTokenContractAddress, accounts[1].wallet.address, true, { gasLimit: 1_000_000 });
expect((await txApprove1.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);

Expand All @@ -867,7 +871,7 @@ describe('@htsprecompile Acceptance Tests', async function () {

try{
const txXfer = await baseHTSContract.cryptoTransferPublic(tokenTransferList);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
expect((await txXfer.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode).to.equal(TX_SUCCESS_CODE);
} catch (error: any) {
expect(error.code).to.equal("CALL_EXCEPTION");
expect(error.reason).to.equal("transaction failed");
Expand Down Expand Up @@ -904,4 +908,55 @@ describe('@htsprecompile Acceptance Tests', async function () {
}
});
});


describe('HTS Precompile Key management Tests', async function() {
it('should be able to execute getTokenKey', async function() {
const tx = await baseHTSContract.getTokenKeyPublic(HTSTokenContractAddress, 2);
const result = await tx.wait();
const { responseCode } = result.events.filter(e => e.event === 'ResponseCode')[0].args;
expect(responseCode).to.equal(TX_SUCCESS_CODE);
const { key } = result.events.filter(e => e.event === 'TokenKey')[0].args;

expect(key).to.exist;
expect(key.inheritAccountKey).to.eq(false);
expect(key.contractId).to.eq('0x0000000000000000000000000000000000000000');
expect(key.ed25519).to.eq('0x');
expect(key.ECDSA_secp256k1).to.exist;
expect(key.delegatableContractId).to.eq('0x0000000000000000000000000000000000000000');
});

it('should be able to execute updateTokenKeys', async function() {
// Get key value before update
const getKeyTx = await baseHTSContract.getTokenKeyPublic(HTSTokenContractAddress, 2);
const originalKey = (await getKeyTx.wait()).events.filter(e => e.event === 'TokenKey')[0].args.key;
const updateKey = [
false,
'0x0000000000000000000000000000000000000000',
'0x',
'0x03dfcc94dfd843649cc594ada5ac6627031454602aa190223f996de25a05828f36',
'0x0000000000000000000000000000000000000000',
];

// Update keys. After updating there should be only one key with keyValue = 6. Other keys are removed
const updateTx = await baseHTSContract.updateTokenKeysPublic(HTSTokenContractAddress, [[ 2, updateKey]]);
const updateResponseCode = (await updateTx.wait()).events.filter(e => e.event === 'ResponseCode')[0].args.responseCode;
expect(Number(updateResponseCode.toString())).to.equal(TX_SUCCESS_CODE);

// Assert updated key
const tx = await baseHTSContract.getTokenKeyPublic(HTSTokenContractAddress, 2);
const result = await tx.wait();
const {responseCode} = result.events.filter(e => e.event === 'ResponseCode')[0].args;
expect(Number(responseCode.toString())).to.equal(TX_SUCCESS_CODE);
const updatedKey = result.events.filter(e => e.event === 'TokenKey')[0].args.key;

expect(updatedKey).to.exist;
expect(updatedKey.inheritAccountKey).to.eq(updateKey[0]);
expect(updatedKey.contractId).to.eq(updateKey[1]);
expect(updatedKey.ed25519).to.eq(updateKey[2]);
expect(updatedKey.ECDSA_secp256k1).to.eq(updateKey[3]);
expect(updatedKey.delegatableContractId).to.eq(updateKey[4]);
expect(updatedKey.ECDSA_secp256k1).to.not.eq(originalKey.ECDSA_secp256k1);
});
});
});
187 changes: 173 additions & 14 deletions packages/server/tests/contracts/BaseHTS.json

Large diffs are not rendered by default.

28 changes: 27 additions & 1 deletion packages/server/tests/contracts/BaseHTS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ contract BaseHTS is FeeHelper {
string symbol = "tokenSymbol";
string memo = "memo";
uint64 initialTotalSupply = 1000;
int64 maxSupply = 1000;
uint32 maxSupply = 1000;
uint32 decimals = 8;
bool freezeDefaultStatus = false;

Expand All @@ -30,6 +30,7 @@ contract BaseHTS is FeeHelper {
event TokenCustomFees(IHederaTokenService.FixedFee[] fixedFees, IHederaTokenService.FractionalFee[] fractionalFees, IHederaTokenService.RoyaltyFee[] royaltyFees);
event TokenDefaultFreezeStatus(bool defaultFreezeStatus);
event TokenDefaultKycStatus(bool defaultKycStatus);
event TokenKey(IHederaTokenService.KeyValue key);
event KycGranted(bool kycGranted);
event TokenExpiryInfo(IHederaTokenService.Expiry expiryInfo);

Expand Down Expand Up @@ -450,4 +451,29 @@ contract BaseHTS is FeeHelper {
revert();
}
}

function updateTokenKeysPublic(address token, IHederaTokenService.TokenKey[] memory keys)
public returns (int64 responseCode){

(responseCode) = HederaTokenService.updateTokenKeys(token, keys);

emit ResponseCode(responseCode);

if(responseCode != HederaResponseCodes.SUCCESS) {
revert();
}
}

function getTokenKeyPublic(address token, uint keyType)
public returns (int64 responseCode, IHederaTokenService.KeyValue memory key){
(responseCode, key) = HederaTokenService.getTokenKey(token, keyType);

emit ResponseCode(responseCode);

if(responseCode != HederaResponseCodes.SUCCESS) {
revert();
}

emit TokenKey(key);
}
}
27 changes: 26 additions & 1 deletion packages/server/tests/contracts/HederaTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ abstract contract HederaTokenService is HederaResponseCodes {
/// Initiates a Token Transfer
/// @param tokenTransfers the list of transfers to do
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
function cryptoTransfer(IHederaTokenService.TokenTransferList[] memory tokenTransfers) internal
function cryptoTransfer(IHederaTokenService.TokenTransferList[] memory tokenTransfers) internal
returns (int responseCode)
{
(bool success, bytes memory result) = precompileAddress.call(
Expand Down Expand Up @@ -556,6 +556,31 @@ abstract contract HederaTokenService is HederaResponseCodes {
(responseCode) = success ? abi.decode(result, (int32)) : HederaResponseCodes.UNKNOWN;
}

/// Operation to update token expiry info
/// @param token The token address
/// @param keys The token keys
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
function updateTokenKeys(address token, IHederaTokenService.TokenKey[] memory keys)
internal returns (int64 responseCode){
(bool success, bytes memory result) = precompileAddress.call(
abi.encodeWithSelector(IHederaTokenService.updateTokenKeys.selector, token, keys));
(responseCode) = success ? abi.decode(result, (int32)) : HederaResponseCodes.UNKNOWN;
}

/// Query token KeyValue
/// @param token The token address to check
/// @param keyType The keyType of the desired KeyValue
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
/// @return key KeyValue info for key of type `keyType`
function getTokenKey(address token, uint keyType)
internal returns (int64 responseCode, IHederaTokenService.KeyValue memory key){
(bool success, bytes memory result) = precompileAddress.call(
abi.encodeWithSelector(IHederaTokenService.getTokenKey.selector, token, keyType));
IHederaTokenService.KeyValue memory defaultKeyValueInfo;
(responseCode, key) = success ? abi.decode(result, (int32,IHederaTokenService.KeyValue) ) : (HederaResponseCodes.UNKNOWN, defaultKeyValueInfo);
}


/// Operation to get token expiry info
/// @param token The token address
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
Expand Down
119 changes: 119 additions & 0 deletions packages/server/tests/contracts/IHederaTokenService.json
Original file line number Diff line number Diff line change
Expand Up @@ -2086,6 +2086,62 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "keyType",
"type": "uint256"
}
],
"name": "getTokenKey",
"outputs": [
{
"internalType": "int64",
"name": "responseCode",
"type": "int64"
},
{
"components": [
{
"internalType": "bool",
"name": "inheritAccountKey",
"type": "bool"
},
{
"internalType": "address",
"name": "contractId",
"type": "address"
},
{
"internalType": "bytes",
"name": "ed25519",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "ECDSA_secp256k1",
"type": "bytes"
},
{
"internalType": "address",
"name": "delegatableContractId",
"type": "address"
}
],
"internalType": "struct IHederaTokenService.KeyValue",
"name": "key",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -2528,6 +2584,69 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"components": [
{
"internalType": "uint256",
"name": "keyType",
"type": "uint256"
},
{
"components": [
{
"internalType": "bool",
"name": "inheritAccountKey",
"type": "bool"
},
{
"internalType": "address",
"name": "contractId",
"type": "address"
},
{
"internalType": "bytes",
"name": "ed25519",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "ECDSA_secp256k1",
"type": "bytes"
},
{
"internalType": "address",
"name": "delegatableContractId",
"type": "address"
}
],
"internalType": "struct IHederaTokenService.KeyValue",
"name": "key",
"type": "tuple"
}
],
"internalType": "struct IHederaTokenService.TokenKey[]",
"name": "keys",
"type": "tuple[]"
}
],
"name": "updateTokenKeys",
"outputs": [
{
"internalType": "int64",
"name": "responseCode",
"type": "int64"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
17 changes: 17 additions & 0 deletions packages/server/tests/contracts/IHederaTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,15 @@ interface IHederaTokenService {
/// @param token The ID of the token as a solidity address
function getTokenInfo(address token) external returns (int responseCode, TokenInfo memory tokenInfo);

/// Query token KeyValue
/// @param token The token address to check
/// @param keyType The keyType of the desired KeyValue
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
/// @return key KeyValue info for key of type `keyType`
function getTokenKey(address token, uint keyType)
external
returns (int64 responseCode, KeyValue memory key);

/// Retrieves non-fungible specific token info for a given NFT
/// @param token The ID of the token as a solidity address
/// @param serialNumber The serial number of the NFT for which to retrieve information
Expand Down Expand Up @@ -617,4 +626,12 @@ interface IHederaTokenService {
/// @param token The token address
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
function updateTokenExpiryInfo(address token, Expiry memory expiryInfo) external returns (int responseCode);

/// Operation to update token keys
/// @param token The token address
/// @param keys The token keys
/// @return responseCode The response code for the status of the request. SUCCESS is 22.
function updateTokenKeys(address token, TokenKey[] memory keys)
external
returns (int64 responseCode);
}