-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: add minted coins to balance in x/collection MsgMintFT (#1105)
* Add coins * Add the corresponding unit test * Update CHANGELOG.md * Add total-supply invariants * Add migration logic * Add unit tests on migration * Do not verify nfts * Add a unit test on the invariant * Lint * Update consensus version * Revert "Add a unit test on the invariant" This reverts commit a779379. * Revert "Do not verify nfts" This reverts commit e85305f. * Revert "Add total-supply invariants" This reverts commit 3a3e423. * Apply suggestions from code review
- Loading branch information
Showing
8 changed files
with
450 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.