Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement improvements to new address generation #1014

Merged
merged 20 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions contrib/local/02-contracts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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,16 @@ 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 instantiate "$CODE_ID" "$INIT" --admin="$(wasmd keys show validator -a)" \
--from validator --amount="100ustake" --label "local0.1.0" \
--salt=$(echo -n "testing" | xxd -ps) --fix-msg \
alpe marked this conversation as resolved.
Show resolved Hide resolved
--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

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.
alpe marked this conversation as resolved.
Show resolved Hide resolved
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 {
alpe marked this conversation as resolved.
Show resolved Hide resolved
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