Skip to content

Commit

Permalink
Make contract addresses predictable
Browse files Browse the repository at this point in the history
  • Loading branch information
alpe committed Sep 9, 2022
1 parent d9f9f91 commit ccb2fdd
Show file tree
Hide file tree
Showing 43 changed files with 1,175 additions and 513 deletions.
23 changes: 23 additions & 0 deletions app/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ func setup(t testing.TB, withGenesis bool, invCheckPeriod uint, opts ...wasm.Opt
return app, GenesisState{}
}

// Setup initializes a new WasmApp with DefaultNodeHome for integration tests
func Setup(isCheckTx bool, opts ...wasm.Option) *WasmApp {
db := dbm.NewMemDB()
app := NewWasmApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, MakeEncodingConfig(), wasm.EnableAllProposals, EmptyBaseAppOptions{}, opts)

if !isCheckTx {
genesisState := NewDefaultGenesisState()
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
if err != nil {
panic(err)
}

app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
}
return app
}

// SetupWithGenesisValSet initializes a new WasmApp with a validator set and genesis accounts
// that also act as delegators. For simplicity, each validator is bonded with a delegation
// of one consensus engine unit (10^6) in the default token of the WasmApp from first genesis
Expand Down
1 change: 1 addition & 0 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ MsgStoreCodeResponse returns store result data.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `code_id` | [uint64](#uint64) | | CodeID is the reference to the stored WASM code |
| `checksum` | [bytes](#bytes) | | Checksum is the sha256 hash of the stored code |



Expand Down
2 changes: 2 additions & 0 deletions proto/cosmwasm/wasm/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ message MsgStoreCode {
message MsgStoreCodeResponse {
// CodeID is the reference to the stored WASM code
uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ];
// Checksum is the sha256 hash of the stored code
bytes checksum = 2;
}

// MsgInstantiateContract create a new smart contract instance for the given
Expand Down
1 change: 0 additions & 1 deletion x/wasm/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ var (
ErrQueryFailed = types.ErrQueryFailed
ErrInvalidMsg = types.ErrInvalidMsg
KeyLastCodeID = types.KeyLastCodeID
KeyLastInstanceID = types.KeyLastInstanceID
CodeKeyPrefix = types.CodeKeyPrefix
ContractKeyPrefix = types.ContractKeyPrefix
ContractStorePrefix = types.ContractStorePrefix
Expand Down
76 changes: 43 additions & 33 deletions x/wasm/client/cli/genesis_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator Genesi
}

return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error {
// simple sanity check that sender has some balance although it may be consumed by appState previous message already
// simple sanity check that sender has some balance, although it may be consumed by appState previous message already
switch ok, err := hasAccountBalance(cmd, appState, senderAddr, msg.Funds); {
case err != nil:
return err
Expand All @@ -112,7 +112,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator Genesi
}

// does code id exists?
codeInfos, err := GetAllCodes(state)
codeInfos := GetAllCodes(state)
if err != nil {
return err
}
Expand Down Expand Up @@ -171,7 +171,7 @@ func GenesisExecuteContractCmd(defaultNodeHome string, genesisMutator GenesisMut
}

return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error {
// simple sanity check that sender has some balance although it may be consumed by appState previous message already
// simple sanity check that sender has some balance, although it may be consumed by appState previous message already
switch ok, err := hasAccountBalance(cmd, appState, senderAddr, msg.Funds); {
case err != nil:
return err
Expand Down Expand Up @@ -211,7 +211,7 @@ func GenesisListCodesCmd(defaultNodeHome string, genReader GenesisReader) *cobra
if err != nil {
return err
}
all, err := GetAllCodes(g.WasmModuleState)
all := GetAllCodes(g.WasmModuleState)
if err != nil {
return err
}
Expand All @@ -236,7 +236,10 @@ func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *c
return err
}
state := g.WasmModuleState
all := GetAllContracts(state)
all, err := GetAllContracts(state)
if err != nil {
return err
}
return printJSONOutput(cmd, all)
},
}
Expand All @@ -245,7 +248,7 @@ func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *c
return cmd
}

// clientCtx marshaller works only with proto or bytes so we marshal the output ourself
// clientCtx marshaller works only with proto or bytes, so we marshal the output ourselves
func printJSONOutput(cmd *cobra.Command, obj interface{}) error {
clientCtx := client.GetClientContextFromCmd(cmd)
bz, err := json.MarshalIndent(obj, "", " ")
Expand All @@ -260,7 +263,7 @@ type CodeMeta struct {
Info types.CodeInfo `json:"info"`
}

func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) {
func GetAllCodes(state *types.GenesisState) []CodeMeta {
all := make([]CodeMeta, len(state.Codes))
for i, c := range state.Codes {
all[i] = CodeMeta{
Expand All @@ -277,10 +280,7 @@ func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) {
accessConfig = *msg.InstantiatePermission
} else {
// default
creator, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return nil, fmt.Errorf("sender: %s", err)
}
creator := sdk.MustAccAddressFromBech32(msg.Sender)
accessConfig = state.Params.InstantiateDefaultPermission.With(creator)
}
hash := sha256.Sum256(msg.WASMByteCode)
Expand All @@ -295,15 +295,26 @@ func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) {
seq++
}
}
return all, nil
return all
}

type ContractMeta struct {
ContractAddress string `json:"contract_address"`
Info types.ContractInfo `json:"info"`
}

func GetAllContracts(state *types.GenesisState) []ContractMeta {
// 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) {
all := make([]ContractMeta, len(state.Contracts))
for i, c := range state.Contracts {
all[i] = ContractMeta{
Expand All @@ -312,22 +323,28 @@ func GetAllContracts(state *types.GenesisState) []ContractMeta {
}
}
// 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(msg.CodeID, seq).String(),
ContractAddress: keeper.BuildContractAddress(codeHash, senderAddr, msg.Label).String(),
Info: types.ContractInfo{
CodeID: msg.CodeID,
Creator: msg.Sender,
Admin: msg.Admin,
Label: msg.Label,
},
})
seq++
}
}
return all
return all, nil
}

func hasAccountBalance(cmd *cobra.Command, appState map[string]json.RawMessage, sender sdk.AccAddress, coins sdk.Coins) (bool, error) {
Expand All @@ -354,13 +371,19 @@ func hasContract(state *types.GenesisState, contractAddr string) bool {
return true
}
}
seq := contractSeqValue(state)
for _, m := range state.GenMsgs {
if msg := m.GetInstantiateContract(); msg != nil {
if keeper.BuildContractAddress(msg.CodeID, seq).String() == contractAddr {
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 {
return true
}
seq++
}
}
return false
Expand Down Expand Up @@ -453,19 +476,6 @@ 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

0 comments on commit ccb2fdd

Please sign in to comment.