Skip to content

Commit

Permalink
added posttx tests
Browse files Browse the repository at this point in the history
  • Loading branch information
freeelancer committed Mar 2, 2024
1 parent a859d3a commit fb448bb
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 61 deletions.
40 changes: 27 additions & 13 deletions x/smartaccount/ante/pretransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package ante
import (
"encoding/json"

sdkerrors "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrortypes "github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
tx2 "github.com/cosmos/cosmos-sdk/types/tx"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"

wasmvmtypes "github.com/CosmWasm/wasmvm/types"
smartaccounttypes "github.com/terra-money/core/v2/x/smartaccount/types"
"github.com/terra-money/core/v2/x/smartaccount/types"
)

// SmartAccountCheckDecorator does authentication for smart accounts
Expand All @@ -28,7 +27,7 @@ func NewPreTransactionHookDecorator(sak SmartAccountKeeper, wk WasmKeeper) PreTr

// AnteHandle checks if the tx provides sufficient fee to cover the required fee from the fee market.
func (pth PreTransactionHookDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
setting, ok := ctx.Value(smartaccounttypes.ModuleName).(smartaccounttypes.Setting)
setting, ok := ctx.Value(types.ModuleName).(types.Setting)
if !ok {
return next(ctx, tx, simulate)
}
Expand All @@ -54,25 +53,25 @@ func (pth PreTransactionHookDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, si
}

// TODO: to refactor
func BuildPreTransactionHookMsg(tx sdk.Tx) ([]byte, error) {
func BuildPrePostTransactionHookMsg(tx sdk.Tx, isPreTx bool) ([]byte, error) {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
if !ok {
return nil, sdkerrors.Wrap(sdkerrortypes.ErrInvalidType, "expected SigVerifiableTx")
return nil, sdkerrors.ErrInvalidType.Wrap("expected SigVerifiableTx")
}

// Signer here is the account that the state transition is affecting
// e.g. Account that is transferring some Coins
signers := sigTx.GetSigners()
// Current only supports one signer (TODO review in the future)
if len(signers) != 1 {
return nil, sdkerrors.Wrap(sdkerrortypes.ErrorInvalidSigner, "only one signer is supported")
return nil, sdkerrors.ErrorInvalidSigner.Wrap("only one signer is supported")
}

// Sender here is the account that signed the transaction
// Could be different from the account above (confusingly named signer)
signatures, _ := sigTx.GetSignaturesV2()
if len(signatures) == 0 {
return nil, sdkerrors.Wrap(sdkerrortypes.ErrNoSignatures, "no signatures found")
return nil, sdkerrors.ErrNoSignatures.Wrap("no signatures found")
}
senderAddr, err := sdk.AccAddressFromHexUnsafe(signatures[0].PubKey.Address().String())
if err != nil {
Expand All @@ -94,11 +93,26 @@ func BuildPreTransactionHookMsg(tx sdk.Tx) ([]byte, error) {
Stargate: &stargateMsg,
})
}
preTx := smartaccounttypes.PreTransaction{
Sender: senderAddr.String(),
Account: signers[0].String(),
Messages: stargateMsgs,
var msg types.SudoMsg
if isPreTx {
preTx := types.PreTransaction{
Sender: senderAddr.String(),
Account: signers[0].String(),
Messages: stargateMsgs,
}
msg = types.SudoMsg{PreTransaction: &preTx}
} else {
postTx := types.PostTransaction{
Sender: senderAddr.String(),
Account: signers[0].String(),
Messages: stargateMsgs,
}
msg = types.SudoMsg{PostTransaction: &postTx}
}
msg := smartaccounttypes.SudoMsg{PreTransaction: &preTx}

return json.Marshal(msg)
}

func BuildPreTransactionHookMsg(tx sdk.Tx) ([]byte, error) {
return BuildPrePostTransactionHookMsg(tx, true)
}
File renamed without changes.
4 changes: 4 additions & 0 deletions x/smartaccount/post/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ import (
type SmartAccountKeeper interface {
GetSetting(ctx sdk.Context, ownerAddr string) (*smartaccounttypes.Setting, error)
}

type WasmKeeper interface {
Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error)
}
60 changes: 60 additions & 0 deletions x/smartaccount/post/posttransaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package post

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/terra-money/core/v2/x/smartaccount/ante"
"github.com/terra-money/core/v2/x/smartaccount/types"
)

// PostTransactionHookDecorator does authentication for smart accounts
type PostTransactionHookDecorator struct {
smartaccountKeeper SmartAccountKeeper
wasmKeeper WasmKeeper
}

func NewPostTransactionHookDecorator(sak SmartAccountKeeper, wk WasmKeeper) PostTransactionHookDecorator {
return PostTransactionHookDecorator{
smartaccountKeeper: sak,
wasmKeeper: wk,
}
}

// FeeSharePostHandler if the smartaccount module is enabled
// takes the total fees paid for each transaction and
// split these fees equally between all the contacts
// involved in the transaction based on the module params.
func (pth PostTransactionHookDecorator) PostHandle(
ctx sdk.Context,
tx sdk.Tx,
simulate bool,
success bool,
next sdk.PostHandler,
) (newCtx sdk.Context, err error) {
setting, ok := ctx.Value(types.ModuleName).(types.Setting)
if !ok {
return next(ctx, tx, simulate, success)
}

if setting.PostTransaction != nil && len(setting.PostTransaction) > 0 {
for _, postTx := range setting.PostTransaction {
contractAddr, err := sdk.AccAddressFromBech32(postTx)
if err != nil {
return ctx, err
}
data, err := BuildPostTransactionHookMsg(tx)
if err != nil {
return ctx, err
}
_, err = pth.wasmKeeper.Sudo(ctx, contractAddr, data)
if err != nil {
return ctx, err
}
}
}
return next(ctx, tx, simulate, success)
}

func BuildPostTransactionHookMsg(tx sdk.Tx) ([]byte, error) {
return ante.BuildPrePostTransactionHookMsg(tx, false)
}
162 changes: 162 additions & 0 deletions x/smartaccount/post/posttransaction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package post_test

import (
"testing"

wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/terra-money/core/v2/x/smartaccount/post"
"github.com/terra-money/core/v2/x/smartaccount/test_helpers"
smartaccounttypes "github.com/terra-money/core/v2/x/smartaccount/types"
)

type PostTxTestSuite struct {
test_helpers.SmartAccountTestSuite

PostTxDecorator post.PostTransactionHookDecorator
WasmKeeper *wasmkeeper.PermissionedKeeper
}

func TestAnteSuite(t *testing.T) {
suite.Run(t, new(PostTxTestSuite))
}

func (s *PostTxTestSuite) Setup() {
s.SmartAccountTestSuite.SetupTests()
s.WasmKeeper = wasmkeeper.NewDefaultPermissionKeeper(s.App.Keepers.WasmKeeper)
s.PostTxDecorator = post.NewPostTransactionHookDecorator(s.SmartAccountKeeper, s.WasmKeeper)
s.Ctx = s.Ctx.WithChainID("test")
}

func (s *PostTxTestSuite) TestPostTransactionHookWithoutSmartAccount() {
s.Setup()
txBuilder := s.BuildDefaultMsgTx(0, &types.MsgSend{
FromAddress: s.TestAccs[0].String(),
ToAddress: s.TestAccs[1].String(),
Amount: sdk.NewCoins(sdk.NewInt64Coin("uluna", 100000000)),
})
_, err := s.PostTxDecorator.PostHandle(s.Ctx, txBuilder.GetTx(), false, true, sdk.ChainPostDecorators(sdk.Terminator{}))
require.NoError(s.T(), err)
}

func (s *PostTxTestSuite) TestPostTransactionHookWithEmptySmartAccount() {
s.Setup()
s.Ctx = s.Ctx.WithValue(smartaccounttypes.ModuleName, smartaccounttypes.Setting{})
txBuilder := s.BuildDefaultMsgTx(0, &types.MsgSend{
FromAddress: s.TestAccs[0].String(),
ToAddress: s.TestAccs[1].String(),
Amount: sdk.NewCoins(sdk.NewInt64Coin("uluna", 100000000)),
})
_, err := s.PostTxDecorator.PostHandle(s.Ctx, txBuilder.GetTx(), false, true, sdk.ChainPostDecorators(sdk.Terminator{}))
require.NoError(s.T(), err)
}

func (s *PostTxTestSuite) TestInvalidContractAddress() {
s.Setup()
s.Ctx = s.Ctx.WithValue(smartaccounttypes.ModuleName, smartaccounttypes.Setting{
PostTransaction: []string{s.TestAccs[0].String()},
})
txBuilder := s.BuildDefaultMsgTx(0, &types.MsgSend{
FromAddress: s.TestAccs[0].String(),
ToAddress: s.TestAccs[1].String(),
Amount: sdk.NewCoins(sdk.NewInt64Coin("uluna", 100000000)),
})
_, err := s.PostTxDecorator.PostHandle(s.Ctx, txBuilder.GetTx(), false, true, sdk.ChainPostDecorators(sdk.Terminator{}))
require.ErrorContainsf(s.T(), err, "no such contract", "error message: %s", err)
}

func (s *PostTxTestSuite) TestSendCoinsWithLimitSendHook() {
s.Setup()

acc := s.TestAccs[0]
codeId, _, err := s.WasmKeeper.Create(s.Ctx, acc, test_helpers.LimitSendOnlyHookWasm, nil)
require.NoError(s.T(), err)
contractAddr, _, err := s.WasmKeeper.Instantiate(s.Ctx, codeId, acc, acc, []byte("{}"), "limit send", sdk.NewCoins())
require.NoError(s.T(), err)

s.Ctx = s.Ctx.WithValue(smartaccounttypes.ModuleName, smartaccounttypes.Setting{
PostTransaction: []string{contractAddr.String()},
})
txBuilder := s.BuildDefaultMsgTx(0, &types.MsgSend{
FromAddress: acc.String(),
ToAddress: acc.String(),
Amount: sdk.NewCoins(sdk.NewInt64Coin("uluna", 100000000)),
})
_, err = s.PostTxDecorator.PostHandle(s.Ctx, txBuilder.GetTx(), false, true, sdk.ChainPostDecorators(sdk.Terminator{}))
require.NoError(s.T(), err)
}

func (s *PostTxTestSuite) TestStakingWithLimitSendHook() {
s.Setup()

acc := s.TestAccs[0]
codeId, _, err := s.WasmKeeper.Create(s.Ctx, acc, test_helpers.LimitSendOnlyHookWasm, nil)
require.NoError(s.T(), err)
contractAddr, _, err := s.WasmKeeper.Instantiate(s.Ctx, codeId, acc, acc, []byte("{}"), "limit send", sdk.NewCoins())
require.NoError(s.T(), err)

s.Ctx = s.Ctx.WithValue(smartaccounttypes.ModuleName, smartaccounttypes.Setting{
PostTransaction: []string{contractAddr.String()},
})
txBuilder := s.BuildDefaultMsgTx(0, &stakingtypes.MsgDelegate{
DelegatorAddress: acc.String(),
ValidatorAddress: acc.String(),
Amount: sdk.NewInt64Coin("uluna", 100000000),
})
_, err = s.PostTxDecorator.PostHandle(s.Ctx, txBuilder.GetTx(), false, true, sdk.ChainPostDecorators(sdk.Terminator{}))
require.ErrorContainsf(s.T(), err, "Unauthorized message type", "error message: %s", err)
}

func (s *PostTxTestSuite) BuildDefaultMsgTx(accountIndex int, msgs ...sdk.Msg) client.TxBuilder {
pk := s.TestAccPrivs[accountIndex]
sender := s.TestAccs[accountIndex]
acc := s.App.Keepers.AccountKeeper.GetAccount(s.Ctx, msgs[0].GetSigners()[0])
txBuilder := s.EncodingConfig.TxConfig.NewTxBuilder()
err := txBuilder.SetMsgs(
msgs...,
)
require.NoError(s.T(), err)

signer := authsigning.SignerData{
Address: sender.String(),
ChainID: "test",
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
PubKey: pk.PubKey(),
}

emptySig := signing.SignatureV2{
PubKey: signer.PubKey,
Data: &signing.SingleSignatureData{
SignMode: s.EncodingConfig.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
Sequence: signer.Sequence,
}

err = txBuilder.SetSignatures(emptySig)
require.NoError(s.T(), err)

sigV2, err := tx.SignWithPrivKey(
s.EncodingConfig.TxConfig.SignModeHandler().DefaultMode(),
signer,
txBuilder,
pk,
s.EncodingConfig.TxConfig,
acc.GetSequence(),
)
require.NoError(s.T(), err)

err = txBuilder.SetSignatures(sigV2)
require.NoError(s.T(), err)

return txBuilder
}
45 changes: 0 additions & 45 deletions x/smartaccount/post/smartaccount_posttx.go

This file was deleted.

Binary file modified x/smartaccount/test_helpers/test_data/limit_send_only_hooks.wasm
Binary file not shown.
6 changes: 3 additions & 3 deletions x/smartaccount/types/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type PreTransaction struct {
}

type PostTransaction struct {
Sender string `json:"sender"`
Account string `json:"account"`
Msgs []types.CosmosMsg `json:"msgs"`
Sender string `json:"sender"`
Account string `json:"account"`
Messages []types.CosmosMsg `json:"msgs"`
}

0 comments on commit fb448bb

Please sign in to comment.