From 94c4ecca152ba56d974f6226fe9c670b1a46d822 Mon Sep 17 00:00:00 2001 From: Alex Towle Date: Fri, 7 Aug 2020 18:31:32 -0500 Subject: [PATCH 1/2] Reject taker addresses that are nonzero and not on the whitelist --- CHANGELOG.md | 3 +- ethereum/contract_addresses.go | 127 ++++++++++++----------- scenario/orderopts/orderopts.go | 7 ++ zeroex/ordervalidator/order_validator.go | 4 + zeroex/orderwatch/order_watcher.go | 18 ++++ zeroex/orderwatch/order_watcher_test.go | 62 +++++++++++ 6 files changed, 160 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe3079f86..980abac85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,12 @@ This changelog is a work in progress and may contain notes for versions which have not actually been released. Check the [Releases](https://github.com/0xProject/0x-mesh/releases) page to see full release notes and more information about the latest released versions. -## v9.5.0 +## v10.0.0 ### Features ✅ - Upgraded to Go 1.14, which contains several WebAssembly performance improvements [#815](https://github.com/0xProject/0x-mesh/pull/815). +- Reject orders that have taker addresses that are non-zero and are not whitelisted. [#896](https://github.com/0xProject/0x-mesh/pull/896). ## v9.4.1 diff --git a/ethereum/contract_addresses.go b/ethereum/contract_addresses.go index 56861631b..a3dc05773 100644 --- a/ethereum/contract_addresses.go +++ b/ethereum/contract_addresses.go @@ -9,16 +9,18 @@ import ( // ContractAddresses maps a contract's name to it's Ethereum address type ContractAddresses struct { - ERC20Proxy common.Address `json:"erc20Proxy"` - ERC721Proxy common.Address `json:"erc721Proxy"` - ERC1155Proxy common.Address `json:"erc1155Proxy"` - Exchange common.Address `json:"exchange"` - DevUtils common.Address `json:"devUtils"` - WETH9 common.Address `json:"weth9"` - ZRXToken common.Address `json:"zrxToken"` - ChaiBridge common.Address `json:"chaiBridge"` - ChaiToken common.Address `json:"chaiToken"` - MaximumGasPrice common.Address `json:"maximumGasPrice"` + ERC20Proxy common.Address `json:"erc20Proxy"` + ERC721Proxy common.Address `json:"erc721Proxy"` + ERC1155Proxy common.Address `json:"erc1155Proxy"` + Exchange common.Address `json:"exchange"` + // TODO(jalextowle): This should be removed when 0x v4 is released. + ExchangeProxyFlashWallet common.Address `json:"exchangeProxyFlashWallet"` + DevUtils common.Address `json:"devUtils"` + WETH9 common.Address `json:"weth9"` + ZRXToken common.Address `json:"zrxToken"` + ChaiBridge common.Address `json:"chaiBridge"` + ChaiToken common.Address `json:"chaiToken"` + MaximumGasPrice common.Address `json:"maximumGasPrice"` } // GanacheAddresses The addresses that the 0x contracts were deployed to on the Ganache snapshot (chainID = 1337). @@ -29,55 +31,59 @@ func NewContractAddressesForChainID(chainID int) (ContractAddresses, error) { switch chainID { case 1: return ContractAddresses{ - ERC20Proxy: common.HexToAddress("0x95e6f48254609a6ee006f7d493c8e5fb97094cef"), - ERC721Proxy: common.HexToAddress("0xefc70a1b18c432bdc64b596838b4d138f6bc6cad"), - Exchange: common.HexToAddress("0x61935cbdd02287b511119ddb11aeb42f1593b7ef"), - ERC1155Proxy: common.HexToAddress("0x7eefbd48fd63d441ec7435d024ec7c5131019add"), - DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), - WETH9: common.HexToAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"), - ZRXToken: common.HexToAddress("0xe41d2489571d322189246dafa5ebde1f4699f498"), - ChaiBridge: common.HexToAddress("0x77c31eba23043b9a72d13470f3a3a311344d7438"), - ChaiToken: common.HexToAddress("0x06af07097c9eeb7fd685c692751d5c66db49c215"), - MaximumGasPrice: common.HexToAddress("0xe2bfd35306495d11e3c9db0d8de390cda24563cf"), + ERC20Proxy: common.HexToAddress("0x95e6f48254609a6ee006f7d493c8e5fb97094cef"), + ERC721Proxy: common.HexToAddress("0xefc70a1b18c432bdc64b596838b4d138f6bc6cad"), + ERC1155Proxy: common.HexToAddress("0x7eefbd48fd63d441ec7435d024ec7c5131019add"), + Exchange: common.HexToAddress("0x61935cbdd02287b511119ddb11aeb42f1593b7ef"), + ExchangeProxyFlashWallet: common.HexToAddress("0x22f9dcf4647084d6c31b2765f6910cd85c178c18"), + DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), + WETH9: common.HexToAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"), + ZRXToken: common.HexToAddress("0xe41d2489571d322189246dafa5ebde1f4699f498"), + ChaiBridge: common.HexToAddress("0x77c31eba23043b9a72d13470f3a3a311344d7438"), + ChaiToken: common.HexToAddress("0x06af07097c9eeb7fd685c692751d5c66db49c215"), + MaximumGasPrice: common.HexToAddress("0xe2bfd35306495d11e3c9db0d8de390cda24563cf"), }, nil case 3: return ContractAddresses{ - ERC20Proxy: common.HexToAddress("0xb1408f4c245a23c31b98d2c626777d4c0d766caa"), - ERC721Proxy: common.HexToAddress("0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4"), - Exchange: common.HexToAddress("0xfb2dd2a1366de37f7241c83d47da58fd503e2c64"), - ERC1155Proxy: common.HexToAddress("0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d"), - DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), - WETH9: common.HexToAddress("0xc778417e063141139fce010982780140aa0cd5ab"), - ZRXToken: common.HexToAddress("0xff67881f8d12f372d91baae9752eb3631ff0ed00"), - ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), - ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), - MaximumGasPrice: common.HexToAddress("0x407b4128e9ecad8769b2332312a9f655cb9f5f3a"), + ERC20Proxy: common.HexToAddress("0xb1408f4c245a23c31b98d2c626777d4c0d766caa"), + ERC721Proxy: common.HexToAddress("0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4"), + ERC1155Proxy: common.HexToAddress("0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d"), + Exchange: common.HexToAddress("0xfb2dd2a1366de37f7241c83d47da58fd503e2c64"), + ExchangeProxyFlashWallet: common.HexToAddress("0x22f9dcf4647084d6c31b2765f6910cd85c178c18"), + DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), + WETH9: common.HexToAddress("0xc778417e063141139fce010982780140aa0cd5ab"), + ZRXToken: common.HexToAddress("0xff67881f8d12f372d91baae9752eb3631ff0ed00"), + ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), + ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), + MaximumGasPrice: common.HexToAddress("0x407b4128e9ecad8769b2332312a9f655cb9f5f3a"), }, nil case 4: return ContractAddresses{ - ERC20Proxy: common.HexToAddress("0x2f5ae4f6106e89b4147651688a92256885c5f410"), - ERC721Proxy: common.HexToAddress("0x7656d773e11ff7383a14dcf09a9c50990481cd10"), - ERC1155Proxy: common.HexToAddress("0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d"), - Exchange: common.HexToAddress("0x198805e9682fceec29413059b68550f92868c129"), - DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), - WETH9: common.HexToAddress("0xc778417e063141139fce010982780140aa0cd5ab"), - ZRXToken: common.HexToAddress("0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa"), - ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), - ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), - MaximumGasPrice: common.HexToAddress("0x47697b44bd89051e93b4d5857ba8e024800a74ac"), + ERC20Proxy: common.HexToAddress("0x2f5ae4f6106e89b4147651688a92256885c5f410"), + ERC721Proxy: common.HexToAddress("0x7656d773e11ff7383a14dcf09a9c50990481cd10"), + ERC1155Proxy: common.HexToAddress("0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d"), + Exchange: common.HexToAddress("0x198805e9682fceec29413059b68550f92868c129"), + ExchangeProxyFlashWallet: common.HexToAddress("0x22f9dcf4647084d6c31b2765f6910cd85c178c18"), + DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), + WETH9: common.HexToAddress("0xc778417e063141139fce010982780140aa0cd5ab"), + ZRXToken: common.HexToAddress("0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa"), + ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), + ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), + MaximumGasPrice: common.HexToAddress("0x47697b44bd89051e93b4d5857ba8e024800a74ac"), }, nil case 42: return ContractAddresses{ - ERC20Proxy: common.HexToAddress("0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e"), - ERC721Proxy: common.HexToAddress("0x2a9127c745688a165106c11cd4d647d2220af821"), - Exchange: common.HexToAddress("0x4eacd0af335451709e1e7b570b8ea68edec8bc97"), - ERC1155Proxy: common.HexToAddress("0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f"), - DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), - WETH9: common.HexToAddress("0xd0a1e359811322d97991e03f863a0c30c2cf029c"), - ZRXToken: common.HexToAddress("0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa"), - ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), - ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), - MaximumGasPrice: common.HexToAddress("0x67a094cf028221ffdd93fc658f963151d05e2a74"), + ERC20Proxy: common.HexToAddress("0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e"), + ERC721Proxy: common.HexToAddress("0x2a9127c745688a165106c11cd4d647d2220af821"), + ERC1155Proxy: common.HexToAddress("0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f"), + Exchange: common.HexToAddress("0x4eacd0af335451709e1e7b570b8ea68edec8bc97"), + ExchangeProxyFlashWallet: common.HexToAddress("0x22f9dcf4647084d6c31b2765f6910cd85c178c18"), + DevUtils: common.HexToAddress("0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6"), + WETH9: common.HexToAddress("0xd0a1e359811322d97991e03f863a0c30c2cf029c"), + ZRXToken: common.HexToAddress("0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa"), + ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), + ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), + MaximumGasPrice: common.HexToAddress("0x67a094cf028221ffdd93fc658f963151d05e2a74"), }, nil case 1337: return ganacheAddresses(), nil @@ -112,15 +118,16 @@ func ValidateContractAddressesForChainID(chainID int, addresses ContractAddresse // function allows these addresses to only be defined in one place. func ganacheAddresses() ContractAddresses { return ContractAddresses{ - ERC20Proxy: common.HexToAddress("0x1dc4c1cefef38a777b15aa20260a54e584b16c48"), - ERC721Proxy: common.HexToAddress("0x1d7022f5b17d2f8b695918fb48fa1089c9f85401"), - ERC1155Proxy: common.HexToAddress("0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8"), - Exchange: common.HexToAddress("0x48bacb9266a570d521063ef5dd96e61686dbe788"), - DevUtils: common.HexToAddress("0xb23672f74749bf7916ba6827c64111a4d6de7f11"), - WETH9: common.HexToAddress("0x0b1ba0af832d7c05fd64161e0db78e85978e8082"), - ZRXToken: common.HexToAddress("0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"), - ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), - ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), - MaximumGasPrice: common.HexToAddress("0x2c530e4ecc573f11bd72cf5fdf580d134d25f15f"), + ERC20Proxy: common.HexToAddress("0x1dc4c1cefef38a777b15aa20260a54e584b16c48"), + ERC721Proxy: common.HexToAddress("0x1d7022f5b17d2f8b695918fb48fa1089c9f85401"), + ERC1155Proxy: common.HexToAddress("0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8"), + Exchange: common.HexToAddress("0x48bacb9266a570d521063ef5dd96e61686dbe788"), + ExchangeProxyFlashWallet: common.HexToAddress("0x22f9dcf4647084d6c31b2765f6910cd85c178c18"), + DevUtils: common.HexToAddress("0xb23672f74749bf7916ba6827c64111a4d6de7f11"), + WETH9: common.HexToAddress("0x0b1ba0af832d7c05fd64161e0db78e85978e8082"), + ZRXToken: common.HexToAddress("0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"), + ChaiBridge: common.HexToAddress("0x0000000000000000000000000000000000000000"), + ChaiToken: common.HexToAddress("0x0000000000000000000000000000000000000000"), + MaximumGasPrice: common.HexToAddress("0x2c530e4ecc573f11bd72cf5fdf580d134d25f15f"), } } diff --git a/scenario/orderopts/orderopts.go b/scenario/orderopts/orderopts.go index 04b6a6d8e..53e3d3c22 100644 --- a/scenario/orderopts/orderopts.go +++ b/scenario/orderopts/orderopts.go @@ -92,6 +92,13 @@ func SenderAddress(address common.Address) Option { } } +func TakerAddress(address common.Address) Option { + return func(cfg *Config) error { + cfg.Order.TakerAddress = address + return nil + } +} + func FeeRecipientAddress(address common.Address) Option { return func(cfg *Config) error { cfg.Order.FeeRecipientAddress = address diff --git a/zeroex/ordervalidator/order_validator.go b/zeroex/ordervalidator/order_validator.go index 1e3024b86..f68aafd6b 100644 --- a/zeroex/ordervalidator/order_validator.go +++ b/zeroex/ordervalidator/order_validator.go @@ -170,6 +170,10 @@ var ( Code: "DatabaseFullOfOrders", Message: "database is full of pinned orders and no orders can be deleted to make space (consider increasing MAX_ORDERS_IN_STORAGE)", } + ROTakerAddressNotAllowed = RejectedOrderStatus{ + Code: "TakerAddressNotAllowed", + Message: "the taker address is not a whitelisted address", + } ) // ROInvalidSchemaCode is the RejectedOrderStatus emitted if an order doesn't conform to the order schema diff --git a/zeroex/orderwatch/order_watcher.go b/zeroex/orderwatch/order_watcher.go index 696a0d145..b0c01d12a 100644 --- a/zeroex/orderwatch/order_watcher.go +++ b/zeroex/orderwatch/order_watcher.go @@ -132,6 +132,7 @@ func New(config Config) (*Watcher, error) { blockEventsChan: make(chan []*blockwatch.Event, 100), atLeastOneBlockProcessed: make(chan struct{}), didProcessABlock: false, + recentlyValidatedOrders: []*types.OrderWithMetadata{}, } // Pre-populate the OrderWatcher with all orders already stored in the DB @@ -1670,6 +1671,23 @@ func (w *Watcher) meshSpecificOrderValidation(orders []*zeroex.SignedOrder, chai }) continue } + // NOTE(jalextowle): Orders with a taker address are only accessible to + // one taker, which complicates more sophisticated pruning technology. + // With this in mind, we only allow whitelisted taker addresses to be + // propogated throughout the network. This whitelist should only include + // addresses that correspond to contracts allow anyone to fill these + // orders. + // TODO(jalextowle): If any other addresses are whitelisted, create + // a isTakerAddressWhitelisted function. + if order.TakerAddress != constants.NullAddress && order.TakerAddress != w.contractAddresses.ExchangeProxyFlashWallet { + results.Rejected = append(results.Rejected, &ordervalidator.RejectedOrderInfo{ + OrderHash: orderHash, + SignedOrder: order, + Kind: ordervalidator.MeshValidation, + Status: ordervalidator.ROTakerAddressNotAllowed, + }) + continue + } if order.ChainID.Cmp(big.NewInt(int64(chainID))) != 0 { results.Rejected = append(results.Rejected, &ordervalidator.RejectedOrderInfo{ OrderHash: orderHash, diff --git a/zeroex/orderwatch/order_watcher_test.go b/zeroex/orderwatch/order_watcher_test.go index 0f92e4cea..67345d794 100644 --- a/zeroex/orderwatch/order_watcher_test.go +++ b/zeroex/orderwatch/order_watcher_test.go @@ -118,6 +118,68 @@ func init() { } } +func TestOrderWatcherTakerWhitelist(t *testing.T) { + teardownSubTest := setupSubTest(t) + defer teardownSubTest(t) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + database, err := db.New(ctx, db.TestOptions()) + require.NoError(t, err) + + blockWatcher, orderWatcher := setupOrderWatcher(ctx, t, ethRPCClient, database) + require.NoError(t, err) + + testCases := []*struct { + order *zeroex.SignedOrder + isTakerAddressWhitelisted bool + }{ + { + scenario.NewSignedTestOrder(t, orderopts.SetupMakerState(true)), + true, + }, + { + scenario.NewSignedTestOrder( + t, + orderopts.SetupMakerState(true), + orderopts.TakerAddress(ganacheAddresses.ExchangeProxyFlashWallet), + ), + true, + }, + { + scenario.NewSignedTestOrder( + t, + orderopts.SetupMakerState(true), + orderopts.TakerAddress(common.HexToAddress("0x1")), + ), + false, + }, + } + + err = blockWatcher.SyncToLatestBlock() + require.NoError(t, err) + + for _, testCase := range testCases { + results, err := orderWatcher.ValidateAndStoreValidOrders(ctx, []*zeroex.SignedOrder{testCase.order}, false, constants.TestChainID) + require.NoError(t, err) + if testCase.isTakerAddressWhitelisted { + orderHash, err := testCase.order.ComputeOrderHash() + require.NoError(t, err) + assert.Len(t, results.Rejected, 0) + require.Len(t, results.Accepted, 1) + assert.Equal(t, results.Accepted[0].OrderHash, orderHash) + } else { + orderHash, err := testCase.order.ComputeOrderHash() + require.NoError(t, err) + assert.Len(t, results.Accepted, 0) + require.Len(t, results.Rejected, 1) + assert.Equal(t, results.Rejected[0].OrderHash, orderHash) + assert.Equal(t, results.Rejected[0].Kind, ordervalidator.MeshValidation) + assert.Equal(t, results.Rejected[0].Status, ordervalidator.ROTakerAddressNotAllowed) + } + } +} + func TestOrderWatcherStoresValidOrders(t *testing.T) { if !serialTestsEnabled { t.Skip("Serial tests (tests which cannot run in parallel) are disabled. You can enable them with the --serial flag") From 1e0e624c1885b7aa5d3811ce0984ae6f6a2fb20c Mon Sep 17 00:00:00 2001 From: Alex Towle Date: Fri, 7 Aug 2020 18:43:15 -0500 Subject: [PATCH 2/2] Addressed review feedback --- zeroex/orderwatch/order_watcher.go | 1 - 1 file changed, 1 deletion(-) diff --git a/zeroex/orderwatch/order_watcher.go b/zeroex/orderwatch/order_watcher.go index b0c01d12a..da6ae283c 100644 --- a/zeroex/orderwatch/order_watcher.go +++ b/zeroex/orderwatch/order_watcher.go @@ -132,7 +132,6 @@ func New(config Config) (*Watcher, error) { blockEventsChan: make(chan []*blockwatch.Event, 100), atLeastOneBlockProcessed: make(chan struct{}), didProcessABlock: false, - recentlyValidatedOrders: []*types.OrderWithMetadata{}, } // Pre-populate the OrderWatcher with all orders already stored in the DB