From 1a8019b38078effa6bca7e5bc6b0ff42490e6ad6 Mon Sep 17 00:00:00 2001 From: Alexander Peters Date: Mon, 6 Mar 2023 14:11:11 +0100 Subject: [PATCH] Fix client checksum verification (#1234) * Fix client checksum verification * Review comments --- x/wasm/client/cli/gov_tx.go | 12 +++- x/wasm/client/cli/gov_tx_test.go | 2 +- x/wasm/client/cli/tx.go | 1 + x/wasm/client/cli/tx_test.go | 65 ++++++++++++++++++++++ x/wasm/keeper/proposal_integration_test.go | 17 +++++- 5 files changed, 90 insertions(+), 7 deletions(-) diff --git a/x/wasm/client/cli/gov_tx.go b/x/wasm/client/cli/gov_tx.go index 36ba6c334f..c516be2932 100644 --- a/x/wasm/client/cli/gov_tx.go +++ b/x/wasm/client/cli/gov_tx.go @@ -9,6 +9,8 @@ import ( "strconv" "strings" + "github.com/CosmWasm/wasmd/x/wasm/ioutils" + "github.com/docker/distribution/reference" "github.com/cosmos/cosmos-sdk/client" @@ -95,7 +97,7 @@ func ProposalStoreCodeCmd() *cobra.Command { return cmd } -func parseVerificationFlags(wasm []byte, flags *flag.FlagSet) (string, string, []byte, error) { +func parseVerificationFlags(gzippedWasm []byte, flags *flag.FlagSet) (string, string, []byte, error) { source, err := flags.GetString(flagSource) if err != nil { return "", "", nil, fmt.Errorf("source: %s", err) @@ -126,10 +128,14 @@ func parseVerificationFlags(wasm []byte, flags *flag.FlagSet) (string, string, [ if len(codeHash) == 0 { return "", "", nil, fmt.Errorf("code hash is required") } - // wasm is unzipped in parseStoreCodeArgs + // wasm is gzipped in parseStoreCodeArgs // checksum generation will be decoupled here // reference https://github.com/CosmWasm/wasmvm/issues/359 - checksum := sha256.Sum256(wasm) + raw, err := ioutils.Uncompress(gzippedWasm, uint64(types.MaxWasmSize)) + if err != nil { + return "", "", nil, fmt.Errorf("invalid zip: %w", err) + } + checksum := sha256.Sum256(raw) if !bytes.Equal(checksum[:], codeHash) { return "", "", nil, fmt.Errorf("code-hash mismatch: %X, checksum: %X", codeHash, checksum) } diff --git a/x/wasm/client/cli/gov_tx_test.go b/x/wasm/client/cli/gov_tx_test.go index 265cad4939..bc498f0fbe 100644 --- a/x/wasm/client/cli/gov_tx_test.go +++ b/x/wasm/client/cli/gov_tx_test.go @@ -101,7 +101,7 @@ func TestParseCodeInfoFlags(t *testing.T) { correctSource := "https://github.com/CosmWasm/wasmd/blob/main/x/wasm/keeper/testdata/hackatom.wasm" correctBuilderRef := "cosmwasm/workspace-optimizer:0.12.9" - wasmBin, err := os.ReadFile("../../keeper/testdata/hackatom.wasm") + wasmBin, err := os.ReadFile("../../keeper/testdata/hackatom.wasm.gzip") require.NoError(t, err) checksumStr := "beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b" diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index 479081bcf3..2f6dbf2fe4 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -99,6 +99,7 @@ func StoreCodeCmd() *cobra.Command { return cmd } +// Prepares MsgStoreCode object from flags with gzipped wasm byte code field func parseStoreCodeArgs(file string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgStoreCode, error) { wasm, err := os.ReadFile(file) if err != nil { diff --git a/x/wasm/client/cli/tx_test.go b/x/wasm/client/cli/tx_test.go index 888df2b51f..8d6dfcf840 100644 --- a/x/wasm/client/cli/tx_test.go +++ b/x/wasm/client/cli/tx_test.go @@ -1,11 +1,14 @@ package cli import ( + "encoding/hex" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/CosmWasm/wasmd/x/wasm/ioutils" "github.com/CosmWasm/wasmd/x/wasm/types" ) @@ -57,3 +60,65 @@ func TestParseAccessConfigFlags(t *testing.T) { }) } } + +func TestParseVerificationFlags(t *testing.T) { + mySender := sdk.MustAccAddressFromBech32("cosmos1wyqh3n50ecatjg4vww5crmtd0nmyzusnwckw4at4gluc0m5m477q4arfek") + + specs := map[string]struct { + srcPath string + args []string + expErr bool + expSource string + expBuilder string + expCodeHash string + }{ + "gov store zipped": { + srcPath: "../../keeper/testdata/hackatom.wasm.gzip", + args: []string{ + "--instantiate-everybody=true", "--code-hash=beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b", + "--code-source-url=https://example.com", "--builder=cosmwasm/workspace-optimizer:0.12.11", + }, + expBuilder: "cosmwasm/workspace-optimizer:0.12.11", + expSource: "https://example.com", + expCodeHash: "beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b", + }, + "gov store raw": { + srcPath: "../../keeper/testdata/hackatom.wasm", + args: []string{ + "--instantiate-everybody=true", "--code-hash=beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b", + "--code-source-url=https://example.com", "--builder=cosmwasm/workspace-optimizer:0.12.11", + }, + expBuilder: "cosmwasm/workspace-optimizer:0.12.11", + expSource: "https://example.com", + expCodeHash: "beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b", + }, + "gov store checksum mismatch": { + srcPath: "../../keeper/testdata/hackatom.wasm", + args: []string{ + "--instantiate-everybody=true", "--code-hash=0000de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b", + "--code-source-url=https://example.com", "--builder=cosmwasm/workspace-optimizer:0.12.11", + }, + expErr: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + flagSet := ProposalStoreCodeCmd().Flags() + require.NoError(t, flagSet.Parse(spec.args)) + + gotMsg, err := parseStoreCodeArgs(spec.srcPath, mySender, flagSet) + require.NoError(t, err) + require.True(t, ioutils.IsGzip(gotMsg.WASMByteCode)) + + gotSource, gotBuilder, gotCodeHash, gotErr := parseVerificationFlags(gotMsg.WASMByteCode, flagSet) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + assert.Equal(t, spec.expSource, gotSource) + assert.Equal(t, spec.expBuilder, gotBuilder) + assert.Equal(t, spec.expCodeHash, hex.EncodeToString(gotCodeHash)) + }) + } +} diff --git a/x/wasm/keeper/proposal_integration_test.go b/x/wasm/keeper/proposal_integration_test.go index ff784f76d5..9f7e49deef 100644 --- a/x/wasm/keeper/proposal_integration_test.go +++ b/x/wasm/keeper/proposal_integration_test.go @@ -27,20 +27,31 @@ func TestStoreCodeProposal(t *testing.T) { CodeUploadAccess: types.AllowNobody, InstantiateDefaultPermission: types.AccessTypeNobody, }) - wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") + rawWasmCode, err := os.ReadFile("./testdata/hackatom.wasm") + require.NoError(t, err) + gzippedWasmCode, err := os.ReadFile("./testdata/hackatom.wasm.gzip") require.NoError(t, err) checksum, err := hex.DecodeString("beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b") require.NoError(t, err) specs := map[string]struct { codeID int64 + code []byte unpinCode bool }{ "upload with pinning (default)": { unpinCode: false, + code: rawWasmCode, }, "upload with code unpin": { unpinCode: true, + code: rawWasmCode, + }, + "upload with raw wasm code": { + code: rawWasmCode, + }, + "upload with zipped wasm code": { + code: gzippedWasmCode, }, } @@ -51,7 +62,7 @@ func TestStoreCodeProposal(t *testing.T) { src := types.StoreCodeProposalFixture(func(p *types.StoreCodeProposal) { p.RunAs = myActorAddress - p.WASMByteCode = wasmCode + p.WASMByteCode = spec.code p.UnpinCode = spec.unpinCode p.CodeHash = checksum }) @@ -73,7 +84,7 @@ func TestStoreCodeProposal(t *testing.T) { storedCode, err := wasmKeeper.GetByteCode(ctx, 1) require.NoError(t, err) - assert.Equal(t, wasmCode, storedCode) + assert.Equal(t, rawWasmCode, storedCode) }) } }