Skip to content
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.

scenario: Add ability to create multiple orders in a single batch #784

Merged
merged 2 commits into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions integration-tests/rpc_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,9 @@ func runGetOrdersTest(t *testing.T, rpcEndpointPrefix, rpcServerType string, rpc
require.NoError(t, err)

// Create 10 new valid orders.
// TODO(albrow): Update this when scenario supports creating orders in a batch.
numOrders := 10
signedTestOrders := make([]*zeroex.SignedOrder, numOrders)
for i := 0; i < numOrders; i++ {
signedTestOrders[i] = scenario.NewSignedTestOrder(t, orderopts.SetupMakerState(true))
}
orderOptions := scenario.OptionsForAll(orderopts.SetupMakerState(true))
signedTestOrders := scenario.NewSignedTestOrdersBatch(t, numOrders, orderOptions)
jalextowle marked this conversation as resolved.
Show resolved Hide resolved
// Creating a valid order involves transferring sufficient funds to the maker, and setting their allowance for
// the maker asset. These transactions must be mined and Mesh's BlockWatcher poller must process these blocks
// in order for the order validation run at order submission to occur at a block number equal or higher then
Expand Down
66 changes: 66 additions & 0 deletions scenario/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,72 @@ func NewSignedTestOrder(t *testing.T, opts ...orderopts.Option) *zeroex.SignedOr
return signedOrder
}

// NewSignedTestOrdersBatch effeciently creates numOrders orders with independent options.
// If the options require setting up maker or taker state, that state will be set up effeciently
// with one transaction per address.
//
// optionsForIndex is a function which returns the options for creating the order at a specific
// index (between 0 and numOrders). For example, you can create ERC721 orders which each have a unique
// token ID. optionsForIndex can be nil to always use the default options. It can return nil to
// use the default options for an order at a specific index.
func NewSignedTestOrdersBatch(t *testing.T, numOrders int, optionsForIndex func(index int) []orderopts.Option) []*zeroex.SignedOrder {
allRequiredBalances := map[common.Address]*tokenBalances{}

allOrders := make([]*zeroex.SignedOrder, numOrders)
for i := 0; i < numOrders; i++ {
// Apply the options (if any) for the order we will create at this index.
cfg := defaultConfig()
if optionsForIndex != nil {
opts := optionsForIndex(i)
if opts != nil {
require.NoError(t, cfg.Apply(opts...))
}
}

// Create the order based on the cfg.
order := newTestOrder(cfg)
signedOrder, err := zeroex.SignTestOrder(order)
require.NoError(t, err, "could not sign order")
allOrders[i] = signedOrder

// Add maker and taker balances as needed to the set of required balances.
if cfg.SetupMakerState {
makerBalancesForThisOrder := requiredMakerBalances(t, signedOrder)
makerBalances, found := allRequiredBalances[signedOrder.MakerAddress]
if !found {
allRequiredBalances[order.MakerAddress] = makerBalancesForThisOrder
} else {
makerBalances.add(makerBalancesForThisOrder)
}
}
if cfg.SetupTakerAddress != constants.NullAddress {
takerBalancesForThisOrder := requiredTakerBalances(t, signedOrder)
takerBalances, found := allRequiredBalances[cfg.SetupTakerAddress]
if !found {
allRequiredBalances[cfg.SetupTakerAddress] = takerBalancesForThisOrder
} else {
takerBalances.add(takerBalancesForThisOrder)
}
}
}

// Setup all the required balances.
for traderAddress, requiredBalances := range allRequiredBalances {
setupBalanceAndAllowance(t, traderAddress, requiredBalances)
}

return allOrders
}

// OptionsForAll is a convenience function which can be used in combination with NewSignedTestOrdersBatch
// when you want all orders to be created with the same options. It returns a function which can be used
// as optionsForIndex which always returns the given options, regardless of the index.
func OptionsForAll(opts ...orderopts.Option) func(_ int) []orderopts.Option {
return func(_ int) []orderopts.Option {
return opts
}
}

