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

fix: add minted coins to balance in x/collection MsgMintFT #1105

Merged
merged 14 commits into from
Sep 1, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/bank) [#1093](https://github.com/Finschia/finschia-sdk/pull/1093) Remove message events including `sender` attribute whose information is already present in the relevant events (backport #1066)
* (ostracon) [\#1099](https://github.com/Finschia/finschia-sdk/pull/1099) feat!: remove libsodium vrf library.
* (x/collection) [\#1102](https://github.com/finschia/finschia-sdk/pull/1102) Reject modifying NFT class with token index filled in MsgModify
* (x/collection) [\#1105](https://github.com/Finschia/finschia-sdk/pull/1105) Add minted coins to balance in x/collection MsgMintFT

### Build, CI
* (build,ci) [\#1043](https://github.com/Finschia/finschia-sdk/pull/1043) Update golang version to 1.20
Expand Down
32 changes: 32 additions & 0 deletions x/collection/keeper/migrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package keeper

import (
sdk "github.com/Finschia/finschia-sdk/types"
"github.com/Finschia/finschia-sdk/types/module"
"github.com/Finschia/finschia-sdk/x/collection"
v2 "github.com/Finschia/finschia-sdk/x/collection/keeper/migrations/v2"
)

// Migrator is a struct for handling in-place store migrations.
type Migrator struct {
keeper Keeper
}

// NewMigrator returns a new Migrator.
func NewMigrator(keeper Keeper) Migrator {
return Migrator{keeper: keeper}
}

func (m Migrator) Register(register func(moduleName string, fromVersion uint64, handler module.MigrationHandler) error) error {
for fromVersion, handler := range map[uint64]module.MigrationHandler{
1: func(ctx sdk.Context) error {
return v2.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc)
},
} {
if err := register(collection.ModuleName, fromVersion, handler); err != nil {
return err
}
}

return nil
}
115 changes: 115 additions & 0 deletions x/collection/keeper/migrations/v2/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package v2

import (
sdk "github.com/Finschia/finschia-sdk/types"
)

var (
contractKeyPrefix = []byte{0x10}
nextClassIDKeyPrefix = []byte{0x12}

balanceKeyPrefix = []byte{0x20}

SupplyKeyPrefix = []byte{0x40}
MintedKeyPrefix = []byte{0x41}
BurntKeyPrefix = []byte{0x42}
)

func ContractKey(contractID string) []byte {
key := make([]byte, len(contractKeyPrefix)+len(contractID))

copy(key, contractKeyPrefix)
copy(key[len(contractKeyPrefix):], contractID)

return key
}

func BalanceKey(contractID string, address sdk.AccAddress, tokenID string) []byte {
prefix := balanceKeyPrefixByAddress(contractID, address)
key := make([]byte, len(prefix)+len(tokenID))

copy(key, prefix)
copy(key[len(prefix):], tokenID)

return key
}

func balanceKeyPrefixByAddress(contractID string, address sdk.AccAddress) []byte {
prefix := balanceKeyPrefixByContractID(contractID)
key := make([]byte, len(prefix)+1+len(address))

begin := 0
copy(key, prefix)

begin += len(prefix)
key[begin] = byte(len(address))

begin++
copy(key[begin:], address)

return key
}

func balanceKeyPrefixByContractID(contractID string) []byte {
key := make([]byte, len(balanceKeyPrefix)+1+len(contractID))

begin := 0
copy(key, balanceKeyPrefix)

begin += len(balanceKeyPrefix)
key[begin] = byte(len(contractID))

begin++
copy(key[begin:], contractID)

return key
}

func splitBalanceKey(key []byte) (contractID string, address sdk.AccAddress, tokenID string) {
begin := len(balanceKeyPrefix) + 1
end := begin + int(key[begin-1])
contractID = string(key[begin:end])

begin = end + 1
end = begin + int(key[begin-1])
address = sdk.AccAddress(key[begin:end])

begin = end
tokenID = string(key[begin:])

return
}

func StatisticKey(keyPrefix []byte, contractID string, classID string) []byte {
prefix := statisticKeyPrefixByContractID(keyPrefix, contractID)
key := make([]byte, len(prefix)+len(classID))

copy(key, prefix)
copy(key[len(prefix):], classID)

return key
}

func statisticKeyPrefixByContractID(keyPrefix []byte, contractID string) []byte {
key := make([]byte, len(keyPrefix)+1+len(contractID))

begin := 0
copy(key, keyPrefix)

begin += len(keyPrefix)
key[begin] = byte(len(contractID))

begin++
copy(key[begin:], contractID)

return key
}

func NextClassIDKey(contractID string) []byte {
key := make([]byte, len(nextClassIDKeyPrefix)+len(contractID))

copy(key, nextClassIDKeyPrefix)
copy(key[len(nextClassIDKeyPrefix):], contractID)

return key
}
134 changes: 134 additions & 0 deletions x/collection/keeper/migrations/v2/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package v2

import (
"fmt"

"github.com/Finschia/finschia-sdk/codec"
storetypes "github.com/Finschia/finschia-sdk/store/types"
sdk "github.com/Finschia/finschia-sdk/types"
"github.com/Finschia/finschia-sdk/x/collection"
)

// MigrateStore performs in-place store migrations from v1 to v2.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error {
store := ctx.KVStore(storeKey)

// fix ft statistics
if err := fixFTStatistics(store, cdc); err != nil {
return err
}

return nil
}

func fixFTStatistics(store storetypes.KVStore, cdc codec.BinaryCodec) error {
iterator := sdk.KVStorePrefixIterator(store, contractKeyPrefix)
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
var contract collection.Contract
if err := cdc.Unmarshal(iterator.Value(), &contract); err != nil {
return err
}

if err := fixContractFTStatistics(store, contract.Id); err != nil {
return err
}
}

return nil
}

func fixContractFTStatistics(store storetypes.KVStore, contractID string) error {
supplies, err := evalContractFTSupplies(store, contractID)
if err != nil {
return err
}

if err := updateContractFTStatistics(store, contractID, supplies); err != nil {
return err
}

return nil
}

func evalContractFTSupplies(store storetypes.KVStore, contractID string) (map[string]sdk.Int, error) {
prefix := balanceKeyPrefixByContractID(contractID)
iterator := sdk.KVStorePrefixIterator(store, prefix)
defer iterator.Close()

supplies := map[string]sdk.Int{}
for ; iterator.Valid(); iterator.Next() {
_, _, tokenID := splitBalanceKey(iterator.Key())
if err := collection.ValidateFTID(tokenID); err != nil {
continue
}

var amount sdk.Int
if err := amount.Unmarshal(iterator.Value()); err != nil {
return nil, err
}

classID := collection.SplitTokenID(tokenID)
if supply, ok := supplies[classID]; ok {
supplies[classID] = supply.Add(amount)
} else {
supplies[classID] = amount
}
}

return supplies, nil
}

func updateContractFTStatistics(store storetypes.KVStore, contractID string, supplies map[string]sdk.Int) error {
bz := store.Get(NextClassIDKey(contractID))
if bz == nil {
return fmt.Errorf("no next class ids of contract %s", contractID)
}

var nextClassIDs collection.NextClassIDs
if err := nextClassIDs.Unmarshal(bz); err != nil {
return err
}

for intClassID := uint64(1); intClassID < nextClassIDs.Fungible.Uint64(); intClassID++ {
classID := fmt.Sprintf("%08x", intClassID)

// update supply
supplyKey := StatisticKey(SupplyKeyPrefix, contractID, classID)
supply, ok := supplies[classID]
if ok {
bz, err := supply.Marshal()
if err != nil {
return err
}
store.Set(supplyKey, bz)
} else {
store.Delete(supplyKey)
}

// get burnt
burntKey := StatisticKey(BurntKeyPrefix, contractID, classID)
burnt := sdk.ZeroInt()
if bz := store.Get(burntKey); bz != nil {
if err := burnt.Unmarshal(bz); err != nil {
return err
}
}

// update minted
minted := supply.Add(burnt)
mintedKey := StatisticKey(MintedKeyPrefix, contractID, classID)
if !minted.IsZero() {
bz, err := minted.Marshal()
if err != nil {
return err
}
store.Set(mintedKey, bz)
} else {
store.Delete(mintedKey)
}
0Tech marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}
Loading