Skip to content

Commit

Permalink
Implement contract state rent
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen committed Jun 26, 2023
1 parent f6a5c6e commit 58c85e8
Show file tree
Hide file tree
Showing 30 changed files with 2,349 additions and 685 deletions.
25 changes: 25 additions & 0 deletions proto/cosmwasm/wasm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ service Query {
rpc PinnedCodes(QueryPinnedCodesRequest) returns (QueryPinnedCodesResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/codes/pinned";
}

rpc RentInfo(QueryRentInfoRequest) returns (QueryRentInfoResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/rent/{address}";
}

rpc StateSize(QueryStateSizeRequest) returns (QueryStateSizeResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/size/{address}";
}
}

// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC
Expand Down Expand Up @@ -222,3 +230,20 @@ message QueryPinnedCodesResponse {
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryRentInfoRequest {
string address = 1;
}

message QueryRentInfoResponse {
uint64 balance = 1;
uint64 last_charged_block = 2;
}

message QueryStateSizeRequest {
string address = 1;
}

message QueryStateSizeResponse {
uint64 state_size = 1;
}
10 changes: 10 additions & 0 deletions proto/cosmwasm/wasm/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ service Msg {
rpc UpdateAdmin(MsgUpdateAdmin) returns (MsgUpdateAdminResponse);
// ClearAdmin removes any admin stored for a smart contract
rpc ClearAdmin(MsgClearAdmin) returns (MsgClearAdminResponse);
// DepositRent adds to the rent balance for a smart contract
rpc DepositRent(MsgDepositRent) returns (MsgDepositRentResponse);
}

// MsgStoreCode submit Wasm code to the system
Expand Down Expand Up @@ -133,3 +135,11 @@ message MsgClearAdmin {

// MsgClearAdminResponse returns empty data
message MsgClearAdminResponse {}

message MsgDepositRent {
string contract = 1;
uint64 amount = 2;
string sender = 3;
}

message MsgDepositRentResponse {}
9 changes: 9 additions & 0 deletions proto/cosmwasm/wasm/v1/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ message Params {
];
AccessType instantiate_default_permission = 2
[ (gogoproto.moretags) = "yaml:\"instantiate_default_permission\"" ];
// rent price per byte per block
string unit_rent_price = 3
[
(gogoproto.moretags) = "yaml:\"unit_gas_price\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string rent_denom = 4
[ (gogoproto.moretags) = "yaml:\"rent_denom\"" ];
}

// CodeInfo is data for the uploaded contract WASM code
Expand Down
93 changes: 93 additions & 0 deletions store/sized/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package sized

import (
"io"

"github.com/cosmos/cosmos-sdk/store/types"
)

var _ types.KVStore = &Store{}

type Store struct {
parent types.KVStore
sizeChange int64
}

func NewStore(parent types.KVStore) *Store {
return &Store{parent: parent}
}
func (store *Store) GetWorkingHash() ([]byte, error) {
return store.parent.GetWorkingHash()
}

func (s *Store) GetSizeChanged() int64 {
return s.sizeChange
}

func (s *Store) Get(key []byte) []byte {
value := s.parent.Get(key)
return value
}

func (s *Store) Set(key []byte, value []byte) {
oldValue := s.Get(key)
if oldValue != nil {
// reduce size due to overwrite
s.sizeChange -= int64(len(key))
s.sizeChange -= int64(len(oldValue))
}
s.parent.Set(key, value)
if key != nil {
s.sizeChange += int64(len(key))
}
if value != nil {
s.sizeChange += int64(len(value))
}
}

func (s *Store) Delete(key []byte) {
// has to perform a read here to know the size change
value := s.Get(key)
s.parent.Delete(key)
if value != nil {
// only reduce size if the key used to have a value
s.sizeChange -= int64(len(key))
s.sizeChange -= int64(len(value))
}
}

func (s *Store) Has(key []byte) bool {
return s.parent.Has(key)
}

func (s *Store) Iterator(start, end []byte) types.Iterator {
return s.parent.Iterator(start, end)
}

func (s *Store) ReverseIterator(start, end []byte) types.Iterator {
return s.parent.ReverseIterator(start, end)
}

// GetStoreType implements the KVStore interface. It returns the underlying
// KVStore type.
func (s *Store) GetStoreType() types.StoreType {
return s.parent.GetStoreType()
}

// CacheWrap implements the KVStore interface. It panics as a Store
// cannot be cache wrapped.
func (s *Store) CacheWrap(_ types.StoreKey) types.CacheWrap {
panic("cannot CacheWrap a ListenKVStore")
}

// CacheWrapWithTrace implements the KVStore interface. It panics as a
// Store cannot be cache wrapped.
func (s *Store) CacheWrapWithTrace(_ types.StoreKey, _ io.Writer, _ types.TraceContext) types.CacheWrap {
panic("cannot CacheWrapWithTrace a ListenKVStore")
}

// CacheWrapWithListeners implements the KVStore interface. It panics as a
// Store cannot be cache wrapped.
func (s *Store) CacheWrapWithListeners(_ types.StoreKey, _ []types.WriteListener) types.CacheWrap {
panic("cannot CacheWrapWithListeners a ListenKVStore")
}
2 changes: 2 additions & 0 deletions x/wasm/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ type (
MsgClearAdmin = types.MsgClearAdmin
MsgWasmIBCCall = types.MsgIBCSend
MsgClearAdminResponse = types.MsgClearAdminResponse
MsgDepositRent = types.MsgDepositRent
MsgDepositRentResponse = types.MsgDepositRentResponse
MsgServer = types.MsgServer
Model = types.Model
CodeInfo = types.CodeInfo
Expand Down
32 changes: 32 additions & 0 deletions x/wasm/client/cli/new_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,35 @@ func ClearContractAdminCmd() *cobra.Command {
flags.AddTxFlagsToCmd(cmd)
return cmd
}

func DepositRentCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "deposit-rent [contract_addr_bech32] [amount]",
Short: "deposits rent for a contract",
Aliases: []string{"deposit"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

amount, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return err
}

msg := types.MsgDepositRent{
Sender: clientCtx.GetFromAddress().String(),
Contract: args[0],
Amount: amount,
}
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
74 changes: 74 additions & 0 deletions x/wasm/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func GetQueryCmd() *cobra.Command {
GetCmdGetContractState(),
GetCmdListPinnedCode(),
GetCmdLibVersion(),
GetCmdGetRentInfo(),
GetCmdGetStateSize(),
)
return queryCmd
}
Expand Down Expand Up @@ -481,6 +483,78 @@ func GetCmdListPinnedCode() *cobra.Command {
return cmd
}

func GetCmdGetRentInfo() *cobra.Command {
cmd := &cobra.Command{
Use: "rent-info [bech32_address]",
Short: "Gets the rent info for a contract given its address",
Long: "Gets the rent info for a contract given its address",
Aliases: []string{"rent"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.RentInfo(
context.Background(),
&types.QueryRentInfoRequest{
Address: args[0],
},
)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}

func GetCmdGetStateSize() *cobra.Command {
cmd := &cobra.Command{
Use: "state-size [bech32_address]",
Short: "Gets total number of bytes for a contract state given its address",
Long: "Gets total number of bytes for a contract state given its address",
Aliases: []string{"ss"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.StateSize(
context.Background(),
&types.QueryStateSizeRequest{
Address: args[0],
},
)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}

type argumentDecoder struct {
// dec is the default decoder
dec func(string) ([]byte, error)
Expand Down
1 change: 1 addition & 0 deletions x/wasm/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func GetTxCmd() *cobra.Command {
MigrateContractCmd(),
UpdateContractAdminCmd(),
ClearContractAdminCmd(),
DepositRentCmd(),
)
return txCmd
}
Expand Down
2 changes: 2 additions & 0 deletions x/wasm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func NewHandler(k types.ContractOpsKeeper) sdk.Handler {
res, err = msgServer.UpdateAdmin(sdk.WrapSDKContext(ctx), msg)
case *MsgClearAdmin:
res, err = msgServer.ClearAdmin(sdk.WrapSDKContext(ctx), msg)
case *MsgDepositRent:
res, err = msgServer.DepositRent(sdk.WrapSDKContext(ctx), msg)
default:
errMsg := fmt.Sprintf("unrecognized wasm message type: %T", msg)
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
Expand Down
15 changes: 13 additions & 2 deletions x/wasm/ibctesting/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ type PacketAck struct {
Ack []byte
}

<<<<<<< HEAD
=======
var _ tmtypes.PrivValidator = PV{}

// MockPV implements PrivValidator without any safety or persistence.
// Only use it for testing.
>>>>>>> 0f58bca (Implement contract state rent)
type PV struct {
PrivKey cryptotypes.PrivKey
}
Expand Down Expand Up @@ -457,8 +464,6 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64,
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
ProposerAddress: tmValSet.Proposer.Address, //nolint:staticcheck
}
hhash := tmHeader.Hash()
blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set")))
voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet)
for i, val := range tmValSet.Validators {
voteSet.AddVote(&tmtypes.Vote{
Expand Down Expand Up @@ -549,8 +554,11 @@ func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.Scope
require.NoError(chain.t, err)
}

<<<<<<< HEAD
wasmApp := chain.App.(*TestingAppDecorator).WasmApp
wasmApp.SetDeliverStateToCommit()
=======
>>>>>>> 0f58bca (Implement contract state rent)
chain.App.Commit(context.Background())

chain.NextBlock()
Expand Down Expand Up @@ -579,8 +587,11 @@ func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.Sc
require.NoError(chain.t, err)
}

<<<<<<< HEAD
wasmApp := chain.App.(*TestingAppDecorator).WasmApp
wasmApp.SetDeliverStateToCommit()
=======
>>>>>>> 0f58bca (Implement contract state rent)
chain.App.Commit(context.Background())

chain.NextBlock()
Expand Down
5 changes: 5 additions & 0 deletions x/wasm/keeper/contract_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type decoratedKeeper interface {
Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error)
setContractInfoExtension(ctx sdk.Context, contract sdk.AccAddress, extra types.ContractInfoExtension) error
setAccessConfig(ctx sdk.Context, codeID uint64, config types.AccessConfig) error
DepositRent(ctx sdk.Context, contractAddress sdk.AccAddress, senderAddress sdk.AccAddress, amount int64) error
}

type PermissionedKeeper struct {
Expand Down Expand Up @@ -84,3 +85,7 @@ func (p PermissionedKeeper) SetContractInfoExtension(ctx sdk.Context, contract s
func (p PermissionedKeeper) SetAccessConfig(ctx sdk.Context, codeID uint64, config types.AccessConfig) error {
return p.nested.setAccessConfig(ctx, codeID, config)
}

func (p PermissionedKeeper) DepositRent(ctx sdk.Context, contractAddress sdk.AccAddress, senderAddress sdk.AccAddress, amount int64) error {
return p.nested.DepositRent(ctx, contractAddress, senderAddress, amount)
}
Loading

0 comments on commit 58c85e8

Please sign in to comment.