Skip to content

Commit

Permalink
feat: Provide source, builder and codehash information in store code …
Browse files Browse the repository at this point in the history
…proposal message (CosmWasm#1072)

* Provide source, builder and codehash information in store code proposal message

* Make linter happy

* Update x/wasm/simulation/proposals.go

Co-authored-by: Alexander Peters <alpe@users.noreply.github.com>

* Update x/wasm/client/cli/gov_tx.go

* Update x/wasm/client/cli/gov_tx.go

* Bump github.com/cosmos/gogoproto from 1.4.2 to 1.4.3

Bumps [github.com/cosmos/gogoproto](https://github.com/cosmos/gogoproto) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/cosmos/gogoproto/releases)
- [Changelog](https://github.com/cosmos/gogoproto/blob/main/CHANGELOG.md)
- [Commits](cosmos/gogoproto@v1.4.2...v1.4.3)

---
updated-dependencies:
- dependency-name: github.com/cosmos/gogoproto
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update authz grant examples

* Enable larger wasm bytecode upload for gov proposals (CosmWasm#1095)

* Enable larger wasm bytecode upload for gov proposals

* Set max proposal wasm code size to 3MB

* Bump SDK to v0.45.11

* Fix license head

* Fix README header

* Bump version go to 1.19 (CosmWasm#1044)

* bump go 1.19

* add change log

* correct change log

* Provide source, builder and codehash information in store code proposal message

* Implement source, builder, code_info for StoreAndInstantiateProposal

* Apply review recommendations

* Make linter happy

* Fix tests

* Formatting only

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Alexander Peters <alpe@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Giancarlos Salas <giansalex@gmail.com>
Co-authored-by: pinosu <95283998+pinosu@users.noreply.github.com>
Co-authored-by: GNaD13 <89174180+GNaD13@users.noreply.github.com>
  • Loading branch information
6 people authored and Magicloud committed Jan 13, 2023
1 parent e18dd0f commit d07acd4
Show file tree
Hide file tree
Showing 17 changed files with 786 additions and 72 deletions.
6 changes: 6 additions & 0 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,9 @@ and instantiate the contract.
| `label` | [string](#string) | | Label is optional metadata to be stored with a constract instance. |
| `msg` | [bytes](#bytes) | | Msg json encoded message to be passed to the contract on instantiation |
| `funds` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | Funds coins that are transferred to the contract on instantiation |
| `source` | [string](#string) | | Source is the URL where the code is hosted |
| `builder` | [string](#string) | | Builder is the docker image used to build the code deterministically, used for smart contract verification |
| `code_hash` | [bytes](#bytes) | | CodeHash is the SHA256 sum of the code outputted by builder, used for smart contract verification |



Expand All @@ -1024,6 +1027,9 @@ StoreCodeProposal gov proposal content type to submit WASM code to the system
| `wasm_byte_code` | [bytes](#bytes) | | WASMByteCode can be raw or gzip compressed |
| `instantiate_permission` | [AccessConfig](#cosmwasm.wasm.v1.AccessConfig) | | InstantiatePermission to apply on contract creation, optional |
| `unpin_code` | [bool](#bool) | | UnpinCode code on upload, optional |
| `source` | [string](#string) | | Source is the URL where the code is hosted |
| `builder` | [string](#string) | | Builder is the docker image used to build the code deterministically, used for smart contract verification |
| `code_hash` | [bytes](#bytes) | | CodeHash is the SHA256 sum of the code outputted by builder, used for smart contract verification |



Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/cosmos/iavl v0.19.4
github.com/cosmos/ibc-go/v4 v4.2.0
github.com/cosmos/interchain-accounts v0.2.4
github.com/docker/distribution v2.8.1+incompatible
github.com/dvsekhvalnov/jose2go v1.5.0
github.com/gogo/protobuf v1.3.3
github.com/golang/protobuf v1.5.2
Expand Down Expand Up @@ -95,6 +96,7 @@ require (
github.com/minio/highwayhash v1.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down Expand Up @@ -587,6 +589,7 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
Expand Down
16 changes: 16 additions & 0 deletions proto/cosmwasm/wasm/v1/proposal.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ message StoreCodeProposal {
AccessConfig instantiate_permission = 7;
// UnpinCode code on upload, optional
bool unpin_code = 8;
// Source is the URL where the code is hosted
string source = 9;
// Builder is the docker image used to build the code deterministically, used
// for smart contract verification
string builder = 10;
// CodeHash is the SHA256 sum of the code outputted by builder, used for smart
// contract verification
bytes code_hash = 11;
}

// InstantiateContractProposal gov proposal content type to instantiate a
Expand Down Expand Up @@ -199,4 +207,12 @@ message StoreAndInstantiateContractProposal {
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Source is the URL where the code is hosted
string source = 11;
// Builder is the docker image used to build the code deterministically, used
// for smart contract verification
string builder = 12;
// CodeHash is the SHA256 sum of the code outputted by builder, used for smart
// contract verification
bytes code_hash = 13;
}
74 changes: 72 additions & 2 deletions x/wasm/client/cli/gov_tx.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package cli

import (
"bytes"
"crypto/sha256"
"fmt"
"net/url"
"strconv"
"strings"

"github.com/docker/distribution/reference"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -13,13 +18,14 @@ import (
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

"github.com/CosmWasm/wasmd/x/wasm/types"
)

func ProposalStoreCodeCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "wasm-store [wasm file] --title [text] --description [text] --run-as [address]",
Use: "wasm-store [wasm file] --title [text] --description [text] --run-as [address] --unpin-code [unpin_code] --source [source] --builder [builder] --code-hash [code_hash]",
Short: "Submit a wasm binary proposal",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -45,13 +51,20 @@ func ProposalStoreCodeCmd() *cobra.Command {
return err
}

source, builder, codeHash, err := parseVerificationFlags(src.WASMByteCode, cmd.Flags())
if err != nil {
return err
}
content := types.StoreCodeProposal{
Title: proposalTitle,
Description: proposalDescr,
RunAs: runAs,
WASMByteCode: src.WASMByteCode,
InstantiatePermission: src.InstantiatePermission,
UnpinCode: unpinCode,
Source: source,
Builder: builder,
CodeHash: codeHash,
}

msg, err := govtypes.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress())
Expand All @@ -73,6 +86,9 @@ func ProposalStoreCodeCmd() *cobra.Command {
cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional")
cmd.Flags().Bool(flagUnpinCode, false, "Unpin code on upload, optional")
cmd.Flags().StringSlice(flagInstantiateByAnyOfAddress, []string{}, "Any of the addresses can instantiate a contract from the code, optional")
cmd.Flags().String(flagSource, "", "Code Source URL is a valid absolute HTTPS URI to the contract's source code,")
cmd.Flags().String(flagBuilder, "", "Builder is a valid docker image name with tag, such as \"cosmwasm/workspace-optimizer:0.12.9\"")
cmd.Flags().BytesHex(flagCodeHash, nil, "CodeHash is the sha256 hash of the wasm code")

// proposal flags
cmd.Flags().String(cli.FlagTitle, "", "Title of proposal")
Expand All @@ -81,6 +97,48 @@ func ProposalStoreCodeCmd() *cobra.Command {
return cmd
}

func parseVerificationFlags(wasm []byte, flags *flag.FlagSet) (string, string, []byte, error) {
source, err := flags.GetString(flagSource)
if err != nil {
return "", "", nil, fmt.Errorf("source: %s", err)
}
builder, err := flags.GetString(flagBuilder)
if err != nil {
return "", "", nil, fmt.Errorf("builder: %s", err)
}
codeHash, err := flags.GetBytesHex(flagCodeHash)
if err != nil {
return "", "", nil, fmt.Errorf("codeHash: %s", err)
}

// if any set require others to be set
if len(source) != 0 || len(builder) != 0 || len(codeHash) != 0 {
if source == "" {
return "", "", nil, fmt.Errorf("source is required")
}
if _, err = url.ParseRequestURI(source); err != nil {
return "", "", nil, fmt.Errorf("source: %s", err)
}
if builder == "" {
return "", "", nil, fmt.Errorf("builder is required")
}
if _, err := reference.ParseDockerRef(builder); err != nil {
return "", "", nil, fmt.Errorf("builder: %s", err)
}
if len(codeHash) == 0 {
return "", "", nil, fmt.Errorf("code hash is required")
}
// wasm is unzipped in parseStoreCodeArgs
// checksum generation will be decoupled here
// reference https://github.com/CosmWasm/wasmvm/issues/359
checksum := sha256.Sum256(wasm)
if !bytes.Equal(checksum[:], codeHash) {
return "", "", nil, fmt.Errorf("code-hash mismatch: %X, checksum: %X", codeHash, checksum)
}
}
return source, builder, codeHash, nil
}

func ProposalInstantiateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address] --admin [address,optional] --amount [coins,optional]",
Expand Down Expand Up @@ -143,7 +201,8 @@ func ProposalInstantiateContractCmd() *cobra.Command {

func ProposalStoreAndInstantiateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "store-instantiate [wasm file] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address] --admin [address,optional] --amount [coins,optional]",
Use: "store-instantiate [wasm file] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address]" +
"--unpin-code [unpin_code,optional] --source [source,optional] --builder [builder,optional] --code-hash [code_hash,optional] --admin [address,optional] --amount [coins,optional]",
Short: "Submit and instantiate a wasm contract proposal",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -169,6 +228,11 @@ func ProposalStoreAndInstantiateContractCmd() *cobra.Command {
return err
}

source, builder, codeHash, err := parseVerificationFlags(src.WASMByteCode, cmd.Flags())
if err != nil {
return err
}

amountStr, err := cmd.Flags().GetString(flagAmount)
if err != nil {
return fmt.Errorf("amount: %s", err)
Expand Down Expand Up @@ -208,6 +272,9 @@ func ProposalStoreAndInstantiateContractCmd() *cobra.Command {
WASMByteCode: src.WASMByteCode,
InstantiatePermission: src.InstantiatePermission,
UnpinCode: unpinCode,
Source: source,
Builder: builder,
CodeHash: codeHash,
Admin: adminStr,
Label: label,
Msg: []byte(args[1]),
Expand All @@ -232,6 +299,9 @@ func ProposalStoreAndInstantiateContractCmd() *cobra.Command {
cmd.Flags().String(flagInstantiateNobody, "", "Nobody except the governance process can instantiate a contract from the code, optional")
cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional")
cmd.Flags().Bool(flagUnpinCode, false, "Unpin code on upload, optional")
cmd.Flags().String(flagSource, "", "Code Source URL is a valid absolute HTTPS URI to the contract's source code,")
cmd.Flags().String(flagBuilder, "", "Builder is a valid docker image name with tag, such as \"cosmwasm/workspace-optimizer:0.12.9\"")
cmd.Flags().BytesHex(flagCodeHash, nil, "CodeHash is the sha256 hash of the wasm code")
cmd.Flags().StringSlice(flagInstantiateByAnyOfAddress, []string{}, "Any of the addresses can instantiate a contract from the code, optional")
cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
Expand Down
61 changes: 61 additions & 0 deletions x/wasm/client/cli/gov_tx_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -95,3 +96,63 @@ func TestParseAccessConfigUpdates(t *testing.T) {
})
}
}

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")
require.NoError(t, err)

checksumStr := "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"

specs := map[string]struct {
args []string
expErr bool
}{
"source missing": {
args: []string{"--builder=" + correctBuilderRef, "--code-hash=" + checksumStr},
expErr: true,
},
"builder missing": {
args: []string{"--code-source-url=" + correctSource, "--code-hash=" + checksumStr},
expErr: true,
},
"code hash missing": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + correctBuilderRef},
expErr: true,
},
"source format wrong": {
args: []string{"--code-source-url=" + "format_wrong", "--builder=" + correctBuilderRef, "--code-hash=" + checksumStr},
expErr: true,
},
"builder format wrong": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + "format//", "--code-hash=" + checksumStr},
expErr: true,
},
"code hash wrong": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + correctBuilderRef, "--code-hash=" + "AA"},
expErr: true,
},
"happy path, none set": {
args: []string{},
expErr: false,
},
"happy path all set": {
args: []string{"--code-source-url=" + correctSource, "--builder=" + correctBuilderRef, "--code-hash=" + checksumStr},
expErr: false,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
flags := ProposalStoreCodeCmd().Flags()
require.NoError(t, flags.Parse(spec.args))
_, _, _, gotErr := parseVerificationFlags(wasmBin, flags)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
})
}
}
3 changes: 3 additions & 0 deletions x/wasm/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import (
const (
flagAmount = "amount"
flagLabel = "label"
flagSource = "code-source-url"
flagBuilder = "builder"
flagCodeHash = "code-hash"
flagAdmin = "admin"
flagNoAdmin = "no-admin"
flagFixMsg = "fix-msg"
Expand Down
Loading

0 comments on commit d07acd4

Please sign in to comment.