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/encoding": "^2.0.2",
"@iov/utils": "^2.0.2",
"@types/pako": "^1.0.1",
"axios": "^0.19.0",
"bn.js": "^5.1.1"
"bn.js": "^5.1.1",
"pako": "^1.0.11"
},
"devDependencies": {
"@types/bn.js": "^4.11.6",

View File

@ -321,7 +321,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
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 contractAddress = await client.instantiate(codeId, initMsg);
contract = { initMsg: initMsg, address: contractAddress };
@ -372,7 +372,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
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 contractAddress = await client.instantiate(codeId, initMsg);
contract = { initMsg: initMsg, address: contractAddress };

View File

@ -28,4 +28,9 @@ export {
decodeSignature,
makeSecp256k1SignatureFromFixedLength,
} 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 { Secp256k1Pen } from "./pen";
@ -6,6 +8,8 @@ import { SigningCosmWasmClient } from "./signingcosmwasmclient";
import { getRandomizedHackatom, makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec";
import { Coin } from "./types";
const { toHex } = Encoding;
const httpUrl = "http://localhost:1317";
const faucet = {
@ -32,7 +36,18 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
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);
});
});
@ -42,7 +57,7 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
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[] = [
{
@ -74,7 +89,7 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
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, {
verifier: faucet.address,
@ -93,7 +108,7 @@ describe("SigningCosmWasmClient", () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const codeId = await client.upload(getRandomizedHackatom());
const { codeId } = await client.upload(getRandomizedHackatom());
// instantiate
const transferAmount: readonly Coin[] = [

View File

@ -1,4 +1,6 @@
import { Sha256 } from "@iov/crypto";
import { Encoding } from "@iov/encoding";
import pako from "pako";
import { CosmWasmClient, GetNonceResult, PostTxResult } from "./cosmwasmclient";
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 {
readonly logs: readonly Log[];
}
@ -80,14 +95,15 @@ export class SigningCosmWasmClient extends CosmWasmClient {
return super.getAccount(address || this.senderAddress);
}
/** Uploads code and returns a code ID */
public async upload(wasmCode: Uint8Array, memo = ""): Promise<number> {
/** Uploads code and returns a receipt, including the code ID */
public async upload(wasmCode: Uint8Array, memo = ""): Promise<UploadReceipt> {
const compressed = pako.gzip(wasmCode, { level: 9 });
const storeCodeMsg: MsgStoreCode = {
type: "wasm/store-code",
value: {
sender: this.senderAddress,
// eslint-disable-next-line @typescript-eslint/camelcase
wasm_byte_code: Encoding.toBase64(wasmCode),
wasm_byte_code: Encoding.toBase64(compressed),
source: "",
builder: "",
},
@ -106,8 +122,13 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const result = await this.postTx(marshalTx(signedTx));
const codeIdAttr = findAttribute(result.logs, "message", "code_id");
const codeId = Number.parseInt(codeIdAttr.value, 10);
return codeId;
return {
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(

View File

@ -27,4 +27,9 @@ export {
decodeSignature,
makeSecp256k1SignatureFromFixedLength,
} 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 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 {
readonly logs: readonly Log[];
}
@ -27,8 +39,8 @@ export declare class SigningCosmWasmClient extends CosmWasmClient {
);
getNonce(address?: string): Promise<GetNonceResult>;
getAccount(address?: string): Promise<CosmosSdkAccount | undefined>;
/** Uploads code and returns a code ID */
upload(wasmCode: Uint8Array, memo?: string): Promise<number>;
/** Uploads code and returns a receipt, including the code ID */
upload(wasmCode: Uint8Array, memo?: string): Promise<UploadReceipt>;
instantiate(
codeId: number,
initMsg: object,

View File

@ -72,12 +72,12 @@ async function main() {
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const wasm = fs.readFileSync(__dirname + "/contracts/cw-erc20.wasm");
const codeId = await client.upload(wasm, "Upload ERC20 contract");
console.info(`Upload succeeded. Code ID is ${codeId}`);
const uploadReceipt = await client.upload(wasm, "Upload ERC20 contract");
console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`);
for (const initMsg of [initMsgHash, initMsgIsa, initMsgJade]) {
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}`);
}
}

View File

@ -1125,6 +1125,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
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":
version "1.0.31"
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:
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:
version "1.0.10"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"