Commit 6cdb730a authored by Son of Odin's avatar Son of Odin 💬

Merge branch 'memo-slash-patch' into 'master'

[bugfix] slash ygg, migrate, and ragnarok memo when appropriate

See merge request !695
parents 41073db5 b90a3a53
Pipeline #130201526 passed with stages
in 62 minutes and 20 seconds
......@@ -60,6 +60,17 @@ func (h MigrateHandler) handle(ctx sdk.Context, msg MsgMigrate, version semver.V
return errBadVersion.Result()
}
func (h MigrateHandler) slash(ctx sdk.Context, tx ObservedTx) error {
var returnErr error
for _, c := range tx.Tx.Coins {
if err := slashNodeAccount(ctx, h.keeper, tx.ObservedPubKey, c.Asset, c.Amount); err != nil {
ctx.Logger().Error("fail to slash account", "error", err)
returnErr = err
}
}
return returnErr
}
func (h MigrateHandler) handleV1(ctx sdk.Context, msg MsgMigrate) sdk.Result {
// update txOut record with our TxID that sent funds out of the pool
txOut, err := h.keeper.GetTxOut(ctx, msg.BlockHeight)
......@@ -67,14 +78,8 @@ func (h MigrateHandler) handleV1(ctx sdk.Context, msg MsgMigrate) sdk.Result {
ctx.Logger().Error("unable to get txOut record", "error", err)
return sdk.ErrUnknownRequest(err.Error()).Result()
}
if txOut.IsEmpty() {
return sdk.Result{
Code: sdk.CodeOK,
Codespace: DefaultCodespace,
}
}
hasChanged := false
shouldSlash := true
for i, tx := range txOut.TxArray {
// migrate is the memo used by thorchain to identify fund migration between asgard vault.
// it use migrate:{block height} to mark a tx out caused by vault rotation
......@@ -85,22 +90,25 @@ func (h MigrateHandler) handleV1(ctx sdk.Context, msg MsgMigrate) sdk.Result {
msg.Tx.Tx.Coins.Contains(tx.Coin) &&
tx.ToAddress.Equals(msg.Tx.Tx.ToAddress) &&
fromAddress.Equals(msg.Tx.Tx.FromAddress) {
txOut.TxArray[i].OutHash = msg.Tx.Tx.ID
hasChanged = true
shouldSlash = false
if err := h.keeper.SetTxOut(ctx, txOut); nil != err {
ctx.Logger().Error("fail to save tx out", "error", err)
return sdk.ErrInternal("fail to save tx out").Result()
}
break
}
}
if !hasChanged {
return sdk.Result{
Code: sdk.CodeOK,
Codespace: DefaultCodespace,
if shouldSlash {
if err := h.slash(ctx, msg.Tx); err != nil {
return sdk.ErrInternal("fail to slash account").Result()
}
}
if err := h.keeper.SetTxOut(ctx, txOut); nil != err {
ctx.Logger().Error("fail to save tx out", "error", err)
return sdk.ErrInternal("fail to save tx out").Result()
}
h.keeper.SetLastSignedHeight(ctx, msg.BlockHeight)
return sdk.Result{
......
......@@ -73,6 +73,7 @@ type TestMigrateKeeperHappyPath struct {
newVault Vault
retireVault Vault
txout *TxOut
pool Pool
}
func (k *TestMigrateKeeperHappyPath) GetTxOut(ctx sdk.Context, blockHeight int64) (*TxOut, error) {
......@@ -90,6 +91,28 @@ func (k *TestMigrateKeeperHappyPath) SetTxOut(ctx sdk.Context, blockOut *TxOut)
return kaboom
}
func (k *TestMigrateKeeperHappyPath) GetNodeAccountByPubKey(_ sdk.Context, _ common.PubKey) (NodeAccount, error) {
return k.activeNodeAccount, nil
}
func (k *TestMigrateKeeperHappyPath) SetNodeAccount(_ sdk.Context, na NodeAccount) error {
k.activeNodeAccount = na
return nil
}
func (k *TestMigrateKeeperHappyPath) GetPool(_ sdk.Context, _ common.Asset) (Pool, error) {
return k.pool, nil
}
func (k *TestMigrateKeeperHappyPath) SetPool(_ sdk.Context, p Pool) error {
k.pool = p
return nil
}
func (k *TestMigrateKeeperHappyPath) UpsertEvent(_ sdk.Context, _ Event) error {
return nil
}
func (HandlerMigrateSuite) TestMigrateHappyPath(c *C) {
ctx, _ := setupKeeperForTest(c)
retireVault := GetRandomVault()
......@@ -132,3 +155,46 @@ func (HandlerMigrateSuite) TestMigrateHappyPath(c *C) {
c.Assert(result.Code, Equals, sdk.CodeOK)
c.Assert(keeper.txout.TxArray[0].OutHash.Equals(tx.Tx.ID), Equals, true)
}
func (HandlerMigrateSuite) TestSlash(c *C) {
ctx, _ := setupKeeperForTest(c)
retireVault := GetRandomVault()
newVault := GetRandomVault()
txout := NewTxOut(1)
newVaultAddr, err := newVault.PubKey.GetAddress(common.BNBChain)
c.Assert(err, IsNil)
pool := NewPool()
pool.Asset = common.BNBAsset
pool.BalanceAsset = sdk.NewUint(100 * common.One)
pool.BalanceRune = sdk.NewUint(100 * common.One)
na := GetRandomNodeAccount(NodeActive)
na.Bond = sdk.NewUint(100 * common.One)
keeper := &TestMigrateKeeperHappyPath{
activeNodeAccount: na,
newVault: newVault,
retireVault: retireVault,
txout: txout,
pool: pool,
}
addr, err := keeper.retireVault.PubKey.GetAddress(common.BNBChain)
c.Assert(err, IsNil)
handler := NewMigrateHandler(keeper)
tx := NewObservedTx(common.Tx{
ID: GetRandomTxHash(),
Chain: common.BNBChain,
Coins: common.Coins{
common.NewCoin(common.BNBAsset, sdk.NewUint(1024)),
},
Memo: NewMigrateMemo(1).String(),
FromAddress: addr,
ToAddress: newVaultAddr,
Gas: common.BNBGasFeeSingleton,
}, 1, retireVault.PubKey)
msgMigrate := NewMsgMigrate(tx, 1, keeper.activeNodeAccount.NodeAddress)
result := handler.handleV1(ctx, msgMigrate)
c.Assert(result.Code, Equals, sdk.CodeOK, Commentf("%s", result.Log))
c.Assert(keeper.activeNodeAccount.Bond.Equal(sdk.NewUint(9999998464)), Equals, true, Commentf("%d", keeper.activeNodeAccount.Bond.Uint64()))
}
......@@ -60,6 +60,17 @@ func (h RagnarokHandler) handle(ctx sdk.Context, msg MsgRagnarok, version semver
return errBadVersion.Result()
}
func (h RagnarokHandler) slash(ctx sdk.Context, tx ObservedTx) error {
var returnErr error
for _, c := range tx.Tx.Coins {
if err := slashNodeAccount(ctx, h.keeper, tx.ObservedPubKey, c.Asset, c.Amount); err != nil {
ctx.Logger().Error("fail to slash account", "error", err)
returnErr = err
}
}
return returnErr
}
func (h RagnarokHandler) handleV1(ctx sdk.Context, msg MsgRagnarok) sdk.Result {
// update txOut record with our TxID that sent funds out of the pool
txOut, err := h.keeper.GetTxOut(ctx, msg.BlockHeight)
......@@ -67,14 +78,8 @@ func (h RagnarokHandler) handleV1(ctx sdk.Context, msg MsgRagnarok) sdk.Result {
ctx.Logger().Error("unable to get txOut record", "error", err)
return sdk.ErrUnknownRequest(err.Error()).Result()
}
if txOut.IsEmpty() {
return sdk.Result{
Code: sdk.CodeOK,
Codespace: DefaultCodespace,
}
}
hasChanged := false
shouldSlash := true
for i, tx := range txOut.TxArray {
// ragnarok is the memo used by thorchain to identify fund returns to
// bonders, LPs, and reserve contributors.
......@@ -87,22 +92,24 @@ func (h RagnarokHandler) handleV1(ctx sdk.Context, msg MsgRagnarok) sdk.Result {
msg.Tx.Tx.Coins.Contains(tx.Coin) &&
tx.ToAddress.Equals(msg.Tx.Tx.ToAddress) &&
fromAddress.Equals(msg.Tx.Tx.FromAddress) {
txOut.TxArray[i].OutHash = msg.Tx.Tx.ID
hasChanged = true
shouldSlash = false
if err := h.keeper.SetTxOut(ctx, txOut); nil != err {
ctx.Logger().Error("fail to save tx out", "error", err)
return sdk.ErrInternal("fail to save tx out").Result()
}
break
}
}
if !hasChanged {
return sdk.Result{
Code: sdk.CodeOK,
Codespace: DefaultCodespace,
if shouldSlash {
if err := h.slash(ctx, msg.Tx); err != nil {
return sdk.ErrInternal("fail to slash account").Result()
}
}
if err := h.keeper.SetTxOut(ctx, txOut); nil != err {
ctx.Logger().Error("fail to save tx out", "error", err)
return sdk.ErrInternal("fail to save tx out").Result()
}
h.keeper.SetLastSignedHeight(ctx, msg.BlockHeight)
return sdk.Result{
......
......@@ -73,6 +73,7 @@ type TestRagnarokKeeperHappyPath struct {
newVault Vault
retireVault Vault
txout *TxOut
pool Pool
}
func (k *TestRagnarokKeeperHappyPath) GetTxOut(ctx sdk.Context, blockHeight int64) (*TxOut, error) {
......@@ -90,6 +91,28 @@ func (k *TestRagnarokKeeperHappyPath) SetTxOut(ctx sdk.Context, blockOut *TxOut)
return kaboom
}
func (k *TestRagnarokKeeperHappyPath) GetNodeAccountByPubKey(_ sdk.Context, _ common.PubKey) (NodeAccount, error) {
return k.activeNodeAccount, nil
}
func (k *TestRagnarokKeeperHappyPath) SetNodeAccount(_ sdk.Context, na NodeAccount) error {
k.activeNodeAccount = na
return nil
}
func (k *TestRagnarokKeeperHappyPath) GetPool(_ sdk.Context, _ common.Asset) (Pool, error) {
return k.pool, nil
}
func (k *TestRagnarokKeeperHappyPath) SetPool(_ sdk.Context, p Pool) error {
k.pool = p
return nil
}
func (k *TestRagnarokKeeperHappyPath) UpsertEvent(_ sdk.Context, _ Event) error {
return nil
}
func (HandlerRagnarokSuite) TestRagnarokHappyPath(c *C) {
ctx, _ := setupKeeperForTest(c)
retireVault := GetRandomVault()
......@@ -132,3 +155,46 @@ func (HandlerRagnarokSuite) TestRagnarokHappyPath(c *C) {
c.Assert(result.Code, Equals, sdk.CodeOK)
c.Assert(keeper.txout.TxArray[0].OutHash.Equals(tx.Tx.ID), Equals, true)
}
func (HandlerRagnarokSuite) TestSlash(c *C) {
ctx, _ := setupKeeperForTest(c)
retireVault := GetRandomVault()
newVault := GetRandomVault()
txout := NewTxOut(1)
newVaultAddr, err := newVault.PubKey.GetAddress(common.BNBChain)
c.Assert(err, IsNil)
pool := NewPool()
pool.Asset = common.BNBAsset
pool.BalanceAsset = sdk.NewUint(100 * common.One)
pool.BalanceRune = sdk.NewUint(100 * common.One)
na := GetRandomNodeAccount(NodeActive)
na.Bond = sdk.NewUint(100 * common.One)
keeper := &TestRagnarokKeeperHappyPath{
activeNodeAccount: na,
newVault: newVault,
retireVault: retireVault,
txout: txout,
pool: pool,
}
addr, err := keeper.retireVault.PubKey.GetAddress(common.BNBChain)
c.Assert(err, IsNil)
handler := NewRagnarokHandler(keeper)
tx := NewObservedTx(common.Tx{
ID: GetRandomTxHash(),
Chain: common.BNBChain,
Coins: common.Coins{
common.NewCoin(common.BNBAsset, sdk.NewUint(1024)),
},
Memo: NewRagnarokMemo(1).String(),
FromAddress: addr,
ToAddress: newVaultAddr,
Gas: common.BNBGasFeeSingleton,
}, 1, retireVault.PubKey)
msgRagnarok := NewMsgRagnarok(tx, 1, keeper.activeNodeAccount.NodeAddress)
result := handler.handleV1(ctx, msgRagnarok)
c.Assert(result.Code, Equals, sdk.CodeOK, Commentf("%s", result.Log))
c.Assert(keeper.activeNodeAccount.Bond.Equal(sdk.NewUint(9999998464)), Equals, true, Commentf("%d", keeper.activeNodeAccount.Bond.Uint64()))
}
......@@ -75,6 +75,17 @@ func (h YggdrasilHandler) handle(ctx sdk.Context, msg MsgYggdrasil, version semv
}
}
func (h YggdrasilHandler) slash(ctx sdk.Context, pk common.PubKey, coins common.Coins) error {
var returnErr error
for _, c := range coins {
if err := slashNodeAccount(ctx, h.keeper, pk, c.Asset, c.Amount); err != nil {
ctx.Logger().Error("fail to slash account", "error", err)
returnErr = err
}
}
return returnErr
}
func (h YggdrasilHandler) handleV1(ctx sdk.Context, msg MsgYggdrasil, version semver.Version) sdk.Result {
// update txOut record with our TxID that sent funds out of the pool
txOut, err := h.keeper.GetTxOut(ctx, msg.BlockHeight)
......@@ -83,6 +94,7 @@ func (h YggdrasilHandler) handleV1(ctx sdk.Context, msg MsgYggdrasil, version se
return sdk.ErrUnknownRequest(err.Error()).Result()
}
shouldSlash := true
for i, tx := range txOut.TxArray {
// yggdrasil is the memo used by thorchain to identify fund migration
// to a yggdrasil vault.
......@@ -100,15 +112,24 @@ func (h YggdrasilHandler) handleV1(ctx sdk.Context, msg MsgYggdrasil, version se
if msg.AddFunds && !msg.Tx.Coins.Contains(tx.Coin) {
continue
}
txOut.TxArray[i].OutHash = msg.Tx.ID
shouldSlash = false
if err := h.keeper.SetTxOut(ctx, txOut); nil != err {
ctx.Logger().Error("fail to save tx out", "error", err)
}
h.keeper.SetLastSignedHeight(ctx, msg.BlockHeight)
break
}
}
if shouldSlash {
if err := h.slash(ctx, msg.PubKey, msg.Tx.Coins); err != nil {
return sdk.ErrInternal("fail to slash account").Result()
}
}
vault, err := h.keeper.GetVault(ctx, msg.PubKey)
if err != nil && !stdErrors.Is(err, ErrVaultNotFound) {
ctx.Logger().Error("fail to get yggdrasil", "error", err)
......@@ -119,6 +140,8 @@ func (h YggdrasilHandler) handleV1(ctx sdk.Context, msg MsgYggdrasil, version se
vault.Type = YggdrasilVault
}
h.keeper.SetLastSignedHeight(ctx, msg.BlockHeight)
if msg.AddFunds {
return h.handleYggdrasilFund(ctx, msg, vault)
}
......
......@@ -37,6 +37,10 @@ func (k yggdrasilTestKeeper) GetNodeAccountByPubKey(ctx sdk.Context, pk common.P
return k.Keeper.GetNodeAccountByPubKey(ctx, pk)
}
func (k *yggdrasilTestKeeper) SetNodeAccount(ctx sdk.Context, na NodeAccount) error {
return k.Keeper.SetNodeAccount(ctx, na)
}
func (k yggdrasilTestKeeper) GetPool(ctx sdk.Context, asset common.Asset) (Pool, error) {
if k.errGetPool {
return Pool{}, kaboom
......@@ -44,6 +48,14 @@ func (k yggdrasilTestKeeper) GetPool(ctx sdk.Context, asset common.Asset) (Pool,
return k.Keeper.GetPool(ctx, asset)
}
func (k *yggdrasilTestKeeper) SetPool(ctx sdk.Context, p Pool) error {
return k.Keeper.SetPool(ctx, p)
}
func (k *yggdrasilTestKeeper) UpsertEvent(ctx sdk.Context, evt Event) error {
return k.Keeper.UpsertEvent(ctx, evt)
}
func (k yggdrasilTestKeeper) GetVault(ctx sdk.Context, pk common.PubKey) (Vault, error) {
if k.errGetVault {
return Vault{}, kaboom
......@@ -251,11 +263,10 @@ func (s *HandlerYggdrasilSuite) TestYggdrasilHandler(c *C) {
},
expectedResult: sdk.CodeOK,
validator: func(helper yggdrasilHandlerTestHelper, msg sdk.Msg, result sdk.Result, c *C) {
beforeBond := helper.nodeAccount.Bond
slashAmount := sdk.NewUint(common.One).MulUint64(3)
expectedBond := helper.nodeAccount.Bond.Sub(sdk.NewUint(603787879))
na, err := helper.keeper.GetNodeAccount(helper.ctx, helper.nodeAccount.NodeAddress)
c.Assert(err, IsNil)
c.Assert(na.Bond.Equal(common.SafeSub(beforeBond, slashAmount)), Equals, true)
c.Assert(na.Bond.Equal(expectedBond), Equals, true, Commentf("%d/%d", na.Bond.Uint64(), expectedBond.Uint64()))
},
},
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment