-
Notifications
You must be signed in to change notification settings - Fork 98
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
test(2.8): ibc hooks #212
test(2.8): ibc hooks #212
Changes from all commits
8f36b22
ab5afac
baf15e3
de74832
2f0c6cd
9995f15
6ec43a5
ce9ba83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,5 @@ module.exports = { | |
testMatch: ['**/*.test.ts'], | ||
verbose: true, | ||
testTimeout: 30000, | ||
maxConcurrency: 3, | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
export const SAFE_VOTING_PERIOD_TIME = 4100; | ||
export const SAFE_IBC_TRANSFER = 4100; | ||
export const SAFE_BLOCK_INCLUSION_TIME = 1100; | ||
|
||
export const blockInclusion = () => new Promise((resolve) => setTimeout(() => resolve(SAFE_BLOCK_INCLUSION_TIME), SAFE_BLOCK_INCLUSION_TIME)); | ||
export const votingPeriod = () => new Promise((resolve) => setTimeout(() => resolve(SAFE_VOTING_PERIOD_TIME), SAFE_VOTING_PERIOD_TIME)); | ||
export const ibcTransfer = () => new Promise((resolve) => setTimeout(() => resolve(SAFE_IBC_TRANSFER), SAFE_IBC_TRANSFER)); | ||
export const votingPeriod = () => new Promise((resolve) => setTimeout(() => resolve(SAFE_VOTING_PERIOD_TIME), SAFE_VOTING_PERIOD_TIME)); |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { getMnemonics } from "../../helpers/mnemonics"; | ||
import { getLCDClient } from "../../helpers/lcd.connection"; | ||
import { StakeAuthorization, MsgGrantAuthorization, AuthorizationGrant, Coin, MsgExecAuthorized, MsgDelegate } from "@terra-money/feather.js"; | ||
import { AuthorizationType } from "@terra-money/terra.proto/cosmos/staking/v1beta1/authz"; | ||
import moment from "moment"; | ||
import { blockInclusion } from "../../helpers/const"; | ||
|
||
describe("Authz Module (https://github.com/terra-money/cosmos-sdk/tree/release/v0.47.x/x/authz)", () => { | ||
const LCD = getLCDClient(); | ||
const accounts = getMnemonics(); | ||
// Accounts used in chain2, which means that | ||
// will not cause conflicts with txs nonces | ||
const granterWallet = LCD.chain2.wallet(accounts.feeshareMnemonic); | ||
const granteeWallet = LCD.chain2.wallet(accounts.pobMnemonic); | ||
const granterAddr = accounts.feeshareMnemonic.accAddress("terra"); | ||
const granteeAddr = accounts.pobMnemonic.accAddress("terra"); | ||
const val2Addr = accounts.val2.valAddress("terra"); | ||
|
||
test('Must register the granter', async () => { | ||
let tx = await granterWallet.createAndSignTx({ | ||
msgs: [new MsgGrantAuthorization( | ||
granterAddr, | ||
granteeAddr, | ||
new AuthorizationGrant( | ||
new StakeAuthorization( | ||
AuthorizationType.AUTHORIZATION_TYPE_DELEGATE, | ||
Coin.fromString("1000000uluna"), | ||
), | ||
moment().add(1, "hour").toDate(), | ||
), | ||
)], | ||
chainID: "test-2", | ||
}); | ||
let result = await LCD.chain2.tx.broadcastSync(tx, "test-2"); | ||
await blockInclusion(); | ||
|
||
// Check the MsgGrantAuthorization executed as expected | ||
let txResult = await LCD.chain2.tx.txInfo(result.txhash, "test-2") as any; | ||
expect(txResult.logs[0].events) | ||
.toStrictEqual([{ | ||
"type": "message", | ||
"attributes": [{ | ||
"key": "action", | ||
"value": "/cosmos.authz.v1beta1.MsgGrant" | ||
}, { | ||
"key": "sender", | ||
"value": "terra120rzk7n6cd2vufkmwrat34adqh0rgca9tkyfe5" | ||
}, { | ||
"key": "module", | ||
"value": "authz" | ||
}] | ||
}, { | ||
"type": "cosmos.authz.v1beta1.EventGrant", | ||
"attributes": [{ | ||
"key": "grantee", | ||
"value": "\"terra1v0eee20gjl68fuk0chyrkch2z7suw2mhg3wkxf\"" | ||
}, { | ||
"key": "granter", | ||
"value": "\"terra120rzk7n6cd2vufkmwrat34adqh0rgca9tkyfe5\"" | ||
}, { | ||
"key": "msg_type_url", | ||
"value": "\"/cosmos.staking.v1beta1.MsgDelegate\"" | ||
}] | ||
}]); | ||
}); | ||
|
||
describe("Grantee must execute", () => { | ||
test("delegation on belhalf of granter", async () => { | ||
let tx = await granteeWallet.createAndSignTx({ | ||
msgs: [new MsgExecAuthorized( | ||
granteeAddr, | ||
[new MsgDelegate( | ||
granterAddr, | ||
val2Addr, | ||
Coin.fromString("1000000uluna"), | ||
)] | ||
)], | ||
chainID: "test-2", | ||
}); | ||
let result = await LCD.chain2.tx.broadcastSync(tx, "test-2"); | ||
await blockInclusion(); | ||
|
||
let txResult = await LCD.chain2.tx.txInfo(result.txhash, "test-2") as any; | ||
let eventsList = txResult.logs[0].events; | ||
let latestIndex = eventsList.length - 1; | ||
|
||
expect(eventsList[0]) | ||
.toStrictEqual({ | ||
"type": "message", | ||
"attributes": [{ | ||
"key": "action", | ||
"value": "/cosmos.authz.v1beta1.MsgExec" | ||
}, { | ||
"key": "sender", | ||
"value": "terra1v0eee20gjl68fuk0chyrkch2z7suw2mhg3wkxf" | ||
}, { | ||
"key": "module", | ||
"value": "authz" | ||
}] | ||
}); | ||
expect(eventsList[1]) | ||
.toStrictEqual({ | ||
"type": "cosmos.authz.v1beta1.EventRevoke", | ||
"attributes": [{ | ||
"key": "grantee", | ||
"value": "\"terra1v0eee20gjl68fuk0chyrkch2z7suw2mhg3wkxf\"" | ||
}, { | ||
"key": "granter", | ||
"value": "\"terra120rzk7n6cd2vufkmwrat34adqh0rgca9tkyfe5\"" | ||
}, { | ||
"key": "msg_type_url", | ||
"value": "\"/cosmos.staking.v1beta1.MsgDelegate\"" | ||
}] | ||
}); | ||
|
||
expect(eventsList[latestIndex]) | ||
.toStrictEqual({ | ||
"type": "delegate", | ||
"attributes": [{ | ||
"key": "validator", | ||
"value": "terravaloper1llgzglr9yyy4gyjh8p5kepgm5wyl358de47rqk" | ||
}, { | ||
"key": "delegator", | ||
"value": "terra120rzk7n6cd2vufkmwrat34adqh0rgca9tkyfe5" | ||
}, { | ||
"key": "amount", | ||
"value": "1000000uluna" | ||
}, { | ||
"key": "new_shares", | ||
"value": "1000000.000000000000000000" | ||
}, { | ||
"key": "authz_msg_index", | ||
"value": "0" | ||
}] | ||
}); | ||
}); | ||
}) | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import { Coin, Coins, MsgInstantiateContract, MsgStoreCode, MsgTransfer } from "@terra-money/feather.js"; | ||
import { deriveIbcHooksSender } from "@terra-money/feather.js/dist/core/ibc-hooks"; | ||
import { ibcTransfer, getMnemonics, getLCDClient, blockInclusion } from "../../helpers"; | ||
import fs from "fs"; | ||
import path from 'path'; | ||
import moment from "moment"; | ||
// import { Height } from "@terra-money/feather.js/dist/core/ibc/core/client/Height"; | ||
|
||
describe("IbcHooks Module (github.com/cosmos/ibc-apps/modules/ibc-hooks/v7) ", () => { | ||
// Prepare the LCD and wallets. chain1Wallet is the one that will | ||
// deploy the contract on chain 1 and chain2Wallet will be used | ||
// to send IBC messages from chain 2 to interact with the contract. | ||
const LCD = getLCDClient(); | ||
const accounts = getMnemonics(); | ||
const chain1Wallet = LCD.chain1.wallet(accounts.ibcHooksMnemonic); | ||
const chain2Wallet = LCD.chain2.wallet(accounts.ibcHooksMnemonic); | ||
const walletAddress = accounts.ibcHooksMnemonic.accAddress("terra"); | ||
const derivedHooksWalletAddress = deriveIbcHooksSender("channel-0", walletAddress, "terra"); | ||
let contractAddress: string; | ||
|
||
// Read the counter contract, store on chain, | ||
// instantiate to be used in the following tests | ||
// and finally save the contract address. | ||
beforeAll(async () => { | ||
let tx = await chain1Wallet.createAndSignTx({ | ||
msgs: [new MsgStoreCode( | ||
walletAddress, | ||
fs.readFileSync(path.join(__dirname, "/../../contracts/counter.wasm")).toString("base64"), | ||
)], | ||
chainID: "test-1", | ||
}); | ||
|
||
let result = await LCD.chain1.tx.broadcastSync(tx, "test-1"); | ||
await blockInclusion(); | ||
let txResult = await LCD.chain1.tx.txInfo(result.txhash, "test-1") as any; | ||
let codeId = Number(txResult.logs[0].events[1].attributes[1].value); | ||
expect(codeId).toBeDefined(); | ||
|
||
const msgInstantiateContract = new MsgInstantiateContract( | ||
walletAddress, | ||
walletAddress, | ||
codeId, | ||
{ count: 0 }, | ||
Coins.fromString("1uluna"), | ||
"counter contract " + Math.random(), | ||
); | ||
|
||
tx = await chain1Wallet.createAndSignTx({ | ||
msgs: [msgInstantiateContract], | ||
chainID: "test-1", | ||
}); | ||
result = await LCD.chain1.tx.broadcastSync(tx, "test-1"); | ||
await blockInclusion(); | ||
txResult = await LCD.chain1.tx.txInfo(result.txhash, "test-1") as any; | ||
contractAddress = txResult.logs[0].events[4].attributes[0].value; | ||
expect(contractAddress).toBeDefined(); | ||
}) | ||
|
||
// This test send an IBC message to **chain-2** which is **relayed** to **chain-1** | ||
// The flow represents a successful MsgTransfe with IBCHooks request to the smart contrat. | ||
describe("Should execute hooks from chain 2 to chain 1", () => { | ||
test('must increment the counter successfully', async () => { | ||
let tx = await chain2Wallet.createAndSignTx({ | ||
msgs: [ | ||
new MsgTransfer( | ||
"transfer", | ||
"channel-0", | ||
Coin.fromString("1uluna"), | ||
walletAddress, | ||
contractAddress, | ||
undefined, | ||
moment.utc().add(1, "minute").unix().toString() + "000000000", | ||
`{"wasm":{"contract": "${contractAddress}" ,"msg": {"increment": {}}}}` | ||
), | ||
new MsgTransfer( | ||
"transfer", | ||
"channel-0", | ||
Coin.fromString("1uluna"), | ||
walletAddress, | ||
contractAddress, | ||
undefined, | ||
moment.utc().add(1, "minute").unix().toString() + "000000000", | ||
`{"wasm":{"contract": "${contractAddress}" ,"msg": {"increment": {}}}}` | ||
), | ||
], | ||
chainID: "test-2", | ||
}); | ||
let result = await LCD.chain2.tx.broadcastSync(tx, "test-2"); | ||
await ibcTransfer(); | ||
let txResult = await LCD.chain2.tx.txInfo(result.txhash, "test-2") as any; | ||
expect(txResult.logs[0].eventsByType.ibc_transfer) | ||
.toStrictEqual({ | ||
"sender": [walletAddress], | ||
"receiver": [contractAddress], | ||
"amount": ["1"], | ||
"denom": ["uluna"], | ||
"memo": [`{"wasm":{"contract": "${contractAddress}" ,"msg": {"increment": {}}}}`] | ||
}); | ||
// query to validate the count is 1 | ||
let res = await LCD.chain1.wasm.contractQuery( | ||
contractAddress, | ||
{ "get_count": { "addr": derivedHooksWalletAddress } } | ||
); | ||
expect(res).toStrictEqual({ "count": 1 }); | ||
|
||
// query to validate the count is 1 | ||
res = await LCD.chain1.wasm.contractQuery( | ||
contractAddress, | ||
{ "get_total_funds": { "addr": derivedHooksWalletAddress } } | ||
); | ||
expect(res).toStrictEqual({ | ||
"total_funds": [{ | ||
"denom": "ibc/4627AD2524E3E0523047E35BB76CC90E37D9D57ACF14F0FCBCEB2480705F3CB8", | ||
"amount": "2" | ||
}] | ||
}); | ||
}); | ||
}) | ||
|
||
// This test send an IBC message to **chain-1** which is **relayed** to **chain-2** | ||
// with an acknowledgement callback for chain-1. | ||
// The flow represents a successful MsgTransfer with callback to the smart contract. | ||
describe("Must execute hooks callback from chain 1 to chain 2", () => { | ||
test('increment the counter on successful callback', async () => { | ||
let tx = await chain1Wallet.createAndSignTx({ | ||
msgs: [ | ||
new MsgTransfer( | ||
"transfer", | ||
"channel-0", | ||
Coin.fromString("1uluna"), | ||
walletAddress, | ||
derivedHooksWalletAddress, | ||
undefined, | ||
moment.utc().add(10, "second").unix().toString() + "000000000", | ||
`{"ibc_callback": "${contractAddress}"}` | ||
), | ||
new MsgTransfer( | ||
"transfer", | ||
"channel-0", | ||
Coin.fromString("1uluna"), | ||
walletAddress, | ||
derivedHooksWalletAddress, | ||
undefined, | ||
moment.utc().add(10, "second").unix().toString() + "000000000", | ||
`{"ibc_callback": "${contractAddress}"}` | ||
), | ||
], | ||
chainID: "test-1", | ||
}); | ||
await LCD.chain1.tx.broadcastSync(tx, "test-1") | ||
await ibcTransfer(); | ||
await blockInclusion(); | ||
let res = await LCD.chain1.wasm.contractQuery( | ||
contractAddress, | ||
{ "get_count": { "addr": contractAddress } } | ||
); | ||
expect(res).toStrictEqual({ "count": 2 }); | ||
res = await LCD.chain1.wasm.contractQuery( | ||
contractAddress, | ||
{ "get_total_funds": { "addr": contractAddress } } | ||
); | ||
expect(res).toStrictEqual({ "total_funds": [] }); | ||
}); | ||
}) | ||
|
||
|
||
// This test send an IBC message to **chain-1** which is **NOT relayed** because of timeout. | ||
// The flow represents a failed MsgTransfer with callback to the smart contract. | ||
describe("Must execute hooks callback on chain 1", () => { | ||
test('with a timeout of -1 second', async () => { | ||
let tx = await chain1Wallet.createAndSignTx({ | ||
msgs: [ | ||
new MsgTransfer( | ||
"transfer", | ||
"channel-0", | ||
Coin.fromString("1uluna"), | ||
walletAddress, | ||
derivedHooksWalletAddress, | ||
undefined, | ||
moment.utc().add(-1, "second").unix().toString() + "000000000", | ||
`{"ibc_callback": "${contractAddress}"}` | ||
), | ||
new MsgTransfer( | ||
"transfer", | ||
"channel-0", | ||
Coin.fromString("1uluna"), | ||
walletAddress, | ||
derivedHooksWalletAddress, | ||
undefined, | ||
moment.utc().add(-1, "second").unix().toString() + "000000000", | ||
`{"ibc_callback": "${contractAddress}"}` | ||
), | ||
], | ||
chainID: "test-1", | ||
}); | ||
await LCD.chain1.tx.broadcastSync(tx, "test-1") | ||
await ibcTransfer(); | ||
let res = await LCD.chain1.wasm.contractQuery( | ||
contractAddress, | ||
{ "get_count": { "addr": contractAddress } } | ||
); | ||
expect(res).toStrictEqual({ "count": 22 }); | ||
res = await LCD.chain1.wasm.contractQuery( | ||
contractAddress, | ||
{ "get_total_funds": { "addr": contractAddress } } | ||
); | ||
expect(res).toStrictEqual({ "total_funds": [] }); | ||
}) | ||
}); | ||
}); |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@emidev98 is this deletion expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@javiersuweijie yes because the folder structure was created by mistake as wasmd instead of wasm