Skip to content

Commit

Permalink
Merge pull request #319 from enigmampc/#311-fix-client-decryption
Browse files Browse the repository at this point in the history
#311 fix client decryption
  • Loading branch information
assafmo authored Jun 28, 2020
2 parents ac34cfc + c393351 commit 75fad83
Show file tree
Hide file tree
Showing 25 changed files with 224 additions and 165 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
run: |
source "$HOME/.sgxsdk/sgxsdk/environment"
make vendor
SGX_MODE=SW BUILD_PROFILE="" RUSTC_WRAPPER="$HOME/sccache" make build-linux
SGX_MODE=SW BUILD_PROFILE="minimal" RUSTC_WRAPPER="$HOME/sccache" make build-linux
- name: Old bash+js sanity tests
run: |
source "$HOME/.sgxsdk/sgxsdk/environment"
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ WORKDIR /go/src/github.com/enigmampc/EnigmaBlockchain/

COPY Makefile Makefile

RUN make clean
# RUN make clean
RUN make vendor

WORKDIR /go/src/github.com/enigmampc/EnigmaBlockchain/go-cosmwasm
Expand Down
27 changes: 18 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ xgo_build_enigmad: go.sum
xgo_build_enigmacli: go.sum
xgo --go latest --targets $(XGO_TARGET) $(BUILD_FLAGS) github.com/enigmampc/EnigmaBlockchain/cmd/enigmacli

build_local_no_rust:
cp go-cosmwasm/target/release/libgo_cosmwasm.so go-cosmwasm/api
# this pulls out ELF symbols, 80% size reduction!
go build -mod=readonly $(BUILD_FLAGS) ./cmd/enigmad
go build -mod=readonly $(BUILD_FLAGS) ./cmd/enigmacli

build-linux: vendor
BUILD_PROFILE=$(BUILD_PROFILE) $(MAKE) -C go-cosmwasm build-rust
cp go-cosmwasm/target/$(BUILD_PROFILE)/libgo_cosmwasm.so go-cosmwasm/api
Expand Down Expand Up @@ -188,22 +194,25 @@ callback-sanity-test:
SGX_MODE=SW ./cosmwasm/testing/callback-test.sh

build-test-contract:
# sudo apt install binaryen
$(MAKE) -C ./x/compute/internal/keeper/testdata/test-contract

go-tests: build-test-contract
SGX_MODE=SW $(MAKE) build-linux # empty BUILD_PROFILE means debug mode which compiles faster
# empty BUILD_PROFILE means debug mode which compiles faster
SGX_MODE=SW $(MAKE) build-linux
cp ./cosmwasm/packages/wasmi-runtime/librust_cosmwasm_enclave.signed.so ./x/compute/internal/keeper
SGX_MODE=SW go test -p 1 -v ./x/compute/internal/...

build-cosmwasm-test-contracts:
cd ./cosmwasm/contracts/staking && cargo wasm
cp ./cosmwasm/contracts/staking/target/wasm32-unknown-unknown/release/staking.wasm ./x/compute/internal/keeper/testdata
# sudo apt install binaryen
cd ./cosmwasm/contracts/staking && RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown --locked
wasm-opt -Os ./cosmwasm/contracts/staking/target/wasm32-unknown-unknown/release/staking.wasm -o ./x/compute/internal/keeper/testdata/staking.wasm

cd ./cosmwasm/contracts/reflect && cargo wasm
cp ./cosmwasm/contracts/reflect/target/wasm32-unknown-unknown/release/reflect.wasm ./x/compute/internal/keeper/testdata
cd ./cosmwasm/contracts/reflect && RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown --locked
wasm-opt -Os ./cosmwasm/contracts/reflect/target/wasm32-unknown-unknown/release/reflect.wasm -o ./x/compute/internal/keeper/testdata/reflect.wasm

