Auto-compress contracts in upload

This commit is contained in:
Simon Warta 2020-02-19 12:52:12 +01:00
parent c106975df9
commit 5504288de4
9 changed files with 89 additions and 19 deletions

View File

@ -41,8 +41,10 @@
"@iov/crypto": "^2.0.2", "@iov/crypto": "^2.0.2",
"@iov/encoding": "^2.0.2", "@iov/encoding": "^2.0.2",
"@iov/utils": "^2.0.2", "@iov/utils": "^2.0.2",
"@types/pako": "^1.0.1",
"axios": "^0.19.0", "axios": "^0.19.0",
"bn.js": "^5.1.1" "bn.js": "^5.1.1",
"pako": "^1.0.11"
}, },
"devDependencies": { "devDependencies": {
"@types/bn.js": "^4.11.6", "@types/bn.js": "^4.11.6",

View File

@ -321,7 +321,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom()); const { codeId } = await client.upload(getRandomizedHackatom());
const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() }; const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() };
const contractAddress = await client.instantiate(codeId, initMsg); const contractAddress = await client.instantiate(codeId, initMsg);
contract = { initMsg: initMsg, address: contractAddress }; contract = { initMsg: initMsg, address: contractAddress };
@ -372,7 +372,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom()); const { codeId } = await client.upload(getRandomizedHackatom());
const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() }; const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() };
const contractAddress = await client.instantiate(codeId, initMsg); const contractAddress = await client.instantiate(codeId, initMsg);
contract = { initMsg: initMsg, address: contractAddress }; contract = { initMsg: initMsg, address: contractAddress };

View File

@ -28,4 +28,9 @@ export {
decodeSignature, decodeSignature,
makeSecp256k1SignatureFromFixedLength, makeSecp256k1SignatureFromFixedLength,
} from "./signature"; } from "./signature";
export { SigningCallback, SigningCosmWasmClient, ExecuteResult } from "./signingcosmwasmclient"; export {
SigningCallback,
SigningCosmWasmClient,
ExecuteResult,
UploadReceipt,
} from "./signingcosmwasmclient";

View File

