Skip to content

Commit

Permalink
Use slice based loop instead of map based loop in Begin/EndBlock (sei…
Browse files Browse the repository at this point in the history
…-protocol#33)

* Use slice based loop instead of map based loop in Begin/EndBlock

* add idempotency test
  • Loading branch information
codchen authored Jun 6, 2022
1 parent 6008711 commit 0e313eb
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 80 deletions.
4 changes: 3 additions & 1 deletion app/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ func (app *App) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
}

func (app *App) Commit() (res abci.ResponseCommit) {
defer (*app.tracingInfo.BlockSpan).End()
if app.tracingInfo.BlockSpan != nil {
defer (*app.tracingInfo.BlockSpan).End()
}
_, span := (*app.tracingInfo.Tracer).Start(app.tracingInfo.TracerContext, "Commit")
defer span.End()
app.tracingInfo.TracerContext = context.Background()
Expand Down
3 changes: 2 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package app
import (
"context"
"fmt"
appparams "github.com/sei-protocol/sei-chain/app/params"
"io"
"os"
"path/filepath"
"strings"

appparams "github.com/sei-protocol/sei-chain/app/params"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
Expand Down
29 changes: 29 additions & 0 deletions app/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package app_test

import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/sei-protocol/sei-chain/app"
"github.com/stretchr/testify/require"
)

func TestEmptyBlockIdempotency(t *testing.T) {
commitData := [][]byte{}
tm := time.Now().UTC()
valPub := secp256k1.GenPrivKey().PubKey()

for i := 1; i <= 10; i++ {
testWrapper := app.NewTestWrapper(t, tm, valPub)
testWrapper.BeginBlock()
testWrapper.EndBlock()
data := testWrapper.App.Commit().Data
commitData = append(commitData, data)
}

referenceData := commitData[0]
for _, data := range commitData[1:] {
require.Equal(t, len(referenceData), len(data))
}
}
211 changes: 211 additions & 0 deletions app/test_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package app

import (
"encoding/json"
"os"
"testing"
"time"

"github.com/CosmWasm/wasmd/x/wasm"
crptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
)

type TestWrapper struct {
suite.Suite

App *App
Ctx sdk.Context
}

func NewTestWrapper(t *testing.T, tm time.Time, valPub crptotypes.PubKey) *TestWrapper {
appPtr := Setup(false)
wrapper := &TestWrapper{
App: appPtr,
Ctx: appPtr.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "sei-test", Time: tm}),
}
wrapper.SetT(t)
wrapper.setupValidator(stakingtypes.Bonded, valPub)
return wrapper
}

func (s *TestWrapper) FundAcc(acc sdk.AccAddress, amounts sdk.Coins) {
err := s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, amounts)
s.Require().NoError(err)

err = s.App.BankKeeper.SendCoinsFromModuleToAccount(s.Ctx, minttypes.ModuleName, acc, amounts)
s.Require().NoError(err)
}

func (s *TestWrapper) setupValidator(bondStatus stakingtypes.BondStatus, valPub crptotypes.PubKey) sdk.ValAddress {
valAddr := sdk.ValAddress(valPub.Address())
bondDenom := s.App.StakingKeeper.GetParams(s.Ctx).BondDenom
selfBond := sdk.NewCoins(sdk.Coin{Amount: sdk.NewInt(100), Denom: bondDenom})

s.FundAcc(sdk.AccAddress(valAddr), selfBond)

sh := teststaking.NewHelper(s.Suite.T(), s.Ctx, s.App.StakingKeeper)
msg := sh.CreateValidatorMsg(valAddr, valPub, selfBond[0].Amount)
sh.Handle(msg, true)

val, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr)
s.Require().True(found)

val = val.UpdateStatus(bondStatus)
s.App.StakingKeeper.SetValidator(s.Ctx, val)

consAddr, err := val.GetConsAddr()
s.Suite.Require().NoError(err)

signingInfo := slashingtypes.NewValidatorSigningInfo(
consAddr,
s.Ctx.BlockHeight(),
0,
time.Unix(0, 0),
false,
0,
)
s.App.SlashingKeeper.SetValidatorSigningInfo(s.Ctx, consAddr, signingInfo)

return valAddr
}

func (s *TestWrapper) BeginBlock() {
var proposer sdk.ValAddress

validators := s.App.StakingKeeper.GetAllValidators(s.Ctx)
s.Require().Equal(1, len(validators))

valAddrFancy, err := validators[0].GetConsAddr()
s.Require().NoError(err)
proposer = valAddrFancy.Bytes()

validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, proposer)
s.Assert().True(found)

