From 6911e169410549944a20d387a4541b540ac7faea Mon Sep 17 00:00:00 2001 From: fudongbai <296179868@qq.com> Date: Sun, 17 May 2020 23:00:38 +0800 Subject: [PATCH] add side chain gov func --- client/rpc/dex_client.go | 156 ++++++++++- common/types/dec.go | 45 +++- common/types/proposal.go | 6 + common/types/stake.go | 3 - e2e/e2e_rpc_test.go | 66 ++++- types/msg/msg-gov.go | 8 + types/msg/msg-oracle.go | 6 +- types/msg/msg-side-gov.go | 442 +++++++++++++++++++++++++++++++ types/msg/msg-sidechain-stake.go | 1 - types/msg/wire.go | 11 + 10 files changed, 730 insertions(+), 14 deletions(-) create mode 100644 types/msg/msg-side-gov.go diff --git a/client/rpc/dex_client.go b/client/rpc/dex_client.go index f2ca4065..d4ed6470 100644 --- a/client/rpc/dex_client.go +++ b/client/rpc/dex_client.go @@ -2,10 +2,10 @@ package rpc import ( "encoding/binary" + "encoding/json" "errors" "fmt" - - core_types "github.com/tendermint/tendermint/rpc/core/types" + "time" "github.com/binance-chain/go-sdk/common" "github.com/binance-chain/go-sdk/common/types" @@ -14,6 +14,7 @@ import ( gtypes "github.com/binance-chain/go-sdk/types" "github.com/binance-chain/go-sdk/types/msg" "github.com/binance-chain/go-sdk/types/tx" + core_types "github.com/tendermint/tendermint/rpc/core/types" ) type SyncType int @@ -51,12 +52,15 @@ type DexClient interface { GetTradingPairs(offset int, limit int) ([]types.TradingPair, error) GetDepth(tradePair string, level int) (*types.OrderBook, error) GetProposals(status types.ProposalStatus, numLatest int64) ([]types.Proposal, error) + GetSideChainProposals(status types.ProposalStatus, numLatest int64, sideChainId string) ([]types.Proposal, error) + GetSideChainProposal(proposalId int64, sideChainId string) (types.Proposal, error) GetProposal(proposalId int64) (types.Proposal, error) GetTimelocks(addr types.AccAddress) ([]types.TimeLockRecord, error) GetTimelock(addr types.AccAddress, recordID int64) (*types.TimeLockRecord, error) GetSwapByID(swapID types.SwapBytes) (types.AtomicSwap, error) GetSwapByCreator(creatorAddr string, offset int64, limit int64) ([]types.SwapBytes, error) GetSwapByRecipient(recipientAddr string, offset int64, limit int64) ([]types.SwapBytes, error) + GetSideChainParams(sideChainId string) ([]msg.SCParam, error) SetKeyManager(k keys.KeyManager) SendToken(transfers []msg.Transfer, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) @@ -76,6 +80,17 @@ type DexClient interface { Claim(chainId sdk.IbcChainID, sequence uint64, payload []byte, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) GetProphecy(chainId sdk.IbcChainID, sequence int64) (*msg.Prophecy, error) GetCurrentOracleSequence(chainId sdk.IbcChainID) (int64, error) + + SideChainVote(proposalID int64, option msg.VoteOption, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + SideChainDeposit(proposalID int64, amount types.Coins, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + SideChainSubmitSCParamsProposal(title string, scParam msg.SCChangeParams, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + SideChainSubmitCSCParamsProposal(title string, cscParam msg.CSCParamChange, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + SideChainSubmitProposal(title string, description string, proposalType msg.ProposalKind, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + SubmitListProposal(title string, param msg.ListTradingPairParams, proposalType msg.ProposalKind, initialDeposit types.Coins, votingPeriod time.Duration, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + + SubmitProposal(title string, description string, proposalType msg.ProposalKind, initialDeposit types.Coins, votingPeriod time.Duration, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + Deposit(proposalID int64, amount types.Coins, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) + Vote(proposalID int64, option msg.VoteOption, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) } func (c *HTTP) TxInfoSearch(query string, prove bool, page, perPage int) ([]Info, error) { @@ -386,6 +401,14 @@ func (c *HTTP) GetTimelock(addr types.AccAddress, recordID int64) (*types.TimeLo } func (c *HTTP) GetProposals(status types.ProposalStatus, numLatest int64) ([]types.Proposal, error) { + return c.getProposals(status, "", numLatest) +} + +func (c *HTTP) GetSideChainProposals(status types.ProposalStatus, numLatest int64, sideChainId string) ([]types.Proposal, error) { + return c.getProposals(status, sideChainId, numLatest) +} + +func (c *HTTP) getProposals(status types.ProposalStatus, sideChainId string, numLatest int64) ([]types.Proposal, error) { params := types.QueryProposalsParams{} if status != types.StatusNil { params.ProposalStatus = status @@ -393,6 +416,7 @@ func (c *HTTP) GetProposals(status types.ProposalStatus, numLatest int64) ([]typ if numLatest > 0 { params.NumLatestProposals = numLatest } + params.SideChainId = sideChainId bz, err := c.cdc.MarshalJSON(¶ms) if err != nil { @@ -412,9 +436,18 @@ func (c *HTTP) GetProposals(status types.ProposalStatus, numLatest int64) ([]typ } func (c *HTTP) GetProposal(proposalId int64) (types.Proposal, error) { + return c.getProposal(proposalId, "") +} + +func (c *HTTP) GetSideChainProposal(proposalId int64, sideChainId string) (types.Proposal, error) { + return c.getProposal(proposalId, sideChainId) +} + +func (c *HTTP) getProposal(proposalId int64, sideChainId string) (types.Proposal, error) { params := types.QueryProposalParams{ ProposalID: proposalId, } + params.SideChainId = sideChainId bz, err := c.cdc.MarshalJSON(params) if err != nil { return nil, err @@ -432,6 +465,23 @@ func (c *HTTP) GetProposal(proposalId int64) (types.Proposal, error) { return proposal, err } +func (c *HTTP) GetSideChainParams(sideChainId string) ([]msg.SCParam, error) { + data, err := c.cdc.MarshalJSON(sideChainId) + if err != nil { + return nil, err + } + rawParams, err := c.ABCIQuery("param/sideParams", data) + if err != nil { + return nil, err + } + if !rawParams.Response.IsOK() { + return nil, fmt.Errorf(rawParams.Response.Log) + } + var params []msg.SCParam + err = c.cdc.UnmarshalJSON(rawParams.Response.GetValue(), ¶ms) + return params, err +} + func (c *HTTP) existsCC(symbol string) bool { resp, err := c.ABCIQuery(fmt.Sprintf("tokens/info/%s", symbol), nil) if err != nil { @@ -641,6 +691,108 @@ func (c *HTTP) RefundHTLT(swapID []byte, syncType SyncType, options ...tx.Option return c.Broadcast(refundHTLTMsg, syncType, options...) } +func (c *HTTP) SideChainVote(proposalID int64, option msg.VoteOption, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + fromAddr := c.key.GetAddr() + msg := msg.NewSideChainVoteMsg(fromAddr, proposalID, option, sideChainId) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) SideChainDeposit(proposalID int64, amount types.Coins, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + fromAddr := c.key.GetAddr() + msg := msg.NewSideChainDepositMsg(fromAddr, proposalID, amount, sideChainId) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) SideChainSubmitSCParamsProposal(title string, scParam msg.SCChangeParams, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + err := scParam.Check() + if err != nil { + return nil, err + } + scParamsBz, err := c.cdc.MarshalJSON(scParam) + if err != nil { + return nil, err + } + fromAddr := c.key.GetAddr() + msg := msg.NewSideChainSubmitProposalMsg(title, string(scParamsBz), msg.ProposalTypeSCParamsChange, fromAddr, initialDeposit, votingPeriod, sideChainId) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) SideChainSubmitCSCParamsProposal(title string, cscParam msg.CSCParamChange, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + err := cscParam.Check() + if err != nil { + return nil, err + } + // cscParam get interface field, use amino + cscParamsBz, err := c.cdc.MarshalJSON(cscParam) + if err != nil { + return nil, err + } + fromAddr := c.key.GetAddr() + msg := msg.NewSideChainSubmitProposalMsg(title, string(cscParamsBz), msg.ProposalTypeCSCParamsChange, fromAddr, initialDeposit, votingPeriod, sideChainId) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) SideChainSubmitProposal(title string, description string, proposalType msg.ProposalKind, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + fromAddr := c.key.GetAddr() + msg := msg.NewSideChainSubmitProposalMsg(title, description, proposalType, fromAddr, initialDeposit, votingPeriod, sideChainId) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) SubmitListProposal(title string, param msg.ListTradingPairParams, proposalType msg.ProposalKind, initialDeposit types.Coins, votingPeriod time.Duration, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + bz, err := json.Marshal(¶m) + if err != nil { + return nil, err + } + fromAddr := c.key.GetAddr() + msg := msg.NewMsgSubmitProposal(title, string(bz), proposalType, fromAddr, initialDeposit, votingPeriod) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) SubmitProposal(title string, description string, proposalType msg.ProposalKind, initialDeposit types.Coins, votingPeriod time.Duration, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + fromAddr := c.key.GetAddr() + msg := msg.NewMsgSubmitProposal(title, description, proposalType, fromAddr, initialDeposit, votingPeriod) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) Deposit(proposalID int64, amount types.Coins, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + fromAddr := c.key.GetAddr() + msg := msg.NewDepositMsg(fromAddr, proposalID, amount) + return c.Broadcast(msg, syncType, options...) +} + +func (c *HTTP) Vote(proposalID int64, option msg.VoteOption, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { + if c.key == nil { + return nil, KeyMissingError + } + fromAddr := c.key.GetAddr() + msg := msg.NewMsgVote(fromAddr, proposalID, option) + return c.Broadcast(msg, syncType, options...) +} + func (c *HTTP) CancelOrder(baseAssetSymbol, quoteAssetSymbol, refId string, syncType SyncType, options ...tx.Option) (*core_types.ResultBroadcastTx, error) { if c.key == nil { return nil, KeyMissingError diff --git a/common/types/dec.go b/common/types/dec.go index fd1b2623..1113f699 100644 --- a/common/types/dec.go +++ b/common/types/dec.go @@ -17,12 +17,21 @@ const ( ) var ( - precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil).Int64() - zeroInt = big.NewInt(0) - oneInt = big.NewInt(1) - tenInt = big.NewInt(10) + precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil).Int64() + precisionMultipliers []int64 + zeroInt = big.NewInt(0) + oneInt = big.NewInt(1) + tenInt = big.NewInt(10) ) +// Set precision multipliers +func init() { + precisionMultipliers = make([]int64, Precision+1) + for i := 0; i <= Precision; i++ { + precisionMultipliers[i] = calcPrecisionMultiplier(int64(i)) + } +} + func precisionInt() int64 { return precisionReuse } @@ -111,3 +120,31 @@ func (d Dec) Sub(d2 Dec) Dec { // nolint - common values func ZeroDec() Dec { return Dec{0} } func OneDec() Dec { return Dec{precisionInt()} } +func NewDecWithPrec(i, prec int64) Dec { + if i == 0 { + return Dec{0} + } + c := i * precisionMultiplier(prec) + if c/i != precisionMultiplier(prec) { + panic("Int overflow") + } + return Dec{c} +} + +// get the precision multiplier, do not mutate result +func precisionMultiplier(prec int64) int64 { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + return precisionMultipliers[prec] +} + +// calculate the precision multiplier +func calcPrecisionMultiplier(prec int64) int64 { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + zerosToAdd := Precision - prec + multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil).Int64() + return multiplier +} diff --git a/common/types/proposal.go b/common/types/proposal.go index 108a6b5e..2f6ce3d9 100644 --- a/common/types/proposal.go +++ b/common/types/proposal.go @@ -287,12 +287,18 @@ func (tp *TextProposal) SetVotingPeriod(votingPeriod time.Duration) { tp.VotingPeriod = votingPeriod } +type BaseParams struct { + SideChainId string +} + type QueryProposalsParams struct { + BaseParams ProposalStatus ProposalStatus NumLatestProposals int64 } // Params for query 'custom/gov/proposal' type QueryProposalParams struct { + BaseParams ProposalID int64 } diff --git a/common/types/stake.go b/common/types/stake.go index 1c9be623..a7015d83 100644 --- a/common/types/stake.go +++ b/common/types/stake.go @@ -327,9 +327,6 @@ func (ca ConsAddress) Format(s fmt.State, verb rune) { } } -type BaseParams struct { - SideChainId string -} func NewBaseParams(sideChainId string) BaseParams { return BaseParams{SideChainId:sideChainId} diff --git a/e2e/e2e_rpc_test.go b/e2e/e2e_rpc_test.go index 517054a4..ecd17a5c 100644 --- a/e2e/e2e_rpc_test.go +++ b/e2e/e2e_rpc_test.go @@ -7,20 +7,22 @@ import ( "fmt" "math/rand" "os/exec" + "strconv" "sync" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/common" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" "github.com/tendermint/tendermint/types" - "github.com/binance-chain/go-sdk/types/tx" "github.com/binance-chain/go-sdk/client/rpc" "github.com/binance-chain/go-sdk/client/transaction" ctypes "github.com/binance-chain/go-sdk/common/types" "github.com/binance-chain/go-sdk/keys" "github.com/binance-chain/go-sdk/types/msg" + "github.com/binance-chain/go-sdk/types/tx" ) var ( @@ -36,6 +38,8 @@ var ( mnemonic = "test mnemonic" onceClient = sync.Once{} testClientInstance *rpc.HTTP + + scParams = `[{"type": "params/StakeParamSet","value": {"unbonding_time": "604800000000000","max_validators": 11,"bond_denom": "BNB","min_self_delegation": "5000000000000","min_delegation_change": "100000000"}},{"type": "params/SlashParamSet","value": {"max_evidence_age": "259200000000000","signed_blocks_window": "0","min_signed_per_window": "0","double_sign_unbond_duration": "9223372036854775807","downtime_unbond_duration": "172800000000000","too_low_del_unbond_duration": "86400000000000","slash_fraction_double_sign": "0","slash_fraction_downtime": "0","double_sign_slash_amount": "1000000000000","downtime_slash_amount": "5000000000","submitter_reward": "100000000000","downtime_slash_fee": "1000000000"}},{"type": "params/OracleParamSet","value": {"ConsensusNeeded": "70000000"}},{"type": "params/IbcParamSet","value": {"relayer_fee": "1000000"}}]` ) func startBnbchaind(t *testing.T) *exec.Cmd { @@ -523,6 +527,66 @@ func TestSendToken(t *testing.T) { fmt.Println(string(bz)) } +func TestQuerySideChainParam(t *testing.T) { + c := defaultClient() + ctypes.Network = ctypes.TestNetwork + params, err := c.GetSideChainParams("bsc") + assert.NoError(t, err) + bz, _ := json.Marshal(params) + fmt.Println(string(bz)) +} + +func TestSubmitSideProposal(t *testing.T) { + c := defaultClient() + ctypes.Network = ctypes.TestNetwork + + keyManager, err := keys.NewMnemonicKeyManager(mnemonic) + assert.NoError(t, err) + c.SetKeyManager(keyManager) + + iScPrams := make([]msg.SCParam, 0) + + err = tx.Cdc.UnmarshalJSON([]byte(scParams), &iScPrams) + assert.NoError(t, err) + + res, err := c.SideChainSubmitSCParamsProposal("title", msg.SCChangeParams{SCParams: iScPrams, Description: "des"}, ctypes.Coins{{msg.NativeToken, 5e11}}, 5*time.Second, "rialto", rpc.Sync) + assert.NoError(t, err) + assert.True(t, res.Code == 0) + proposalIdStr := string(res.Data) + id, err := strconv.ParseInt(proposalIdStr, 10, 64) + assert.NoError(t, err) + res, err = c.SideChainVote(int64(id), msg.OptionYes, "rialto", rpc.Sync) + assert.NoError(t, err) + assert.True(t, res.Code == 0) +} + +func TestSubmitCSCProposal(t *testing.T) { + c := defaultClient() + ctypes.Network = ctypes.TestNetwork + + keyManager, err := keys.NewMnemonicKeyManager(mnemonic) + assert.NoError(t, err) + c.SetKeyManager(keyManager) + + cscPrams := msg.CSCParamChange{ + Key: common.RandStr(common.RandIntn(255) + 1), + Value: hex.EncodeToString(common.RandBytes(common.RandIntn(255) + 1)), + Target: hex.EncodeToString(common.RandBytes(20)), + } + + res, err := c.SideChainSubmitCSCParamsProposal("title", cscPrams, ctypes.Coins{{msg.NativeToken, 5e8}}, 5*time.Second, "rialto", rpc.Sync) + assert.NoError(t, err) + assert.True(t, res.Code == 0) + + proposalIdStr := string(res.Data) + id, err := strconv.ParseInt(proposalIdStr, 10, 64) + assert.NoError(t, err) + res, err = c.SideChainDeposit(int64(id), ctypes.Coins{{"BNB", 1e8}}, "rialto", rpc.Sync) + assert.NoError(t, err) + assert.True(t, res.Code == 0) + +} + func TestCreateOrder(t *testing.T) { c := defaultClient() ctypes.Network = ctypes.TestNetwork diff --git a/types/msg/msg-gov.go b/types/msg/msg-gov.go index c937f517..d158ddad 100644 --- a/types/msg/msg-gov.go +++ b/types/msg/msg-gov.go @@ -135,6 +135,10 @@ func ProposalTypeFromString(str string) (ProposalKind, error) { return ProposalTypeListTradingPair, nil case "FeeChange": return ProposalTypeFeeChange, nil + case "SCParamsChange": + return ProposalTypeSCParamsChange, nil + case "CSCParamsChange": + return ProposalTypeCSCParamsChange, nil default: return ProposalKind(0xff), errors.Errorf("'%s' is not a valid proposal type", str) } @@ -197,6 +201,10 @@ func (pt ProposalKind) String() string { return "ListTradingPair" case ProposalTypeFeeChange: return "FeeChange" + case ProposalTypeSCParamsChange: + return "SCParamsChange" + case ProposalTypeCSCParamsChange: + return "CSCParamsChange" default: return "" } diff --git a/types/msg/msg-oracle.go b/types/msg/msg-oracle.go index e87c0c4a..37b8472a 100644 --- a/types/msg/msg-oracle.go +++ b/types/msg/msg-oracle.go @@ -157,7 +157,7 @@ var protoMetrics = map[sdk.IbcChannelID]map[CrossChainPackageType]func() interfa return new(CommonAckPackage) }, FailAckCrossChainPackageType: func() interface{} { - return new(CSCParamChange) + return new(CrossParamChange) }, }, sdk.IbcChannelID(11): { @@ -225,10 +225,10 @@ type IbcValidator struct { Power uint64 } -type CSCParamChange struct { +type CrossParamChange struct { Key string Value []byte - Target []byte `json:"-"` // the address of the target contract + Target []byte } type SideDowntimeSlashPackage struct { diff --git a/types/msg/msg-side-gov.go b/types/msg/msg-side-gov.go new file mode 100644 index 00000000..55b041bf --- /dev/null +++ b/types/msg/msg-side-gov.go @@ -0,0 +1,442 @@ +package msg + +import ( + "encoding/hex" + "fmt" + "math" + "time" + + "github.com/tendermint/go-amino" + + "github.com/binance-chain/go-sdk/common/types" +) + +const ( + MsgTypeSideSubmitProposal = "side_submit_proposal" + MsgTypeSideDeposit = "side_deposit" + MsgTypeSideVote = "side_vote" + + // side chain params change + ProposalTypeSCParamsChange ProposalKind = 0x81 + // cross side chain param change + ProposalTypeCSCParamsChange ProposalKind = 0x82 + + MaxSideChainIdLength = 20 +) + +//----------------------------------------------------------- +// SideChainSubmitProposalMsg +type SideChainSubmitProposalMsg struct { + Title string `json:"title"` // Title of the proposal + Description string `json:"description"` // Description of the proposal + ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} + Proposer types.AccAddress `json:"proposer"` // Address of the proposer + InitialDeposit types.Coins `json:"initial_deposit"` // Initial deposit paid by sender. Must be strictly positive. + VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period (s) + SideChainId string `json:"side_chain_id"` +} + +func NewSideChainSubmitProposalMsg(title string, description string, proposalType ProposalKind, proposer types.AccAddress, initialDeposit types.Coins, votingPeriod time.Duration, sideChainId string) SideChainSubmitProposalMsg { + return SideChainSubmitProposalMsg{ + Title: title, + Description: description, + ProposalType: proposalType, + Proposer: proposer, + InitialDeposit: initialDeposit, + VotingPeriod: votingPeriod, + SideChainId: sideChainId, + } +} + +//nolint +func (msg SideChainSubmitProposalMsg) Route() string { return MsgRoute } +func (msg SideChainSubmitProposalMsg) Type() string { return MsgTypeSideSubmitProposal } + +// Implements Msg. +func (msg SideChainSubmitProposalMsg) ValidateBasic() error { + if len(msg.Title) == 0 { + return fmt.Errorf("title can't be empty") + } + if len(msg.Title) > MaxTitleLength { + return fmt.Errorf("Proposal title is longer than max length of %d", MaxTitleLength) + } + if len(msg.Description) == 0 { + return fmt.Errorf("description can't be empty") + } + + if len(msg.Description) > MaxDescriptionLength { + return fmt.Errorf("Proposal description is longer than max length of %d", MaxDescriptionLength) + } + if !validSideProposalType(msg.ProposalType) { + return fmt.Errorf("invalid proposal type %v ", msg.ProposalType) + } + if len(msg.Proposer) == 0 { + return fmt.Errorf("proposer can't be empty") + } + if !msg.InitialDeposit.IsValid() { + return fmt.Errorf("initial deposit %v is invalid. ", msg.InitialDeposit) + } + if !msg.InitialDeposit.IsNotNegative() { + return fmt.Errorf("initial deposit %v is negative. ", msg.InitialDeposit) + } + if msg.VotingPeriod <= 0 || msg.VotingPeriod > MaxVotingPeriod { + return fmt.Errorf("voting period should between 0 and %d weeks", MaxVotingPeriod/(7*24*60*60*time.Second)) + } + return nil + return nil +} + +func (msg SideChainSubmitProposalMsg) String() string { + return fmt.Sprintf("SideChainSubmitProposalMsg{%s, %s, %s, %v, %s, %s}", msg.Title, + msg.Description, msg.ProposalType, msg.InitialDeposit, msg.VotingPeriod, msg.SideChainId) +} + +// Implements Msg. +func (msg SideChainSubmitProposalMsg) GetSignBytes() []byte { + b, err := amino.NewCodec().MarshalJSON(msg) + if err != nil { + panic(err) + } + return MustSortJSON(b) +} + +// Implements Msg. Identical to MsgSubmitProposal, keep here for code readability. +func (msg SideChainSubmitProposalMsg) GetSigners() []types.AccAddress { + return []types.AccAddress{msg.Proposer} +} + +// Implements Msg. Identical to MsgSubmitProposal, keep here for code readability. +func (msg SideChainSubmitProposalMsg) GetInvolvedAddresses() []types.AccAddress { + return msg.GetSigners() +} + +//----------------------------------------------------------- +// SideChainDepositMsg +type SideChainDepositMsg struct { + ProposalID int64 `json:"proposal_id"` // ID of the proposal + Depositer types.AccAddress `json:"depositer"` // Address of the depositer + Amount types.Coins `json:"amount"` // Coins to add to the proposal's deposit + SideChainId string `json:"side_chain_id"` +} + +func NewSideChainDepositMsg(depositer types.AccAddress, proposalID int64, amount types.Coins, sideChainId string) SideChainDepositMsg { + return SideChainDepositMsg{ + ProposalID: proposalID, + Depositer: depositer, + Amount: amount, + SideChainId: sideChainId, + } +} + +// nolint +func (msg SideChainDepositMsg) Route() string { return MsgRoute } +func (msg SideChainDepositMsg) Type() string { return MsgTypeSideDeposit } + +// Implements Msg. +func (msg SideChainDepositMsg) ValidateBasic() error { + if len(msg.SideChainId) == 0 || len(msg.SideChainId) > MaxSideChainIdLength { + return fmt.Errorf("invalid side chain id %s", msg.SideChainId) + } + if len(msg.Depositer) == 0 { + return fmt.Errorf("depositer can't be empty ") + } + if !msg.Amount.IsValid() { + return fmt.Errorf("amount is invalid ") + } + if !msg.Amount.IsNotNegative() { + return fmt.Errorf("amount can't be negative ") + } + if msg.ProposalID < 0 { + return fmt.Errorf("proposalId can't be negative ") + } + return nil +} + +func (msg SideChainDepositMsg) String() string { + return fmt.Sprintf("SideChainDepositMsg{%s=>%v: %v, %s}", msg.Depositer, msg.ProposalID, msg.Amount, msg.SideChainId) +} + +// Implements Msg. +func (msg SideChainDepositMsg) GetSignBytes() []byte { + b, err := amino.NewCodec().MarshalJSON(msg) + if err != nil { + panic(err) + } + return MustSortJSON(b) +} + +// Implements Msg. Identical to MsgDeposit, keep here for code readability. +func (msg SideChainDepositMsg) GetSigners() []types.AccAddress { + return []types.AccAddress{msg.Depositer} +} + +// Implements Msg. Identical to MsgDeposit, keep here for code readability. +func (msg SideChainDepositMsg) GetInvolvedAddresses() []types.AccAddress { + return msg.GetSigners() +} + +//----------------------------------------------------------- +// SideChainVoteMsg + +type SideChainVoteMsg struct { + ProposalID int64 `json:"proposal_id"` // ID of the proposal + Voter types.AccAddress `json:"voter"` // address of the voter + Option VoteOption `json:"option"` // option from OptionSet chosen by the voter + SideChainId string `json:"side_chain_id"` +} + +func NewSideChainVoteMsg(voter types.AccAddress, proposalID int64, option VoteOption, sideChainId string) SideChainVoteMsg { + return SideChainVoteMsg{ + ProposalID: proposalID, + Voter: voter, + Option: option, + SideChainId: sideChainId, + } +} + +func (msg SideChainVoteMsg) Route() string { return MsgRoute } +func (msg SideChainVoteMsg) Type() string { return MsgTypeSideVote } + +// Implements Msg. +func (msg SideChainVoteMsg) ValidateBasic() error { + if len(msg.SideChainId) == 0 || len(msg.SideChainId) > MaxSideChainIdLength { + return fmt.Errorf("invalid side chain id %s", msg.SideChainId) + } + if len(msg.Voter.Bytes()) == 0 { + return fmt.Errorf("vaoter can't be empty ") + } + if msg.ProposalID < 0 { + return fmt.Errorf("proposalId can't be less than 0") + } + if !validVoteOption(msg.Option) { + return fmt.Errorf("invalid msg option %v", msg.Option) + } + return nil +} + +func (msg SideChainVoteMsg) String() string { + return fmt.Sprintf("SideChainVoteMsg{%v - %s, %s}", msg.ProposalID, msg.Option, msg.SideChainId) +} + +// Implements Msg. +func (msg SideChainVoteMsg) GetSignBytes() []byte { + b, err := amino.NewCodec().MarshalJSON(msg) + if err != nil { + panic(err) + } + return MustSortJSON(b) +} + +// Implements Msg. Identical to MsgVote, keep here for code readability. +func (msg SideChainVoteMsg) GetSigners() []types.AccAddress { + return []types.AccAddress{msg.Voter} +} + +// Implements Msg. Identical to MsgVote, keep here for code readability. +func (msg SideChainVoteMsg) GetInvolvedAddresses() []types.AccAddress { + return msg.GetSigners() +} + +func validSideProposalType(pt ProposalKind) bool { + if pt == ProposalTypeSCParamsChange || + pt == ProposalTypeCSCParamsChange { + return true + } + return false +} + +// --------- Definition side chain prams change ------------------- // +type SCParam interface { + UpdateCheck() error + // native means weather the parameter stored in native store context or side chain store context + //GetParamAttribute() (string, bool) + GetParamAttribute() (string, bool) +} + +type SCChangeParams struct { + SCParams []SCParam `json:"sc_params"` + Description string `json:"description"` +} + +func (s *SCChangeParams) Check() error { + // use literal string to avoid import cycle + supportParams := []string{"slash", "ibc", "oracle", "staking"} + + if len(s.SCParams) != len(supportParams) { + return fmt.Errorf("the sc_params length mismatch, suppose %d", len(supportParams)) + } + + paramSet := make(map[string]bool) + for _, s := range supportParams { + paramSet[s] = true + } + + for _, sc := range s.SCParams { + if sc == nil { + return fmt.Errorf("sc_params contains empty element") + } + err := sc.UpdateCheck() + if err != nil { + return err + } + paramType, _ := sc.GetParamAttribute() + if exist := paramSet[paramType]; exist { + delete(paramSet, paramType) + } else { + return fmt.Errorf("unsupported param type %s", paramType) + } + } + return nil +} + +type IbcParams struct { + RelayerFee int64 `json:"relayer_fee"` +} + +func (p *IbcParams) UpdateCheck() error { + if p.RelayerFee <= 0 { + return fmt.Errorf("the syn_package_fee should be greater than 0") + } + return nil +} + +func (p *IbcParams) GetParamAttribute() (string, bool) { + return "ibc", false +} + +type OracleParams struct { + ConsensusNeeded types.Dec `json:"ConsensusNeeded"` // Minimum deposit for a proposal to enter voting period. +} + +func (p *OracleParams) UpdateCheck() error { + if p.ConsensusNeeded.IsNil() || p.ConsensusNeeded.GT(types.OneDec()) || p.ConsensusNeeded.LT(types.NewDecWithPrec(5, 1)) { + return fmt.Errorf("the value should be in range 0.5 to 1") + } + return nil +} + +func (p *OracleParams) GetParamAttribute() (string, bool) { + return "oracle", true +} + +type SlashParams struct { + MaxEvidenceAge time.Duration `json:"max_evidence_age"` + SignedBlocksWindow int64 `json:"signed_blocks_window"` + MinSignedPerWindow types.Dec `json:"min_signed_per_window"` + DoubleSignUnbondDuration time.Duration `json:"double_sign_unbond_duration"` + DowntimeUnbondDuration time.Duration `json:"downtime_unbond_duration"` + TooLowDelUnbondDuration time.Duration `json:"too_low_del_unbond_duration"` + SlashFractionDoubleSign types.Dec `json:"slash_fraction_double_sign"` + SlashFractionDowntime types.Dec `json:"slash_fraction_downtime"` + DoubleSignSlashAmount int64 `json:"double_sign_slash_amount"` + DowntimeSlashAmount int64 `json:"downtime_slash_amount"` + SubmitterReward int64 `json:"submitter_reward"` + DowntimeSlashFee int64 `json:"downtime_slash_fee"` +} + +func (p *SlashParams) GetParamAttribute() (string, bool) { + return "slash", false +} + +func (p *SlashParams) UpdateCheck() error { + // no check for SignedBlocksWindow, MinSignedPerWindow, SlashFractionDoubleSign, SlashFractionDowntime + if p.MaxEvidenceAge < 1*time.Minute || p.MaxEvidenceAge > 100*24*time.Hour { + return fmt.Errorf("the max_evidence_age should be in range 1 minutes to 100 day") + } + if p.DoubleSignUnbondDuration < 1*time.Hour { + return fmt.Errorf("the double_sign_unbond_duration should be greate than 1 hour") + } + if p.DowntimeUnbondDuration < 60*time.Second || p.DowntimeUnbondDuration > 100*24*time.Hour { + return fmt.Errorf("the downtime_unbond_duration should be in range 1 minutes to 100 day") + } + if p.TooLowDelUnbondDuration < 60*time.Second || p.TooLowDelUnbondDuration > 100*24*time.Hour { + return fmt.Errorf("the too_low_del_unbond_duration should be in range 1 minutes to 100 day") + } + if p.DoubleSignSlashAmount < 1e8 { + return fmt.Errorf("the double_sign_slash_amount should be larger than 1e8") + } + if p.DowntimeSlashAmount < 1e8 || p.DowntimeSlashAmount > 10000e8 { + return fmt.Errorf("the downtime_slash_amount should be in range 1e8 to 10000e8") + } + if p.SubmitterReward < 1e7 || p.SubmitterReward > 1000e8 { + return fmt.Errorf("the submitter_reward should be in range 1e7 to 1000e8") + } + if p.DowntimeSlashFee < 1e8 || p.DowntimeSlashFee > 1000e8 { + return fmt.Errorf("the downtime_slash_fee should be in range 1e8 to 1000e8") + } + return nil +} + +// Params defines the high level settings for staking +type StakeParams struct { + UnbondingTime time.Duration `json:"unbonding_time"` + + MaxValidators uint16 `json:"max_validators"` // maximum number of validators + BondDenom string `json:"bond_denom"` // bondable coin denomination + MinSelfDelegation int64 `json:"min_self_delegation"` // the minimal self-delegation amount + MinDelegationChange int64 `json:"min_delegation_change"` // the minimal delegation amount changed +} + +func (p *StakeParams) GetParamAttribute() (string, bool) { + return "staking", false +} + +func (p *StakeParams) UpdateCheck() error { + if p.BondDenom != NativeToken { + return fmt.Errorf("only native token is availabe as bond_denom so far") + } + // the valid range is 1 minute to 100 day. + if p.UnbondingTime > 100*24*time.Hour || p.UnbondingTime < time.Minute { + return fmt.Errorf("the UnbondingTime should be in range 1 minute to 100 days") + } + if p.MaxValidators < 1 || p.MaxValidators > 500 { + return fmt.Errorf("the max validator should be in range 1 to 500") + } + // BondDenom do not check here, it should be native token and do not support update so far. + // Leave the check in node repo. + + if p.MinSelfDelegation > 10000000e8 || p.MinSelfDelegation < 1e8 { + return fmt.Errorf("the min_self_delegation should be in range 1e8 to 10000000e8]") + } + if p.MinDelegationChange < 1e5 { + return fmt.Errorf("the min_delegation_change should be no less than 1e5") + } + return nil +} + +// --------- Definition cross side chain prams change ------------------- // + +type CSCParamChange struct { + Key string `json:"key"` // the name of the parameter + Value string `json:"value"` + Target string `json:"target"` + + // Since byte slice is not friendly to show in proposal description, omit it. + ValueBytes []byte `json:"-"` // the value of the parameter + TargetBytes []byte `json:"-"` // the address of the target contract +} + +func (c *CSCParamChange) Check() error { + targetBytes, err := hex.DecodeString(c.Target) + if err != nil { + return fmt.Errorf("target is not hex encoded, err %v", err) + } + c.TargetBytes = targetBytes + + valueBytes, err := hex.DecodeString(c.Value) + if err != nil { + return fmt.Errorf("value is not hex encoded, err %v", err) + } + c.ValueBytes = valueBytes + keyBytes := []byte(c.Key) + if len(keyBytes) <= 0 || len(keyBytes) > math.MaxUint8 { + return fmt.Errorf("the length of key exceed the limitation") + } + if len(c.ValueBytes) <= 0 || len(c.ValueBytes) > math.MaxUint8 { + return fmt.Errorf("the length of value exceed the limitation") + } + if len(c.TargetBytes) != types.AddrLen { + return fmt.Errorf("the length of target address is not %d", types.AddrLen) + } + return nil +} diff --git a/types/msg/msg-sidechain-stake.go b/types/msg/msg-sidechain-stake.go index 694946dc..2d18fbd6 100644 --- a/types/msg/msg-sidechain-stake.go +++ b/types/msg/msg-sidechain-stake.go @@ -15,7 +15,6 @@ const ( TypeSideChainUndelegate = "side_undelegate" SideChainStakeMsgRoute = "stake" - MaxSideChainIdLength = 20 SideChainAddrLen = 20 MinDelegationAmount = 1e8 diff --git a/types/msg/wire.go b/types/msg/wire.go index 9e28b5d3..8aa614ac 100644 --- a/types/msg/wire.go +++ b/types/msg/wire.go @@ -10,6 +10,17 @@ func RegisterCodec(cdc *amino.Codec) { cdc.RegisterInterface((*Msg)(nil), nil) + cdc.RegisterConcrete(SideChainSubmitProposalMsg{}, "cosmos-sdk/MsgSideChainSubmitProposal", nil) + cdc.RegisterConcrete(SideChainDepositMsg{}, "cosmos-sdk/MsgSideChainDeposit", nil) + cdc.RegisterConcrete(SideChainVoteMsg{}, "cosmos-sdk/MsgSideChainVote", nil) + + cdc.RegisterInterface((*SCParam)(nil), nil) + cdc.RegisterConcrete(&OracleParams{}, "params/OracleParamSet", nil) + cdc.RegisterConcrete(&StakeParams{}, "params/StakeParamSet", nil) + cdc.RegisterConcrete(&SlashParams{}, "params/SlashParamSet", nil) + cdc.RegisterConcrete(&IbcParams{}, "params/IbcParamSet", nil) + + cdc.RegisterConcrete(CreateOrderMsg{}, "dex/NewOrder", nil) cdc.RegisterConcrete(CancelOrderMsg{}, "dex/CancelOrder", nil) cdc.RegisterConcrete(TokenIssueMsg{}, "tokens/IssueMsg", nil)