@ -1,3 +1,5 @@
import { Sha256 } from "@iov/crypto";
import { Encoding } from "@iov/encoding";
import { assert } from "@iov/utils"; import { assert } from "@iov/utils";
import { Secp256k1Pen } from "./pen"; import { Secp256k1Pen } from "./pen";
@ -6,6 +8,8 @@ import { SigningCosmWasmClient } from "./signingcosmwasmclient";
import { getRandomizedHackatom, makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec"; import { getRandomizedHackatom, makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec";
import { Coin } from "./types"; import { Coin } from "./types";
const { toHex } = Encoding;
const httpUrl = "http://localhost:1317"; const httpUrl = "http://localhost:1317";
const faucet = { const faucet = {
@ -32,7 +36,18 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom()); const wasm = getRandomizedHackatom();
const {
codeId,
originalChecksum,
originalSize,
compressedChecksum,
compressedSize,
} = await client.upload(wasm);
expect(originalChecksum).toEqual(toHex(new Sha256(wasm).digest()));
expect(originalSize).toEqual(wasm.length);
expect(compressedChecksum).toMatch(/^[0-9a-f]{64}$/);
expect(compressedSize).toBeLessThan(wasm.length * 0.5);
expect(codeId).toBeGreaterThanOrEqual(1); expect(codeId).toBeGreaterThanOrEqual(1);
}); });
}); });
@ -42,7 +57,7 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom()); const { codeId } = await client.upload(getRandomizedHackatom());
const transferAmount: readonly Coin[] = [ const transferAmount: readonly Coin[] = [
{ {
@ -74,7 +89,7 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom()); const { codeId } = await client.upload(getRandomizedHackatom());
const contractAddress1 = await client.instantiate(codeId, { const contractAddress1 = await client.instantiate(codeId, {
verifier: faucet.address, verifier: faucet.address,
@ -93,7 +108,7 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom()); const { codeId } = await client.upload(getRandomizedHackatom());
// instantiate // instantiate
const transferAmount: readonly Coin[] = [ const transferAmount: readonly Coin[] = [

View File

@ -1,4 +1,6 @@
import { Sha256 } from "@iov/crypto";
import { Encoding } from "@iov/encoding"; import { Encoding } from "@iov/encoding";
import pako from "pako";
import { CosmWasmClient, GetNonceResult, PostTxResult } from "./cosmwasmclient"; import { CosmWasmClient, GetNonceResult, PostTxResult } from "./cosmwasmclient";
import { makeSignBytes, marshalTx } from "./encoding"; import { makeSignBytes, marshalTx } from "./encoding";
@ -49,6 +51,19 @@ const defaultFees: FeeTable = {
}, },
}; };
export interface UploadReceipt {
/** Size of the original wasm code in bytes */
readonly originalSize: number;
/** A hex encoded sha256 checksum of the original wasm code (that is stored on chain) */
readonly originalChecksum: string;
/** Size of the compressed wasm code in bytes */
readonly compressedSize: number;
/** A hex encoded sha256 checksum of the compressed wasm code (that stored in the transaction) */
readonly compressedChecksum: string;
/** The ID of the code asigned by the chain */
readonly codeId: number;
}
export interface ExecuteResult { export interface ExecuteResult {
readonly logs: readonly Log[]; readonly logs: readonly Log[];
} }
@ -80,14 +95,15 @@ export class SigningCosmWasmClient extends CosmWasmClient {
return super.getAccount(address || this.senderAddress); return super.getAccount(address || this.senderAddress);
} }
/** Uploads code and returns a code ID */ /** Uploads code and returns a receipt, including the code ID */
public async upload(wasmCode: Uint8Array, memo = ""): Promise<number> { public async upload(wasmCode: Uint8Array, memo = ""): Promise<UploadReceipt> {
const compressed = pako.gzip(wasmCode, { level: 9 });
const storeCodeMsg: MsgStoreCode = { const storeCodeMsg: MsgStoreCode = {
type: "wasm/store-code", type: "wasm/store-code",
value: { value: {
sender: this.senderAddress, sender: this.senderAddress,
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
wasm_byte_code: Encoding.toBase64(wasmCode), wasm_byte_code: Encoding.toBase64(compressed),
source: "", source: "",
builder: "", builder: "",
}, },
@ -106,8 +122,13 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const result = await this.postTx(marshalTx(signedTx)); const result = await this.postTx(marshalTx(signedTx));
const codeIdAttr = findAttribute(result.logs, "message", "code_id"); const codeIdAttr = findAttribute(result.logs, "message", "code_id");
const codeId = Number.parseInt(codeIdAttr.value, 10); return {
return codeId; originalSize: wasmCode.length,
originalChecksum: Encoding.toHex(new Sha256(wasmCode).digest()),
compressedSize: compressed.length,
compressedChecksum: Encoding.toHex(new Sha256(compressed).digest()),
codeId: Number.parseInt(codeIdAttr.value, 10),
};
} }
public async instantiate( public async instantiate(

View File

@ -27,4 +27,9 @@ export {
decodeSignature, decodeSignature,
makeSecp256k1SignatureFromFixedLength, makeSecp256k1SignatureFromFixedLength,
} from "./signature"; } from "./signature";
export { SigningCallback, SigningCosmWasmClient, ExecuteResult } from "./signingcosmwasmclient"; export {
SigningCallback,
SigningCosmWasmClient,
ExecuteResult,
UploadReceipt,
} from "./signingcosmwasmclient";

View File

@ -11,6 +11,18 @@ export interface FeeTable {
readonly exec: StdFee; readonly exec: StdFee;
readonly send: StdFee; readonly send: StdFee;
} }
export interface UploadReceipt {
/** Size of the original wasm code in bytes */
readonly originalSize: number;
/** A hex encoded sha256 checksum of the original wasm code (that is stored on chain) */
readonly originalChecksum: string;
/** Size of the compressed wasm code in bytes */
readonly compressedSize: number;
/** A hex encoded sha256 checksum of the compressed wasm code (that stored in the transaction) */
readonly compressedChecksum: string;
/** The ID of the code asigned by the chain */
readonly codeId: number;
}
export interface ExecuteResult { export interface ExecuteResult {
readonly logs: readonly Log[]; readonly logs: readonly Log[];
} }
@ -27,8 +39,8 @@ export declare class SigningCosmWasmClient extends CosmWasmClient {
); );
getNonce(address?: string): Promise<GetNonceResult>; getNonce(address?: string): Promise<GetNonceResult>;
getAccount(address?: string): Promise<CosmosSdkAccount | undefined>; getAccount(address?: string): Promise<CosmosSdkAccount | undefined>;
/** Uploads code and returns a code ID */ /** Uploads code and returns a receipt, including the code ID */
upload(wasmCode: Uint8Array, memo?: string): Promise<number>; upload(wasmCode: Uint8Array, memo?: string): Promise<UploadReceipt>;
instantiate( instantiate(
codeId: number, codeId: number,
initMsg: object, initMsg: object,

View File

@ -72,12 +72,12 @@ async function main() {
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const wasm = fs.readFileSync(__dirname + "/contracts/cw-erc20.wasm"); const wasm = fs.readFileSync(__dirname + "/contracts/cw-erc20.wasm");
const codeId = await client.upload(wasm, "Upload ERC20 contract"); const uploadReceipt = await client.upload(wasm, "Upload ERC20 contract");
console.info(`Upload succeeded. Code ID is ${codeId}`); console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`);
for (const initMsg of [initMsgHash, initMsgIsa, initMsgJade]) { for (const initMsg of [initMsgHash, initMsgIsa, initMsgJade]) {
const memo = `Create an ERC20 instance for ${initMsg.symbol}`; const memo = `Create an ERC20 instance for ${initMsg.symbol}`;
const contractAddress = await client.instantiate(codeId, initMsg, memo); const contractAddress = await client.instantiate(uploadReceipt.codeId, initMsg, memo);
console.info(`Contract instantiated for ${initMsg.symbol} at ${contractAddress}`); console.info(`Contract instantiated for ${initMsg.symbol} at ${contractAddress}`);
} }
} }

View File

@ -1125,6 +1125,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
"@types/pako@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.1.tgz#33b237f3c9aff44d0f82fe63acffa4a365ef4a61"
integrity sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg==
"@types/random-js@^1.0.31": "@types/random-js@^1.0.31":
version "1.0.31" version "1.0.31"
resolved "https://registry.yarnpkg.com/@types/random-js/-/random-js-1.0.31.tgz#18a8bcc075afa504421e638fcbe021f27e802941" resolved "https://registry.yarnpkg.com/@types/random-js/-/random-js-1.0.31.tgz#18a8bcc075afa504421e638fcbe021f27e802941"
@ -5964,6 +5969,11 @@ p-waterfall@^1.0.0:
dependencies: dependencies:
p-reduce "^1.0.0" p-reduce "^1.0.0"
pako@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
pako@~1.0.5: pako@~1.0.5:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"