diff --git a/ethereum/eth_watcher.go b/ethereum/eth_watcher.go index dc4c887d7..358a7c5c1 100644 --- a/ethereum/eth_watcher.go +++ b/ethereum/eth_watcher.go @@ -37,11 +37,11 @@ type ETHWatcher struct { addressToBalanceMu sync.Mutex wasStartedOnce bool mu sync.Mutex - rateLimiter *ratelimit.RateLimiter + rateLimiter ratelimit.IRateLimiter } // NewETHWatcher creates a new instance of ETHWatcher -func NewETHWatcher(minPollingInterval time.Duration, ethClient *ethclient.Client, chainID int, rateLimiter *ratelimit.RateLimiter) (*ETHWatcher, error) { +func NewETHWatcher(minPollingInterval time.Duration, ethClient *ethclient.Client, chainID int, rateLimiter ratelimit.IRateLimiter) (*ETHWatcher, error) { contractAddresses, err := GetContractAddressesForNetworkID(chainID) if err != nil { return nil, err @@ -57,7 +57,7 @@ func NewETHWatcher(minPollingInterval time.Duration, ethClient *ethclient.Client minPollingInterval: minPollingInterval, ethClient: ethClient, devUtils: devUtils, - rateLimiter: rateLimiter, + rateLimiter: rateLimiter, }, nil } diff --git a/ethereum/eth_watcher_test.go b/ethereum/eth_watcher_test.go index 9feb38815..6cef58894 100644 --- a/ethereum/eth_watcher_test.go +++ b/ethereum/eth_watcher_test.go @@ -14,6 +14,7 @@ import ( "time" "github.com/0xProject/0x-mesh/constants" + "github.com/0xProject/0x-mesh/ratelimit" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" @@ -53,13 +54,19 @@ var ethAccountToBalance = map[common.Address]*big.Int{ common.HexToAddress("0x91c987bf62d25945db517bdaa840a6c661374402"): hundredEth, } -var pollingInterval = 100 * time.Millisecond +var ( + pollingInterval = 100 * time.Millisecond + maxEthRPCRequestsPer24HrUTC = 1000000 + maxEthRPCRequestsPerSeconds = float64(1000.0) + defaultCheckpointInterval = 1 * time.Minute +) func TestAddingAddressesToETHWatcher(t *testing.T) { ethClient, err := ethclient.Dial(constants.GanacheEndpoint) require.NoError(t, err) - ethWatcher, err := NewETHWatcher(pollingInterval, ethClient, constants.TestChainID) + rateLimiter := ratelimit.NewFakeRateLimiter() + ethWatcher, err := NewETHWatcher(pollingInterval, ethClient, constants.TestChainID, rateLimiter) require.NoError(t, err) addresses := []common.Address{} @@ -87,7 +94,8 @@ func TestUpdateBalancesETHWatcher(t *testing.T) { ethClient, err := ethclient.Dial(constants.GanacheEndpoint) require.NoError(t, err) - ethWatcher, err := NewETHWatcher(pollingInterval, ethClient, constants.TestChainID) + rateLimiter := ratelimit.NewFakeRateLimiter() + ethWatcher, err := NewETHWatcher(pollingInterval, ethClient, constants.TestChainID, rateLimiter) require.NoError(t, err) addresses := []common.Address{} diff --git a/examples/go/add-order/main.go b/examples/go/add-order/main.go index 7e847e8a1..b85c6fddf 100644 --- a/examples/go/add-order/main.go +++ b/examples/go/add-order/main.go @@ -9,6 +9,7 @@ import ( "github.com/0xProject/0x-mesh/constants" "github.com/0xProject/0x-mesh/ethereum" + "github.com/0xProject/0x-mesh/ethereum/signer" "github.com/0xProject/0x-mesh/rpc" "github.com/0xProject/0x-mesh/zeroex" "github.com/ethereum/go-ethereum/common" @@ -57,7 +58,7 @@ func main() { log.WithError(err).Fatal("could not create Ethereum rpc client") } - signer := ethereum.NewEthRPCSigner(ethClient) + signer := signer.NewEthRPCSigner(ethClient) signedTestOrder, err := zeroex.SignOrder(signer, testOrder) if err != nil { log.WithError(err).Fatal("could not sign 0x order") diff --git a/ratelimit/fake_rate_limiter.go b/ratelimit/fake_rate_limiter.go new file mode 100644 index 000000000..3db7daac8 --- /dev/null +++ b/ratelimit/fake_rate_limiter.go @@ -0,0 +1,24 @@ +package ratelimit + +import ( + "context" + "time" +) + +// FakeRateLimiter is a fake RateLimiter that always allows a request through +type FakeRateLimiter struct{} + +// Wait blocks until the rateLimiter allows for another request to be sent +func (f *FakeRateLimiter) Wait(ctx context.Context) error { + return nil +} + +// Start starts the fake rateLimiter +func (f *FakeRateLimiter) Start(ctx context.Context, checkpointInterval time.Duration) error { + return nil +} + +// NewFakeRateLimiter returns a new FakeRateLimiter +func NewFakeRateLimiter() *FakeRateLimiter { + return &FakeRateLimiter{} +} diff --git a/ratelimit/rate_limiter.go b/ratelimit/rate_limiter.go index afdae9d08..11e781723 100644 --- a/ratelimit/rate_limiter.go +++ b/ratelimit/rate_limiter.go @@ -13,6 +13,12 @@ import ( "golang.org/x/time/rate" ) +// IRateLimiter is the interface one must satisfy to be considered a RateLimiter +type IRateLimiter interface { + Wait(ctx context.Context) error + Start(ctx context.Context, checkpointInterval time.Duration) error +} + // RateLimiter is a rate-limiter for requests type RateLimiter struct { maxRequestsPer24Hrs int diff --git a/ratelimit/rate_limiter_test.go b/ratelimit/rate_limiter_test.go index db431a099..15ea10f66 100644 --- a/ratelimit/rate_limiter_test.go +++ b/ratelimit/rate_limiter_test.go @@ -2,7 +2,6 @@ package ratelimit import ( "context" - "fmt" "math" "testing" "time" diff --git a/scenario/scenario.go b/scenario/scenario.go index 26cda23ce..5251efdc8 100644 --- a/scenario/scenario.go +++ b/scenario/scenario.go @@ -9,6 +9,7 @@ import ( "github.com/0xProject/0x-mesh/constants" "github.com/0xProject/0x-mesh/ethereum" + "github.com/0xProject/0x-mesh/ethereum/signer" "github.com/0xProject/0x-mesh/ethereum/wrappers" "github.com/0xProject/0x-mesh/zeroex" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -380,13 +381,13 @@ func CreateERC1155ForZRXSignedTestOrder(t *testing.T, ethClient *ethclient.Clien // GetTestSignerFn returns a test signer function that can be used to sign Ethereum transactions func GetTestSignerFn(signerAddress common.Address) func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { - return func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { - testSigner := ethereum.NewTestSigner() - signature, err := testSigner.(*ethereum.TestSigner).SignTx(signer.Hash(tx).Bytes(), signerAddress) + return func(s types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { + testSigner := signer.NewTestSigner() + signature, err := testSigner.(*signer.TestSigner).SignTx(s.Hash(tx).Bytes(), signerAddress) if err != nil { return nil, err } - return tx.WithSignature(signer, signature) + return tx.WithSignature(s, signature) } } diff --git a/zeroex/ordervalidator/order_validator.go b/zeroex/ordervalidator/order_validator.go index 52a61cb1e..34a01a248 100644 --- a/zeroex/ordervalidator/order_validator.go +++ b/zeroex/ordervalidator/order_validator.go @@ -240,11 +240,11 @@ type OrderValidator struct { cachedFeeRecipientToEndpoint map[common.Address]string contractAddresses ethereum.ContractAddresses expirationBuffer time.Duration - rateLimiter *ratelimit.RateLimiter + rateLimiter ratelimit.IRateLimiter } // New instantiates a new order validator -func New(ethClient *ethclient.Client, chainID int, maxRequestContentLength int, expirationBuffer time.Duration, rateLimiter *ratelimit.RateLimiter) (*OrderValidator, error) { +func New(ethClient *ethclient.Client, chainID int, maxRequestContentLength int, expirationBuffer time.Duration, rateLimiter ratelimit.IRateLimiter) (*OrderValidator, error) { contractAddresses, err := ethereum.GetContractAddressesForNetworkID(chainID) if err != nil { return nil, err diff --git a/zeroex/ordervalidator/order_validator_test.go b/zeroex/ordervalidator/order_validator_test.go index e3757500e..e4856b8fc 100644 --- a/zeroex/ordervalidator/order_validator_test.go +++ b/zeroex/ordervalidator/order_validator_test.go @@ -16,6 +16,8 @@ import ( "github.com/0xProject/0x-mesh/constants" "github.com/0xProject/0x-mesh/ethereum" + "github.com/0xProject/0x-mesh/ethereum/signer" + "github.com/0xProject/0x-mesh/ratelimit" "github.com/0xProject/0x-mesh/scenario" "github.com/0xProject/0x-mesh/zeroex" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -30,16 +32,22 @@ import ( const areNewOrders = false -var makerAddress = constants.GanacheAccount1 -var takerAddress = constants.GanacheAccount2 -var eighteenDecimalsInBaseUnits = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) -var wethAmount = new(big.Int).Mul(big.NewInt(50), eighteenDecimalsInBaseUnits) -var zrxAmount = new(big.Int).Mul(big.NewInt(100), eighteenDecimalsInBaseUnits) - -var unsupportedAssetData = common.Hex2Bytes("a2cb61b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064") -var malformedAssetData = []byte("9HJhsAAAAAAAAAAAAAAAAInSSmtMyxtvqiYl") -var malformedSignature = []byte("9HJhsAAAAAAAAAAAAAAAAInSSmtMyxtvqiYl") -var multiAssetAssetData = common.Hex2Bytes("94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000204a7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") +var ( + makerAddress = constants.GanacheAccount1 + takerAddress = constants.GanacheAccount2 + eighteenDecimalsInBaseUnits = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) + wethAmount = new(big.Int).Mul(big.NewInt(50), eighteenDecimalsInBaseUnits) + zrxAmount = new(big.Int).Mul(big.NewInt(100), eighteenDecimalsInBaseUnits) + + unsupportedAssetData = common.Hex2Bytes("a2cb61b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064") + malformedAssetData = []byte("9HJhsAAAAAAAAAAAAAAAAInSSmtMyxtvqiYl") + malformedSignature = []byte("9HJhsAAAAAAAAAAAAAAAAInSSmtMyxtvqiYl") + multiAssetAssetData = common.Hex2Bytes("94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000204a7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + + maxEthRPCRequestsPer24HrUTC = 1000000 + maxEthRPCRequestsPerSeconds = float64(1000.0) + defaultCheckpointInterval = 1 * time.Minute +) // Since these tests must be run sequentially, we don't want them to run as part of // the normal testing process. They will only be run if the "--serial" flag is used. @@ -145,7 +153,8 @@ func TestBatchValidateOffChainCases(t *testing.T) { &testCase.SignedOrder, } - orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0, rateLimiter) require.NoError(t, err) offchainValidOrders, rejectedOrderInfos := orderValidator.BatchOffchainValidation(signedOrders) @@ -172,7 +181,8 @@ func TestBatchValidateAValidOrder(t *testing.T) { signedOrder, } - orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0, rateLimiter) require.NoError(t, err) validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) @@ -197,7 +207,8 @@ func TestBatchValidateSignatureInvalid(t *testing.T) { ethClient := ethclient.NewClient(rpcClient) - orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0, rateLimiter) require.NoError(t, err) validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) @@ -222,7 +233,8 @@ func TestBatchValidateUnregisteredCoordinatorSoftCancels(t *testing.T) { ethClient := ethclient.NewClient(rpcClient) - orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0, rateLimiter) require.NoError(t, err) validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) @@ -249,7 +261,8 @@ func TestBatchValidateCoordinatorSoftCancels(t *testing.T) { } ethClient := ethclient.NewClient(rpcClient) - orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, constants.TestMaxContentLength, 0, rateLimiter) require.NoError(t, err) // generate a test server so we can capture and inspect the request @@ -265,13 +278,13 @@ func TestBatchValidateCoordinatorSoftCancels(t *testing.T) { opts := &bind.TransactOpts{ From: signedOrder.FeeRecipientAddress, Context: ctx, - Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { - testSigner := ethereum.NewTestSigner() - signature, err := testSigner.(*ethereum.TestSigner).SignTx(signer.Hash(tx).Bytes(), address) + Signer: func(s types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { + testSigner := signer.NewTestSigner() + signature, err := testSigner.(*signer.TestSigner).SignTx(s.Hash(tx).Bytes(), address) if err != nil { return nil, err } - return tx.WithSignature(signer, signature) + return tx.WithSignature(s, signature) }, } _, err = orderValidator.coordinatorRegistry.SetCoordinatorEndpoint(opts, testServer.URL) @@ -293,7 +306,8 @@ func TestComputeOptimalChunkSizesMaxContentLengthTooLow(t *testing.T) { ethClient := ethclient.NewClient(rpcClient) maxContentLength := singleOrderPayloadSize - 10 - orderValidator, err := New(ethClient, constants.TestChainID, maxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, maxContentLength, 0, rateLimiter) require.NoError(t, err) signedOrders := []*zeroex.SignedOrder{signedOrder} @@ -309,7 +323,8 @@ func TestComputeOptimalChunkSizes(t *testing.T) { ethClient := ethclient.NewClient(rpcClient) maxContentLength := singleOrderPayloadSize * 3 - orderValidator, err := New(ethClient, constants.TestChainID, maxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, maxContentLength, 0, rateLimiter) require.NoError(t, err) signedOrders := []*zeroex.SignedOrder{signedOrder, signedOrder, signedOrder, signedOrder} @@ -345,7 +360,8 @@ func TestComputeOptimalChunkSizesMultiAssetOrder(t *testing.T) { ethClient := ethclient.NewClient(rpcClient) maxContentLength := singleOrderPayloadSize * 3 - orderValidator, err := New(ethClient, constants.TestChainID, maxContentLength, 0) + rateLimiter := ratelimit.NewFakeRateLimiter() + orderValidator, err := New(ethClient, constants.TestChainID, maxContentLength, 0, rateLimiter) require.NoError(t, err) signedOrders := []*zeroex.SignedOrder{signedMultiAssetOrder, signedOrder, signedOrder, signedOrder, signedOrder}