diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce6448cc..ec83374de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +##v0.26.3 +* [sdk] [\#289] (https://github.com/bnb-chain/bnc-cosmos-sdk/pull/289) feat: implement bep126 +* [sdk] [\#335] (https://github.com/bnb-chain/bnc-cosmos-sdk/pull/335) feat: implement new slash mechanism +* [fix] [\#340] (https://github.com/bnb-chain/bnc-cosmos-sdk/pull/340) fix: use height in store for checking upgrade status +* [fix] [\#341] (https://github.com/bnb-chain/bnc-cosmos-sdk/pull/341) dep: update dependency to fix security issue + ##v0.26.2 * [cli] [\#336] (https://github.com/bnb-chain/bnc-cosmos-sdk/pull/336) fix: fix merkle proof invalid issue of cli query diff --git a/client/flags.go b/client/flags.go index ba29a7092..c2c6ae55e 100644 --- a/client/flags.go +++ b/client/flags.go @@ -57,7 +57,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") - c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") + c.Flags().Int64(FlagAccountNumber, 0, "Account number to sign the tx") c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx") c.Flags().String(FlagMemo, "", "Memo to send along with transaction") c.Flags().Int64(FlagSource, 0, "Source of tx") @@ -65,14 +65,14 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") c.Flags().Bool(FlagUseTss, false, "Use a tss vault") - c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") - c.Flags().Bool(FlagJson, false, "return output in json format") - c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") + c.Flags().Bool(FlagAsync, false, "Broadcast transactions asynchronously") + c.Flags().Bool(FlagJson, false, "Return output in json format") + c.Flags().Bool(FlagPrintResponse, true, "Return tx response (only works with async = false)") c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)") - c.Flags().Bool(FlagDryRun, false, "ignore the perform a simulation of a transaction, but don't broadcast it") + c.Flags().Bool(FlagDryRun, false, "Ignore the perform a simulation of a transaction, but don't broadcast it") c.Flags().Bool(FlagDry, false, "Generate and return the tx bytes (do not broadcast)") c.Flags().Bool(FlagOffline, false, "Offline mode. Do not query blockchain data") - c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT") + c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT") viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) viper.BindPFlag(FlagUseTss, c.Flags().Lookup(FlagUseTss)) diff --git a/go.mod b/go.mod index 5c2e39805..e1c39cdb1 100644 --- a/go.mod +++ b/go.mod @@ -65,9 +65,9 @@ require ( github.com/spf13/cast v1.3.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/zondax/hid v0.9.0 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect google.golang.org/grpc v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -77,7 +77,7 @@ require ( replace ( github.com/tendermint/go-amino => github.com/bnb-chain/bnc-go-amino v0.14.1-binance.2 github.com/tendermint/iavl => github.com/bnb-chain/bnc-tendermint-iavl v0.12.0-binance.5 - github.com/tendermint/tendermint => github.com/bnb-chain/bnc-tendermint v0.32.3-bc.9 + github.com/tendermint/tendermint => github.com/bnb-chain/bnc-tendermint v0.32.3-bc.10 github.com/zondax/ledger-cosmos-go => github.com/bnb-chain/ledger-cosmos-go v0.9.10-0.20230201065744-d644bede1667 golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae ) diff --git a/go.sum b/go.sum index 402c2c880..46a847fd7 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bnb-chain/bnc-go-amino v0.14.1-binance.2 h1:iAlp9gqG0f2LGAauf3ZiijWlT6NI+W2r9y70HH9LI3k= github.com/bnb-chain/bnc-go-amino v0.14.1-binance.2/go.mod h1:LiCO7jev+3HwLGAiN9gpD0z+jTz95RqgSavbse55XOY= -github.com/bnb-chain/bnc-tendermint v0.32.3-bc.9 h1:ubmtJkfE9SZfdDTzGBoXkXO8htHCniH8vUL/lLhxd8A= -github.com/bnb-chain/bnc-tendermint v0.32.3-bc.9/go.mod h1:q5x58ZV0f3ZImJnOjKYERhRMiPNzVMaO72HLUVmuxmI= +github.com/bnb-chain/bnc-tendermint v0.32.3-bc.10 h1:E4iSwEbJCLYchHiHE1gnOM3jjmJXLBxARhy/RCl8CpI= +github.com/bnb-chain/bnc-tendermint v0.32.3-bc.10/go.mod h1:wKxpgQYxtZxPasF59zca7NbmIazOjUdualm1gMEMGTU= github.com/bnb-chain/bnc-tendermint-iavl v0.12.0-binance.5 h1:8trIShwwXvCUQz34DwIsgPz2yJgVlZRMv+2IwEuKjTM= github.com/bnb-chain/bnc-tendermint-iavl v0.12.0-binance.5/go.mod h1:4Kf0qoRDM4vc1ZTBSHqwiQyVniCPS+HAAY434oYzr6w= github.com/bnb-chain/ics23 v0.0.0-20221021082321-d0a365dd9898/go.mod h1:cU6lTGolbbLFsGCgceNB2AzplH1xecLp6+KXvxM32nI= @@ -480,8 +480,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -553,11 +553,11 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -565,8 +565,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 3114736c7..3de96ae45 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -421,7 +421,7 @@ func (si StoreInfo) GetHash() []byte { // Hash returns the simple merkle root hash of the stores sorted by name. func (ci CommitInfo) Hash() []byte { m := make(map[string][]byte, len(ci.StoreInfos)) - if sdk.IsUpgrade(sdk.BEP171) { + if sdk.IsUpgradeWithHeight(sdk.BEP171, ci.Version) { for _, storeInfo := range ci.StoreInfos { m[storeInfo.Name] = storeInfo.GetHash() } @@ -443,7 +443,7 @@ func (ci CommitInfo) CommitID() CommitID { func (ci CommitInfo) toMap() map[string][]byte { m := make(map[string][]byte, len(ci.StoreInfos)) - if sdk.IsUpgrade(sdk.BEP171) { + if sdk.IsUpgradeWithHeight(sdk.BEP171, ci.Version) { for _, storeInfo := range ci.StoreInfos { m[storeInfo.Name] = storeInfo.Core.CommitID.Hash } diff --git a/types/address.go b/types/address.go index 840ad0dbf..c90654c81 100644 --- a/types/address.go +++ b/types/address.go @@ -11,13 +11,14 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/encoding/amino" - "github.com/tendermint/tendermint/libs/bech32" ) const ( // AddrLen defines a valid address length AddrLen = 20 + // VoteAddrLen defines a valid vote address length + VoteAddrLen = 48 // SmartChainAddressLength defines a valid smart chain address length SmartChainAddressLength = 20 diff --git a/types/stake.go b/types/stake.go index c1ce835d6..4a1a02170 100644 --- a/types/stake.go +++ b/types/stake.go @@ -15,7 +15,7 @@ const ( Bonded BondStatus = 0x02 ) -//BondStatusToString for pretty prints of Bond Status +// BondStatusToString for pretty prints of Bond Status func BondStatusToString(b BondStatus) string { switch b { case 0x00: @@ -50,6 +50,7 @@ type Validator interface { GetDelegatorShares() Dec // Total out standing delegator shares GetBondHeight() int64 // height in which the validator became active GetSideChainConsAddr() []byte // validation consensus address on side chain + GetSideChainVoteAddr() []byte // validation vote address on side chain IsSideChainValidator() bool // if it belongs to side chain } @@ -73,6 +74,7 @@ type ValidatorSet interface { Validator(Context, ValAddress) Validator // get a particular validator by operator address ValidatorByConsAddr(Context, ConsAddress) Validator // get a particular validator by consensus address + ValidatorByVoteAddr(Context, []byte) Validator // get a particular validator by vote address TotalPower(Context) Dec // total power of the validator set // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction diff --git a/types/upgrade.go b/types/upgrade.go index 84d78eb06..08668891b 100644 --- a/types/upgrade.go +++ b/types/upgrade.go @@ -23,6 +23,8 @@ const ( BEP171 = "BEP171" //https://github.com/bnb-chain/BEPs/pull/171 BEP173 = "BEP173" // https://github.com/bnb-chain/BEPs/pull/173 FixDoubleSignChainId = "FixDoubleSignChainId" + BEP126 = "BEP126" //https://github.com/binance-chain/BEPs/pull/126 + ) var MainNetConfig = UpgradeConfig{ @@ -171,6 +173,15 @@ func IsUpgrade(name string) bool { return UpgradeMgr.GetHeight() >= upgradeHeight } +func IsUpgradeWithHeight(name string, height int64) bool { + upgradeHeight := UpgradeMgr.GetUpgradeHeight(name) + if upgradeHeight == 0 { + return false + } + + return height >= upgradeHeight +} + func ShouldCommitStore(storeKeyName string) bool { storeKeyHeight := UpgradeMgr.GetStoreKeyHeight(storeKeyName) if storeKeyHeight == 0 { diff --git a/x/paramHub/hub.go b/x/paramHub/hub.go index 886a5a1b8..f9e4cdd76 100644 --- a/x/paramHub/hub.go +++ b/x/paramHub/hub.go @@ -133,6 +133,13 @@ func RegisterUpgradeBeginBlocker(paramHub *ParamHub) { } paramHub.UpdateFeeParams(ctx, updateFeeParams) }) + sdk.UpgradeMgr.RegisterBeginBlocker(sdk.BEP126, func(ctx sdk.Context) { + updateFeeParams := []param.FeeParam{ + ¶m.FixedFeeParams{MsgType: "side_create_validator_with_vote_addr", Fee: CreateSideChainValidatorFee, FeeFor: sdk.FeeForProposer}, + ¶m.FixedFeeParams{MsgType: "side_edit_validator_with_vote_addr", Fee: EditSideChainValidatorFee, FeeFor: sdk.FeeForProposer}, + } + paramHub.UpdateFeeParams(ctx, updateFeeParams) + }) } func EndBreatheBlock(ctx sdk.Context, paramHub *ParamHub) { @@ -149,56 +156,58 @@ func init() { // CalculatorsGen is defined in a common package which can't import app package. // Reasonable to init here, since fee param drive the calculator. fees.CalculatorsGen = map[string]fees.FeeCalculatorGenerator{ - "submit_proposal": fees.FixedFeeCalculatorGen, - "deposit": fees.FixedFeeCalculatorGen, - "vote": fees.FixedFeeCalculatorGen, - "side_submit_proposal": fees.FixedFeeCalculatorGen, - "side_deposit": fees.FixedFeeCalculatorGen, - "side_vote": fees.FixedFeeCalculatorGen, - "create_validator": fees.FixedFeeCalculatorGen, - "remove_validator": fees.FixedFeeCalculatorGen, - "side_create_validator": fees.FixedFeeCalculatorGen, - "side_edit_validator": fees.FixedFeeCalculatorGen, - "side_delegate": fees.FixedFeeCalculatorGen, - "side_redelegate": fees.FixedFeeCalculatorGen, - "side_undelegate": fees.FixedFeeCalculatorGen, - "bsc_submit_evidence": fees.FixedFeeCalculatorGen, - "side_chain_unjail": fees.FixedFeeCalculatorGen, - "dexList": fees.FixedFeeCalculatorGen, - "orderNew": fees.FixedFeeCalculatorGen, - "orderCancel": fees.FixedFeeCalculatorGen, - "issueMsg": fees.FixedFeeCalculatorGen, - "mintMsg": fees.FixedFeeCalculatorGen, - "tokensBurn": fees.FixedFeeCalculatorGen, - "setAccountFlags": fees.FixedFeeCalculatorGen, - "tokensFreeze": fees.FixedFeeCalculatorGen, - "timeLock": fees.FixedFeeCalculatorGen, - "timeUnlock": fees.FixedFeeCalculatorGen, - "timeRelock": fees.FixedFeeCalculatorGen, - "transferOwnership": fees.FixedFeeCalculatorGen, - "send": bank.TransferFeeCalculatorGen, - "HTLT": fees.FixedFeeCalculatorGen, - "depositHTLT": fees.FixedFeeCalculatorGen, - "claimHTLT": fees.FixedFeeCalculatorGen, - "refundHTLT": fees.FixedFeeCalculatorGen, - "crossBind": fees.FixedFeeCalculatorGen, - "crossUnbind": fees.FixedFeeCalculatorGen, - "crossTransferOut": fees.FixedFeeCalculatorGen, - "crossBindRelayFee": fees.FixedFeeCalculatorGen, - "crossUnbindRelayFee": fees.FixedFeeCalculatorGen, - "crossTransferOutRelayFee": fees.FixedFeeCalculatorGen, - "oracleClaim": fees.FixedFeeCalculatorGen, - "miniTokensSetURI": fees.FixedFeeCalculatorGen, - "dexListMini": fees.FixedFeeCalculatorGen, - "tinyIssueMsg": fees.FixedFeeCalculatorGen, - "miniIssueMsg": fees.FixedFeeCalculatorGen, - "crossDistributeRewardRelayFee": fees.FixedFeeCalculatorGen, - "crossDistributeUndelegatedRelayFee": fees.FixedFeeCalculatorGen, - "create_validator_open": fees.FixedFeeCalculatorGen, - "edit_validator": fees.FixedFeeCalculatorGen, - "delegate": fees.FixedFeeCalculatorGen, - "redelegate": fees.FixedFeeCalculatorGen, - "undelegate": fees.FixedFeeCalculatorGen, - "unjail": fees.FixedFeeCalculatorGen, + "submit_proposal": fees.FixedFeeCalculatorGen, + "deposit": fees.FixedFeeCalculatorGen, + "vote": fees.FixedFeeCalculatorGen, + "side_submit_proposal": fees.FixedFeeCalculatorGen, + "side_deposit": fees.FixedFeeCalculatorGen, + "side_vote": fees.FixedFeeCalculatorGen, + "create_validator": fees.FixedFeeCalculatorGen, + "remove_validator": fees.FixedFeeCalculatorGen, + "side_create_validator": fees.FixedFeeCalculatorGen, + "side_edit_validator": fees.FixedFeeCalculatorGen, + "side_create_validator_with_vote_addr": fees.FixedFeeCalculatorGen, + "side_edit_validator_with_vote_addr": fees.FixedFeeCalculatorGen, + "side_delegate": fees.FixedFeeCalculatorGen, + "side_redelegate": fees.FixedFeeCalculatorGen, + "side_undelegate": fees.FixedFeeCalculatorGen, + "bsc_submit_evidence": fees.FixedFeeCalculatorGen, + "side_chain_unjail": fees.FixedFeeCalculatorGen, + "dexList": fees.FixedFeeCalculatorGen, + "orderNew": fees.FixedFeeCalculatorGen, + "orderCancel": fees.FixedFeeCalculatorGen, + "issueMsg": fees.FixedFeeCalculatorGen, + "mintMsg": fees.FixedFeeCalculatorGen, + "tokensBurn": fees.FixedFeeCalculatorGen, + "setAccountFlags": fees.FixedFeeCalculatorGen, + "tokensFreeze": fees.FixedFeeCalculatorGen, + "timeLock": fees.FixedFeeCalculatorGen, + "timeUnlock": fees.FixedFeeCalculatorGen, + "timeRelock": fees.FixedFeeCalculatorGen, + "transferOwnership": fees.FixedFeeCalculatorGen, + "send": bank.TransferFeeCalculatorGen, + "HTLT": fees.FixedFeeCalculatorGen, + "depositHTLT": fees.FixedFeeCalculatorGen, + "claimHTLT": fees.FixedFeeCalculatorGen, + "refundHTLT": fees.FixedFeeCalculatorGen, + "crossBind": fees.FixedFeeCalculatorGen, + "crossUnbind": fees.FixedFeeCalculatorGen, + "crossTransferOut": fees.FixedFeeCalculatorGen, + "crossBindRelayFee": fees.FixedFeeCalculatorGen, + "crossUnbindRelayFee": fees.FixedFeeCalculatorGen, + "crossTransferOutRelayFee": fees.FixedFeeCalculatorGen, + "oracleClaim": fees.FixedFeeCalculatorGen, + "miniTokensSetURI": fees.FixedFeeCalculatorGen, + "dexListMini": fees.FixedFeeCalculatorGen, + "tinyIssueMsg": fees.FixedFeeCalculatorGen, + "miniIssueMsg": fees.FixedFeeCalculatorGen, + "crossDistributeRewardRelayFee": fees.FixedFeeCalculatorGen, + "crossDistributeUndelegatedRelayFee": fees.FixedFeeCalculatorGen, + "create_validator_open": fees.FixedFeeCalculatorGen, + "edit_validator": fees.FixedFeeCalculatorGen, + "delegate": fees.FixedFeeCalculatorGen, + "redelegate": fees.FixedFeeCalculatorGen, + "undelegate": fees.FixedFeeCalculatorGen, + "unjail": fees.FixedFeeCalculatorGen, } } diff --git a/x/paramHub/types/types.go b/x/paramHub/types/types.go index 5a1c1b957..464ff2ace 100644 --- a/x/paramHub/types/types.go +++ b/x/paramHub/types/types.go @@ -52,11 +52,13 @@ var ( "claimHTLT": {}, "refundHTLT": {}, - "side_create_validator": {}, - "side_edit_validator": {}, - "side_delegate": {}, - "side_redelegate": {}, - "side_undelegate": {}, + "side_create_validator": {}, + "side_edit_validator": {}, + "side_create_validator_with_vote_addr": {}, + "side_edit_validator_with_vote_addr": {}, + "side_delegate": {}, + "side_redelegate": {}, + "side_undelegate": {}, "bsc_submit_evidence": {}, "side_chain_unjail": {}, diff --git a/x/sidechain/chanManage.go b/x/sidechain/chanManage.go index cf0396002..4fd423778 100644 --- a/x/sidechain/chanManage.go +++ b/x/sidechain/chanManage.go @@ -1,25 +1,15 @@ package sidechain import ( - "encoding/hex" "time" - "github.com/cosmos/cosmos-sdk/bsc/rlp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" - pTypes "github.com/cosmos/cosmos-sdk/x/paramHub/types" "github.com/cosmos/cosmos-sdk/x/sidechain/types" ) const ( SafeToleratePeriod = 2 * 7 * 24 * 60 * 60 * time.Second // 2 weeks - - EnableOrDisableChannelKey = "enableOrDisableChannel" - AddOrUpdateChannelKey = "addOrUpdateChannel" -) - -var ( - CrossChainContractAddr, _ = hex.DecodeString("0000000000000000000000000000000000002000") ) func (k *Keeper) getLastChanPermissionChanges(ctx sdk.Context) []types.ChanPermissionSetting { @@ -71,32 +61,5 @@ func (k *Keeper) getLastChanPermissionChanges(ctx sdk.Context) []types.ChanPermi func (k *Keeper) SaveChannelSettingChangeToIbc(ctx sdk.Context, sideChainId sdk.ChainID, channelId sdk.ChannelID, permission sdk.ChannelPermission) (seq uint64, sdkErr sdk.Error) { valueBytes := []byte{byte(channelId), byte(permission)} - paramChange := pTypes.CSCParamChange{ - Key: EnableOrDisableChannelKey, - ValueBytes: valueBytes, - TargetBytes: CrossChainContractAddr, - } - - bz, err := rlp.EncodeToBytes(¶mChange) - if err != nil { - return 0, sdk.ErrInternal("failed to encode paramChange") - } - return k.ibcKeeper.CreateRawIBCPackageById(ctx, sideChainId, types.GovChannelId, sdk.SynCrossChainPackageType, bz) -} - -func (k *Keeper) CreateNewChannelToIbc(ctx sdk.Context, sideChainId sdk.ChainID, channelId sdk.ChannelID, rewardConfig sdk.RewardConfig, handleContract []byte) (seq uint64, sdkErr sdk.Error) { - valueBytes := []byte{byte(channelId), byte(rewardConfig)} - valueBytes = append(valueBytes, handleContract...) - - paramChange := pTypes.CSCParamChange{ - Key: AddOrUpdateChannelKey, - ValueBytes: valueBytes, - TargetBytes: CrossChainContractAddr, - } - - bz, err := rlp.EncodeToBytes(¶mChange) - if err != nil { - return 0, sdk.ErrInternal("failed to encode paramChange") - } - return k.ibcKeeper.CreateRawIBCPackageById(ctx, sideChainId, types.GovChannelId, sdk.SynCrossChainPackageType, bz) + return k.sendParamChangeToIbc(ctx, sideChainId, EnableOrDisableChannelKey, valueBytes, CrossChainContractAddr) } diff --git a/x/sidechain/cscParamChange.go b/x/sidechain/cscParamChange.go new file mode 100644 index 000000000..d14d629b1 --- /dev/null +++ b/x/sidechain/cscParamChange.go @@ -0,0 +1,46 @@ +package sidechain + +import ( + "encoding/hex" + "github.com/cosmos/cosmos-sdk/bsc/rlp" + sdk "github.com/cosmos/cosmos-sdk/types" + pTypes "github.com/cosmos/cosmos-sdk/x/paramHub/types" + "github.com/cosmos/cosmos-sdk/x/sidechain/types" +) + +const ( + EnableOrDisableChannelKey = "enableOrDisableChannel" + AddOrUpdateChannelKey = "addOrUpdateChannel" + AddOperatorKey = "addOperator" + DeleteOperatorKey = "deleteOperator" +) + +var ( + SystemRewardContractAddr, _ = hex.DecodeString("0000000000000000000000000000000000001002") + CrossChainContractAddr, _ = hex.DecodeString("0000000000000000000000000000000000002000") +) + +func (k *Keeper) CreateNewCrossChainChannel(ctx sdk.Context, sideChainId sdk.ChainID, channelId sdk.ChannelID, rewardConfig sdk.RewardConfig, handleContract []byte) (seq uint64, sdkErr sdk.Error) { + valueBytes := []byte{byte(channelId), byte(rewardConfig)} + valueBytes = append(valueBytes, handleContract...) + + return k.sendParamChangeToIbc(ctx, sideChainId, AddOrUpdateChannelKey, valueBytes, CrossChainContractAddr) +} + +func (k *Keeper) AddSystemRewardOperator(ctx sdk.Context, sideChainId sdk.ChainID, operator sdk.SmartChainAddress) (seq uint64, sdkErr sdk.Error) { + return k.sendParamChangeToIbc(ctx, sideChainId, AddOperatorKey, operator[:], SystemRewardContractAddr) +} + +func (k *Keeper) sendParamChangeToIbc(ctx sdk.Context, sideChainId sdk.ChainID, key string, valueBytes []byte, targetBytes []byte) (seq uint64, sdkErr sdk.Error) { + paramChange := pTypes.CSCParamChange{ + Key: key, + ValueBytes: valueBytes, + TargetBytes: targetBytes, + } + + bz, err := rlp.EncodeToBytes(¶mChange) + if err != nil { + return 0, sdk.ErrInternal("failed to encode paramChange") + } + return k.ibcKeeper.CreateRawIBCPackageById(ctx, sideChainId, types.GovChannelId, sdk.SynCrossChainPackageType, bz) +} diff --git a/x/slashing/errors.go b/x/slashing/errors.go index 6bd3145e0..18ebe73ca 100644 --- a/x/slashing/errors.go +++ b/x/slashing/errors.go @@ -1,8 +1,9 @@ -//nolint +// nolint package slashing import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -21,18 +22,23 @@ const ( CodeSelfDelegationTooLowToUnjail CodeType = 105 CodeInvalidClaim CodeType = 106 - CodeExpiredEvidence CodeType = 201 - CodeFailSlash CodeType = 202 - CodeHandledEvidence CodeType = 203 - CodeInvalidEvidence CodeType = 204 - CodeInvalidSideChain CodeType = 205 - CodeDuplicateDowntimeClaim CodeType = 206 + CodeExpiredEvidence CodeType = 201 + CodeFailSlash CodeType = 202 + CodeHandledEvidence CodeType = 203 + CodeInvalidEvidence CodeType = 204 + CodeInvalidSideChain CodeType = 205 + CodeDuplicateDowntimeClaim CodeType = 206 + CodeDuplicateMaliciousVoteClaim CodeType = 207 ) func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "that address is not associated with any known validator") } +func ErrNoValidatorWithVoteAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "that vote address is not associated with any known validator") +} + func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address") } @@ -81,6 +87,10 @@ func ErrDuplicateDowntimeClaim(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeDuplicateDowntimeClaim, "duplicate downtime claim") } +func ErrDuplicateMaliciousVoteClaim(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeDuplicateMaliciousVoteClaim, "duplicate malicious vote claim") +} + func ErrInvalidInput(codespace sdk.CodespaceType, msg string) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, msg) } diff --git a/x/slashing/execute_test.go b/x/slashing/execute_test.go index e4a9ead29..d33aa70f3 100644 --- a/x/slashing/execute_test.go +++ b/x/slashing/execute_test.go @@ -1,10 +1,11 @@ package slashing import ( - "github.com/cosmos/cosmos-sdk/bsc/rlp" "testing" "time" + "github.com/cosmos/cosmos-sdk/bsc/rlp" + "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" @@ -33,8 +34,8 @@ func TestSideChainSlashDowntime(t *testing.T) { sideHeight := uint64(100) sideChainId := "bsc" sideTimestamp := ctx.BlockHeader().Time.Add(-6 * 60 * 60 * time.Second) - claim := SideDowntimeSlashPackage{ - SideConsAddr: sideConsAddr, + claim := SideSlashPackage{ + SideAddr: sideConsAddr, SideHeight: sideHeight, SideChainId: sdk.ChainID(1), SideTimestamp: uint64(sideTimestamp.Unix()), @@ -74,22 +75,22 @@ func TestSideChainSlashDowntime(t *testing.T) { claim.SideHeight = 0 bz, _ := rlp.EncodeToBytes(&claim) - _, result = keeper.checkSideDowntimeSlashPackage(bz) + _, result = keeper.checkSideSlashPackage(bz) require.NotNil(t, result) claim.SideHeight = sideHeight - claim.SideConsAddr = createSideAddr(21) + claim.SideAddr = createSideAddr(21) result = keeper.slashingSideDowntime(ctx, &claim) require.NotNil(t, result) - claim.SideConsAddr = sideConsAddr + claim.SideAddr = sideConsAddr claim.SideTimestamp = uint64(ctx.BlockHeader().Time.Add(-24 * 60 * 60 * time.Second).Unix()) result = keeper.slashingSideDowntime(ctx, &claim) require.EqualValues(t, CodeExpiredEvidence, result.Code(), "Expected got 201 err code, but got err: %v", result) claim.SideTimestamp = uint64(ctx.BlockHeader().Time.Add(-6 * 60 * 60 * time.Second).Unix()) - claim.SideConsAddr = sideConsAddr + claim.SideAddr = sideConsAddr claim.SideChainId = sdk.ChainID(2) result = keeper.slashingSideDowntime(ctx, &claim) @@ -97,7 +98,7 @@ func TestSideChainSlashDowntime(t *testing.T) { require.EqualValues(t, CodeInvalidSideChain, result.Code(), "Expected got 205 error code, but got err: %v", result) claim.SideHeight = sideHeight - claim.SideConsAddr = createSideAddr(20) + claim.SideAddr = createSideAddr(20) claim.SideChainId = sdk.ChainID(1) result = keeper.slashingSideDowntime(ctx, &claim) @@ -138,8 +139,8 @@ func TestSlashDowntimeBalanceVerify(t *testing.T) { sideHeight := uint64(50) sideTimestamp := ctx.BlockHeader().Time.Add(-6 * 60 * 60 * time.Second) - claim := SideDowntimeSlashPackage{ - SideConsAddr: sideConsAddr2, + claim := SideSlashPackage{ + SideAddr: sideConsAddr2, SideHeight: sideHeight, SideChainId: sdk.ChainID(1), SideTimestamp: uint64(sideTimestamp.Unix()), @@ -166,8 +167,8 @@ func TestSlashDowntimeBalanceVerify(t *testing.T) { sideHeight = uint64(80) sideTimestamp = ctx.BlockHeader().Time.Add(-3 * 60 * 60 * time.Second) - claim = SideDowntimeSlashPackage{ - SideConsAddr: sideConsAddr2, + claim = SideSlashPackage{ + SideAddr: sideConsAddr2, SideHeight: sideHeight, SideChainId: sdk.ChainID(1), SideTimestamp: uint64(sideTimestamp.Unix()), diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index abaf21e95..48755ce4b 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -237,9 +237,13 @@ func (k *Keeper) SubscribeParamChange(hub types.ParamChangePublisher) { // implement cross chain app func (k *Keeper) ExecuteSynPackage(ctx sdk.Context, payload []byte, _ int64) sdk.ExecuteResult { var resCode uint32 - pack, err := k.checkSideDowntimeSlashPackage(payload) + sideSlashPack, err := k.checkSideSlashPackage(payload) if err == nil { - err = k.slashingSideDowntime(ctx, pack) + if sideSlashPack.addrType == SideConsAddrType { + err = k.slashingSideDowntime(ctx, sideSlashPack) + } else if sideSlashPack.addrType == SideVoteAddrType { + err = k.slashingSideMaliciousVote(ctx, sideSlashPack) + } } if err != nil { resCode = uint32(err.ABCICode()) @@ -263,14 +267,19 @@ func (k *Keeper) ExecuteFailAckPackage(ctx sdk.Context, payload []byte) sdk.Exec panic("receive unexpected fail ack package") } -func (k *Keeper) checkSideDowntimeSlashPackage(payload []byte) (*SideDowntimeSlashPackage, sdk.Error) { - var slashEvent SideDowntimeSlashPackage +func (k *Keeper) checkSideSlashPackage(payload []byte) (*SideSlashPackage, sdk.Error) { + var slashEvent SideSlashPackage err := rlp.DecodeBytes(payload, &slashEvent) if err != nil { return nil, ErrInvalidInput(k.Codespace, "failed to parse the payload") } - if len(slashEvent.SideConsAddr) != sdk.AddrLen { - return nil, ErrInvalidClaim(k.Codespace, fmt.Sprintf("wrong sideConsAddr length, expected=%d", slashEvent.SideConsAddr)) + + if len(slashEvent.SideAddr) == sdk.AddrLen { + slashEvent.addrType = SideConsAddrType + } else if len(slashEvent.SideAddr) == sdk.VoteAddrLen { + slashEvent.addrType = SideVoteAddrType + } else { + return nil, ErrInvalidClaim(k.Codespace, fmt.Sprintf("wrong sideAddr length:%d, expected:%d or %d", len(slashEvent.SideAddr), sdk.AddrLen, sdk.VoteAddrLen)) } if slashEvent.SideHeight <= 0 { @@ -287,7 +296,8 @@ func (k *Keeper) checkSideDowntimeSlashPackage(payload []byte) (*SideDowntimeSla return &slashEvent, nil } -func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideDowntimeSlashPackage) sdk.Error { +func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideSlashPackage) sdk.Error { + sideConsAddr := pack.SideAddr sideChainName, err := k.ScKeeper.GetDestChainName(pack.SideChainId) if err != nil { return ErrInvalidSideChainId(DefaultCodespace) @@ -303,12 +313,12 @@ func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideDowntimeSlashPa return ErrExpiredEvidence(DefaultCodespace) } - if k.hasSlashRecord(sideCtx, pack.SideConsAddr, Downtime, pack.SideHeight) { + if k.hasSlashRecord(sideCtx, sideConsAddr, Downtime, pack.SideHeight) { return ErrDuplicateDowntimeClaim(k.Codespace) } slashAmt := k.DowntimeSlashAmount(sideCtx) - validator, slashedAmt, err := k.validatorSet.SlashSideChain(ctx, sideChainName, pack.SideConsAddr, sdk.NewDec(slashAmt)) + validator, slashedAmt, err := k.validatorSet.SlashSideChain(ctx, sideChainName, sideConsAddr, sdk.NewDec(slashAmt)) if err != nil { return ErrFailedToSlash(k.Codespace, err.Error()) } @@ -327,7 +337,7 @@ func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideDowntimeSlashPa var validatorsAllocatedAmt map[string]int64 var found bool if remaining > 0 { - found, validatorsAllocatedAmt, err = k.validatorSet.AllocateSlashAmtToValidators(sideCtx, pack.SideConsAddr, sdk.NewDec(remaining)) + found, validatorsAllocatedAmt, err = k.validatorSet.AllocateSlashAmtToValidators(sideCtx, sideConsAddr, sdk.NewDec(remaining)) if err != nil { return ErrFailedToSlash(k.Codespace, err.Error()) } @@ -340,7 +350,7 @@ func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideDowntimeSlashPa jailUntil := header.Time.Add(k.DowntimeUnbondDuration(sideCtx)) sr := SlashRecord{ - ConsAddr: pack.SideConsAddr, + ConsAddr: sideConsAddr, InfractionType: Downtime, InfractionHeight: pack.SideHeight, SlashHeight: header.Height, @@ -351,12 +361,14 @@ func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideDowntimeSlashPa k.setSlashRecord(sideCtx, sr) // Set or updated validator jail duration - signInfo, found := k.getValidatorSigningInfo(sideCtx, pack.SideConsAddr) + signInfo, found := k.getValidatorSigningInfo(sideCtx, sideConsAddr) if !found { - return sdk.ErrInternal(fmt.Sprintf("Expected signing info for validator %s but not found", sdk.HexEncode(pack.SideConsAddr))) + return sdk.ErrInternal(fmt.Sprintf("Expected signing info for validator %s but not found", sdk.HexEncode(sideConsAddr))) } + //if jailUntil.After(signInfo.JailedUntil) { signInfo.JailedUntil = jailUntil - k.setValidatorSigningInfo(sideCtx, pack.SideConsAddr, signInfo) + //} + k.setValidatorSigningInfo(sideCtx, sideConsAddr, signInfo) if k.PbsbServer != nil { event := SideSlashEvent{ @@ -376,6 +388,105 @@ func (k *Keeper) slashingSideDowntime(ctx sdk.Context, pack *SideDowntimeSlashPa return nil } +func (k *Keeper) slashingSideMaliciousVote(ctx sdk.Context, pack *SideSlashPackage) sdk.Error { + logger := ctx.Logger().With("module", "x/slashing") + sideVoteAddr := pack.SideAddr + sideChainName, err := k.ScKeeper.GetDestChainName(pack.SideChainId) + if err != nil { + return ErrInvalidSideChainId(DefaultCodespace) + } + sideCtx, err := k.ScKeeper.PrepareCtxForSideChain(ctx, sideChainName) + if err != nil { + return ErrInvalidSideChainId(DefaultCodespace) + } + + header := sideCtx.BlockHeader() + age := uint64(header.Time.Unix()) - pack.SideTimestamp + maxEvidenceAge := uint64(k.MaxEvidenceAge(sideCtx).Seconds()) + if age > maxEvidenceAge { + return ErrExpiredEvidence(DefaultCodespace) + } + + validator := k.validatorSet.ValidatorByVoteAddr(sideCtx, sideVoteAddr) + if validator == nil { + return ErrNoValidatorWithVoteAddr(k.Codespace) + } + + sideConsAddr := []byte(validator.GetSideChainConsAddr()) + signInfo, found := k.getValidatorSigningInfo(sideCtx, sideConsAddr) + if !found { + return sdk.ErrInternal(fmt.Sprintf("Expected signing info for validator %s but not found", sdk.HexEncode(sideConsAddr))) + } + // in duration of malicious vote slash, validator can only be slashed once, to protect validator from funds drained + if k.isMaliciousVoteSlashed(sideCtx, sideConsAddr) && pack.SideTimestamp < uint64(signInfo.JailedUntil.Unix()) { + logger.Info(fmt.Sprintf("slashing is blocked because %s is still in duration of lastest malicious vote slash", sideConsAddr)) + return ErrFailedToSlash(k.Codespace, "still in duration of lastest malicious vote slash") + } else if k.hasSlashRecord(sideCtx, sideConsAddr, MaliciousVote, pack.SideHeight) { + logger.Info("slashing is blocked for duplicate malicious vote claim") + return ErrDuplicateMaliciousVoteClaim(k.Codespace) + } + + // Malicious vote confirmed + logger.Info(fmt.Sprintf("Confirmed malicious vote from %s at height %d, age %d is less than max age %d, summit at %d, jailed until %d before slashing", + sdk.HexAddress(sideConsAddr), pack.SideHeight, age, maxEvidenceAge, pack.SideTimestamp, uint64(signInfo.JailedUntil.Unix()))) + + slashAmt := k.DoubleSignSlashAmount(sideCtx) + validator, slashedAmt, err := k.validatorSet.SlashSideChain(ctx, sideChainName, sideConsAddr, sdk.NewDec(slashAmt)) + if err != nil { + return ErrFailedToSlash(k.Codespace, err.Error()) + } + + var toFeePool int64 + var validatorsCompensation map[string]int64 + if slashAmt > 0 { + found, validatorsCompensation, err = k.validatorSet.AllocateSlashAmtToValidators(sideCtx, sideConsAddr, sdk.NewDec(slashAmt)) + if err != nil { + return ErrFailedToSlash(k.Codespace, err.Error()) + } + if !found && ctx.IsDeliverTx() { + bondDenom := k.validatorSet.BondDenom(sideCtx) + toFeePool = slashAmt + remainingCoin := sdk.NewCoin(bondDenom, slashAmt) + fees.Pool.AddAndCommitFee("side_malicious_vote_slash", sdk.NewFee(sdk.Coins{remainingCoin}, sdk.FeeForAll)) + } + } + + // Set or updated validator jail duration + jailUntil := header.Time.Add(k.DoubleSignUnbondDuration(sideCtx)) + sr := SlashRecord{ + ConsAddr: sideConsAddr, + InfractionType: MaliciousVote, + InfractionHeight: pack.SideHeight, + SlashHeight: header.Height, + JailUntil: jailUntil, + SlashAmt: slashedAmt.RawInt(), + SideChainId: sideChainName, + } + k.setSlashRecord(sideCtx, sr) + + if jailUntil.After(signInfo.JailedUntil) { + signInfo.JailedUntil = jailUntil + } + k.setValidatorSigningInfo(sideCtx, sideConsAddr, signInfo) + + if k.PbsbServer != nil { + event := SideSlashEvent{ + Validator: validator.GetOperator(), + InfractionType: MaliciousVote, + InfractionHeight: int64(pack.SideHeight), + SlashHeight: header.Height, + JailUtil: jailUntil, + SlashAmt: slashedAmt.RawInt(), + ToFeePool: toFeePool, + SideChainId: sideChainName, + ValidatorsCompensation: validatorsCompensation, + } + k.PbsbServer.Publish(event) + } + + return nil +} + // TODO: Make a method to remove the pubkey from the map when a validator is unbonded. func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { addr := pubkey.Address() diff --git a/x/slashing/slash_record.go b/x/slashing/slash_record.go index 75ae3f86a..a435feccf 100644 --- a/x/slashing/slash_record.go +++ b/x/slashing/slash_record.go @@ -13,6 +13,7 @@ import ( const ( DoubleSign byte = iota Downtime + MaliciousVote ) type SlashRecord struct { @@ -150,6 +151,21 @@ func (k Keeper) getSlashRecordsByConsAddr(ctx sdk.Context, consAddr []byte) (sla return } +func (k Keeper) isMaliciousVoteSlashed(ctx sdk.Context, consAddr []byte) bool { + store := ctx.KVStore(k.storeKey) + consAddrPrefixKey := GetSlashRecordsByAddrIndexKey(consAddr) + iterator := sdk.KVStorePrefixIterator(store, consAddrPrefixKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + slashRecord := MustUnmarshalSlashRecord(k.cdc, iterator.Key(), iterator.Value()) + if slashRecord.InfractionType == MaliciousVote { + return true + } + } + return false +} + func (k Keeper) getSlashRecordsByConsAddrAndType(ctx sdk.Context, consAddr []byte, infractionType byte) (slashRecords []SlashRecord) { store := ctx.KVStore(k.storeKey) consAddrPrefixKey := GetSlashRecordsByAddrAndTypeIndexKey(consAddr, infractionType) diff --git a/x/slashing/types.go b/x/slashing/types.go index a985610b0..5448f730c 100644 --- a/x/slashing/types.go +++ b/x/slashing/types.go @@ -2,9 +2,17 @@ package slashing import "github.com/cosmos/cosmos-sdk/types" -type SideDowntimeSlashPackage struct { - SideConsAddr []byte `json:"side_cons_addr"` +type AddressType int + +const ( + SideConsAddrType AddressType = iota + 1 + SideVoteAddrType +) + +type SideSlashPackage struct { + SideAddr []byte `json:"side_addr"` SideHeight uint64 `json:"side_height"` SideChainId types.ChainID `json:"side_chain_id"` SideTimestamp uint64 `json:"side_timestamp"` + addrType AddressType `rlp:"-"` } diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index 0688df9f1..4577d2bcd 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -1,8 +1,9 @@ package cli import ( - "github.com/cosmos/cosmos-sdk/x/stake/types" flag "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/x/stake/types" ) // nolint @@ -40,6 +41,7 @@ const ( FlagSideChainId = "side-chain-id" FlagSideConsAddr = "side-cons-addr" FlagSideFeeAddr = "side-fee-addr" + FlagSideVoteAddr = "side-vote-addr" ) // common flagsets to add to various functions @@ -64,27 +66,29 @@ func init() { fsAmount.String(FlagAmount, "", "Amount of coins to bond") fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") fsShares.String(FlagSharesPercent, "", "Percent of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") - fsDescriptionCreate.String(FlagMoniker, "", "validator name") - fsDescriptionCreate.String(FlagIdentity, "", "optional identity signature (ex. UPort or Keybase)") - fsDescriptionCreate.String(FlagWebsite, "", "optional website") - fsDescriptionCreate.String(FlagDetails, "", "optional details") + fsDescriptionCreate.String(FlagMoniker, "", "Validator name") + fsDescriptionCreate.String(FlagIdentity, "", "Optional identity signature (ex. UPort or Keybase)") + fsDescriptionCreate.String(FlagWebsite, "", "Optional website") + fsDescriptionCreate.String(FlagDetails, "", "Optional details") fsCommissionUpdate.String(FlagCommissionRate, "", "The new commission rate percentage") fsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") fsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") fsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") - fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "validator name") - fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "optional identity signature (ex. UPort or Keybase)") - fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "optional website") - fsDescriptionEdit.String(FlagDetails, types.DoNotModifyDesc, "optional details") - fsValidator.String(FlagAddressValidator, "", "bech address of the validator") - fsDelegator.String(FlagAddressDelegator, "", "bech address of the delegator") - fsRedelegation.String(FlagAddressValidatorSrc, "", "bech address of the source validator") - fsRedelegation.String(FlagAddressValidatorDst, "", "bech address of the destination validator") - fsSideChainFull.String(FlagSideChainId, "", "chain-id of the side chain the validator belongs to") - fsSideChainFull.String(FlagSideConsAddr, "", "consensus address of the validator on side chain, please use hex format prefixed with 0x") - fsSideChainFull.String(FlagSideFeeAddr, "", "address that validator collects fee rewards on side chain, please use hex format prefixed with 0x") - fsSideChainEdit.String(FlagSideChainId, "", "chain-id of the side chain the validator belongs to") - fsSideChainEdit.String(FlagSideFeeAddr, "", "address that validator collects fee rewards on side chain, please use hex format prefixed with 0x") + fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "Validator name") + fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "Optional identity signature (ex. UPort or Keybase)") + fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "Optional website") + fsDescriptionEdit.String(FlagDetails, types.DoNotModifyDesc, "Optional details") + fsValidator.String(FlagAddressValidator, "", "Bech address of the validator") + fsDelegator.String(FlagAddressDelegator, "", "Bech address of the delegator") + fsRedelegation.String(FlagAddressValidatorSrc, "", "Bech address of the source validator") + fsRedelegation.String(FlagAddressValidatorDst, "", "Bech address of the destination validator") + fsSideChainFull.String(FlagSideChainId, "", "Chain-id of the side chain the validator belongs to") + fsSideChainFull.String(FlagSideConsAddr, "", "Consensus address of the validator on side chain, please use hex format prefixed with 0x") + fsSideChainFull.String(FlagSideFeeAddr, "", "Address that validator collects fee rewards on side chain, please use hex format prefixed with 0x") + fsSideChainFull.String(FlagSideVoteAddr, "", "BLS public key that validator votes for block on side chain, please use hex format prefixed with 0x") + fsSideChainEdit.String(FlagSideChainId, "", "Chain-id of the side chain the validator belongs to") + fsSideChainEdit.String(FlagSideFeeAddr, "", "Address that validator collects fee rewards on side chain, please use hex format prefixed with 0x") fsSideChainEdit.String(FlagSideConsAddr, "", "consensus address of the validator on side chain, please use hex format prefixed with 0x") - fsSideChainId.String(FlagSideChainId, "", "chain-id of the side chain the validator belongs to") + fsSideChainEdit.String(FlagSideVoteAddr, "", "BLS public key that validator votes for block on side chain, please use hex format prefixed with 0x") + fsSideChainId.String(FlagSideChainId, "", "Chain-id of the side chain the validator belongs to") } diff --git a/x/stake/client/cli/tx_sidechain.go b/x/stake/client/cli/tx_sidechain.go index 14e2a5fe7..f6f5a114a 100644 --- a/x/stake/client/cli/tx_sidechain.go +++ b/x/stake/client/cli/tx_sidechain.go @@ -60,23 +60,38 @@ func GetCmdCreateSideChainValidator(cdc *codec.Codec) *cobra.Command { return err } - sideChainId, sideConsAddr, sideFeeAddr, err := getSideChainInfo(true, true) + sideChainId, sideConsAddr, sideFeeAddr, sideVoteAddr, err := getSideChainInfo(true, true) if err != nil { return err } var msg sdk.Msg - if viper.GetString(FlagAddressDelegator) != "" { - delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) - if err != nil { - return err + if sideVoteAddr != nil { + if viper.GetString(FlagAddressDelegator) != "" { + delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) + if err != nil { + return err + } + + msg = stake.NewMsgCreateSideChainValidatorWithVoteAddrOnBehalfOf(delAddr, sdk.ValAddress(valAddr), amount, description, + commissionMsg, sideChainId, sideConsAddr, sideFeeAddr, sideVoteAddr) + } else { + msg = stake.NewMsgCreateSideChainValidatorWithVoteAddr( + sdk.ValAddress(valAddr), amount, description, commissionMsg, sideChainId, sideConsAddr, sideFeeAddr, sideVoteAddr) } - - msg = stake.NewMsgCreateSideChainValidatorOnBehalfOf(delAddr, sdk.ValAddress(valAddr), amount, description, - commissionMsg, sideChainId, sideConsAddr, sideFeeAddr) } else { - msg = stake.NewMsgCreateSideChainValidator( - sdk.ValAddress(valAddr), amount, description, commissionMsg, sideChainId, sideConsAddr, sideFeeAddr) + if viper.GetString(FlagAddressDelegator) != "" { + delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) + if err != nil { + return err + } + + msg = stake.NewMsgCreateSideChainValidatorOnBehalfOf(delAddr, sdk.ValAddress(valAddr), amount, description, + commissionMsg, sideChainId, sideConsAddr, sideFeeAddr) + } else { + msg = stake.NewMsgCreateSideChainValidator( + sdk.ValAddress(valAddr), amount, description, commissionMsg, sideChainId, sideConsAddr, sideFeeAddr) + } } return utils.GenerateOrBroadcastMsgs(txBldr, cliCtx, []sdk.Msg{msg}) @@ -126,11 +141,18 @@ func GetCmdEditSideChainValidator(cdc *codec.Codec) *cobra.Command { newRate = &rate } - sideChainId, sideConsAddr, sideFeeAddr, err := getSideChainInfo(false, false) + sideChainId, sideConsAddr, sideFeeAddr, sideVoteAddr, err := getSideChainInfo(false, false) if err != nil { return err } - msg := stake.NewMsgEditSideChainValidator(sideChainId, sdk.ValAddress(valAddr), description, newRate, sideFeeAddr, sideConsAddr) + + var msg sdk.Msg + if sideVoteAddr != nil { + msg = stake.NewMsgEditSideChainValidatorWithVoteAddr(sideChainId, sdk.ValAddress(valAddr), description, newRate, sideFeeAddr, sideConsAddr, sideVoteAddr) + } else { + msg = stake.NewMsgEditSideChainValidator(sideChainId, sdk.ValAddress(valAddr), description, newRate, sideFeeAddr, sideConsAddr) + + } return utils.GenerateOrBroadcastMsgs(txBldr, cliCtx, []sdk.Msg{msg}) } @@ -276,7 +298,7 @@ func getSideChainId() (sideChainId string, err error) { return } -func getSideChainInfo(requireConsAddr, requireFeeAddr bool) (sideChainId string, sideConsAddr, sideFeeAddr []byte, err error) { +func getSideChainInfo(requireConsAddr, requireFeeAddr bool) (sideChainId string, sideConsAddr, sideFeeAddr, sideVoteAddr []byte, err error) { sideChainId, err = getSideChainId() if err != nil { return @@ -307,6 +329,14 @@ func getSideChainInfo(requireConsAddr, requireFeeAddr bool) (sideChainId string, return } } + + sideVoteAddrStr := viper.GetString(FlagSideVoteAddr) + if len(sideVoteAddrStr) != 0 { + sideVoteAddr, err = sdk.HexDecode(sideVoteAddrStr) + if err != nil { + return + } + } return } diff --git a/x/stake/endblock.go b/x/stake/endblock.go index 7dbf619a4..106af295a 100644 --- a/x/stake/endblock.go +++ b/x/stake/endblock.go @@ -3,10 +3,11 @@ package stake import ( "fmt" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" ) func EndBlocker(ctx sdk.Context, k keeper.Keeper) (validatorUpdates []abci.ValidatorUpdate, completedUbds []types.UnbondingDelegation) { @@ -116,22 +117,43 @@ func publishCompletedRED(k keeper.Keeper, completedReds []types.DVVTriplet, side } func saveSideChainValidatorsToIBC(ctx sdk.Context, sideChainId string, newVals []types.Validator, k keeper.Keeper) { - ibcPackage := types.IbcValidatorSetPackage{ - Type: types.StakePackageType, - ValidatorSet: make([]types.IbcValidator, len(newVals)), - } - for i := range newVals { - ibcPackage.ValidatorSet[i] = types.IbcValidator{ - ConsAddr: newVals[i].SideConsAddr, - FeeAddr: newVals[i].SideFeeAddr, - DistAddr: newVals[i].DistributionAddr, - Power: uint64(newVals[i].GetPower().RawInt()), + if sdk.IsUpgrade(sdk.BEP126) { + ibcPackage := types.IbcValidatorWithVoteAddrSetPackage{ + Type: types.StakePackageType, + ValidatorSet: make([]types.IbcValidatorWithVoteAddr, len(newVals)), + } + for i := range newVals { + ibcPackage.ValidatorSet[i] = types.IbcValidatorWithVoteAddr{ + ConsAddr: newVals[i].SideConsAddr, + FeeAddr: newVals[i].SideFeeAddr, + DistAddr: newVals[i].DistributionAddr, + Power: uint64(newVals[i].GetPower().RawInt()), + VoteAddr: newVals[i].SideVoteAddr, + } + } + _, err := k.SaveValidatorWithVoteAddrSetToIbc(ctx, sideChainId, ibcPackage) + if err != nil { + k.Logger(ctx).Error("save validators to ibc package failed: " + err.Error()) + return + } + } else { + ibcPackage := types.IbcValidatorSetPackage{ + Type: types.StakePackageType, + ValidatorSet: make([]types.IbcValidator, len(newVals)), + } + for i := range newVals { + ibcPackage.ValidatorSet[i] = types.IbcValidator{ + ConsAddr: newVals[i].SideConsAddr, + FeeAddr: newVals[i].SideFeeAddr, + DistAddr: newVals[i].DistributionAddr, + Power: uint64(newVals[i].GetPower().RawInt()), + } + } + _, err := k.SaveValidatorSetToIbc(ctx, sideChainId, ibcPackage) + if err != nil { + k.Logger(ctx).Error("save validators to ibc package failed: " + err.Error()) + return } - } - _, err := k.SaveValidatorSetToIbc(ctx, sideChainId, ibcPackage) - if err != nil { - k.Logger(ctx).Error("save validators to ibc package failed: " + err.Error()) - return } if k.PbsbServer != nil { sideValidatorsEvent := types.ElectedValidatorsEvent{ diff --git a/x/stake/handler.go b/x/stake/handler.go index 66332388b..4b2cada72 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -5,13 +5,14 @@ import ( "encoding/json" "fmt" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/tags" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/tendermint/tendermint/crypto/ed25519" ) func NewHandler(k keeper.Keeper, govKeeper gov.Keeper) sdk.Handler { @@ -37,11 +38,15 @@ func NewHandler(k keeper.Keeper, govKeeper gov.Keeper) sdk.Handler { return handleMsgDelegateV1(ctx, msg, k) case types.MsgUndelegate: return handleMsgUndelegate(ctx, msg, k) - //case MsgSideChain + // case MsgSideChain case types.MsgCreateSideChainValidator: return handleMsgCreateSideChainValidator(ctx, msg, k) case types.MsgEditSideChainValidator: return handleMsgEditSideChainValidator(ctx, msg, k) + case types.MsgCreateSideChainValidatorWithVoteAddr: + return handleMsgCreateSideChainValidatorWithVoteAddr(ctx, msg, k) + case types.MsgEditSideChainValidatorWithVoteAddr: + return handleMsgEditSideChainValidatorWithVoteAddr(ctx, msg, k) case types.MsgSideChainDelegate: return handleMsgSideChainDelegate(ctx, msg, k) case types.MsgSideChainRedelegate: diff --git a/x/stake/handler_sidechain.go b/x/stake/handler_sidechain.go index ba3017418..2e487ba90 100644 --- a/x/stake/handler_sidechain.go +++ b/x/stake/handler_sidechain.go @@ -17,6 +17,10 @@ func handleMsgCreateSideChainValidator(ctx sdk.Context, msg MsgCreateSideChainVa } else { ctx = scCtx } + // check to see if BEP126 has upgraded + if sdk.IsUpgrade(sdk.BEP126) { + return types.ErrNilValidatorSideVoteAddr(k.Codespace()).Result() + } // check to see if the pubkey or sender has been registered before _, found := k.GetValidator(ctx, msg.ValidatorAddr) @@ -40,7 +44,7 @@ func handleMsgCreateSideChainValidator(ctx sdk.Context, msg MsgCreateSideChainVa // self-delegate address will be used to collect fees. feeAddr := msg.DelegatorAddr - validator := NewSideChainValidator(feeAddr, msg.ValidatorAddr, msg.Description, msg.SideChainId, msg.SideConsAddr, msg.SideFeeAddr) + validator := NewSideChainValidator(feeAddr, msg.ValidatorAddr, msg.Description, msg.SideChainId, msg.SideConsAddr, msg.SideFeeAddr, nil) commission := NewCommissionWithTime( msg.Commission.Rate, msg.Commission.MaxRate, msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, @@ -141,6 +145,156 @@ func handleMsgEditSideChainValidator(ctx sdk.Context, msg MsgEditSideChainValida } } +func handleMsgCreateSideChainValidatorWithVoteAddr(ctx sdk.Context, msg MsgCreateSideChainValidatorWithVoteAddr, k keeper.Keeper) sdk.Result { + if scCtx, err := k.ScKeeper.PrepareCtxForSideChain(ctx, msg.SideChainId); err != nil { + return ErrInvalidSideChainId(k.Codespace()).Result() + } else { + ctx = scCtx + } + + // check to see if BEP126 has upgraded + if !sdk.IsUpgrade(sdk.BEP126) { + return types.ErrBadValidatorSideVoteAddr(k.Codespace()).Result() + } + + // check to see if the pubkey or sender has been registered before + _, found := k.GetValidator(ctx, msg.ValidatorAddr) + if found { + return ErrValidatorOwnerExists(k.Codespace()).Result() + } + + _, found = k.GetValidatorBySideConsAddr(ctx, msg.SideConsAddr) + if found { + return ErrValidatorSideConsAddrExist(k.Codespace()).Result() + } + + _, found = k.GetValidatorBySideVoteAddr(ctx, msg.SideVoteAddr) + if found { + return ErrValidatorSideVoteAddrExist(k.Codespace()).Result() + } + + minSelfDelegation := k.MinSelfDelegation(ctx) + if msg.Delegation.Amount < minSelfDelegation { + return ErrBadDelegationAmount(DefaultCodespace, + fmt.Sprintf("self delegation must not be less than %d", minSelfDelegation)).Result() + } + if msg.Delegation.Denom != k.GetParams(ctx).BondDenom { + return ErrBadDenom(k.Codespace()).Result() + } + + // self-delegate address will be used to collect fees. + feeAddr := msg.DelegatorAddr + validator := NewSideChainValidator(feeAddr, msg.ValidatorAddr, msg.Description, msg.SideChainId, msg.SideConsAddr, msg.SideFeeAddr, msg.SideVoteAddr) + commission := NewCommissionWithTime( + msg.Commission.Rate, msg.Commission.MaxRate, + msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, + ) + var err sdk.Error + validator, err = validator.SetInitialCommission(commission) + if err != nil { + return err.Result() + } + + k.SetValidator(ctx, validator) + k.SetValidatorByConsAddr(ctx, validator) // here consAddr is the sideConsAddr + k.SetValidatorBySideVoteAddr(ctx, validator) + k.SetNewValidatorByPowerIndex(ctx, validator) + k.OnValidatorCreated(ctx, validator.OperatorAddr) + + // move coins from the msg.Address account to a (self-delegation) delegator account + // the validator account and global shares are updated within here + _, err = k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true) + if err != nil { + return err.Result() + } + + return sdk.Result{ + Tags: sdk.NewTags( + tags.DstValidator, []byte(msg.ValidatorAddr.String()), + tags.Moniker, []byte(msg.Description.Moniker), + tags.Identity, []byte(msg.Description.Identity), + ), + } +} + +func handleMsgEditSideChainValidatorWithVoteAddr(ctx sdk.Context, msg MsgEditSideChainValidatorWithVoteAddr, k keeper.Keeper) sdk.Result { + if scCtx, err := k.ScKeeper.PrepareCtxForSideChain(ctx, msg.SideChainId); err != nil { + return ErrInvalidSideChainId(k.Codespace()).Result() + } else { + ctx = scCtx + } + + // check to see if BEP126 has upgraded + if !sdk.IsUpgrade(sdk.BEP126) { + return types.ErrBadValidatorSideVoteAddr(k.Codespace()).Result() + } + + // validator must already be registered + validator, found := k.GetValidator(ctx, msg.ValidatorAddr) + if !found { + return ErrNoValidatorFound(k.Codespace()).Result() + } + + // replace all editable fields (clients should autofill existing values) + if description, err := validator.Description.UpdateDescription(msg.Description); err != nil { + return err.Result() + } else { + validator.Description = description + } + + if msg.CommissionRate != nil { + commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate) + if err != nil { + return err.Result() + } + validator.Commission = commission + k.OnValidatorModified(ctx, msg.ValidatorAddr) + } + + if len(msg.SideFeeAddr) != 0 { + validator.SideFeeAddr = msg.SideFeeAddr + } + + if len(msg.SideConsAddr) != 0 && sdk.IsUpgrade(sdk.BEP159) { + _, found = k.GetValidatorBySideConsAddr(ctx, msg.SideConsAddr) + if found { + return ErrValidatorSideConsAddrExist(k.Codespace()).Result() + } + if sdk.IsUpgrade(sdk.LimitConsAddrUpdateInterval) { + // check update sideConsAddr interval + latestUpdateConsAddrTime, err := k.GetValLatestUpdateConsAddrTime(ctx, validator.OperatorAddr) + if err != nil { + return sdk.ErrInternal(fmt.Sprintf("failed to get latest update cons addr time: %s", err)).Result() + } + if ctx.BlockHeader().Time.Sub(latestUpdateConsAddrTime).Hours() < types.ConsAddrUpdateIntervalInHours { + return types.ErrConsAddrUpdateTime().Result() + } + k.SetValLatestUpdateConsAddrTime(ctx, validator.OperatorAddr, ctx.BlockHeader().Time) + } + // here consAddr is the sideConsAddr + k.UpdateSideValidatorConsAddr(ctx, validator, msg.SideConsAddr) + validator.SideConsAddr = msg.SideConsAddr + } + + if len(msg.SideVoteAddr) != 0 { + _, found = k.GetValidatorBySideVoteAddr(ctx, msg.SideVoteAddr) + if found { + return ErrValidatorSideVoteAddrExist(k.Codespace()).Result() + } + validator.SideVoteAddr = msg.SideVoteAddr + k.SetValidatorBySideVoteAddr(ctx, validator) + } + + k.SetValidator(ctx, validator) + return sdk.Result{ + Tags: sdk.NewTags( + tags.DstValidator, []byte(msg.ValidatorAddr.String()), + tags.Moniker, []byte(validator.Description.Moniker), + tags.Identity, []byte(validator.Description.Identity), + ), + } +} + func handleMsgSideChainDelegate(ctx sdk.Context, msg MsgSideChainDelegate, k keeper.Keeper) sdk.Result { if scCtx, err := k.ScKeeper.PrepareCtxForSideChain(ctx, msg.SideChainId); err != nil { return ErrInvalidSideChainId(k.Codespace()).Result() @@ -173,7 +327,6 @@ func handleMsgSideChainDelegate(ctx sdk.Context, msg MsgSideChainDelegate, k kee } _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true) - if err != nil { return err.Result() } diff --git a/x/stake/keeper/ibc.go b/x/stake/keeper/ibc.go index b0c9a5db9..4e6a51f9d 100644 --- a/x/stake/keeper/ibc.go +++ b/x/stake/keeper/ibc.go @@ -19,3 +19,14 @@ func (k Keeper) SaveValidatorSetToIbc(ctx sdk.Context, sideChainId string, ibcPa } return k.ibcKeeper.CreateIBCSyncPackage(ctx, sideChainId, ChannelName, bz) } + +func (k Keeper) SaveValidatorWithVoteAddrSetToIbc(ctx sdk.Context, sideChainId string, ibcPackage types.IbcValidatorWithVoteAddrSetPackage) (seq uint64, sdkErr sdk.Error) { + if k.ibcKeeper == nil { + return 0, sdk.ErrInternal("the keeper is not prepared for side chain") + } + bz, err := rlp.EncodeToBytes(ibcPackage) + if err != nil { + return 0, sdk.ErrInternal("failed to encode IbcValidatorSetPackage") + } + return k.ibcKeeper.CreateIBCSyncPackage(ctx, sideChainId, ChannelName, bz) +} diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index ab74ecc68..e76bcb124 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -25,10 +25,11 @@ var ( LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators LastTotalPowerKey = []byte{0x12} // prefix for the total power - ValidatorsKey = []byte{0x21} // prefix for each key to a validator - ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey - ValidatorsByPowerIndexKey = []byte{0x23} // prefix for each key to a validator index, sorted by power - ValidatorsByHeightKey = []byte{0x24} // prefix for each key to a validator index, by height + ValidatorsKey = []byte{0x21} // prefix for each key to a validator + ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey + ValidatorsByPowerIndexKey = []byte{0x23} // prefix for each key to a validator index, sorted by power + ValidatorsByHeightKey = []byte{0x24} // prefix for each key to a validator index, by height + ValidatorsBySideVoteAddrKey = []byte{0x25} // prefix for each key to a validator index, by vote address DelegationKey = []byte{0x31} // key for a delegation UnbondingDelegationKey = []byte{0x32} // key for an unbonding-delegation @@ -74,6 +75,10 @@ func GetValidatorBySideConsAddrKey(sideConsAddr []byte) []byte { return append(ValidatorsByConsAddrKey, sideConsAddr...) } +func GetValidatorBySideVoteAddrKey(sideVoteAddr []byte) []byte { + return append(ValidatorsBySideVoteAddrKey, sideVoteAddr...) +} + // Get the validator operator address from LastValidatorPowerKey func AddressFromLastValidatorPowerKey(key []byte) []byte { return key[1:] // remove prefix bytes diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 070f87e29..f8c0b80c4 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -65,6 +65,15 @@ func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) sdk.V return val } +// get the sdk.validator for a particular vote address +func (k Keeper) ValidatorByVoteAddr(ctx sdk.Context, VoteAddress []byte) sdk.Validator { + val, found := k.GetValidatorBySideVoteAddr(ctx, VoteAddress) + if !found { + return nil + } + return val +} + // get the sdk.validator for a particular consensus address func (k Keeper) ValidatorBySideChainConsAddr(ctx sdk.Context, sideChainConsAddr []byte) sdk.Validator { val, found := k.GetValidatorBySideConsAddr(ctx, sideChainConsAddr) diff --git a/x/stake/keeper/slash_sidechain.go b/x/stake/keeper/slash_sidechain.go index 0fe608658..0005a874c 100644 --- a/x/stake/keeper/slash_sidechain.go +++ b/x/stake/keeper/slash_sidechain.go @@ -75,19 +75,37 @@ func (k Keeper) SlashSideChain(ctx sdk.Context, sideChainId string, sideConsAddr k.AddrPool.AddAddrs([]sdk.AccAddress{DelegationAccAddr}) } if validator.IsBonded() { - ibcPackage := types.IbcValidatorSetPackage{ - Type: types.JailPackageType, - ValidatorSet: []types.IbcValidator{ - { - ConsAddr: validator.SideConsAddr, - FeeAddr: validator.SideFeeAddr, - DistAddr: validator.DistributionAddr, - Power: uint64(validator.GetPower().RawInt()), + if sdk.IsUpgrade(sdk.BEP126) { + ibcPackage := types.IbcValidatorWithVoteAddrSetPackage{ + Type: types.JailPackageType, + ValidatorSet: []types.IbcValidatorWithVoteAddr{ + { + ConsAddr: validator.SideConsAddr, + FeeAddr: validator.SideFeeAddr, + DistAddr: validator.DistributionAddr, + Power: uint64(validator.GetPower().RawInt()), + VoteAddr: validator.SideVoteAddr, + }, }, - }, - } - if _, err := k.SaveValidatorSetToIbc(ctx, sideChainId, ibcPackage); err != nil { - return nil, sdk.ZeroDec(), errors.New(err.Error()) + } + if _, err := k.SaveValidatorWithVoteAddrSetToIbc(ctx, sideChainId, ibcPackage); err != nil { + return nil, sdk.ZeroDec(), errors.New(err.Error()) + } + } else { + ibcPackage := types.IbcValidatorSetPackage{ + Type: types.JailPackageType, + ValidatorSet: []types.IbcValidator{ + { + ConsAddr: validator.SideConsAddr, + FeeAddr: validator.SideFeeAddr, + DistAddr: validator.DistributionAddr, + Power: uint64(validator.GetPower().RawInt()), + }, + }, + } + if _, err := k.SaveValidatorSetToIbc(ctx, sideChainId, ibcPackage); err != nil { + return nil, sdk.ZeroDec(), errors.New(err.Error()) + } } } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 636e05810..c60e81f81 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -61,6 +61,15 @@ func (k Keeper) GetValidatorBySideConsAddr(ctx sdk.Context, sideConsAddr []byte) return k.GetValidator(ctx, opAddr) } +func (k Keeper) GetValidatorBySideVoteAddr(ctx sdk.Context, sideVoteAddr []byte) (validator types.Validator, found bool) { + store := ctx.KVStore(k.storeKey) + opAddr := store.Get(GetValidatorBySideVoteAddrKey(sideVoteAddr)) + if opAddr == nil { + return validator, false + } + return k.GetValidator(ctx, opAddr) +} + func (k Keeper) mustGetValidatorBySideConsAddr(ctx sdk.Context, sideConsAddr []byte) types.Validator { store := ctx.KVStore(k.storeKey) opAddr := store.Get(GetValidatorBySideConsAddrKey(sideConsAddr)) @@ -176,6 +185,16 @@ func (k Keeper) UpdateValidatorPubKey(ctx sdk.Context, validator types.Validator } } +// validator index +func (k Keeper) SetValidatorBySideVoteAddr(ctx sdk.Context, validator types.Validator) { + // jailed validators are not kept in the power index + if validator.Jailed { + return + } + store := ctx.KVStore(k.storeKey) + store.Set(GetValidatorBySideVoteAddrKey(validator.SideVoteAddr), validator.OperatorAddr) +} + // validator index func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { // jailed validators are not kept in the power index diff --git a/x/stake/stake.go b/x/stake/stake.go index 7b9b55bd0..da841e942 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -35,11 +35,13 @@ type ( QueryTopValidatorsParams = querier.QueryTopValidatorsParams BaseParams = querier.BaseParams - MsgCreateSideChainValidator = types.MsgCreateSideChainValidator - MsgEditSideChainValidator = types.MsgEditSideChainValidator - MsgSideChainDelegate = types.MsgSideChainDelegate - MsgSideChainRedelegate = types.MsgSideChainRedelegate - MsgSideChainUndelegate = types.MsgSideChainUndelegate + MsgCreateSideChainValidator = types.MsgCreateSideChainValidator + MsgEditSideChainValidator = types.MsgEditSideChainValidator + MsgCreateSideChainValidatorWithVoteAddr = types.MsgCreateSideChainValidatorWithVoteAddr + MsgEditSideChainValidatorWithVoteAddr = types.MsgEditSideChainValidatorWithVoteAddr + MsgSideChainDelegate = types.MsgSideChainDelegate + MsgSideChainRedelegate = types.MsgSideChainRedelegate + MsgSideChainUndelegate = types.MsgSideChainUndelegate DistributionEvent = types.DistributionEvent DistributionData = types.DistributionData @@ -76,6 +78,7 @@ var ( LastTotalPowerKey = keeper.LastTotalPowerKey ValidatorsKey = keeper.ValidatorsKey ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey + ValidatorsByVoteAddrKey = keeper.ValidatorsBySideVoteAddrKey ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey DelegationKey = keeper.DelegationKey GetUBDKey = keeper.GetUBDKey @@ -128,6 +131,10 @@ var ( NewMsgSideChainRedelegate = types.NewMsgSideChainRedelegate NewMsgSideChainUndelegate = types.NewMsgSideChainUndelegate + NewMsgCreateSideChainValidatorWithVoteAddr = types.NewMsgCreateSideChainValidatorWithVoteAddr + NewMsgCreateSideChainValidatorWithVoteAddrOnBehalfOf = types.NewMsgCreateSideChainValidatorWithVoteAddrOnBehalfOf + NewMsgEditSideChainValidatorWithVoteAddr = types.NewMsgEditSideChainValidatorWithVoteAddr + NewQuerier = querier.NewQuerier NewBaseParams = querier.NewBaseParams @@ -174,6 +181,7 @@ var ( ErrValidatorOwnerExists = types.ErrValidatorOwnerExists ErrValidatorPubKeyExists = types.ErrValidatorPubKeyExists ErrValidatorSideConsAddrExist = types.ErrValidatorSideConsAddrExists + ErrValidatorSideVoteAddrExist = types.ErrValidatorSideVoteAddrExists ErrInvalidDelegator = types.ErrInvalidDelegator ErrValidatorJailed = types.ErrValidatorJailed ErrInvalidProposal = types.ErrInvalidProposal diff --git a/x/stake/types/codec.go b/x/stake/types/codec.go index 77bd7650b..6d78180bb 100644 --- a/x/stake/types/codec.go +++ b/x/stake/types/codec.go @@ -17,7 +17,9 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgUndelegate{}, "cosmos-sdk/MsgUndelegate", nil) cdc.RegisterConcrete(MsgCreateSideChainValidator{}, "cosmos-sdk/MsgCreateSideChainValidator", nil) + cdc.RegisterConcrete(MsgCreateSideChainValidatorWithVoteAddr{}, "cosmos-sdk/MsgCreateSideChainValidatorWithVoteAddr", nil) cdc.RegisterConcrete(MsgEditSideChainValidator{}, "cosmos-sdk/MsgEditSideChainValidator", nil) + cdc.RegisterConcrete(MsgEditSideChainValidatorWithVoteAddr{}, "cosmos-sdk/MsgEditSideChainValidatorWithVoteAddr", nil) cdc.RegisterConcrete(MsgSideChainDelegate{}, "cosmos-sdk/MsgSideChainDelegate", nil) cdc.RegisterConcrete(MsgSideChainRedelegate{}, "cosmos-sdk/MsgSideChainRedelegate", nil) cdc.RegisterConcrete(MsgSideChainUndelegate{}, "cosmos-sdk/MsgSideChainUndelegate", nil) diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index da24d7e31..71f7b28a2 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -68,6 +68,10 @@ func ErrValidatorSideConsAddrExists(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this sideConsAddr, must use new validator sideConsAddr") } +func ErrValidatorSideVoteAddrExists(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this sideVoteAddr, must use new validator sideVoteAddr") +} + func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently jailed") } @@ -245,6 +249,14 @@ func ErrInvalidCrosschainPackage(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidCrossChainPackage, "invalid cross chain package") } +func ErrNilValidatorSideVoteAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "validator side-chain vote address is nil") +} + +func ErrBadValidatorSideVoteAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "validator side-chain vote address is invalid") +} + func ErrDeserializePackageFailed(msg string) sdk.Error { return sdk.NewError(DefaultCodespace, CodeDeserializePackageFailed, msg) } diff --git a/x/stake/types/msg_sidechain.go b/x/stake/types/msg_sidechain.go index 9b2bc276f..86ba52dc3 100644 --- a/x/stake/types/msg_sidechain.go +++ b/x/stake/types/msg_sidechain.go @@ -9,11 +9,13 @@ import ( ) const ( - MsgTypeCreateSideChainValidator = "side_create_validator" - MsgTypeEditSideChainValidator = "side_edit_validator" - MsgTypeSideChainDelegate = "side_delegate" - MsgTypeSideChainRedelegate = "side_redelegate" - MsgTypeSideChainUndelegate = "side_undelegate" + MsgTypeCreateSideChainValidator = "side_create_validator" + MsgTypeEditSideChainValidator = "side_edit_validator" + MsgTypeCreateSideChainValidatorWithVoteAddr = "side_create_validator_with_vote_addr" + MsgTypeEditSideChainValidatorWithVoteAddr = "side_edit_validator_with_vote_addr" + MsgTypeSideChainDelegate = "side_delegate" + MsgTypeSideChainRedelegate = "side_redelegate" + MsgTypeSideChainUndelegate = "side_undelegate" ) type SideChainIder interface { @@ -31,13 +33,27 @@ type MsgCreateSideChainValidator struct { SideFeeAddr []byte `json:"side_fee_addr"` } +type MsgCreateSideChainValidatorWithVoteAddr struct { + Description Description `json:"description"` + Commission CommissionMsg `json:"commission"` + DelegatorAddr sdk.AccAddress `json:"delegator_address"` + ValidatorAddr sdk.ValAddress `json:"validator_address"` + Delegation sdk.Coin `json:"delegation"` + SideChainId string `json:"side_chain_id"` + SideConsAddr []byte `json:"side_cons_addr"` + SideFeeAddr []byte `json:"side_fee_addr"` + SideVoteAddr []byte `json:"side_vote_addr"` +} + func NewMsgCreateSideChainValidator(valAddr sdk.ValAddress, delegation sdk.Coin, - description Description, commission CommissionMsg, sideChainId string, sideConsAddr []byte, sideFeeAddr []byte) MsgCreateSideChainValidator { + description Description, commission CommissionMsg, sideChainId string, sideConsAddr, sideFeeAddr []byte, +) MsgCreateSideChainValidator { return NewMsgCreateSideChainValidatorOnBehalfOf(sdk.AccAddress(valAddr), valAddr, delegation, description, commission, sideChainId, sideConsAddr, sideFeeAddr) } func NewMsgCreateSideChainValidatorOnBehalfOf(delegatorAddr sdk.AccAddress, valAddr sdk.ValAddress, delegation sdk.Coin, - description Description, commission CommissionMsg, sideChainId string, sideConsAddr []byte, sideFeeAddr []byte) MsgCreateSideChainValidator { + description Description, commission CommissionMsg, sideChainId string, sideConsAddr, sideFeeAddr []byte, +) MsgCreateSideChainValidator { return MsgCreateSideChainValidator{ Description: description, Commission: commission, @@ -50,7 +66,29 @@ func NewMsgCreateSideChainValidatorOnBehalfOf(delegatorAddr sdk.AccAddress, valA } } -//nolint +func NewMsgCreateSideChainValidatorWithVoteAddr(valAddr sdk.ValAddress, delegation sdk.Coin, + description Description, commission CommissionMsg, sideChainId string, sideConsAddr, sideFeeAddr, sideVoteAddr []byte, +) MsgCreateSideChainValidatorWithVoteAddr { + return NewMsgCreateSideChainValidatorWithVoteAddrOnBehalfOf(sdk.AccAddress(valAddr), valAddr, delegation, description, commission, sideChainId, sideConsAddr, sideFeeAddr, sideVoteAddr) +} + +func NewMsgCreateSideChainValidatorWithVoteAddrOnBehalfOf(delegatorAddr sdk.AccAddress, valAddr sdk.ValAddress, delegation sdk.Coin, + description Description, commission CommissionMsg, sideChainId string, sideConsAddr, sideFeeAddr, sideVoteAddr []byte, +) MsgCreateSideChainValidatorWithVoteAddr { + return MsgCreateSideChainValidatorWithVoteAddr{ + Description: description, + Commission: commission, + DelegatorAddr: delegatorAddr, + ValidatorAddr: valAddr, + Delegation: delegation, + SideChainId: sideChainId, + SideConsAddr: sideConsAddr, + SideFeeAddr: sideFeeAddr, + SideVoteAddr: sideVoteAddr, + } +} + +// nolint func (msg MsgCreateSideChainValidator) Route() string { return MsgRoute } func (msg MsgCreateSideChainValidator) Type() string { return MsgTypeCreateSideChainValidator } @@ -114,7 +152,78 @@ func (msg MsgCreateSideChainValidator) GetSideChainId() string { return msg.SideChainId } -//______________________________________________________________________ +// nolint +func (msg MsgCreateSideChainValidatorWithVoteAddr) Route() string { return MsgRoute } + +func (msg MsgCreateSideChainValidatorWithVoteAddr) Type() string { + return MsgTypeCreateSideChainValidatorWithVoteAddr +} + +// GetSigners returns address(es) that must sign over msg.GetSignBytes() +func (msg MsgCreateSideChainValidatorWithVoteAddr) GetSigners() []sdk.AccAddress { + // delegator is first signer so delegator pays fees + addrs := []sdk.AccAddress{msg.DelegatorAddr} + + if !bytes.Equal(msg.DelegatorAddr.Bytes(), msg.ValidatorAddr.Bytes()) { + // if validator addr is not same as delegator addr, validator must sign + // msg as well + addrs = append(addrs, sdk.AccAddress(msg.ValidatorAddr)) + } + return addrs +} + +// GetSignBytes gets the bytes for the message signer to sign on +func (msg MsgCreateSideChainValidatorWithVoteAddr) GetSignBytes() []byte { + bz := MsgCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg MsgCreateSideChainValidatorWithVoteAddr) GetInvolvedAddresses() []sdk.AccAddress { + return msg.GetSigners() +} + +func (msg MsgCreateSideChainValidatorWithVoteAddr) ValidateBasic() sdk.Error { + if len(msg.DelegatorAddr) != sdk.AddrLen { + return sdk.ErrInvalidAddress(fmt.Sprintf("Expected delegator address length is %d, actual length is %d", sdk.AddrLen, len(msg.DelegatorAddr))) + } + if len(msg.ValidatorAddr) != sdk.AddrLen { + return sdk.ErrInvalidAddress(fmt.Sprintf("Expected validator address length is %d, actual length is %d", sdk.AddrLen, len(msg.ValidatorAddr))) + } + if msg.Description == (Description{}) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "description must be included") + } + if _, err := msg.Description.EnsureLength(); err != nil { + return err + } + commission := NewCommission(msg.Commission.Rate, msg.Commission.MaxRate, msg.Commission.MaxChangeRate) + if err := commission.Validate(); err != nil { + return err + } + + if len(msg.SideChainId) == 0 || len(msg.SideChainId) > types.MaxSideChainIdLength { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "side chain id must be included and max length is 20 bytes") + } + + if err := checkSideChainAddr("SideConsAddr", msg.SideConsAddr); err != nil { + return err + } + + if err := checkSideChainAddr("SideFeeAddr", msg.SideFeeAddr); err != nil { + return err + } + + if err := checkSideChainVoteAddr("SideVoteAddr", msg.SideVoteAddr); err != nil { + return err + } + + return nil +} + +func (msg MsgCreateSideChainValidatorWithVoteAddr) GetSideChainId() string { + return msg.SideChainId +} + +// ______________________________________________________________________ type MsgEditSideChainValidator struct { Description Description `json:"description"` ValidatorAddr sdk.ValAddress `json:"address"` @@ -133,6 +242,26 @@ type MsgEditSideChainValidator struct { SideConsAddr []byte `json:"side_cons_addr,omitempty"` } +type MsgEditSideChainValidatorWithVoteAddr struct { + Description Description `json:"description"` + ValidatorAddr sdk.ValAddress `json:"address"` + + // We pass a reference to the new commission rate as it's not mandatory to + // update. If not updated, the deserialized rate will be zero with no way to + // distinguish if an update was intended. + // + // REF: #2373 + CommissionRate *sdk.Dec `json:"commission_rate"` + + SideChainId string `json:"side_chain_id"` + // for SideFeeAddr and SideVoteAddr, we do not update the values if they are not provided. + SideFeeAddr []byte `json:"side_fee_addr"` + + SideConsAddr []byte `json:"side_cons_addr,omitempty"` + + SideVoteAddr []byte `json:"side_vote_addr"` +} + func NewMsgEditSideChainValidator(sideChainId string, validatorAddr sdk.ValAddress, description Description, commissionRate *sdk.Dec, sideFeeAddr, sideConsAddr []byte) MsgEditSideChainValidator { return MsgEditSideChainValidator{ Description: description, @@ -144,7 +273,19 @@ func NewMsgEditSideChainValidator(sideChainId string, validatorAddr sdk.ValAddre } } -//nolint +func NewMsgEditSideChainValidatorWithVoteAddr(sideChainId string, validatorAddr sdk.ValAddress, description Description, commissionRate *sdk.Dec, sideFeeAddr, sideConsAddr, sideVoteAddr []byte) MsgEditSideChainValidatorWithVoteAddr { + return MsgEditSideChainValidatorWithVoteAddr{ + Description: description, + ValidatorAddr: validatorAddr, + CommissionRate: commissionRate, + SideChainId: sideChainId, + SideFeeAddr: sideFeeAddr, + SideConsAddr: sideConsAddr, + SideVoteAddr: sideVoteAddr, + } +} + +// nolint func (msg MsgEditSideChainValidator) Route() string { return MsgRoute } func (msg MsgEditSideChainValidator) Type() string { return MsgTypeEditSideChainValidator } func (msg MsgEditSideChainValidator) GetSigners() []sdk.AccAddress { @@ -201,6 +342,79 @@ func (msg MsgEditSideChainValidator) GetSideChainId() string { return msg.SideChainId } +// nolint +func (msg MsgEditSideChainValidatorWithVoteAddr) Route() string { return MsgRoute } + +func (msg MsgEditSideChainValidatorWithVoteAddr) Type() string { + return MsgTypeEditSideChainValidatorWithVoteAddr +} + +func (msg MsgEditSideChainValidatorWithVoteAddr) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)} +} + +// GetSignBytes gets the bytes for the message signer to sign on +func (msg MsgEditSideChainValidatorWithVoteAddr) GetSignBytes() []byte { + bz := MsgCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg MsgEditSideChainValidatorWithVoteAddr) ValidateBasic() sdk.Error { + if len(msg.ValidatorAddr) != sdk.AddrLen { + return sdk.ErrInvalidAddress(fmt.Sprintf("Expected validator address length is %d, actual length is %d", sdk.AddrLen, len(msg.ValidatorAddr))) + } + + if msg.Description == (Description{}) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "description must be included") + } + if _, err := msg.Description.EnsureLength(); err != nil { + return err + } + + if msg.CommissionRate != nil { + if msg.CommissionRate.GT(sdk.OneDec()) || msg.CommissionRate.LT(sdk.ZeroDec()) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "commission rate must be between 0 and 1 (inclusive)") + } + } + + if len(msg.SideChainId) == 0 || len(msg.SideChainId) > types.MaxSideChainIdLength { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "side chain id must be included and max length is 20 bytes") + } + + // if SideFeeAddr or SideVoteAddr is empty, we do not update it. + if len(msg.SideFeeAddr) != 0 { + if err := checkSideChainAddr("SideFeeAddr", msg.SideFeeAddr); err != nil { + return err + } + } + + if len(msg.SideConsAddr) != 0 { + if !sdk.IsUpgrade(sdk.BEP159) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "side consensus address cannot be updated before BEP159") + } + if err := checkSideChainAddr("SideConsAddr", msg.SideConsAddr); err != nil { + return err + } + } + + // if SideFeeAddr is empty, we do not update it. + if len(msg.SideVoteAddr) != 0 { + if err := checkSideChainVoteAddr("SideVoteAddr", msg.SideVoteAddr); err != nil { + return err + } + } + + return nil +} + +func (msg MsgEditSideChainValidatorWithVoteAddr) GetInvolvedAddresses() []sdk.AccAddress { + return msg.GetSigners() +} + +func (msg MsgEditSideChainValidatorWithVoteAddr) GetSideChainId() string { + return msg.SideChainId +} + func checkSideChainAddr(addrName string, addr []byte) sdk.Error { if len(addr) != sdk.AddrLen { return sdk.ErrInvalidAddress(fmt.Sprintf("Expected %s length is %d, got %d", @@ -209,7 +423,15 @@ func checkSideChainAddr(addrName string, addr []byte) sdk.Error { return nil } -//______________________________________________________________________ +func checkSideChainVoteAddr(addrName string, addr []byte) sdk.Error { + if len(addr) != sdk.VoteAddrLen { + return sdk.ErrInvalidAddress(fmt.Sprintf("Expected %s length is %d, got %d", + addrName, sdk.VoteAddrLen, len(addr))) + } + return nil +} + +// ______________________________________________________________________ type MsgSideChainDelegate struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.ValAddress `json:"validator_addr"` @@ -227,7 +449,7 @@ func NewMsgSideChainDelegate(sideChainId string, delAddr sdk.AccAddress, valAddr } } -//nolint +// nolint func (msg MsgSideChainDelegate) Route() string { return MsgRoute } func (msg MsgSideChainDelegate) Type() string { return MsgTypeSideChainDelegate } func (msg MsgSideChainDelegate) GetSigners() []sdk.AccAddress { @@ -262,7 +484,7 @@ func (msg MsgSideChainDelegate) GetSideChainId() string { return msg.SideChainId } -//______________________________________________________________________ +// ______________________________________________________________________ type MsgSideChainRedelegate struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` @@ -281,7 +503,7 @@ func NewMsgSideChainRedelegate(sideChainId string, delegatorAddr sdk.AccAddress, } } -//nolint +// nolint func (msg MsgSideChainRedelegate) Route() string { return MsgRoute } func (msg MsgSideChainRedelegate) Type() string { return MsgTypeSideChainRedelegate } func (msg MsgSideChainRedelegate) GetSigners() []sdk.AccAddress { @@ -321,7 +543,7 @@ func (msg MsgSideChainRedelegate) GetSideChainId() string { return msg.SideChainId } -//______________________________________________________________________ +// ______________________________________________________________________ type MsgSideChainUndelegate struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.ValAddress `json:"validator_addr"` @@ -338,7 +560,7 @@ func NewMsgSideChainUndelegate(sideChainId string, delegatorAddr sdk.AccAddress, } } -//nolint +// nolint func (msg MsgSideChainUndelegate) Route() string { return MsgRoute } func (msg MsgSideChainUndelegate) Type() string { return MsgTypeSideChainUndelegate } func (msg MsgSideChainUndelegate) GetSigners() []sdk.AccAddress { diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index d1e5580dc..af9b7514f 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -2,15 +2,17 @@ package types import ( "bytes" + "encoding/hex" "fmt" "time" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) const ChainIDForBeaconChain string = "bbc" // short for BNB Beacon Chain @@ -45,6 +47,7 @@ type Validator struct { SideChainId string `json:"side_chain_id,omitempty"` // side chain id to distinguish different side chains SideConsAddr []byte `json:"side_cons_addr,omitempty"` // consensus address of the side chain validator, this replaces the `ConsPubKey` SideFeeAddr []byte `json:"side_fee_addr,omitempty"` // fee address on the side chain + SideVoteAddr []byte `json:"side_vote_addr,omitempty"` // bls vote address on the side chain StakeSnapshots []sdk.Dec `json:"stake_snapshots,omitempty"` // staked tokens snapshot over a period of time, e.g. 30 days AccumulatedStake sdk.Dec `json:"accumulated_stake,omitempty"` // accumulated stake, sum of StakeSnapshots @@ -78,25 +81,48 @@ func NewValidatorWithFeeAddr(feeAddr sdk.AccAddress, operator sdk.ValAddress, pu return val } -func NewSideChainValidator(feeAddr sdk.AccAddress, operator sdk.ValAddress, description Description, sideChainId string, sideConsAddr, sideFeeAddr []byte) Validator { - return Validator{ - FeeAddr: feeAddr, - OperatorAddr: operator, - ConsPubKey: nil, // side chain validators do not need this - Jailed: false, - Status: sdk.Unbonded, - Tokens: sdk.ZeroDec(), - DelegatorShares: sdk.ZeroDec(), - Description: description, - BondHeight: int64(0), - BondIntraTxCounter: int16(0), - UnbondingMinTime: time.Unix(0, 0).UTC(), - UnbondingHeight: int64(0), - Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - DistributionAddr: GenerateDistributionAddr(operator, sideChainId), - SideChainId: sideChainId, - SideConsAddr: sideConsAddr, - SideFeeAddr: sideFeeAddr, +func NewSideChainValidator(feeAddr sdk.AccAddress, operator sdk.ValAddress, description Description, sideChainId string, sideConsAddr, sideFeeAddr, sideVoteAddr []byte) Validator { + if sdk.IsUpgrade(sdk.BEP126) { + return Validator{ + FeeAddr: feeAddr, + OperatorAddr: operator, + ConsPubKey: nil, // side chain validators do not need this + Jailed: false, + Status: sdk.Unbonded, + Tokens: sdk.ZeroDec(), + DelegatorShares: sdk.ZeroDec(), + Description: description, + BondHeight: int64(0), + BondIntraTxCounter: int16(0), + UnbondingMinTime: time.Unix(0, 0).UTC(), + UnbondingHeight: int64(0), + Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + DistributionAddr: GenerateDistributionAddr(operator, sideChainId), + SideChainId: sideChainId, + SideConsAddr: sideConsAddr, + SideFeeAddr: sideFeeAddr, + SideVoteAddr: sideVoteAddr, + } + } else { + return Validator{ + FeeAddr: feeAddr, + OperatorAddr: operator, + ConsPubKey: nil, // side chain validators do not need this + Jailed: false, + Status: sdk.Unbonded, + Tokens: sdk.ZeroDec(), + DelegatorShares: sdk.ZeroDec(), + Description: description, + BondHeight: int64(0), + BondIntraTxCounter: int16(0), + UnbondingMinTime: time.Unix(0, 0).UTC(), + UnbondingHeight: int64(0), + Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + DistributionAddr: GenerateDistributionAddr(operator, sideChainId), + SideChainId: sideChainId, + SideConsAddr: sideConsAddr, + SideFeeAddr: sideFeeAddr, + } } } @@ -173,6 +199,9 @@ func (v Validator) HumanReadableString() (string, error) { resp += fmt.Sprintf("Side Chain Id: %s\n", v.SideChainId) resp += fmt.Sprintf("Consensus Addr on Side Chain: %s\n", sdk.HexAddress(v.SideConsAddr)) resp += fmt.Sprintf("Fee Addr on Side Chain: %s\n", sdk.HexAddress(v.SideFeeAddr)) + if v.SideVoteAddr != nil { + resp += fmt.Sprintf("Vote address on Side Chain: %s\n", "0x"+hex.EncodeToString(v.SideVoteAddr)) + } } resp += fmt.Sprintf("StakeSnapshots: %s\n", v.StakeSnapshots) resp += fmt.Sprintf("AccumulatedStake: %s\n", v.AccumulatedStake) @@ -206,6 +235,7 @@ type bechValidator struct { SideChainId string `json:"side_chain_id,omitempty"` // side chain id to distinguish different side chains SideConsAddr string `json:"side_cons_addr,omitempty"` // consensus address of the side chain validator, this replaces the `ConsPubKey` SideFeeAddr string `json:"side_fee_addr,omitempty"` // fee address on the side chain + SideVoteAddr string `json:"side_vote_addr,omitempty"` // public key of BLS on the side chain used for voting StakeSnapshots []sdk.Dec `json:"stake_snapshots,omitempty"` // staked tokens snapshot over a period of time, e.g. 30 days AccumulatedStake sdk.Dec `json:"accumulated_stake,omitempty"` // accumulated stake, sum of StakeSnapshots @@ -221,7 +251,6 @@ func (v Validator) MarshalJSON() ([]byte, error) { return nil, err } } - return codec.Cdc.MarshalJSON(bechValidator{ FeeAddr: v.FeeAddr, OperatorAddr: v.OperatorAddr, @@ -240,6 +269,7 @@ func (v Validator) MarshalJSON() ([]byte, error) { SideChainId: v.SideChainId, SideConsAddr: sdk.HexAddress(v.SideConsAddr), SideFeeAddr: sdk.HexAddress(v.SideFeeAddr), + SideVoteAddr: "0x" + hex.EncodeToString(v.SideVoteAddr), StakeSnapshots: v.StakeSnapshots, AccumulatedStake: v.AccumulatedStake, }) @@ -290,6 +320,11 @@ func (v *Validator) UnmarshalJSON(data []byte) error { } else { v.SideFeeAddr = sideFeeAddr } + if sideVoteAddr, err := sdk.HexDecode(bv.SideVoteAddr); err != nil { + return err + } else { + v.SideVoteAddr = sideVoteAddr + } } return nil @@ -310,7 +345,8 @@ func (v Validator) Equal(v2 Validator) bool { v.SideChainId == v2.SideChainId && v.DistributionAddr.Equals(v2.DistributionAddr) && bytes.Equal(v.SideConsAddr, v2.SideConsAddr) && - bytes.Equal(v.SideFeeAddr, v2.SideFeeAddr) + bytes.Equal(v.SideFeeAddr, v2.SideFeeAddr) && + bytes.Equal(v.SideVoteAddr, v2.SideVoteAddr) } // return the TM validator address @@ -412,7 +448,6 @@ func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { // UpdateStatus updates the location of the shares within a validator // to reflect the new status func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, Pool) { - switch v.Status { case sdk.Unbonded: @@ -494,7 +529,6 @@ func (v Validator) SetInitialCommission(commission Commission) (Validator, sdk.E // AddTokensFromDel adds tokens to a validator func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Dec) { - // bondedShare/delegatedShare amountDec := sdk.NewDecFromInt(amount) @@ -590,6 +624,7 @@ func (v Validator) GetCommission() sdk.Dec { return v.Commission.Rate } func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } func (v Validator) GetBondHeight() int64 { return v.BondHeight } func (v Validator) GetSideChainConsAddr() []byte { return v.SideConsAddr } +func (v Validator) GetSideChainVoteAddr() []byte { return v.SideVoteAddr } func (v Validator) IsSideChainValidator() bool { return len(v.SideChainId) != 0 } func (v Validator) IsSelfDelegator(address sdk.AccAddress) bool { return v.FeeAddr.Equals(address) } diff --git a/x/stake/types/validator_ibc.go b/x/stake/types/validator_ibc.go index d54590279..ac4b847ff 100644 --- a/x/stake/types/validator_ibc.go +++ b/x/stake/types/validator_ibc.go @@ -22,3 +22,16 @@ type IbcValidatorSetPackage struct { Type PackageType ValidatorSet []IbcValidator } + +type IbcValidatorWithVoteAddr struct { + ConsAddr []byte + FeeAddr []byte + DistAddr sdk.AccAddress + Power uint64 + VoteAddr []byte +} + +type IbcValidatorWithVoteAddrSetPackage struct { + Type PackageType + ValidatorSet []IbcValidatorWithVoteAddr +}