From 97f39df4e75a1a022340a2e71a21fc8ee852b506 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 30 Aug 2022 10:31:22 +0200 Subject: [PATCH 01/13] Implement determinstic contract address generation --- packages/cli/examples/contract_addresses.ts | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 packages/cli/examples/contract_addresses.ts diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts new file mode 100644 index 0000000000..9e422cac20 --- /dev/null +++ b/packages/cli/examples/contract_addresses.ts @@ -0,0 +1,57 @@ +import { fromBech32, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; +import { Random, Sha256, sha256 } from "@cosmjs/crypto"; +import { Uint64 } from "@cosmjs/math"; + +/** + * The "Basic Address" Hash from + * https://github.com/cosmos/cosmos-sdk/blob/v0.45.8/docs/architecture/adr-028-public-key-addresses.md + */ +function hash(type: Uint8Array, key: Uint8Array): Uint8Array { + return new Sha256(sha256(type)).update(key).digest(); +} + +/** See https://github.com/CosmWasm/wasmd/issues/942 */ +function deterministicContractAddress(codeId: number, creator: string, label: string, prefix: string) { + const codeIdUint64 = Uint64.fromNumber(codeId); + const creatorData = fromBech32(creator).data; + const creatorDataLen = Uint64.fromNumber(creatorData.length); + + const key1 = new Uint8Array([ + ...codeIdUint64.toBytesBigEndian(), + ...creatorDataLen.toBytesBigEndian(), + ...creatorData, + ...toUtf8(label), + ]); + const key2 = new Uint8Array([...toAscii("wasm"), 0x00, ...key1]); + const addressData = hash(toAscii("module"), key2); + return toBech32(prefix, addressData); +} + +function makeRandomAddress(length: number): string { + return toBech32("purple", Random.getBytes(length)); +} + +let out: Array = []; + +for (let codeId of [1, Number.MAX_SAFE_INTEGER]) { +for (let creator of [makeRandomAddress(20), makeRandomAddress(32)]) { + for (let label of ["instance 1", "instance 2"]) { + const contractAddress = deterministicContractAddress(codeId, creator, label, "purple"); + + out.push({ + in: { + codeId, + label, + creator, + creatorData: toHex(fromBech32(creator).data).toUpperCase(), + }, + out: { + contractAddress, + contractAddressData: toHex(fromBech32(contractAddress).data).toUpperCase(), + }, + }); + } + } +} + +console.log(JSON.stringify(out, undefined, 2)); From b968aae644a7e439ff0f5571a8701d578726c9b9 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 30 Aug 2022 13:34:46 +0200 Subject: [PATCH 02/13] Adapt length encoding --- packages/cli/examples/contract_addresses.ts | 33 ++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index 9e422cac20..27cb0f5ab6 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -1,29 +1,42 @@ import { fromBech32, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; import { Random, Sha256, sha256 } from "@cosmjs/crypto"; -import { Uint64 } from "@cosmjs/math"; +import { Uint53, Uint64 } from "@cosmjs/math"; /** * The "Basic Address" Hash from * https://github.com/cosmos/cosmos-sdk/blob/v0.45.8/docs/architecture/adr-028-public-key-addresses.md */ -function hash(type: Uint8Array, key: Uint8Array): Uint8Array { - return new Sha256(sha256(type)).update(key).digest(); +function hash(type: string, key: Uint8Array): Uint8Array { + return new Sha256(sha256(toAscii(type))).update(key).digest(); +} + +/** + * Takes an integer [0, 255] and returns a one-byte encoding of it. + */ +function toUint8(int: number): number { + const checked = new Uint53(int).toNumber(); + if (checked > 255) { + throw new Error("Integer exceeds uint8 range"); + } + return checked; } /** See https://github.com/CosmWasm/wasmd/issues/942 */ function deterministicContractAddress(codeId: number, creator: string, label: string, prefix: string) { const codeIdUint64 = Uint64.fromNumber(codeId); const creatorData = fromBech32(creator).data; - const creatorDataLen = Uint64.fromNumber(creatorData.length); + const labelData = toUtf8(label); - const key1 = new Uint8Array([ + const key = new Uint8Array([ + ...toAscii("wasm"), + 0x00, ...codeIdUint64.toBytesBigEndian(), - ...creatorDataLen.toBytesBigEndian(), + toUint8(creatorData.length), ...creatorData, - ...toUtf8(label), + toUint8(labelData.length), + ...labelData, ]); - const key2 = new Uint8Array([...toAscii("wasm"), 0x00, ...key1]); - const addressData = hash(toAscii("module"), key2); + const addressData = hash("module", key); return toBech32(prefix, addressData); } @@ -34,7 +47,7 @@ function makeRandomAddress(length: number): string { let out: Array = []; for (let codeId of [1, Number.MAX_SAFE_INTEGER]) { -for (let creator of [makeRandomAddress(20), makeRandomAddress(32)]) { + for (let creator of [makeRandomAddress(20), makeRandomAddress(32)]) { for (let label of ["instance 1", "instance 2"]) { const contractAddress = deterministicContractAddress(codeId, creator, label, "purple"); From ae587c40d4acb052324c0816b60ca5deb1d5eb29 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 30 Aug 2022 13:41:08 +0200 Subject: [PATCH 03/13] Make test addresses deterministic --- packages/cli/examples/contract_addresses.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index 27cb0f5ab6..5179adc111 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -1,5 +1,5 @@ import { fromBech32, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; -import { Random, Sha256, sha256 } from "@cosmjs/crypto"; +import { Sha256, sha256 } from "@cosmjs/crypto"; import { Uint53, Uint64 } from "@cosmjs/math"; /** @@ -40,14 +40,22 @@ function deterministicContractAddress(codeId: number, creator: string, label: st return toBech32(prefix, addressData); } -function makeRandomAddress(length: number): string { - return toBech32("purple", Random.getBytes(length)); +function makeTestingAddress(length: number): string { + let data = new Uint8Array(length); + data.fill(0x99, 0); + data.fill(0xAA, 5); + data.fill(0xBB, 10); + data.fill(0xCC, 15); + data.fill(0xDD, 20); + data.fill(0xEE, 25); + data.fill(0xFF, 30); + return toBech32("purple", data); } let out: Array = []; for (let codeId of [1, Number.MAX_SAFE_INTEGER]) { - for (let creator of [makeRandomAddress(20), makeRandomAddress(32)]) { + for (let creator of [makeTestingAddress(20), makeTestingAddress(32)]) { for (let label of ["instance 1", "instance 2"]) { const contractAddress = deterministicContractAddress(codeId, creator, label, "purple"); From 64cf77532a0c6638b884dd0d74b72636d8df4908 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 5 Sep 2022 15:20:27 +0200 Subject: [PATCH 04/13] Update to use checksum instead of code_id --- packages/cli/examples/contract_addresses.ts | 37 ++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index 5179adc111..4d39c45bfc 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -1,6 +1,7 @@ -import { fromBech32, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; +import { fromBech32, fromHex, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; import { Sha256, sha256 } from "@cosmjs/crypto"; -import { Uint53, Uint64 } from "@cosmjs/math"; +import { Uint53 } from "@cosmjs/math"; +import { assert } from "@cosmjs/utils"; /** * The "Basic Address" Hash from @@ -22,15 +23,16 @@ function toUint8(int: number): number { } /** See https://github.com/CosmWasm/wasmd/issues/942 */ -function deterministicContractAddress(codeId: number, creator: string, label: string, prefix: string) { - const codeIdUint64 = Uint64.fromNumber(codeId); +function deterministicContractAddress(checksum: Uint8Array, creator: string, label: string, prefix: string) { + assert(checksum.length === 32); const creatorData = fromBech32(creator).data; const labelData = toUtf8(label); const key = new Uint8Array([ ...toAscii("wasm"), 0x00, - ...codeIdUint64.toBytesBigEndian(), + toUint8(checksum.length), + ...checksum, toUint8(creatorData.length), ...creatorData, toUint8(labelData.length), @@ -43,28 +45,33 @@ function deterministicContractAddress(codeId: number, creator: string, label: st function makeTestingAddress(length: number): string { let data = new Uint8Array(length); data.fill(0x99, 0); - data.fill(0xAA, 5); - data.fill(0xBB, 10); - data.fill(0xCC, 15); - data.fill(0xDD, 20); - data.fill(0xEE, 25); - data.fill(0xFF, 30); + data.fill(0xaa, 5); + data.fill(0xbb, 10); + data.fill(0xcc, 15); + data.fill(0xdd, 20); + data.fill(0xee, 25); + data.fill(0xff, 30); return toBech32("purple", data); } let out: Array = []; -for (let codeId of [1, Number.MAX_SAFE_INTEGER]) { +const checksums = [ + fromHex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"), + fromHex("1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b"), +]; + +for (let checksum of checksums) { for (let creator of [makeTestingAddress(20), makeTestingAddress(32)]) { for (let label of ["instance 1", "instance 2"]) { - const contractAddress = deterministicContractAddress(codeId, creator, label, "purple"); + const contractAddress = deterministicContractAddress(checksum, creator, label, "purple"); out.push({ in: { - codeId, - label, + checksum: toHex(checksum).toUpperCase(), creator, creatorData: toHex(fromBech32(creator).data).toUpperCase(), + label, }, out: { contractAddress, From 4de6c7ed7f0941a670c57d1f77ab38fdca1d84d9 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 21 Sep 2022 20:39:02 +0200 Subject: [PATCH 05/13] Update contract_addresses.ts --- packages/cli/examples/contract_addresses.ts | 60 ++++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index 4d39c45bfc..24b6b7e508 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -22,11 +22,19 @@ function toUint8(int: number): number { return checked; } -/** See https://github.com/CosmWasm/wasmd/issues/942 */ -function deterministicContractAddress(checksum: Uint8Array, creator: string, label: string, prefix: string) { +/** See https://github.com/CosmWasm/wasmd/pull/1014 */ +function deterministicContractAddress( + checksum: Uint8Array, + creator: string, + salt: Uint8Array, + msg: Uint8Array, + prefix: string, +) { assert(checksum.length === 32); const creatorData = fromBech32(creator).data; - const labelData = toUtf8(label); + + // Validate inputs + if (salt.length < 1 || salt.length > 64) throw new Error("Salt must be between 1 and 64 bytes"); const key = new Uint8Array([ ...toAscii("wasm"), @@ -35,8 +43,10 @@ function deterministicContractAddress(checksum: Uint8Array, creator: string, lab ...checksum, toUint8(creatorData.length), ...creatorData, - toUint8(labelData.length), - ...labelData, + toUint8(salt.length), + ...salt, + toUint8(msg.length), + ...msg, ]); const addressData = hash("module", key); return toBech32(prefix, addressData); @@ -60,24 +70,34 @@ const checksums = [ fromHex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"), fromHex("1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b"), ]; +const salts = [ + toUtf8("a"), + fromHex( + "AABBCCDDEEFFFFEEDDBBCCDDAA66551155aaaaBBCC787878789900AABBCCDDEEFFFFEEDDBBCCDDAA66551155aaaaBBCC787878789900aabbbbcc221100acadae", + ), +]; +const msgs = [null, JSON.stringify({}), JSON.stringify({ some: 123, structure: { nested: ["ok", true] } })]; for (let checksum of checksums) { for (let creator of [makeTestingAddress(20), makeTestingAddress(32)]) { - for (let label of ["instance 1", "instance 2"]) { - const contractAddress = deterministicContractAddress(checksum, creator, label, "purple"); - - out.push({ - in: { - checksum: toHex(checksum).toUpperCase(), - creator, - creatorData: toHex(fromBech32(creator).data).toUpperCase(), - label, - }, - out: { - contractAddress, - contractAddressData: toHex(fromBech32(contractAddress).data).toUpperCase(), - }, - }); + for (let salt of salts) { + for (let msg of msgs) { + const encodedMsg = typeof msg === "string" ? toUtf8(msg) : new Uint8Array(); + const contractAddress = deterministicContractAddress(checksum, creator, salt, encodedMsg, "purple"); + out.push({ + in: { + checksum: toHex(checksum), + creator, + creatorData: toHex(fromBech32(creator).data), + salt: toHex(checksum), + msg, + }, + out: { + contractAddress, + contractAddressData: toHex(fromBech32(contractAddress).data), + }, + }); + } } } } From 09bdaed37d1b54b1032cc819d15a089ac0825eb3 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Sep 2022 11:28:48 +0200 Subject: [PATCH 06/13] Use toUint64 --- packages/cli/examples/contract_addresses.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index 24b6b7e508..e162333751 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -1,6 +1,6 @@ import { fromBech32, fromHex, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; import { Sha256, sha256 } from "@cosmjs/crypto"; -import { Uint53 } from "@cosmjs/math"; +import { Uint64 } from "@cosmjs/math"; import { assert } from "@cosmjs/utils"; /** @@ -12,14 +12,10 @@ function hash(type: string, key: Uint8Array): Uint8Array { } /** - * Takes an integer [0, 255] and returns a one-byte encoding of it. + * Takes an integer [0, 2**64-1] and returns a one-byte encoding of it. */ -function toUint8(int: number): number { - const checked = new Uint53(int).toNumber(); - if (checked > 255) { - throw new Error("Integer exceeds uint8 range"); - } - return checked; +function toUint64(int: number): Uint8Array { + return Uint64.fromNumber(int).toBytesBigEndian(); } /** See https://github.com/CosmWasm/wasmd/pull/1014 */ @@ -39,13 +35,13 @@ function deterministicContractAddress( const key = new Uint8Array([ ...toAscii("wasm"), 0x00, - toUint8(checksum.length), + ...toUint64(checksum.length), ...checksum, - toUint8(creatorData.length), + ...toUint64(creatorData.length), ...creatorData, - toUint8(salt.length), + ...toUint64(salt.length), ...salt, - toUint8(msg.length), + ...toUint64(msg.length), ...msg, ]); const addressData = hash("module", key); From a408f1ed860576b2a14aaeced31b0c02f55dcf71 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Sep 2022 12:27:52 +0200 Subject: [PATCH 07/13] Fix wrong salt output --- packages/cli/examples/contract_addresses.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index e162333751..43ab4778f9 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -85,7 +85,7 @@ for (let checksum of checksums) { checksum: toHex(checksum), creator, creatorData: toHex(fromBech32(creator).data), - salt: toHex(checksum), + salt: toHex(salt), msg, }, out: { From be0bd7a3c3ce24799cea1df617bc47b9965bb58d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Sep 2022 12:28:09 +0200 Subject: [PATCH 08/13] Write intermediate data --- packages/cli/examples/contract_addresses.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/contract_addresses.ts index 43ab4778f9..432ee5bf78 100644 --- a/packages/cli/examples/contract_addresses.ts +++ b/packages/cli/examples/contract_addresses.ts @@ -45,7 +45,8 @@ function deterministicContractAddress( ...msg, ]); const addressData = hash("module", key); - return toBech32(prefix, addressData); + const address = toBech32(prefix, addressData); + return { key, addressData, address }; } function makeTestingAddress(length: number): string { @@ -79,7 +80,13 @@ for (let checksum of checksums) { for (let salt of salts) { for (let msg of msgs) { const encodedMsg = typeof msg === "string" ? toUtf8(msg) : new Uint8Array(); - const contractAddress = deterministicContractAddress(checksum, creator, salt, encodedMsg, "purple"); + const { key, addressData, address } = deterministicContractAddress( + checksum, + creator, + salt, + encodedMsg, + "purple", + ); out.push({ in: { checksum: toHex(checksum), @@ -88,9 +95,12 @@ for (let checksum of checksums) { salt: toHex(salt), msg, }, + intermediate: { + key: toHex(key), + addressData: toHex(addressData), + }, out: { - contractAddress, - contractAddressData: toHex(fromBech32(contractAddress).data), + address: address, }, }); } From bf5e854d087eb48eae4c23e680c01cef5d80717c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 18 Oct 2022 14:31:11 +0200 Subject: [PATCH 09/13] Rename to instantiate2_addresses.ts and add to example runner --- .../{contract_addresses.ts => instantiate2_addresses.ts} | 0 packages/cli/run_examples.sh | 1 + 2 files changed, 1 insertion(+) rename packages/cli/examples/{contract_addresses.ts => instantiate2_addresses.ts} (100%) diff --git a/packages/cli/examples/contract_addresses.ts b/packages/cli/examples/instantiate2_addresses.ts similarity index 100% rename from packages/cli/examples/contract_addresses.ts rename to packages/cli/examples/instantiate2_addresses.ts diff --git a/packages/cli/run_examples.sh b/packages/cli/run_examples.sh index f4edba233d..c276f1197f 100755 --- a/packages/cli/run_examples.sh +++ b/packages/cli/run_examples.sh @@ -10,6 +10,7 @@ if [ -n "${SIMAPP44_ENABLED:-}" ]; then fi yarn node ./bin/cosmjs-cli --init examples/faucet_addresses.ts --code "process.exit(0)" yarn node ./bin/cosmjs-cli --init examples/generate_address.ts --code "process.exit(0)" +yarn node ./bin/cosmjs-cli --init examples/instantiate2_addresses.ts --code "process.exit(0)" yarn node ./bin/cosmjs-cli --init examples/local_faucet.ts --code "process.exit(0)" yarn node ./bin/cosmjs-cli --init examples/mask.ts --code "process.exit(0)" yarn node ./bin/cosmjs-cli --init examples/multisig_address.ts --code "process.exit(0)" From ee5429d1f4856782727d9e4b395af97ecd025bd4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 18 Oct 2022 14:38:28 +0200 Subject: [PATCH 10/13] Move implementation to @cosmwasm/stargate --- .../cli/examples/instantiate2_addresses.ts | 54 +--------------- packages/cosmwasm-stargate/src/index.ts | 1 + .../src/instantiate2_address.ts | 64 +++++++++++++++++++ 3 files changed, 68 insertions(+), 51 deletions(-) create mode 100644 packages/cosmwasm-stargate/src/instantiate2_address.ts diff --git a/packages/cli/examples/instantiate2_addresses.ts b/packages/cli/examples/instantiate2_addresses.ts index 432ee5bf78..18050b5b5f 100644 --- a/packages/cli/examples/instantiate2_addresses.ts +++ b/packages/cli/examples/instantiate2_addresses.ts @@ -1,53 +1,5 @@ -import { fromBech32, fromHex, fromUtf8, toAscii, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; -import { Sha256, sha256 } from "@cosmjs/crypto"; -import { Uint64 } from "@cosmjs/math"; -import { assert } from "@cosmjs/utils"; - -/** - * The "Basic Address" Hash from - * https://github.com/cosmos/cosmos-sdk/blob/v0.45.8/docs/architecture/adr-028-public-key-addresses.md - */ -function hash(type: string, key: Uint8Array): Uint8Array { - return new Sha256(sha256(toAscii(type))).update(key).digest(); -} - -/** - * Takes an integer [0, 2**64-1] and returns a one-byte encoding of it. - */ -function toUint64(int: number): Uint8Array { - return Uint64.fromNumber(int).toBytesBigEndian(); -} - -/** See https://github.com/CosmWasm/wasmd/pull/1014 */ -function deterministicContractAddress( - checksum: Uint8Array, - creator: string, - salt: Uint8Array, - msg: Uint8Array, - prefix: string, -) { - assert(checksum.length === 32); - const creatorData = fromBech32(creator).data; - - // Validate inputs - if (salt.length < 1 || salt.length > 64) throw new Error("Salt must be between 1 and 64 bytes"); - - const key = new Uint8Array([ - ...toAscii("wasm"), - 0x00, - ...toUint64(checksum.length), - ...checksum, - ...toUint64(creatorData.length), - ...creatorData, - ...toUint64(salt.length), - ...salt, - ...toUint64(msg.length), - ...msg, - ]); - const addressData = hash("module", key); - const address = toBech32(prefix, addressData); - return { key, addressData, address }; -} +import { fromBech32, fromHex, toBech32, toHex, toUtf8 } from "@cosmjs/encoding"; +import { _instantiate2AddressIntermediate } from "@cosmjs/cosmwasm-stargate"; function makeTestingAddress(length: number): string { let data = new Uint8Array(length); @@ -80,7 +32,7 @@ for (let checksum of checksums) { for (let salt of salts) { for (let msg of msgs) { const encodedMsg = typeof msg === "string" ? toUtf8(msg) : new Uint8Array(); - const { key, addressData, address } = deterministicContractAddress( + const { key, addressData, address } = _instantiate2AddressIntermediate( checksum, creator, salt, diff --git a/packages/cosmwasm-stargate/src/index.ts b/packages/cosmwasm-stargate/src/index.ts index c41663d936..cf3a0cc6dd 100644 --- a/packages/cosmwasm-stargate/src/index.ts +++ b/packages/cosmwasm-stargate/src/index.ts @@ -1,5 +1,6 @@ export { Code, CodeDetails, Contract, ContractCodeHistoryEntry, CosmWasmClient } from "./cosmwasmclient"; export { fromBinary, toBinary } from "./encoding"; +export { _instantiate2AddressIntermediate, instantiate2Address } from "./instantiate2_address"; export { cosmWasmTypes, createWasmAminoConverters, diff --git a/packages/cosmwasm-stargate/src/instantiate2_address.ts b/packages/cosmwasm-stargate/src/instantiate2_address.ts new file mode 100644 index 0000000000..bf919039ad --- /dev/null +++ b/packages/cosmwasm-stargate/src/instantiate2_address.ts @@ -0,0 +1,64 @@ +import { Sha256, sha256 } from "@cosmjs/crypto"; +import { fromBech32, toAscii, toBech32 } from "@cosmjs/encoding"; +import { Uint64 } from "@cosmjs/math"; +import { assert } from "@cosmjs/utils"; + +/** + * The "Basic Address" Hash from + * https://github.com/cosmos/cosmos-sdk/blob/v0.45.8/docs/architecture/adr-028-public-key-addresses.md + */ +function hash(type: string, key: Uint8Array): Uint8Array { + return new Sha256(sha256(toAscii(type))).update(key).digest(); +} + +/** + * Takes an integer [0, 2**64-1] and returns a one-byte encoding of it. + */ +function toUint64(int: number): Uint8Array { + return Uint64.fromNumber(int).toBytesBigEndian(); +} + +/** + * Private function to export test vector data for https://github.com/cosmos/cosmjs/pull/1253. + * Do not use in production code. + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export function _instantiate2AddressIntermediate( + checksum: Uint8Array, + creator: string, + salt: Uint8Array, + msg: Uint8Array, + prefix: string, +): { key: Uint8Array; addressData: Uint8Array; address: string } { + assert(checksum.length === 32); + const creatorData = fromBech32(creator).data; + + // Validate inputs + if (salt.length < 1 || salt.length > 64) throw new Error("Salt must be between 1 and 64 bytes"); + + const key = new Uint8Array([ + ...toAscii("wasm"), + 0x00, + ...toUint64(checksum.length), + ...checksum, + ...toUint64(creatorData.length), + ...creatorData, + ...toUint64(salt.length), + ...salt, + ...toUint64(msg.length), + ...msg, + ]); + const addressData = hash("module", key); + const address = toBech32(prefix, addressData); + return { key, addressData, address }; +} + +export function instantiate2Address( + checksum: Uint8Array, + creator: string, + salt: Uint8Array, + msg: Uint8Array, + prefix: string, +): string { + return _instantiate2AddressIntermediate(checksum, creator, salt, msg, prefix).address; +} From 652c74d1595647c4d86848e3dd61364106ddcac3 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 18 Oct 2022 14:42:09 +0200 Subject: [PATCH 11/13] Make msg a string | null --- packages/cli/examples/instantiate2_addresses.ts | 3 +-- .../src/instantiate2_address.ts | 16 +++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/cli/examples/instantiate2_addresses.ts b/packages/cli/examples/instantiate2_addresses.ts index 18050b5b5f..566c064052 100644 --- a/packages/cli/examples/instantiate2_addresses.ts +++ b/packages/cli/examples/instantiate2_addresses.ts @@ -31,12 +31,11 @@ for (let checksum of checksums) { for (let creator of [makeTestingAddress(20), makeTestingAddress(32)]) { for (let salt of salts) { for (let msg of msgs) { - const encodedMsg = typeof msg === "string" ? toUtf8(msg) : new Uint8Array(); const { key, addressData, address } = _instantiate2AddressIntermediate( checksum, creator, salt, - encodedMsg, + msg, "purple", ); out.push({ diff --git a/packages/cosmwasm-stargate/src/instantiate2_address.ts b/packages/cosmwasm-stargate/src/instantiate2_address.ts index bf919039ad..1b0d58ea60 100644 --- a/packages/cosmwasm-stargate/src/instantiate2_address.ts +++ b/packages/cosmwasm-stargate/src/instantiate2_address.ts @@ -1,5 +1,5 @@ import { Sha256, sha256 } from "@cosmjs/crypto"; -import { fromBech32, toAscii, toBech32 } from "@cosmjs/encoding"; +import { fromBech32, toAscii, toBech32, toUtf8 } from "@cosmjs/encoding"; import { Uint64 } from "@cosmjs/math"; import { assert } from "@cosmjs/utils"; @@ -27,12 +27,14 @@ export function _instantiate2AddressIntermediate( checksum: Uint8Array, creator: string, salt: Uint8Array, - msg: Uint8Array, + msg: string | null, prefix: string, ): { key: Uint8Array; addressData: Uint8Array; address: string } { assert(checksum.length === 32); const creatorData = fromBech32(creator).data; + const msgData = typeof msg === "string" ? toUtf8(msg) : new Uint8Array(); + // Validate inputs if (salt.length < 1 || salt.length > 64) throw new Error("Salt must be between 1 and 64 bytes"); @@ -45,19 +47,23 @@ export function _instantiate2AddressIntermediate( ...creatorData, ...toUint64(salt.length), ...salt, - ...toUint64(msg.length), - ...msg, + ...toUint64(msgData.length), + ...msgData, ]); const addressData = hash("module", key); const address = toBech32(prefix, addressData); return { key, addressData, address }; } +/** + * Predictable address generation for the MsgInstantiateContract2 + * introduced with wasmd 0.29. + */ export function instantiate2Address( checksum: Uint8Array, creator: string, salt: Uint8Array, - msg: Uint8Array, + msg: string | null, prefix: string, ): string { return _instantiate2AddressIntermediate(checksum, creator, salt, msg, prefix).address; From 330e0bbe5f7a457134b9b193518e5867f0746bf4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 6 Mar 2023 15:36:41 +0100 Subject: [PATCH 12/13] Remove msg argument from instantiate2Address and add tests --- packages/cosmwasm-stargate/src/index.ts | 2 +- .../src/instantiate2.spec.ts | 53 +++++++++++++++++++ ...nstantiate2_address.ts => instantiate2.ts} | 3 +- 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 packages/cosmwasm-stargate/src/instantiate2.spec.ts rename packages/cosmwasm-stargate/src/{instantiate2_address.ts => instantiate2.ts} (97%) diff --git a/packages/cosmwasm-stargate/src/index.ts b/packages/cosmwasm-stargate/src/index.ts index cf3a0cc6dd..2db1cb3911 100644 --- a/packages/cosmwasm-stargate/src/index.ts +++ b/packages/cosmwasm-stargate/src/index.ts @@ -1,6 +1,6 @@ export { Code, CodeDetails, Contract, ContractCodeHistoryEntry, CosmWasmClient } from "./cosmwasmclient"; export { fromBinary, toBinary } from "./encoding"; -export { _instantiate2AddressIntermediate, instantiate2Address } from "./instantiate2_address"; +export { _instantiate2AddressIntermediate, instantiate2Address } from "./instantiate2"; export { cosmWasmTypes, createWasmAminoConverters, diff --git a/packages/cosmwasm-stargate/src/instantiate2.spec.ts b/packages/cosmwasm-stargate/src/instantiate2.spec.ts new file mode 100644 index 0000000000..571346e521 --- /dev/null +++ b/packages/cosmwasm-stargate/src/instantiate2.spec.ts @@ -0,0 +1,53 @@ +import { fromHex } from "@cosmjs/encoding"; + +import { instantiate2Address } from "./instantiate2"; + +describe("instantiate2", () => { + describe("instantiate2Address", () => { + it("works", () => { + // Some entries from https://gist.github.com/webmaster128/e4d401d414bd0e7e6f70482f11877fbe + { + const checksum = fromHex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"); + const creator = "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py"; + const salt = fromHex("61"); + expect(instantiate2Address(checksum, creator, salt, "purple")).toEqual( + "purple1t6r960j945lfv8mhl4mage2rg97w63xeynwrupum2s2l7em4lprs9ce5hk", + ); + } + { + const checksum = fromHex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"); + const creator = "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py"; + const salt = fromHex( + "aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae", + ); + expect(instantiate2Address(checksum, creator, salt, "purple")).toEqual( + "purple1jwzvvfyvpwchrccxl476pxf7c83qawsqv3f2820q0zyrav6eg4jqdcq7gc", + ); + } + { + const checksum = fromHex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"); + const creator = "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m"; + const salt = fromHex("61"); + expect(instantiate2Address(checksum, creator, salt, "purple")).toEqual( + "purple1juj7jn6j3k9h35euyhealntquc2zmzlxp2ek76jmtypkl4g4vrdsfwmwxk", + ); + } + { + const checksum = fromHex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"); + const creator = "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m"; + const salt = fromHex("61"); + expect(instantiate2Address(checksum, creator, salt, "purple")).toEqual( + "purple1juj7jn6j3k9h35euyhealntquc2zmzlxp2ek76jmtypkl4g4vrdsfwmwxk", + ); + } + { + const checksum = fromHex("1da6c16de2cbaf7ad8cbb66f0925ba33f5c278cb2491762d04658c1480ea229b"); + const creator = "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py"; + const salt = fromHex("61"); + expect(instantiate2Address(checksum, creator, salt, "purple")).toEqual( + "purple1h9wyvusc6sy2p7fsgmez0dhvullpsyel7vq38k6d9fa7ehlv59qsvnyh36", + ); + } + }); + }); +}); diff --git a/packages/cosmwasm-stargate/src/instantiate2_address.ts b/packages/cosmwasm-stargate/src/instantiate2.ts similarity index 97% rename from packages/cosmwasm-stargate/src/instantiate2_address.ts rename to packages/cosmwasm-stargate/src/instantiate2.ts index 1b0d58ea60..edc7f251eb 100644 --- a/packages/cosmwasm-stargate/src/instantiate2_address.ts +++ b/packages/cosmwasm-stargate/src/instantiate2.ts @@ -63,8 +63,7 @@ export function instantiate2Address( checksum: Uint8Array, creator: string, salt: Uint8Array, - msg: string | null, prefix: string, ): string { - return _instantiate2AddressIntermediate(checksum, creator, salt, msg, prefix).address; + return _instantiate2AddressIntermediate(checksum, creator, salt, null, prefix).address; } From 428798391a90c3fe8fcb317bd407ba4761708ea4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 6 Mar 2023 15:40:28 +0100 Subject: [PATCH 13/13] Add CHANGELOG entry for instantiate2Address --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4f4d97bf..470eb43f54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,9 +45,12 @@ and this project adheres to - @cosmjs/cosmwasm-stargate: Add constructors `CosmWasmClient.create` and `SigningCosmWasmClient.createWithSigner` to construct with a given Tendermint client ([#1376]). +- @cosmjs/cosmwasm-stargate: Add `instantiate2Address` to pre-calculate + addresses for Instantiate2 ([#1253]). - @cosmjs/stargate: Add `txIndex` to `DeliverTxResponse` and `IndexedTx` ([#1361]). +[#1253]: https://github.com/cosmos/cosmjs/pull/1253 [#1308]: https://github.com/cosmos/cosmjs/pull/1308 [#1361]: https://github.com/cosmos/cosmjs/issues/1361 [#1376]: https://github.com/cosmos/cosmjs/pull/1376