cd ./cosmwasm/contracts/burner && cargo wasm
cp ./cosmwasm/contracts/burner/target/wasm32-unknown-unknown/release/burner.wasm ./x/compute/internal/keeper/testdata
cd ./cosmwasm/contracts/burner && RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown --locked
wasm-opt -Os ./cosmwasm/contracts/burner/target/wasm32-unknown-unknown/release/burner.wasm -o ./x/compute/internal/keeper/testdata/burner.wasm

cd ./cosmwasm/contracts/erc20 && cargo wasm
cp ./cosmwasm/contracts/erc20/target/wasm32-unknown-unknown/release/erc20.wasm ./x/compute/internal/keeper/testdata
cd ./cosmwasm/contracts/erc20 && RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown --locked
wasm-opt -Os ./cosmwasm/contracts/erc20/target/wasm32-unknown-unknown/release/erc20.wasm -o ./x/compute/internal/keeper/testdata/erc20.wasm
2 changes: 1 addition & 1 deletion cosmwasm-js/packages/sdk/src/cosmwasmclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export class CosmWasmClient {
logs: result.logs ? parseLogs(result.logs) : [],
rawLog: result.raw_log || "",
transactionHash: result.txhash,
data: result.data,
data: result.data || "",
};
}

Expand Down
4 changes: 4 additions & 0 deletions cosmwasm-js/packages/sdk/src/enigmautils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ export default class EnigmaUtils {
}