valConsAddr, err := validator.GetConsAddr()

s.Require().NoError(err)

valAddr := valConsAddr.Bytes()

newBlockTime := s.Ctx.BlockTime().Add(2 * time.Second)

header := tmproto.Header{Height: s.Ctx.BlockHeight() + 1, Time: newBlockTime}
newCtx := s.Ctx.WithBlockTime(newBlockTime).WithBlockHeight(s.Ctx.BlockHeight() + 1)
s.Ctx = newCtx
lastCommitInfo := abci.LastCommitInfo{
Votes: []abci.VoteInfo{{
Validator: abci.Validator{Address: valAddr, Power: 1000},
SignedLastBlock: true,
}},
}
reqBeginBlock := abci.RequestBeginBlock{Header: header, LastCommitInfo: lastCommitInfo}

s.App.BeginBlocker(s.Ctx, reqBeginBlock)
}

func (s *TestWrapper) EndBlock() {
reqEndBlock := abci.RequestEndBlock{Height: s.Ctx.BlockHeight()}
s.App.EndBlocker(s.Ctx, reqEndBlock)
}

type EmptyAppOptions struct{}

func (ao EmptyAppOptions) Get(o string) interface{} {
return nil
}

func Setup(isCheckTx bool) *App {
db := dbm.NewMemDB()
encodingConfig := MakeEncodingConfig()
cdc := encodingConfig.Marshaler
app := New(
log.NewNopLogger(),
db,
nil,
true,
map[int64]bool{},
DefaultNodeHome,
5,
encodingConfig,
wasm.EnableAllProposals,
EmptyAppOptions{},
EmptyWasmOpts,
)
if !isCheckTx {
genesisState := NewDefaultGenesisState(cdc)
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
if err != nil {
panic(err)
}

app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: simapp.DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
}

return app
}

func SetupTestingAppWithLevelDb(isCheckTx bool) (*App, func()) {
dir := "sei_testing"
db, err := sdk.NewLevelDB("sei_leveldb_testing", dir)
if err != nil {
panic(err)
}
encodingConfig := MakeEncodingConfig()
cdc := encodingConfig.Marshaler
app := New(
log.NewNopLogger(),
db,
nil,
true,
map[int64]bool{},
DefaultNodeHome,
5,
encodingConfig,
wasm.EnableAllProposals,
EmptyAppOptions{},
EmptyWasmOpts,
)
if !isCheckTx {
genesisState := NewDefaultGenesisState(cdc)
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
if err != nil {
panic(err)
}

app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: simapp.DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
}

cleanupFn := func() {
db.Close()
err = os.RemoveAll(dir)
if err != nil {
panic(err)
}
}

return app, cleanupFn
}
26 changes: 26 additions & 0 deletions x/dex/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,29 @@ func (o *OrderCancellations) UpdateForLiquidation(liquidatedAccounts []string) {
})
}
}

type LiquidationRequest struct {
Requestor string
AccountToLiquidate string
}

type LiquidationRequests []LiquidationRequest

func (lrs *LiquidationRequests) IsAccountLiquidating(accountToLiquidate string) bool {
for _, lr := range *lrs {
if lr.AccountToLiquidate == accountToLiquidate {
return true
}
}
return false
}

func (lrs *LiquidationRequests) AddNewLiquidationRequest(requestor string, accountToLiquidate string) {
if lrs.IsAccountLiquidating(accountToLiquidate) {
return
}
*lrs = append(*lrs, LiquidationRequest{
Requestor: requestor,
AccountToLiquidate: accountToLiquidate,
})
}
38 changes: 21 additions & 17 deletions x/dex/keeper/end_block_cancel_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,37 @@ import (
otrace "go.opentelemetry.io/otel/trace"
)

