Skip to content

Commit

Permalink
Implement improvements to new address generation (#1014)
Browse files Browse the repository at this point in the history
* Revert default instance address generation to classic sequence based method

 Please enter the commit message for your changes. Lines starting

* Start re-adding classic address generator

* Extract address generation to file; minor updates

* Review comments

* Set max salt size

* Support predictable address on instantiation

* Switch attribute order for backwards compatiblity

* Fix salt param check in CLI

* Enable tests

* Add more tests

* Minor fix

* Remove migration

* Better cli description

* Fix init message length prefix

* Add sanity checks to address generation and minor updates

* Reduce max length in tests for CI

* CLI and address generation updates

* Add test vectors

* Minor updates

* Fix cli long doc
  • Loading branch information
alpe authored Sep 22, 2022
1 parent 54fec05 commit 9c5ebbb
Show file tree
Hide file tree
Showing 36 changed files with 2,271 additions and 552 deletions.
16 changes: 15 additions & 1 deletion contrib/local/02-contracts.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
set -o errexit -o nounset -o pipefail -x

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

Expand All @@ -9,7 +9,10 @@ RESP=$(wasmd tx wasm store "$DIR/../../x/wasm/keeper/testdata/hackatom.wasm" \
--from validator --gas 1500000 -y --chain-id=testing --node=http://localhost:26657 -b block -o json)

CODE_ID=$(echo "$RESP" | jq -r '.logs[0].events[1].attributes[-1].value')
CODE_HASH=$(echo "$RESP" | jq -r '.logs[0].events[1].attributes[-2].value')
echo "* Code id: $CODE_ID"
echo "* Code checksum: $CODE_HASH"

echo "* Download code"
TMPDIR=$(mktemp -t wasmdXXXXXX)
wasmd q wasm code "$CODE_ID" "$TMPDIR"
Expand All @@ -27,6 +30,17 @@ wasmd tx wasm instantiate "$CODE_ID" "$INIT" --admin="$(wasmd keys show validato

CONTRACT=$(wasmd query wasm list-contract-by-code "$CODE_ID" -o json | jq -r '.contracts[-1]')
echo "* Contract address: $CONTRACT"

echo "## Create new contract instance with predictable address"
wasmd tx wasm instantiate2 "$CODE_ID" "$INIT" $(echo -n "testing" | xxd -ps) \
--admin="$(wasmd keys show validator -a)" \
--from validator --amount="100ustake" --label "local0.1.0" \
--fix-msg \
--gas 1000000 -y --chain-id=testing -b block -o json | jq

predictedAdress=$(wasmd q wasm build-address "$CODE_HASH" $(wasmd keys show validator -a) $(echo -n "testing" | xxd -ps) "$INIT")
wasmd q wasm contract "$predictedAdress" -o json | jq

echo "### Query all"
RESP=$(wasmd query wasm contract-state all "$CONTRACT" -o json)
echo "$RESP" | jq
Expand Down
50 changes: 46 additions & 4 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- [MsgExecuteContract](#cosmwasm.wasm.v1.MsgExecuteContract)
- [MsgExecuteContractResponse](#cosmwasm.wasm.v1.MsgExecuteContractResponse)
- [MsgInstantiateContract](#cosmwasm.wasm.v1.MsgInstantiateContract)
- [MsgInstantiateContract2](#cosmwasm.wasm.v1.MsgInstantiateContract2)
- [MsgInstantiateContract2Response](#cosmwasm.wasm.v1.MsgInstantiateContract2Response)
- [MsgInstantiateContractResponse](#cosmwasm.wasm.v1.MsgInstantiateContractResponse)
- [MsgMigrateContract](#cosmwasm.wasm.v1.MsgMigrateContract)
- [MsgMigrateContractResponse](#cosmwasm.wasm.v1.MsgMigrateContractResponse)
Expand Down Expand Up @@ -327,7 +329,7 @@ MsgExecuteContractResponse returns execution result data.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `data` | [bytes](#bytes) | | Data contains base64-encoded bytes to returned from the contract |
| `data` | [bytes](#bytes) | | Data contains bytes to returned from the contract |



Expand Down Expand Up @@ -355,6 +357,45 @@ code id.



<a name="cosmwasm.wasm.v1.MsgInstantiateContract2"></a>

### MsgInstantiateContract2
MsgInstantiateContract2 create a new smart contract instance for the given
code id with a predicable address.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `sender` | [string](#string) | | Sender is the that actor that signed the messages |
| `admin` | [string](#string) | | Admin is an optional address that can execute migrations |
| `code_id` | [uint64](#uint64) | | CodeID is the reference to the stored WASM code |
| `label` | [string](#string) | | Label is optional metadata to be stored with a contract 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 |
| `salt` | [bytes](#bytes) | | Salt is an arbitrary value provided by the sender. Size can be 1 to 64. |
| `fix_msg` | [bool](#bool) | | FixMsg include the msg value into the hash for the predictable address. Default is false |






<a name="cosmwasm.wasm.v1.MsgInstantiateContract2Response"></a>

### MsgInstantiateContract2Response
MsgInstantiateContract2Response return instantiation result data


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `address` | [string](#string) | | Address is the bech32 address of the new contract instance. |
| `data` | [bytes](#bytes) | | Data contains bytes to returned from the contract |






<a name="cosmwasm.wasm.v1.MsgInstantiateContractResponse"></a>

### MsgInstantiateContractResponse
Expand All @@ -364,7 +405,7 @@ MsgInstantiateContractResponse return instantiation result data
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `address` | [string](#string) | | Address is the bech32 address of the new contract instance. |
| `data` | [bytes](#bytes) | | Data contains base64-encoded bytes to returned from the contract |
| `data` | [bytes](#bytes) | | Data contains bytes to returned from the contract |



Expand Down Expand Up @@ -478,7 +519,8 @@ Msg defines the wasm Msg service.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `StoreCode` | [MsgStoreCode](#cosmwasm.wasm.v1.MsgStoreCode) | [MsgStoreCodeResponse](#cosmwasm.wasm.v1.MsgStoreCodeResponse) | StoreCode to submit Wasm code to the system | |
| `InstantiateContract` | [MsgInstantiateContract](#cosmwasm.wasm.v1.MsgInstantiateContract) | [MsgInstantiateContractResponse](#cosmwasm.wasm.v1.MsgInstantiateContractResponse) | Instantiate creates a new smart contract instance for the given code id. | |
| `InstantiateContract` | [MsgInstantiateContract](#cosmwasm.wasm.v1.MsgInstantiateContract) | [MsgInstantiateContractResponse](#cosmwasm.wasm.v1.MsgInstantiateContractResponse) | InstantiateContract creates a new smart contract instance for the given code id. | |
| `InstantiateContract2` | [MsgInstantiateContract2](#cosmwasm.wasm.v1.MsgInstantiateContract2) | [MsgInstantiateContract2Response](#cosmwasm.wasm.v1.MsgInstantiateContract2Response) | InstantiateContract2 creates a new smart contract instance for the given code id with a predictable address | |
| `ExecuteContract` | [MsgExecuteContract](#cosmwasm.wasm.v1.MsgExecuteContract) | [MsgExecuteContractResponse](#cosmwasm.wasm.v1.MsgExecuteContractResponse) | Execute submits the given message data to a smart contract | |
| `MigrateContract` | [MsgMigrateContract](#cosmwasm.wasm.v1.MsgMigrateContract) | [MsgMigrateContractResponse](#cosmwasm.wasm.v1.MsgMigrateContractResponse) | Migrate runs a code upgrade/ downgrade for a smart contract | |
| `UpdateAdmin` | [MsgUpdateAdmin](#cosmwasm.wasm.v1.MsgUpdateAdmin) | [MsgUpdateAdminResponse](#cosmwasm.wasm.v1.MsgUpdateAdminResponse) | UpdateAdmin sets a new admin for a smart contract | |
Expand Down Expand Up @@ -560,7 +602,7 @@ order. The intention is to have more human readable data that is auditable.
| ----- | ---- | ----- | ----------- |
| `store_code` | [MsgStoreCode](#cosmwasm.wasm.v1.MsgStoreCode) | | |
| `instantiate_contract` | [MsgInstantiateContract](#cosmwasm.wasm.v1.MsgInstantiateContract) | | |
| `execute_contract` | [MsgExecuteContract](#cosmwasm.wasm.v1.MsgExecuteContract) | | |
| `execute_contract` | [MsgExecuteContract](#cosmwasm.wasm.v1.MsgExecuteContract) | | MsgInstantiateContract2 intentionally not supported see https://github.com/CosmWasm/wasmd/issues/987 |



Expand Down
2 changes: 2 additions & 0 deletions proto/cosmwasm/wasm/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ message GenesisState {
MsgStoreCode store_code = 1;
MsgInstantiateContract instantiate_contract = 2;
MsgExecuteContract execute_contract = 3;
// MsgInstantiateContract2 intentionally not supported
// see https://github.com/CosmWasm/wasmd/issues/987
}
}
}
Expand Down
45 changes: 42 additions & 3 deletions proto/cosmwasm/wasm/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ option (gogoproto.goproto_getters_all) = false;
service Msg {
// StoreCode to submit Wasm code to the system
rpc StoreCode(MsgStoreCode) returns (MsgStoreCodeResponse);
// Instantiate creates a new smart contract instance for the given code id.
// InstantiateContract creates a new smart contract instance for the given
// code id.
rpc InstantiateContract(MsgInstantiateContract)
returns (MsgInstantiateContractResponse);
// InstantiateContract2 creates a new smart contract instance for the given
// code id with a predictable address
rpc InstantiateContract2(MsgInstantiateContract2)
returns (MsgInstantiateContract2Response);
// Execute submits the given message data to a smart contract
rpc ExecuteContract(MsgExecuteContract) returns (MsgExecuteContractResponse);
// Migrate runs a code upgrade/ downgrade for a smart contract
Expand Down Expand Up @@ -64,11 +69,45 @@ message MsgInstantiateContract {
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}

// MsgInstantiateContract2 create a new smart contract instance for the given
// code id with a predicable address.
message MsgInstantiateContract2 {
// Sender is the that actor that signed the messages
string sender = 1;
// Admin is an optional address that can execute migrations
string admin = 2;
// CodeID is the reference to the stored WASM code
uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ];
// Label is optional metadata to be stored with a contract instance.
string label = 4;
// Msg json encoded message to be passed to the contract on instantiation
bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ];
// Funds coins that are transferred to the contract on instantiation
repeated cosmos.base.v1beta1.Coin funds = 6 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Salt is an arbitrary value provided by the sender. Size can be 1 to 64.
bytes salt = 7;
// FixMsg include the msg value into the hash for the predictable address.
// Default is false
bool fix_msg = 8;
}

// MsgInstantiateContractResponse return instantiation result data
message MsgInstantiateContractResponse {
// Address is the bech32 address of the new contract instance.
string address = 1;
// Data contains base64-encoded bytes to returned from the contract
// Data contains bytes to returned from the contract
bytes data = 2;
}

// MsgInstantiateContract2Response return instantiation result data
message MsgInstantiateContract2Response {
// Address is the bech32 address of the new contract instance.
string address = 1;
// Data contains bytes to returned from the contract
bytes data = 2;
}

Expand All @@ -89,7 +128,7 @@ message MsgExecuteContract {

// MsgExecuteContractResponse returns execution result data.
message MsgExecuteContractResponse {
// Data contains base64-encoded bytes to returned from the contract
// Data contains bytes to returned from the contract
bytes data = 1;
}

Expand Down
2 changes: 2 additions & 0 deletions x/wasm/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var (
ErrQueryFailed = types.ErrQueryFailed
ErrInvalidMsg = types.ErrInvalidMsg
KeyLastCodeID = types.KeyLastCodeID
KeyLastInstanceID = types.KeyLastInstanceID
CodeKeyPrefix = types.CodeKeyPrefix
ContractKeyPrefix = types.ContractKeyPrefix
ContractStorePrefix = types.ContractStorePrefix
Expand All @@ -101,6 +102,7 @@ type (
MsgStoreCode = types.MsgStoreCode
MsgStoreCodeResponse = types.MsgStoreCodeResponse
MsgInstantiateContract = types.MsgInstantiateContract
MsgInstantiateContract2 = types.MsgInstantiateContract2
MsgInstantiateContractResponse = types.MsgInstantiateContractResponse
MsgExecuteContract = types.MsgExecuteContract
MsgExecuteContractResponse = types.MsgExecuteContractResponse
Expand Down
65 changes: 25 additions & 40 deletions x/wasm/client/cli/genesis_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import (
"errors"
"fmt"

"github.com/CosmWasm/wasmd/x/wasm/ioutils"

"github.com/CosmWasm/wasmd/x/wasm/keeper"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
Expand All @@ -24,6 +20,8 @@ import (
"github.com/spf13/cobra"
tmtypes "github.com/tendermint/tendermint/types"

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

Expand Down Expand Up @@ -133,7 +131,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator Genesi
return fmt.Errorf("permissions were not granted for %s", senderAddr)
}
state.GenMsgs = append(state.GenMsgs, types.GenesisState_GenMsgs{
Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &msg},
Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: msg},
})
return nil
})
Expand Down Expand Up @@ -238,10 +236,7 @@ func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *c
return err
}
state := g.WasmModuleState
all, err := GetAllContracts(state)
if err != nil {
return err
}
all := GetAllContracts(state)
return printJSONOutput(cmd, all)
},
}
Expand Down Expand Up @@ -313,18 +308,7 @@ type ContractMeta struct {
Info types.ContractInfo `json:"info"`
}

// returns nil when not found
func codeHashByID(state *types.GenesisState, codeID uint64) []byte {
codes := GetAllCodes(state)
for _, v := range codes {
if v.CodeID == codeID {
return v.Info.CodeHash
}
}
return nil
}

func GetAllContracts(state *types.GenesisState) ([]ContractMeta, error) {
func GetAllContracts(state *types.GenesisState) []ContractMeta {
all := make([]ContractMeta, len(state.Contracts))
for i, c := range state.Contracts {
all[i] = ContractMeta{
Expand All @@ -333,28 +317,22 @@ func GetAllContracts(state *types.GenesisState) ([]ContractMeta, error) {
}
}
// add inflight
seq := contractSeqValue(state)
for _, m := range state.GenMsgs {
if msg := m.GetInstantiateContract(); msg != nil {
senderAddr, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
panic(fmt.Sprintf("unsupported address %q: %s", msg.Sender, err))
}
codeHash := codeHashByID(state, msg.CodeID)
if codeHash == nil {
return nil, types.ErrNotFound.Wrapf("hash for code-id: %d", msg.CodeID)
}
all = append(all, ContractMeta{
ContractAddress: keeper.BuildContractAddress(codeHash, senderAddr, msg.Label).String(),
ContractAddress: keeper.BuildContractAddressClassic(msg.CodeID, seq).String(),
Info: types.ContractInfo{
CodeID: msg.CodeID,
Creator: msg.Sender,
Admin: msg.Admin,
Label: msg.Label,
},
})
seq++
}
}
return all, nil
return all
}

func hasAccountBalance(cmd *cobra.Command, appState map[string]json.RawMessage, sender sdk.AccAddress, coins sdk.Coins) (bool, error) {
Expand All @@ -381,19 +359,13 @@ func hasContract(state *types.GenesisState, contractAddr string) bool {
return true
}
}
seq := contractSeqValue(state)
for _, m := range state.GenMsgs {
if msg := m.GetInstantiateContract(); msg != nil {
senderAddr, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
panic(fmt.Sprintf("unsupported address %q: %s", msg.Sender, err))
}
hash := codeHashByID(state, msg.CodeID)
if hash == nil {
panic(fmt.Sprintf("unknown code id: %d", msg.CodeID))
}
if keeper.BuildContractAddress(hash, senderAddr, msg.Label).String() == contractAddr {
if keeper.BuildContractAddressClassic(msg.CodeID, seq).String() == contractAddr {
return true
}
seq++
}
}
return false
Expand Down Expand Up @@ -486,6 +458,19 @@ func (x DefaultGenesisIO) AlterWasmModuleState(cmd *cobra.Command, callback func
return genutil.ExportGenesisFile(g.GenDoc, g.GenesisFile)
}

// contractSeqValue reads the contract sequence from the genesis or
// returns default start value used in the keeper
func contractSeqValue(state *types.GenesisState) uint64 {
var seq uint64 = 1
for _, s := range state.Sequences {
if bytes.Equal(s.IDKey, types.KeyLastInstanceID) {
seq = s.Value
break
}
}
return seq
}

// codeSeqValue reads the code sequence from the genesis or
// returns default start value used in the keeper
func codeSeqValue(state *types.GenesisState) uint64 {
Expand Down
Loading

0 comments on commit 9c5ebbb

Please sign in to comment.