apply comments

This commit is contained in:
chengwenxi 2021-06-10 18:48:39 +08:00
parent 8004004b66
commit 51df2170f2

View File

@ -31,48 +31,148 @@ For example, NFTs can be grouped into collections in a collectibles module to de
The current design is based on the work done by [IRISnet team](https://github.com/irisnet/irismod/tree/master/modules/nft) and an older implementation in the [Cosmos repository](https://github.com/cosmos/modules/tree/master/incubator/nft).
## Decision
We will create a module `x/nft` which only stores NFTs by id and owner.
We will create a module `x/nft`, which provides the most basic storage, query functions for other upper-level modules to call. This module implements the OCAP security model in the [ADR 033](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-033-protobuf-inter-module-comm.md) protocol.
```proto
message NFT {
### Types
We define a general NFT model and `x/nft` module only stores NFTs by id and owner.
```go
type NFT struct{
string id = 1;
string owner = 2;
google.protobuf.Any data = 3;
Data data = 3;
}
// Data defines a minimum funtion set consistent with `ERC721Metadata` interface.
type Data interface {
Name() string // A descriptive name for a collection of NFTs
Symbol() string // An abbreviated name for NFTs
URI() string // A distinct Uniform Resource Identifier (URI) for a given asset
}
```
The NFT conforms to the following specifications:
* The Id is an immutable field used as a unique identifier. NFT identifiers don't currently have a naming convention but
can be used in association with existing Hub attributes, e.g., defining an NFT's identifier as an immutable Hub address allows its integration into existing Hub account management modules.
We envision that identifiers can accommodate mint and transfer actions.
The Id is also the primary index for storing NFTs.
* The Id is an immutable field used as a unique identifier. NFT identifiers don't currently have a naming convention but can be used in association with existing Hub attributes, e.g., defining an NFT's identifier as an immutable Hub address allows its integration into existing Hub account management modules.
We envision that identifiers can accommodate mint and transfer actions.
The Id is also the primary index for storing NFTs.
```
id (string) --> NFT (bytes)
```
* Owner: This mutable field represents the current account owner (on the Hub) of the NFT, i.e., the account that will have authorization
to perform various activities in the future. This can be a user, a module account, or potentially a future NFT module that adds capabilities.
Owner is also the secondary index for storing NFT IDs owned by an address
```
owner (address) --> []string
```
* Data: This mutable field allows attaching special information to the NFT, for example:
* metadata such as title of the work and URI
* immutable data and parameters (such actual NFT data, hash or seed for generators)
* mutable data and parameters that change when transferring or when certain criteria are met (such as provenance)
This ADR doesn't specify values that this field can take; however, best practices recommend upper-level NFT modules clearly specify its contents.
Although the value of this field doesn't provide additional context required to manage NFT records, which means that the field can technically be removed from the specification,
the field's existence allows basic informational/UI functionality.
The logic for transferring the ownership of an NFT to another address (no coins involved) should also be defined in this module
```go
func (k Keeper) TransferOwnership(nftID string, currentOwner, newOwner sdk.AccAddress) error
### `Msg` Service
```protobuf
service Msg {
rpc Mint(MsgMint) returns (MsgMintResponse);
rpc TransferOwnership(MsgTransferOwnership) returns (MsgTransferOwnershipResponse);
rpc Burn(MsgBurn) returns (MsgBurnResponse);
}
message MsgMint {
string id = 1;
string owner = 2;
google.protobuf.Any data = 3;
string minter = 4;
}
message MsgMintResponse {}
message MsgTransferOwnership {
string id = 1;
string sender = 2;
string reveiver = 3;
google.protobuf.Any data = 4;
}
message MsgTransferOwnershipResponse {}
message MsgBurn {
string id = 1;
string owner = 2;
}
message MsgBurnResponse {}
```
Other behaviors of NFT, including minting, burning and other business-logic implementation should be defined in other upper-level modules that import this NFT module.
`MsgMint` provides the ability to create a new nft.
`MsgTransferOwnership` is responsible for transferring the ownership of an NFT to another address (no coins involved).
`MsgBurn` provides the ability to destroy nft, thereby guaranteeing the uniqueness of cross-chain nft.
Other business-logic implementation should be defined in other upper-level modules that import this NFT module. The implementation example of the server is as follows:
```go
type msgServer struct{
k Keeper
}
func (k msgServer) Mint(ctx context.Context, msg *types.MsgMint) (*types.MsgMintResponse, error){
if k.has(msg.id) {
retutn sdkerrors.Wrapf(types.ErrExistedNFT, "%s", msg.id)
}
nft := types.NFT{msg.Id,msg.Owner,msg.Data}
owner, _ := sdk.AccAddressFromBech32(nft.Owner)
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(&nft)
store.Set(types.GetNFTKey(nft.Id), bz)
ownerStore := k.getOwnerStore(ctx, owner)
ownerStore.Set(types.MarshalNFTID(nft.Id), types.MarshalNFTID(nft.Id))
}
func (k msgServer) TransferOwnership(ctx context.Context, msg *types.MsgTransferOwnership) (*types.MsgTransferOwnershipResponse, error){
nft, has := k.GetNFT(ctx, msg.Id)
if !has {
return sdkerrors.Wrapf(types.ErrNotFoundNFT, "%s", msg.Id)
}
// remove nft from current owner store
currentOwnerStore := k.getOwnerStore(ctx, nft.Owner)
currentOwnerStore.Delete(types.MarshalNFTID(nft.Id))
nft.Data = msg.data
nft.Owner = msg.Receiver
k.SetNFT(ctx, nft)
return nil
}
func (k Keeper) Burn(ctx sdk.Context, msg *types.MsgBurn) error {
nft, has := k.GetNFT(ctx, msg.Id)
if !has {
return sdkerrors.Wrapf(types.ErrNotFoundNFT, "%s", msg.Id)
}
// delete nft
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetNFTKey(nft.Id))
owner, _ := sdk.AccAddressFromBech32(nft.Owner)
ownerStore := k.getOwnerStore(ctx, owner)
ownerStore.Delete(types.MarshalNFTID(nft.Id))
return nil
}
```
The upper application calls those methods by holding the MsgClient instance of the `x/nft` module. The execution authority of msg is guaranteed by the OCAPs mechanism.
The query service methods for the `x/nft` module are:
```proto
@ -150,3 +250,4 @@ Other networks in the Cosmos ecosystem could design and implement their own NFT
- Initial discussion: https://github.com/cosmos/cosmos-sdk/discussions/9065
- x/nft: initialize module: https://github.com/cosmos/cosmos-sdk/pull/9174
- [ADR 033](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-033-protobuf-inter-module-comm.md)