func (k *Keeper) HandleEBCancelOrders(ctx context.Context, sdkCtx sdk.Context, tracer *otrace.Tracer, contractAddr string) {
func (k *Keeper) HandleEBCancelOrders(ctx context.Context, sdkCtx sdk.Context, tracer *otrace.Tracer, contractAddr string, registeredPairs []types.Pair) {
_, span := (*tracer).Start(ctx, "SudoCancelOrders")
span.SetAttributes(attribute.String("contractAddr", contractAddr))

msg := k.getCancelSudoMsg(contractAddr)
msg := k.getCancelSudoMsg(contractAddr, registeredPairs)
_ = k.CallContractSudo(sdkCtx, contractAddr, msg)
for pair, orderCancellations := range k.OrderCancellations[contractAddr] {
for _, orderCancellation := range orderCancellations.OrderCancellations {
k.Orders[contractAddr][pair].AddCancelOrder(dexcache.CancelOrder{
Creator: orderCancellation.Creator,
Price: orderCancellation.Price,
Quantity: orderCancellation.Quantity,
Direction: orderCancellation.Direction,
Effect: orderCancellation.Effect,
Leverage: orderCancellation.Leverage,
})
for _, pair := range registeredPairs {
pairStr := pair.String()
if orderCancellations, ok := k.OrderCancellations[contractAddr][pairStr]; ok {
for _, orderCancellation := range orderCancellations.OrderCancellations {
k.Orders[contractAddr][pairStr].AddCancelOrder(dexcache.CancelOrder{
Creator: orderCancellation.Creator,
Price: orderCancellation.Price,
Quantity: orderCancellation.Quantity,
Direction: orderCancellation.Direction,
Effect: orderCancellation.Effect,
Leverage: orderCancellation.Leverage,
})
}
}
}
span.End()
}

func (k *Keeper) getCancelSudoMsg(contractAddr string) types.SudoOrderCancellationMsg {
pairToOrderCancellations := k.OrderCancellations[contractAddr]
func (k *Keeper) getCancelSudoMsg(contractAddr string, registeredPairs []types.Pair) types.SudoOrderCancellationMsg {
contractOrderCancellations := []types.ContractOrderCancellation{}
for _, orderCancellations := range pairToOrderCancellations {
for _, orderCancellation := range orderCancellations.OrderCancellations {
contractOrderCancellations = append(contractOrderCancellations, dexcache.ToContractOrderCancellation(orderCancellation))
for _, pair := range registeredPairs {
if orderCancellations, ok := k.OrderCancellations[contractAddr][pair.String()]; ok {
for _, orderCancellation := range orderCancellations.OrderCancellations {
contractOrderCancellations = append(contractOrderCancellations, dexcache.ToContractOrderCancellation(orderCancellation))
}
}
}
return types.SudoOrderCancellationMsg{
Expand Down
23 changes: 12 additions & 11 deletions x/dex/keeper/end_block_liquidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
otrace "go.opentelemetry.io/otel/trace"
)

func (k *Keeper) HandleEBLiquidation(ctx context.Context, sdkCtx sdk.Context, tracer *otrace.Tracer, contractAddr string) {
func (k *Keeper) HandleEBLiquidation(ctx context.Context, sdkCtx sdk.Context, tracer *otrace.Tracer, contractAddr string, registeredPairs []types.Pair) {
_, liquidationSpan := (*tracer).Start(ctx, "SudoLiquidation")
liquidationSpan.SetAttributes(attribute.String("contractAddr", contractAddr))

Expand All @@ -22,12 +22,13 @@ func (k *Keeper) HandleEBLiquidation(ctx context.Context, sdkCtx sdk.Context, tr
json.Unmarshal(data, &response)
sdkCtx.Logger().Info(fmt.Sprintf("Sudo liquidate response data: %s", response))

for _, cancellations := range k.OrderCancellations[contractAddr] {
cancellations.UpdateForLiquidation(response.SuccessfulAccounts)
}

for _, placements := range k.OrderPlacements[contractAddr] {
placements.FilterOutAccounts(response.SuccessfulAccounts)
for _, pair := range registeredPairs {
if cancellations, ok := k.OrderCancellations[contractAddr][pair.String()]; ok {
cancellations.UpdateForLiquidation(response.SuccessfulAccounts)
}
if placements, ok := k.OrderPlacements[contractAddr][pair.String()]; ok {
placements.FilterOutAccounts(response.SuccessfulAccounts)
}
}
k.placeLiquidationOrders(sdkCtx, contractAddr, response.LiquidationOrders)

Expand All @@ -54,12 +55,12 @@ func (k *Keeper) placeLiquidationOrders(ctx sdk.Context, contractAddr string, li
}

func (k *Keeper) getLiquidationSudoMsg(contractAddr string) types.SudoLiquidationMsg {
liquidationRequestorToAccounts := k.LiquidationRequests[contractAddr]
cachedLiquidationRequests := k.LiquidationRequests[contractAddr]
liquidationRequests := []types.LiquidationRequest{}
for requestor, account := range liquidationRequestorToAccounts {
for _, cachedLiquidationRequest := range *cachedLiquidationRequests {
liquidationRequests = append(liquidationRequests, types.LiquidationRequest{
Requestor: requestor,
Account: account,
Requestor: cachedLiquidationRequest.Requestor,
Account: cachedLiquidationRequest.AccountToLiquidate,
})
}
return types.SudoLiquidationMsg{
Expand Down
Loading

0 comments on commit 0e313eb

Please sign in to comment.