public async decrypt(ciphertext: Uint8Array, nonce: Uint8Array): Promise<Uint8Array> {
if (ciphertext.length === 0) {
return new Uint8Array();
}

const { privkey: txSenderPrivKey } = this.getTxSenderKeyPair();
const txEncryptionKey = await this.getTxEncryptionKey(txSenderPrivKey, nonce);

Expand Down
85 changes: 45 additions & 40 deletions cosmwasm-js/packages/sdk/src/restclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export interface TxsResponse {
readonly codespace?: string;
/** Falsy when transaction execution succeeded. Contains error code on error. */
readonly code?: number;
readonly raw_log: string;
raw_log: string;
data: any;
readonly logs?: Log[];
readonly tx: CosmosSdkTx;
Expand Down Expand Up @@ -469,7 +469,32 @@ export class RestClient {

const encoded = Encoding.toHex(Encoding.toUtf8(Encoding.toBase64(encrypted)));
const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`;
const responseData = (await this.get(path)) as WasmResponse<SmartQueryResponse>;
let responseData;
try {
responseData = (await this.get(path)) as WasmResponse<SmartQueryResponse>;
} catch (err) {
try {
const errorMessageRgx = /wasm contract failed: generic: (.+?) \(HTTP 500\)/g;

const rgxMatches = errorMessageRgx.exec(err.message);
if (rgxMatches == null || rgxMatches.length != 2) {
throw err;
}

const errorCipherB64 = rgxMatches[1];
const errorCipherBz = Encoding.fromBase64(errorCipherB64);

const errorPlainBz = await this.enigmautils.decrypt(errorCipherBz, nonce);

err.message = err.message.replace(errorCipherB64, Encoding.fromUtf8(errorPlainBz));
} catch (decryptionError) {
throw new Error(
`Failed to decrypt the following error message: ${err.message}. Decryption error of the error message: ${decryptionError.message}`,
);
}

throw err;
}

if (isWasmError(responseData)) {
throw new Error(
Expand All @@ -496,48 +521,15 @@ export class RestClient {
return this.get("/register/master-cert");
}

public async decryptDataField(
dataField: string,
nonce: Uint8Array,
): Promise<{ log: Attribute[]; data: Uint8Array; messages: any[] }> {
const wasmOutputs = JSON.parse(Encoding.fromUtf8(Encoding.fromHex(dataField)));

if (wasmOutputs.err) {
throw new Error(wasmOutputs.err);
}
public async decryptDataField(dataField: string = "", nonce: Uint8Array): Promise<Uint8Array> {
const wasmOutputDataCipherBz = Encoding.fromBase64(Encoding.fromUtf8(Encoding.fromHex(dataField)));

// data
const data = wasmOutputs.ok.data
? await this.enigmautils.decrypt(Encoding.fromBase64(wasmOutputs.ok.data), nonce)
: new Uint8Array();

for (let i = 0; i < wasmOutputs.ok.messages.length; i++) {
const m = wasmOutputs.ok.messages[i];
if (!m.contract) {
continue;
}

m.contract.msg = Encoding.fromUtf8(
await this.enigmautils.decrypt(Encoding.fromBase64(m.contract.msg).slice(64), nonce),
);
wasmOutputs.ok.messages[i] = m;
}

const messages = wasmOutputs.ok.messages;

// logs
const wasmEvents: Attribute[] = await Promise.all(
wasmOutputs.ok.log.map(
async (l: Attribute): Promise<Attribute> => ({
key: Encoding.fromUtf8(await this.enigmautils.decrypt(Encoding.fromBase64(l.key), nonce)),
value: Encoding.fromUtf8(await this.enigmautils.decrypt(Encoding.fromBase64(l.value), nonce)),
}),
),
const data = Encoding.fromBase64(
Encoding.fromUtf8(await this.enigmautils.decrypt(wasmOutputDataCipherBz, nonce)),
);

// todo messages

return { log: wasmEvents, data: data, messages: messages };
return data;
}

public async decryptLogs(logs: readonly Log[], nonce: Uint8Array): Promise<readonly Log[]> {
Expand Down Expand Up @@ -597,6 +589,19 @@ export class RestClient {
logs = await this.decryptLogs(txsResponse.logs, nonce);
txsResponse = Object.assign({}, txsResponse, { logs: logs });
}

// decrypt error
const errorMessageRgx = /wasm contract failed: generic: (.+?): failed to execute message; message index: 0/g;

const rgxMatches = errorMessageRgx.exec(txsResponse.raw_log);
if (Array.isArray(rgxMatches) && rgxMatches.length === 2) {
const errorCipherB64 = rgxMatches[1];
const errorCipherBz = Encoding.fromBase64(errorCipherB64);

const errorPlainBz = await this.enigmautils.decrypt(errorCipherBz, nonce);

txsResponse.raw_log = txsResponse.raw_log.replace(errorCipherB64, Encoding.fromUtf8(errorPlainBz));
}
}
}
return txsResponse;
Expand Down
28 changes: 26 additions & 2 deletions cosmwasm-js/packages/sdk/src/signingcosmwasmclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,33 @@ export class SigningCosmWasmClient extends CosmWasmClient {
signatures: [signature],
};

const result = await this.postTx(signedTx);

const nonce = Encoding.fromBase64(executeMsg.value.msg).slice(0, 32);
let result;
try {
result = await this.postTx(signedTx);
} catch (err) {
try {
const errorMessageRgx = /wasm contract failed: generic: (.+?): failed to execute message; message index: 0/g;

const rgxMatches = errorMessageRgx.exec(err.message);
if (rgxMatches == null || rgxMatches.length != 2) {
throw err;
}

const errorCipherB64 = rgxMatches[1];
const errorCipherBz = Encoding.fromBase64(errorCipherB64);

const errorPlainBz = await this.restClient.enigmautils.decrypt(errorCipherBz, nonce);

err.message = err.message.replace(errorCipherB64, Encoding.fromUtf8(errorPlainBz));
} catch (decryptionError) {
throw new Error(
`Failed to decrypt the following error message: ${err.message}. Decryption error of the error message: ${decryptionError.message}`,
);
}

throw err;
}

const data = await this.restClient.decryptDataField(result.data, nonce);
const logs = await this.restClient.decryptLogs(result.logs, nonce);
Expand Down
13 changes: 3 additions & 10 deletions cosmwasm-js/packages/sdk/types/restclient.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Log, Attribute } from "./logs";
import { Log } from "./logs";
import { Coin, CosmosSdkTx, JsonObject, Model, StdTx } from "./types";
import EnigmaUtils from "./enigmautils";
export interface CosmosSdkAccount {
Expand Down Expand Up @@ -99,7 +99,7 @@ export interface TxsResponse {
readonly codespace?: string;
/** Falsy when transaction execution succeeded. Contains error code on error. */
readonly code?: number;
readonly raw_log: string;
raw_log: string;
data: any;
readonly logs?: Log[];
readonly tx: CosmosSdkTx;
Expand Down Expand Up @@ -235,14 +235,7 @@ export declare class RestClient {
* Get the consensus keypair for IO encryption
*/
getMasterCerts(address: string, query: object): Promise<any>;
decryptDataField(
dataField: string,
nonce: Uint8Array,
): Promise<{
log: Attribute[];
data: Uint8Array;
messages: any[];
}>;
decryptDataField(dataField: string | undefined, nonce: Uint8Array): Promise<Uint8Array>;
decryptLogs(logs: readonly Log[], nonce: Uint8Array): Promise<readonly Log[]>;
decryptTxsResponse(txsResponse: TxsResponse): Promise<TxsResponse>;
}
Expand Down
2 changes: 1 addition & 1 deletion cosmwasm/contracts/erc20/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ cranelift = ["cosmwasm-vm/default-cranelift"]
singlepass = ["cosmwasm-vm/default-singlepass"]

[dependencies]
cosmwasm-std = { path = "../../packages/std", features = ["staking"] }
cosmwasm-std = { path = "../../packages/std" }
cosmwasm-storage = { path = "../../packages/storage" }
schemars = "0.7"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
Expand Down
6 changes: 3 additions & 3 deletions cosmwasm/contracts/reflect/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ publish = false
edition = "2018"
description = "Reflect messages to use for test cases - based on cw-mask"
license = "Apache-2.0"

exclude = [
# Those files are cosmwasm-opt artifacts. You might want to commit them for convenience but they should not be part of the source code publication.
"contract.wasm",
"hash.txt",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib", "rlib"]

Expand All @@ -41,7 +39,9 @@ singlepass = ["cosmwasm-vm/default-singlepass"]
cosmwasm-std = { path = "../../packages/std", features = ["staking"] }
cosmwasm-storage = { path = "../../packages/storage" }
schemars = "0.7"
serde = { version = "=1.0.103", default-features = false, features = ["derive"] }
serde = { version = "=1.0.103", default-features = false, features = [
"derive"
] }

[dev-dependencies]
cosmwasm-vm = { path = "../../packages/sgx-vm", package = "cosmwasm-sgx-vm", default-features = false }
Expand Down
5 changes: 2 additions & 3 deletions cosmwasm/testing/callback-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ const assert = require("assert").strict;

assert.deepEqual(execTx.logs, tx.logs);
assert.deepEqual(execTx.data, tx.data);
assert.deepEqual(tx.data.data, Uint8Array.from([65, 103, 77, 61]));

assert.deepEqual(tx.data, Uint8Array.from([2, 3]));
assert.deepEqual(tx.logs[0].events[1].attributes, [
{
key: "contract_address",
Expand All @@ -77,5 +76,5 @@ const assert = require("assert").strict;
value: "🍉",
},
]);
console.log("ok");
console.log("ok 👌");
})();
13 changes: 11 additions & 2 deletions cosmwasm/testing/callback-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,20 @@ export EXEC_TX_HASH=$(
jq -r .txhash
)

wait_for_tx "$EXEC_TX_HASH" "Waiting for reflect to finish on-chain..."
wait_for_tx "$EXEC_TX_HASH" "Waiting for exec to finish on-chain..."

./enigmacli q compute tx "$EXEC_TX_HASH"

# sleep infinity
# exec (generate error inside WASM)
export EXEC_ERR_TX_HASH=$(
yes |
./enigmacli tx compute execute --from a $CONTRACT_ADDRESS "{\"contracterror\":{}}" |
jq -r .txhash
)

wait_for_tx "$EXEC_ERR_TX_HASH" "Waiting for exec to finish on-chain..."

./enigmacli q compute tx "$EXEC_ERR_TX_HASH"

(
cd ./cosmwasm-js
Expand Down
Loading

0 comments on commit 75fad83

Please sign in to comment.