-
Notifications
You must be signed in to change notification settings - Fork 0
/
block_advancement.go
147 lines (128 loc) · 5.14 KB
/
block_advancement.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package app
import (
"testing"
"time"
abcitypes "github.com/cometbft/cometbft/abci/types"
"github.com/jinxprotocol/v4-chain/protocol/app"
testtx "github.com/jinxprotocol/v4-chain/protocol/testutil/tx"
clobtypes "github.com/jinxprotocol/v4-chain/protocol/x/clob/types"
"github.com/stretchr/testify/require"
sdktypes "github.com/cosmos/cosmos-sdk/types"
)
// BlockAdvancement holds orders and matches to be placed in a block. Using this struct and building
// the ops queue with the getOperationsQueue helper function allows us to build the operations queue
// without going through CheckTx and, therefore, not affect the local memclob state. This also allows us to propose
// an invalid set of operations that an honest validator would not generate.
type BlockAdvancement struct {
// should hold Order and OperationRaw types. Stored as slice to allow for ordering.
ShortTermOrdersAndOperations []interface{}
// should hold stateful orders to include in DeliverTx after ProposedOperationsTx
StatefulOrders []clobtypes.Order
}
// TxIndexesToErrors allows us to specify the expected error (if any) for each tx in the
// block proposal.
type TxIndexesToErrors map[int]string
type BlockAdvancementWithErrors struct {
BlockAdvancement BlockAdvancement
ExpectedDeliverTxErrors TxIndexesToErrors
}
// AdvanceToBlock advances the test app to the given block height using the operations queue
// generated from the specified BlockAdvancement. It catches errors in DeliverTx and verifies that
// the error matches the expected error.
func (b BlockAdvancementWithErrors) AdvanceToBlock(
ctx sdktypes.Context,
blockHeight uint32,
tApp *TestApp,
t *testing.T,
) sdktypes.Context {
advanceToBlockOptions := AdvanceToBlockOptions{
ValidateDeliverTxs: func(
ctx sdktypes.Context,
request abcitypes.RequestDeliverTx,
response abcitypes.ResponseDeliverTx,
txIndex int,
) (haltchain bool) {
expectedError, found := b.ExpectedDeliverTxErrors[txIndex]
if found && expectedError != "" {
require.True(t, response.IsErr(), "Expected CheckTx to error. Response: %+v", response)
require.Contains(t, response.Log, expectedError)
} else {
require.True(t, response.IsOK(), "Expected CheckTx to succeed. Response: %+v", response)
}
return false
},
}
deliverTxsOverride := b.BlockAdvancement.getDeliverTxs(ctx, tApp.App)
if len(deliverTxsOverride) > 0 {
advanceToBlockOptions.DeliverTxsOverride = deliverTxsOverride
}
return tApp.AdvanceToBlock(blockHeight, advanceToBlockOptions)
}
// getDeliverTxs returns a slice of tx bytes to be executed in DeliverTx.
func (b BlockAdvancement) getDeliverTxs(ctx sdktypes.Context, app *app.App) [][]byte {
deliverTxs := make([][]byte, 0)
// operations come first in block
if len(b.ShortTermOrdersAndOperations) > 0 {
deliverTxs = append(deliverTxs, b.getProposedOperationsTxBytes(ctx, app))
}
// stateful order placements come after all app-injected messages
deliverTxs = append(deliverTxs, b.getStatefulMsgPlaceOrderTxBytes(ctx, app)...)
return deliverTxs
}
// getStatefulMsgPlaceOrderTxBytes iterates over StatefulOrders and returns a slice of tx bytes corresponding to the
// signed set of MsgPlaceOrder txs.
func (b BlockAdvancement) getStatefulMsgPlaceOrderTxBytes(ctx sdktypes.Context, app *app.App) [][]byte {
txs := make([][]byte, len(b.StatefulOrders))
for i, order := range b.StatefulOrders {
if !order.IsStatefulOrder() {
panic("Order should be stateful")
}
requestTxs := MustMakeCheckTxsWithClobMsg(
ctx,
app,
*clobtypes.NewMsgPlaceOrder(order),
)
txs[i] = requestTxs[0].Tx
}
return txs
}
// getProposedOperationsTxBytes iterates through the ShortTermOrdersAndOperations slice,
// signing every order and appending a short term order placement operation to the operations queue.
// Other elements in the list should be of type OperationRaw and will be appended to the operations queue as is.
// Transaction bytes for tx containing the MsgProposedOperations msg are returned.
func (b BlockAdvancement) getProposedOperationsTxBytes(ctx sdktypes.Context, app *app.App) []byte {
operationsQueue := make([]clobtypes.OperationRaw, len(b.ShortTermOrdersAndOperations))
for i, orderOrOperation := range b.ShortTermOrdersAndOperations {
switch castedValue := orderOrOperation.(type) {
case clobtypes.Order:
order := castedValue
requestTxs := MustMakeCheckTxsWithClobMsg(
ctx,
app,
*clobtypes.NewMsgPlaceOrder(order),
)
operationsQueue[i] = clobtypes.OperationRaw{
Operation: &clobtypes.OperationRaw_ShortTermOrderPlacement{
ShortTermOrderPlacement: requestTxs[0].Tx,
},
}
case clobtypes.OperationRaw:
operationsQueue[i] = castedValue
default:
panic("invalid type")
}
}
msgProposedOperations := &clobtypes.MsgProposedOperations{
OperationsQueue: operationsQueue,
}
return testtx.MustGetTxBytes(msgProposedOperations)
}
// Given genesis time, target block time and block time duration, return the estimated height
// at which the target block time is reached.
func EstimatedHeightForBlockTime(
genesisTime time.Time,
targetBlockTime time.Time,
blockTimeDuration time.Duration,
) uint32 {
return uint32(targetBlockTime.Sub(genesisTime) / blockTimeDuration)
}