diff --git a/Gopkg.lock b/Gopkg.lock index 27bf2d73c..b8b959dd4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -639,6 +639,7 @@ "github.com/tendermint/tendermint/rpc/lib/server", "github.com/tendermint/tendermint/rpc/lib/types", "github.com/tendermint/tendermint/types", + "github.com/tendermint/tendermint/types/time", "golang.org/x/crypto/sha3", "golang.org/x/net/netutil", "golang.org/x/sys/cpu", diff --git a/cmd/export/main.go b/cmd/export/main.go index ea83d9f9e..ec075ccdb 100644 --- a/cmd/export/main.go +++ b/cmd/export/main.go @@ -1,19 +1,54 @@ package main import ( + "crypto/sha256" "encoding/json" + "flag" + "fmt" "github.com/MinterTeam/go-amino" + "github.com/MinterTeam/minter-go-node/cmd/export/types11" "github.com/MinterTeam/minter-go-node/cmd/utils" - "github.com/MinterTeam/minter-go-node/config" - "github.com/MinterTeam/minter-go-node/core/appdb" "github.com/MinterTeam/minter-go-node/core/state" + mtypes "github.com/MinterTeam/minter-go-node/core/types" "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" + "io" + "log" + "os" "time" ) +var ( + height = flag.Uint64("height", 0, "height") + chainID = flag.String("chain_id", "", "chain_id") + genesisTime = flag.Duration("genesis_time", 0, "genesis_time") +) + +const ( + genesisPath = "genesis.json" + + maxSupply = "1000000000000000000000000000000000" +) + func main() { + defer func() { + if r := recover(); r != nil { + fmt.Println("Error occurred", r) + } + }() + + flag.Parse() + + required := []string{"height", "chain_id", "genesis_time"} + seen := make(map[string]bool) + flag.Visit(func(f *flag.Flag) { seen[f.Name] = true }) + for _, req := range required { + if !seen[req] { + log.Fatalf("missing required --%s argument/flag\n", req) + } + } + err := common.EnsureDir(utils.GetMinterHome()+"/config", 0777) if err != nil { panic(err) @@ -24,16 +59,76 @@ func main() { panic(err) } - applicationDB := appdb.NewAppDB(config.GetConfig()) - height := applicationDB.GetLastHeight() - currentState, err := state.New(height, ldb, false) + currentState, err := state.New(*height, ldb, false) if err != nil { panic(err) } - cdc := amino.NewCodec() + oldState := currentState.Export(*height) + newState := types11.AppState{ + Note: oldState.Note, + StartHeight: oldState.StartHeight, + MaxGas: oldState.MaxGas, + TotalSlashed: oldState.TotalSlashed.String(), + } + + for _, account := range oldState.Accounts { + newState.Accounts = append(newState.Accounts, mtypes.Account{ + Address: account.Address, + Balance: account.Balance, + Nonce: account.Nonce, + MultisigData: account.MultisigData, + }) + } + + for _, coin := range oldState.Coins { + newState.Coins = append(newState.Coins, types11.Coin{ + Name: coin.Name, + Symbol: coin.Symbol, + Volume: coin.Volume.String(), + Crr: coin.Crr, + Reserve: coin.ReserveBalance.String(), + MaxSupply: maxSupply, + }) + } + + for _, check := range oldState.UsedChecks { + newState.UsedChecks = append(newState.UsedChecks, check) + } + + for _, ff := range oldState.FrozenFunds { + newState.FrozenFunds = append(newState.FrozenFunds, mtypes.FrozenFund{ + Height: ff.Height, + Address: ff.Address, + CandidateKey: ff.CandidateKey, + Coin: ff.Coin, + Value: ff.Value, + }) + } + + for _, candidate := range oldState.Candidates { + newState.Candidates = append(newState.Candidates, mtypes.Candidate{ + RewardAddress: candidate.RewardAddress, + OwnerAddress: candidate.OwnerAddress, + TotalBipStake: candidate.TotalBipStake, + PubKey: candidate.PubKey, + Commission: candidate.Commission, + Stakes: candidate.Stakes, + Status: candidate.Status, + }) + } + + for _, validator := range oldState.Validators { + newState.Validators = append(newState.Validators, types11.Validator{ + TotalBipStake: validator.TotalBipStake.String(), + PubKey: validator.PubKey, + AccumReward: validator.AccumReward.String(), + AbsentTimes: validator.AbsentTimes, + }) + } - jsonBytes, err := cdc.MarshalJSONIndent(currentState.Export(height), "", " ") + cdc := amino.NewCodec() + jsonBytes, err := cdc.MarshalJSONIndent(newState, "", " ") if err != nil { panic(err) } @@ -41,19 +136,20 @@ func main() { appHash := [32]byte{} // Compose Genesis - genesis := types.GenesisDoc{ - GenesisTime: time.Date(2019, time.April, 2, 17, 0, 0, 0, time.UTC), - ChainID: "minter-test-network-35", - ConsensusParams: &types.ConsensusParams{ - Block: types.BlockParams{ + genesis := types11.GenesisDoc{ + GenesisTime: time.Unix(0, 0).Add(*genesisTime), + ChainID: *chainID, + ConsensusParams: &types11.ConsensusParams{ + Block: types11.BlockParams{ MaxBytes: 10000000, MaxGas: 100000, TimeIotaMs: 1000, }, - Evidence: types.EvidenceParams{ - MaxAge: 1000, + Evidence: types11.EvidenceParams{ + MaxAgeNumBlocks: 1000, + MaxAgeDuration: 24 * time.Hour, }, - Validator: types.ValidatorParams{ + Validator: types11.ValidatorParams{ PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}, }, }, @@ -66,7 +162,24 @@ func main() { panic(err) } - if err := genesis.SaveAs("genesis.json"); err != nil { + if err := genesis.SaveAs(genesisPath); err != nil { panic(err) } + + fmt.Printf("Ok\n%x\n", getSha256Hash(genesisPath)) +} + +func getSha256Hash(file string) []byte { + f, err := os.Open(file) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + log.Fatal(err) + } + + return h.Sum(nil) } diff --git a/cmd/export/readme.md b/cmd/export/readme.md new file mode 100644 index 000000000..6514418a8 --- /dev/null +++ b/cmd/export/readme.md @@ -0,0 +1,8 @@ +# Export Command + +Command to convert the genesis file from version 1.0 to 1.1 +More details will be disclosed later. +#####Example +```bash +./export --height=4350342 --chain_id="minter-mainnnet-2" --genesis_time=1580206824s +``` \ No newline at end of file diff --git a/cmd/export/types11/tmtypes.go b/cmd/export/types11/tmtypes.go new file mode 100644 index 000000000..8abbde036 --- /dev/null +++ b/cmd/export/types11/tmtypes.go @@ -0,0 +1,190 @@ +package types11 + +import ( + "bytes" + "encoding/json" + "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + "io/ioutil" + "time" +) + +type GenesisDoc struct { + GenesisTime time.Time `json:"genesis_time"` + ChainID string `json:"chain_id"` + ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"` + Validators []GenesisValidator `json:"validators,omitempty"` + AppHash common.HexBytes `json:"app_hash"` + AppState json.RawMessage `json:"app_state,omitempty"` +} + +type ConsensusParams struct { + Block BlockParams `json:"block"` + Evidence EvidenceParams `json:"evidence"` + Validator ValidatorParams `json:"validator"` +} + +type BlockParams struct { + MaxBytes int64 `json:"max_bytes"` + MaxGas int64 `json:"max_gas"` + // Minimum time increment between consecutive blocks (in milliseconds) + // Not exposed to the application. + TimeIotaMs int64 `json:"time_iota_ms"` +} + +type GenesisValidator struct { + Address Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + Power int64 `json:"power"` + Name string `json:"name"` +} + +type Address = crypto.Address + +type EvidenceParams struct { + MaxAgeNumBlocks int64 `json:"max_age_num_blocks"` // only accept new evidence more recent than this + MaxAgeDuration time.Duration `json:"max_age_duration"` +} + +type ValidatorParams struct { + PubKeyTypes []string `json:"pub_key_types"` +} + +const ( + MaxChainIDLen = 50 +) + +func (genDoc *GenesisDoc) ValidateAndComplete() error { + if genDoc.ChainID == "" { + return errors.New("genesis doc must include non-empty chain_id") + } + if len(genDoc.ChainID) > MaxChainIDLen { + return errors.Errorf("chain_id in genesis doc is too long (max: %d)", MaxChainIDLen) + } + + if genDoc.ConsensusParams == nil { + genDoc.ConsensusParams = DefaultConsensusParams() + } else if err := genDoc.ConsensusParams.Validate(); err != nil { + return err + } + + for i, v := range genDoc.Validators { + if v.Power == 0 { + return errors.Errorf("the genesis file cannot contain validators with no voting power: %v", v) + } + if len(v.Address) > 0 && !bytes.Equal(v.PubKey.Address(), v.Address) { + return errors.Errorf("incorrect address for validator %v in the genesis file, should be %v", v, v.PubKey.Address()) + } + if len(v.Address) == 0 { + genDoc.Validators[i].Address = v.PubKey.Address() + } + } + + if genDoc.GenesisTime.IsZero() { + genDoc.GenesisTime = tmtime.Now() + } + + return nil +} + +func DefaultConsensusParams() *ConsensusParams { + return &ConsensusParams{ + DefaultBlockParams(), + DefaultEvidenceParams(), + DefaultValidatorParams(), + } +} + +func DefaultBlockParams() BlockParams { + return BlockParams{ + MaxBytes: 22020096, // 21MB + MaxGas: -1, + TimeIotaMs: 1000, // 1s + } +} + +// DefaultEvidenceParams Params returns a default EvidenceParams. +func DefaultEvidenceParams() EvidenceParams { + return EvidenceParams{ + MaxAgeNumBlocks: 100000, // 27.8 hrs at 1block/s + MaxAgeDuration: 48 * time.Hour, + } +} + +// DefaultValidatorParams returns a default ValidatorParams, which allows +// only ed25519 pubkeys. +func DefaultValidatorParams() ValidatorParams { + return ValidatorParams{[]string{types.ABCIPubKeyTypeEd25519}} +} + +const ( + ABCIPubKeyTypeEd25519 = "ed25519" + ABCIPubKeyTypeSr25519 = "sr25519" + ABCIPubKeyTypeSecp256k1 = "secp256k1" + + MaxBlockSizeBytes = 104857600 +) + +var ABCIPubKeyTypesToAminoNames = map[string]string{ + ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoName, + ABCIPubKeyTypeSr25519: "tendermint/PubKeySr25519", + ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyAminoName, +} + +func (params *ConsensusParams) Validate() error { + if params.Block.MaxBytes <= 0 { + return errors.Errorf("block.MaxBytes must be greater than 0. Got %d", + params.Block.MaxBytes) + } + if params.Block.MaxBytes > MaxBlockSizeBytes { + return errors.Errorf("block.MaxBytes is too big. %d > %d", + params.Block.MaxBytes, MaxBlockSizeBytes) + } + + if params.Block.MaxGas < -1 { + return errors.Errorf("block.MaxGas must be greater or equal to -1. Got %d", + params.Block.MaxGas) + } + + if params.Block.TimeIotaMs <= 0 { + return errors.Errorf("block.TimeIotaMs must be greater than 0. Got %v", + params.Block.TimeIotaMs) + } + + if params.Evidence.MaxAgeNumBlocks <= 0 { + return errors.Errorf("evidenceParams.MaxAgeNumBlocks must be greater than 0. Got %d", + params.Evidence.MaxAgeNumBlocks) + } + + if params.Evidence.MaxAgeDuration <= 0 { + return errors.Errorf("evidenceParams.MaxAgeDuration must be grater than 0 if provided, Got %v", + params.Evidence.MaxAgeDuration) + } + + if len(params.Validator.PubKeyTypes) == 0 { + return errors.New("len(Validator.PubKeyTypes) must be greater than 0") + } + + // Check if keyType is a known ABCIPubKeyType + for i := 0; i < len(params.Validator.PubKeyTypes); i++ { + keyType := params.Validator.PubKeyTypes[i] + if _, ok := ABCIPubKeyTypesToAminoNames[keyType]; !ok { + return errors.Errorf("params.Validator.PubKeyTypes[%d], %s, is an unknown pubkey type", + i, keyType) + } + } + + return nil +} +func (genDoc *GenesisDoc) SaveAs(file string) error { + genDocBytes, err := types.GetCodec().MarshalJSONIndent(genDoc, "", " ") + if err != nil { + return err + } + return ioutil.WriteFile(file, genDocBytes, 0644) +} diff --git a/cmd/export/types11/types.go b/cmd/export/types11/types.go new file mode 100644 index 000000000..893b60b16 --- /dev/null +++ b/cmd/export/types11/types.go @@ -0,0 +1,34 @@ +package types11 + +import ( + "github.com/MinterTeam/minter-go-node/core/types" +) + +type AppState struct { + Note string `json:"note"` + StartHeight uint64 `json:"start_height"` + Validators []Validator `json:"validators,omitempty"` + Candidates []types.Candidate `json:"candidates,omitempty"` + Accounts []types.Account `json:"accounts,omitempty"` + Coins []Coin `json:"coins,omitempty"` + FrozenFunds []types.FrozenFund `json:"frozen_funds,omitempty"` + UsedChecks []types.UsedCheck `json:"used_checks,omitempty"` + MaxGas uint64 `json:"max_gas"` + TotalSlashed string `json:"total_slashed"` +} + +type Validator struct { + TotalBipStake string `json:"total_bip_stake"` + PubKey types.Pubkey `json:"pub_key"` + AccumReward string `json:"accum_reward"` + AbsentTimes *types.BitArray `json:"absent_times"` +} + +type Coin struct { + Name string `json:"name"` + Symbol types.CoinSymbol `json:"symbol"` + Volume string `json:"volume"` + Crr uint `json:"crr"` + Reserve string `json:"reserve"` + MaxSupply string `json:"max_supply"` +} diff --git a/core/state/statedb.go b/core/state/statedb.go index 0a63c791f..765a9d008 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1803,10 +1803,10 @@ func (s *StateDB) Export(currentHeight uint64) types.AppState { balance := make([]types.Balance, len(account.Balances().Data)) i := 0 - for coin, value := range account.Balances().Data { + for _, coin := range account.Balances().getCoins() { balance[i] = types.Balance{ Coin: coin, - Value: value, + Value: account.Balance(coin), } i++ } @@ -2152,3 +2152,7 @@ func (s *StateDB) Height() uint64 { func (s *StateDB) DB() dbm.DB { return s.db } + +func (s *StateDB) Hash() []byte { + return s.iavl.Hash() +}