type tokenBalances struct {
zrx *big.Int
weth *big.Int
Expand Down
56 changes: 27 additions & 29 deletions zeroex/orderwatch/order_watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -948,15 +948,17 @@ func TestOrderWatcherDecreaseExpirationTime(t *testing.T) {
blockWatcher, orderWatcher := setupOrderWatcher(ctx, t, ethRPCClient, meshDB)
orderWatcher.maxOrders = 20

// create and watch maxOrders orders
// TODO(albrow): Create orders in a single batch when scenario supports it.
for i := 0; i < orderWatcher.maxOrders; i++ {
expirationTime := time.Now().Add(10*time.Minute + time.Duration(i)*time.Minute)
// Create and watch maxOrders orders. Each order has a different expiration time.
optionsForIndex := func(index int) []orderopts.Option {
expirationTime := time.Now().Add(10*time.Minute + time.Duration(index)*time.Minute)
expirationTimeSeconds := big.NewInt(expirationTime.Unix())
signedOrder := scenario.NewSignedTestOrder(t,
return []orderopts.Option{
orderopts.SetupMakerState(true),
orderopts.ExpirationTimeSeconds(expirationTimeSeconds),
)
}
}
signedOrders := scenario.NewSignedTestOrdersBatch(t, orderWatcher.maxOrders, optionsForIndex)
for _, signedOrder := range signedOrders {
watchOrder(ctx, t, orderWatcher, blockWatcher, ethClient, signedOrder)
}

Expand Down Expand Up @@ -1021,12 +1023,11 @@ func TestOrderWatcherBatchEmitsAddedEvents(t *testing.T) {
orderEventsChan := make(chan []*zeroex.OrderEvent, 10)
orderWatcher.Subscribe(orderEventsChan)

// TODO(albrow): Create orders in a single batch when scenario supports it.
signedOrders := []*zeroex.SignedOrder{}
for i := 0; i < 2; i++ {
signedOrder := scenario.NewSignedTestOrder(t, orderopts.SetupMakerState(true))
signedOrders = append(signedOrders, signedOrder)
}
// Create numOrders test orders in a batch.
numOrders := 2
orderOptions := scenario.OptionsForAll(orderopts.SetupMakerState(true))
signedOrders := scenario.NewSignedTestOrdersBatch(t, numOrders, orderOptions)

// Creating a valid order involves transferring sufficient funds to the maker, and setting their allowance for
// the maker asset. These transactions must be mined and Mesh's BlockWatcher poller must process these blocks
// in order for the order validation run at order submission to occur at a block number equal or higher then
Expand All @@ -1042,15 +1043,15 @@ func TestOrderWatcherBatchEmitsAddedEvents(t *testing.T) {
require.NoError(t, err)

orderEvents := <-orderEventsChan
require.Len(t, orderEvents, 2)
require.Len(t, orderEvents, numOrders)
for _, orderEvent := range orderEvents {
assert.Equal(t, zeroex.ESOrderAdded, orderEvent.EndState)
}

var orders []*meshdb.Order
err = meshDB.Orders.FindAll(&orders)
require.NoError(t, err)
require.Len(t, orders, 2)
require.Len(t, orders, numOrders)
}

func TestOrderWatcherCleanup(t *testing.T) {
Expand All @@ -1069,10 +1070,11 @@ func TestOrderWatcherCleanup(t *testing.T) {
blockWatcher, orderWatcher := setupOrderWatcher(ctx, t, ethRPCClient, meshDB)

// Create and add two orders to OrderWatcher
// TODO(albrow): Create orders in a single batch when scenario supports it.
signedOrderOne := scenario.NewSignedTestOrder(t, orderopts.SetupMakerState(true))
orderOptions := scenario.OptionsForAll(orderopts.SetupMakerState(true))
signedOrders := scenario.NewSignedTestOrdersBatch(t, 2, orderOptions)
signedOrderOne := signedOrders[0]
watchOrder(ctx, t, orderWatcher, blockWatcher, ethClient, signedOrderOne)
signedOrderTwo := scenario.NewSignedTestOrder(t, orderopts.SetupMakerState(true))
signedOrderTwo := signedOrders[1]
watchOrder(ctx, t, orderWatcher, blockWatcher, ethClient, signedOrderTwo)
signedOrderOneHash, err := signedOrderTwo.ComputeOrderHash()
require.NoError(t, err)
Expand Down Expand Up @@ -1274,17 +1276,15 @@ func TestOrderWatcherHandleOrderExpirationsExpired(t *testing.T) {
}()

// Create and add an order (which will later become expired) to OrderWatcher
// TODO(albrow): Create orders in a single batch when scenario supports it.
expirationTime := time.Now().Add(24 * time.Hour)
expirationTimeSeconds := big.NewInt(expirationTime.Unix())
signedOrderOne := scenario.NewSignedTestOrder(t,
orderopts.SetupMakerState(true),
orderopts.ExpirationTimeSeconds(expirationTimeSeconds),
)
signedOrderTwo := scenario.NewSignedTestOrder(t,
orderOptions := scenario.OptionsForAll(
orderopts.SetupMakerState(true),
orderopts.ExpirationTimeSeconds(expirationTimeSeconds),
)
signedOrders := scenario.NewSignedTestOrdersBatch(t, 2, orderOptions)
signedOrderOne := signedOrders[0]
signedOrderTwo := signedOrders[1]
blockwatcher, orderWatcher := setupOrderWatcher(ctx, t, ethRPCClient, meshDB)
watchOrder(ctx, t, orderWatcher, blockwatcher, ethClient, signedOrderOne)
watchOrder(ctx, t, orderWatcher, blockwatcher, ethClient, signedOrderTwo)
Expand Down Expand Up @@ -1344,17 +1344,15 @@ func TestOrderWatcherHandleOrderExpirationsUnexpired(t *testing.T) {
}()

// Create and add an order (which will later become expired) to OrderWatcher
// TODO(albrow): Create orders in a single batch when scenario supports it.
expirationTime := time.Now().Add(24 * time.Hour)
expirationTimeSeconds := big.NewInt(expirationTime.Unix())
signedOrderOne := scenario.NewSignedTestOrder(t,
orderopts.SetupMakerState(true),
orderopts.ExpirationTimeSeconds(expirationTimeSeconds),
)
signedOrderTwo := scenario.NewSignedTestOrder(t,
orderOptions := scenario.OptionsForAll(
orderopts.SetupMakerState(true),
orderopts.ExpirationTimeSeconds(expirationTimeSeconds),
)
signedOrders := scenario.NewSignedTestOrdersBatch(t, 2, orderOptions)
signedOrderOne := signedOrders[0]
signedOrderTwo := signedOrders[1]
blockwatcher, orderWatcher := setupOrderWatcher(ctx, t, ethRPCClient, meshDB)
watchOrder(ctx, t, orderWatcher, blockwatcher, ethClient, signedOrderOne)
watchOrder(ctx, t, orderWatcher, blockwatcher, ethClient, signedOrderTwo)
Expand Down