├── .circleci └── config.yml ├── .drone.yml ├── .gitattributes ├── .gitbook.yaml ├── .github └── dependabot.yml ├── .gitignore ├── .golangci.yml ├── .graphqlconfig ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── GO_LICENSE ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── cut-release │ └── main.go ├── mesh-bootstrap │ ├── main.go │ └── storage.go ├── mesh-bridge │ └── main.go ├── mesh-keygen │ └── main.go ├── mesh │ ├── graphql_server.go │ └── main.go └── peer-id-to-pub-key │ ├── README.md │ └── main.go ├── common └── types │ ├── types.go │ └── types_js.go ├── constants └── constants.go ├── core ├── core.go ├── core_test.go ├── eth_rpc_chain_id.go ├── message_handler.go ├── message_handler_v4.go ├── new_db.go ├── new_db_js.go ├── ordersync │ ├── ordersync.go │ ├── ordersync_test.go │ └── peer_score.go ├── ordersync_subprotocols.go ├── ordersync_v4 │ ├── ordersync_v4.go │ └── peer_score.go └── peer_score.go ├── db ├── common.go ├── common_v4.go ├── db_bench_test.go ├── db_test.go ├── db_v4_test.go ├── dexie_datastore.go ├── dexie_implementation.go ├── dexietypes │ ├── dexietypes.go │ └── dexietypes_test.go ├── kv_store_queries.go ├── sql_implementation.go ├── sql_queries.go ├── sql_v4_implementation.go ├── sql_v4_queries.go ├── sqltypes │ └── sqltypes.go └── types_js.go ├── dockerfiles ├── mesh-bootstrap │ └── Dockerfile ├── mesh-bridge │ └── Dockerfile ├── mesh-fluent-bit │ ├── Dockerfile │ ├── fluent-bit.conf │ └── parsers.conf └── mesh │ └── Dockerfile ├── docs ├── browser-bindings │ ├── browser-lite │ │ ├── README.md │ │ └── reference.md │ └── browser │ │ ├── README.md │ │ └── reference.md ├── browser.md ├── custom_order_filters.md ├── db_syncing.md ├── deployment.md ├── deployment_with_telemetry.md ├── graphql-client │ ├── README.md │ └── reference.md ├── graphql_api.md ├── json_rpc_clients │ └── typescript │ │ ├── README.md │ │ └── reference.md └── summary.md ├── encoding └── encoding.go ├── ethereum ├── blockchain_lifecycle.go ├── blockwatch │ ├── block_watcher.go │ ├── block_watcher_test.go │ ├── client.go │ ├── fake_client.go │ ├── fake_log_client.go │ └── testdata │ │ ├── fake_client_basic_fixture.json │ │ ├── fake_client_block_poller_fixtures.json │ │ ├── fake_client_fast_sync_fixture.json │ │ └── fake_client_reset_fixture.json ├── contract_addresses.go ├── ethrpcclient │ └── eth_rpc_client.go ├── ratelimit │ ├── fake_rate_limiter.go │ ├── rate_limiter.go │ └── rate_limiter_test.go ├── signer │ ├── sign.go │ └── sign_test.go ├── simplestack │ ├── simple_stack.go │ └── simple_stack_test.go └── wrappers │ ├── dev_utils.go │ ├── dummy_erc721_token.go │ ├── erc1155_mintable.go │ ├── exchange.go │ ├── exhange_v4.go │ ├── weth9.go │ └── zrx_token.go ├── go.mod ├── go.sum ├── gqlgen.yml ├── graphql ├── client │ ├── client.go │ ├── client_v4.go │ ├── conversions.go │ ├── conversions_v4.go │ └── types.go ├── constants.go ├── generated │ └── generated.go ├── gqltypes │ ├── conversion_v4.go │ ├── conversions.go │ ├── types_generated.go │ └── utils.go ├── resolver.go ├── schema.graphql └── schema.resolvers.go ├── integration-tests ├── browser_integration_test.go ├── constants.go ├── data │ ├── bootstrap-0 │ │ └── keys │ │ │ └── privkey │ └── standalone-0 │ │ └── keys │ │ └── privkey ├── graphql_integration_test.go ├── graphql_v4_integration_test.go └── utils.go ├── issue_template.md ├── keys ├── fs.go ├── fs_js.go ├── keys.go └── keys_test.go ├── loghooks ├── key_suffix_hook.go ├── key_suffix_hook_test.go └── peer_id_hook.go ├── metrics └── metrics.go ├── orderfilter ├── filter.go ├── filter_bench_test.go ├── filter_js.go ├── filter_test.go ├── schemas.go ├── shared.go ├── validate.go ├── validate_js.go ├── validate_js_test.go └── validate_test.go ├── p2p ├── bandwidth_banning_test.go ├── banner │ ├── banner.go │ ├── banner_test.go │ └── violations_tracker.go ├── bootstrap.go ├── message.go ├── node.go ├── node_test.go ├── node_v4.go ├── notifee.go ├── opts.go ├── opts_js.go ├── ratevalidator │ ├── tracking_rate_limiter.go │ ├── validator.go │ └── validator_test.go └── validatorset │ ├── set.go │ └── set_test.go ├── package.json ├── packages ├── mesh-browser-lite │ ├── README.md │ ├── package.json │ ├── src │ │ ├── database.ts │ │ ├── datastore.ts │ │ ├── index.ts │ │ ├── mesh.ts │ │ ├── schema_validator.ts │ │ ├── types.ts │ │ ├── wasm_exec.js │ │ └── wrapper_conversion.ts │ ├── tsconfig.json │ └── tslint.json ├── mesh-browser-shim │ ├── package.json │ ├── src │ │ └── browser_shim.ts │ ├── tsconfig.json │ ├── tslint.json │ └── webpack.config.js ├── mesh-browser │ ├── README.md │ ├── conversion-tests │ │ └── conversion_test.ts │ ├── dist │ │ └── index.html │ ├── go │ │ ├── browserutil │ │ │ └── browserutil.go │ │ ├── conversion-test │ │ │ ├── conversion_test.go │ │ │ └── main.go │ │ ├── jsutil │ │ │ ├── jsutil.go │ │ │ └── jsutil_bench_test.go │ │ ├── mesh-browser │ │ │ └── main.go │ │ └── providerwrapper │ │ │ └── providerwrapper.go │ ├── package.json │ ├── scripts │ │ └── generate_wasm_buffer.go │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── tslint.json │ ├── webpack.config.js │ └── webpack.tsconfig.json ├── mesh-graphql-client │ ├── README.md │ ├── apollo.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── queries.ts │ │ └── types.ts │ ├── test │ │ ├── graphql_client_test.ts │ │ └── utils │ │ │ ├── chai_setup.ts │ │ │ └── graphql_server.ts │ ├── tsconfig.json │ └── tslint.json ├── mesh-integration-tests │ ├── graphql-dist │ │ └── index.html │ ├── graphql │ │ └── index.ts │ ├── legacy-dist │ │ └── index.html │ ├── legacy │ │ └── index.ts │ ├── package.json │ ├── tsconfig.json │ ├── tslint.json │ ├── webpack.graphql.config.js │ └── webpack.legacy.config.js ├── mesh-webpack-example-lite │ ├── README.md │ ├── dist │ │ └── index.html │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── tslint.json │ └── webpack.config.js └── mesh-webpack-example │ ├── README.md │ ├── dist │ └── index.html │ ├── package.json │ ├── src │ └── index.ts │ ├── tsconfig.json │ ├── tslint.json │ └── webpack.config.js ├── pull_request_template.md ├── scenario ├── orderopts │ └── orderopts.go ├── scenario.go └── scenario_v4.go ├── tsconfig-base.json ├── tsconfig.json ├── yarn.lock └── zeroex ├── asset_data_decoder.go ├── asset_data_decoder_test.go ├── order.go ├── order_js.go ├── order_test.go ├── order_v4.go ├── order_v4_test.go ├── ordervalidator ├── order_validator.go ├── order_validator_bench_test.go ├── order_validator_js.go ├── order_validator_test.go ├── order_validator_v4.go └── order_validator_v4_test.go ├── orderwatch ├── decoder │ ├── event_decoder.go │ ├── event_decoder_js.go │ ├── event_decoder_test.go │ └── event_decoder_v4.go ├── order_watcher.go ├── order_watcher_test.go ├── order_watcher_utils.go ├── order_watcher_v4.go ├── order_watcher_v4_test.go └── topics.go └── utils.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | environment: 5 | BASH_ENV: ~/.nvm/nvm.sh 6 | docker: 7 | - image: circleci/golang:1.15-browsers 8 | - image: 0xorg/ganache-cli:istanbul 9 | environment: 10 | VERSION: 6.5.11 11 | SNAPSHOT_NAME: 0x_ganache_snapshot 12 | resource_class: large 13 | steps: 14 | - checkout 15 | - run: 16 | name: Install nvm 17 | command: curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash 18 | - run: 19 | name: Configure nvm 20 | command: export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 21 | - run: 22 | name: Install Node v11 23 | command: nvm install 11 24 | - run: node --version 25 | - run: 26 | name: Install yarn 27 | command: npm i -g yarn@1.17 28 | - run: 29 | name: Install dependencies 30 | command: make deps-no-lockfile 31 | - run: 32 | name: Install Go linter 33 | command: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0 34 | - run: 35 | # NOTE(jalextowle): We previously vendored our dependencies to avoid 36 | # this issue: https://github.com/golangci/golangci-lint/issues/825. 37 | # Vendoring caused an issue where the output on CI was not the same as 38 | # that locally, so it has been removed. This should be reevaluated if 39 | # the issue reappears. 40 | name: Run linters 41 | command: make lint 42 | - run: 43 | name: Build the TypeScript monorepo 44 | command: yarn build 45 | - run: 46 | name: Run all tests 47 | command: make test-all 48 | - run: 49 | name: Test installing Mesh without CGO 50 | command: CGO_ENABLED=0 go install ./... 51 | - run: 52 | name: Run cut-release script to test it still works 53 | command: VERSION=100.0.0 make cut-release 54 | -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | type: kubernetes 3 | name: mesh 4 | 5 | steps: 6 | - name: mesh-autotag 7 | image: plugins/docker 8 | settings: 9 | repo: 0xorg/mesh 10 | auto_tag: true 11 | username: 12 | from_secret: docker_username 13 | password: 14 | from_secret: docker_password 15 | dockerfile: dockerfiles/mesh/Dockerfile 16 | node_selector: 17 | drone-builds: true 18 | --- 19 | kind: pipeline 20 | type: kubernetes 21 | name: mesh-bootstrap-autotag 22 | 23 | steps: 24 | - name: mesh-bootstrap-autotag 25 | image: plugins/docker 26 | settings: 27 | repo: 0xorg/mesh-bootstrap 28 | auto_tag: true 29 | username: 30 | from_secret: docker_username 31 | password: 32 | from_secret: docker_password 33 | dockerfile: dockerfiles/mesh-bootstrap/Dockerfile 34 | node_selector: 35 | drone-builds: true 36 | --- 37 | kind: pipeline 38 | type: kubernetes 39 | name: mesh-development 40 | 41 | steps: 42 | - name: mesh-dev 43 | image: plugins/docker 44 | settings: 45 | repo: 0xorg/mesh 46 | tags: 47 | - development 48 | username: 49 | from_secret: docker_username 50 | password: 51 | from_secret: docker_password 52 | dockerfile: dockerfiles/mesh/Dockerfile 53 | trigger: 54 | branch: 55 | - development 56 | event: 57 | include: 58 | - push 59 | node_selector: 60 | drone-builds: true 61 | --- 62 | kind: pipeline 63 | type: kubernetes 64 | name: mesh-bootstrap-development 65 | 66 | steps: 67 | - name: mesh-bootstrap-dev 68 | image: plugins/docker 69 | settings: 70 | repo: 0xorg/mesh-bootstrap 71 | tags: 72 | - development 73 | username: 74 | from_secret: docker_username 75 | password: 76 | from_secret: docker_password 77 | dockerfile: dockerfiles/mesh-bootstrap/Dockerfile 78 | trigger: 79 | branch: 80 | - development 81 | event: 82 | include: 83 | - push 84 | node_selector: 85 | drone-builds: true 86 | --- 87 | kind: pipeline 88 | type: kubernetes 89 | name: mesh-publish-release-notes 90 | 91 | steps: 92 | - name: publish 93 | image: plugins/github-release 94 | settings: 95 | api_key: 96 | from_secret: github_public_repo 97 | files: 98 | note: RELEASE_CHANGELOG.md 99 | when: 100 | event: tag 101 | ref: 102 | - refs/tags/v* 103 | trigger: 104 | event: 105 | include: 106 | - tag 107 | node_selector: 108 | drone-builds: true 109 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically collapse generated files in GitHub. 2 | ethereum/wrappers/*.go linguist-generated=true 3 | docs/graphql-clients/typescript/*.md linguist-generated=true 4 | 5 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs/ 2 | 3 | structure: 4 | readme: ../README.md 5 | summary: ./summary.md 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | target-branch: "development" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor/ 3 | node_modules/ 4 | TODO.md 5 | 0x_mesh/ 6 | examples/javascript_websocket_client/node_modules 7 | examples/javascript_websocket_client/lib 8 | *.wasm 9 | */**/tsconfig.tsbuildinfo 10 | **/lib/**/* 11 | yarn-error.log 12 | */**/yarn-error.log* 13 | ganache.log 14 | */**/ganache.log* 15 | packages/**/generated/** 16 | */**/*dist/**/* 17 | !*/**/*dist/index.html 18 | !integration-tests/data/standalone-0/keys/privKey 19 | integration-tests/data/standalone-*/ 20 | integration-tests/data/bootstrap-0/p2p/ 21 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: false 3 | enable: 4 | # Finds repeated strings that could be made constants 5 | - goconst 6 | # Ensures that the code was formatted with `gofmt -s` 7 | - gofmt 8 | # Identifies commonly misspelled words 9 | - misspell 10 | # Identifies unused function parameters 11 | - unparam 12 | -------------------------------------------------------------------------------- /.graphqlconfig: -------------------------------------------------------------------------------- 1 | { 2 | "schemaPath": "graphql/schema.graphql" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | lib 2 | ethereum/blockwatch/testdata 3 | packages/**/generated/** 4 | *.d.ts 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 120, 4 | "trailingComma": "all", 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /GO_LICENSE: -------------------------------------------------------------------------------- 1 | Note: this license covers code copied and/or modified from the Go source 2 | code. Such code is indicated by a comment at the top of the file. 3 | 4 | Copyright (c) 2009 The Go Authors. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following disclaimer 14 | in the documentation and/or other materials provided with the 15 | distribution. 16 | * Neither the name of Google Inc. nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 ZeroEx Intl. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /cmd/mesh-bootstrap/storage.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/0xProject/0x-mesh/keys" 10 | p2pcrypto "github.com/libp2p/go-libp2p-core/crypto" 11 | log "github.com/sirupsen/logrus" 12 | 13 | _ "github.com/lib/pq" // postgres driver 14 | ) 15 | 16 | func getPrivateKeyPath(config Config) string { 17 | return filepath.Join(config.LevelDBDataDir, "keys", "privkey") 18 | } 19 | 20 | func getDHTDir(config Config) string { 21 | return filepath.Join(config.LevelDBDataDir, "p2p", "dht") 22 | } 23 | 24 | func getPeerstoreDir(config Config) string { 25 | return filepath.Join(config.LevelDBDataDir, "p2p", "peerstore") 26 | } 27 | 28 | func initPrivateKey(path string) (p2pcrypto.PrivKey, error) { 29 | privKey, err := keys.GetPrivateKeyFromPath(path) 30 | if err == nil { 31 | return privKey, nil 32 | } else if os.IsNotExist(err) { 33 | // If the private key doesn't exist, generate one. 34 | log.Info("No private key found. Generating a new one.") 35 | return keys.GenerateAndSavePrivateKey(path) 36 | } 37 | 38 | // For any other type of error, return it. 39 | return nil, err 40 | } 41 | -------------------------------------------------------------------------------- /cmd/mesh-bridge/main.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | // mesh-bridge is a short program that bridges two Mesh nodes. This is useful in cases where 4 | // we introduce a network-level breaking change but still want the liquidity from one network 5 | //to flow to another 6 | package main 7 | 8 | // Note(albrow): If we want to use mesh-bridge in the future we will need to update it to 9 | // use the new GraphQL API. Considered low-priority as of the time of writing this comment. 10 | 11 | // import ( 12 | // "context" 13 | // "time" 14 | 15 | // "github.com/0xProject/0x-mesh/rpc" 16 | // "github.com/0xProject/0x-mesh/zeroex" 17 | // ethrpc "github.com/ethereum/go-ethereum/rpc" 18 | // "github.com/plaid/go-envvar/envvar" 19 | // log "github.com/sirupsen/logrus" 20 | // ) 21 | 22 | // const ( 23 | // firstWSRPCAddressLabel = "FirstWSRPCAddress" 24 | // secondWSRPCAddressLabel = "SecondWSRPCAddress" 25 | // maxReceiveBatch = 100 26 | // receiveTimeout = 2 * time.Second 27 | // tenThousand = 10000 28 | // ) 29 | 30 | // type clientEnvVars struct { 31 | // FirstWSRPCAddress string `envvar:"FIRST_WS_RPC_ADDRESS"` 32 | // SecondWSRPCAddress string `envvar:"SECOND_WS_RPC_ADDRESS"` 33 | // Verbosity int `envvar:"VERBOSITY"` 34 | // } 35 | 36 | func main() { 37 | // env := clientEnvVars{} 38 | // if err := envvar.Parse(&env); err != nil { 39 | // panic(err) 40 | // } 41 | 42 | // log.SetLevel(log.Level(env.Verbosity)) 43 | 44 | // firstClient, err := rpc.NewClient(env.FirstWSRPCAddress) 45 | // if err != nil { 46 | // log.WithError(err).Fatal("could not create client") 47 | // } 48 | // stats, err := firstClient.GetStats() 49 | // if err != nil { 50 | // log.Fatal(err) 51 | // } 52 | // log.WithField("stats", stats).Info("Spun up first client") 53 | 54 | // secondClient, err := rpc.NewClient(env.SecondWSRPCAddress) 55 | // if err != nil { 56 | // log.WithError(err).Fatal("could not create client") 57 | // } 58 | // stats, err = secondClient.GetStats() 59 | // if err != nil { 60 | // log.Fatal(err) 61 | // } 62 | // log.WithField("stats", stats).Info("Spun up second client") 63 | 64 | // go func() { 65 | // pipeOrders(secondClient, firstClient, secondWSRPCAddressLabel, firstWSRPCAddressLabel) 66 | // }() 67 | // pipeOrders(firstClient, secondClient, firstWSRPCAddressLabel, secondWSRPCAddressLabel) 68 | } 69 | 70 | // func pipeOrders(inClient, outClient *rpc.Client, inLabel, outLabel string) { 71 | // ctx, cancel := context.WithCancel(context.Background()) 72 | // defer cancel() 73 | // orderEventsChan := make(chan []*zeroex.OrderEvent, tenThousand) 74 | // clientSubscription, err := inClient.SubscribeToOrders(ctx, orderEventsChan) 75 | // if err != nil { 76 | // log.WithError(err).Fatal("Couldn't set up OrderStream subscription") 77 | // } 78 | // defer clientSubscription.Unsubscribe() 79 | // for { 80 | // incomingSignedOrders, err := receiveBatch(orderEventsChan, clientSubscription, inLabel, outLabel) 81 | // if err != nil { 82 | // log.Fatal(err) 83 | // } 84 | // if len(incomingSignedOrders) == 0 { 85 | // continue 86 | // } 87 | // validationResults, err := outClient.AddOrders(incomingSignedOrders) 88 | // if err != nil { 89 | // log.Fatal(err) 90 | // } 91 | // log.WithFields(log.Fields{ 92 | // "from": inLabel, 93 | // "to": outLabel, 94 | // "numSent": len(incomingSignedOrders), 95 | // "numAccepted": len(validationResults.Accepted), 96 | // "numRejected": len(validationResults.Rejected), 97 | // }).Info("Finished bridging orders") 98 | // } 99 | // } 100 | 101 | // func receiveBatch(inChan chan []*zeroex.OrderEvent, subscription *ethrpc.ClientSubscription, inLabel, outLabel string) ([]*zeroex.SignedOrder, error) { 102 | // signedOrders := []*zeroex.SignedOrder{} 103 | // timeoutChan := time.After(receiveTimeout) 104 | // for { 105 | // if len(signedOrders) >= maxReceiveBatch { 106 | // return signedOrders, nil 107 | // } 108 | // select { 109 | // case <-timeoutChan: 110 | // return signedOrders, nil 111 | // case orderEvents := <-inChan: 112 | // for _, orderEvent := range orderEvents { 113 | // if orderEvent.EndState != zeroex.ESOrderAdded { 114 | // continue 115 | // } 116 | // log.WithFields(log.Fields{ 117 | // "from": inLabel, 118 | // "to": outLabel, 119 | // "orderHash": orderEvent.OrderHash.Hex(), 120 | // }).Info("Found new order over bridge") 121 | // signedOrders = append(signedOrders, orderEvent.SignedOrder) 122 | // } 123 | // case err := <-subscription.Err(): 124 | // log.Fatal(err) 125 | // } 126 | // } 127 | // } 128 | -------------------------------------------------------------------------------- /cmd/mesh-keygen/main.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | // mesh-keygen is a short program that can be used to generate private keys. 4 | package main 5 | 6 | import ( 7 | "log" 8 | "os" 9 | 10 | "github.com/0xProject/0x-mesh/keys" 11 | "github.com/plaid/go-envvar/envvar" 12 | ) 13 | 14 | type envVars struct { 15 | // PrivateKeyPath is the path where the private key will be written. 16 | PrivateKeyPath string `envvar:"PRIVATE_KEY_PATH" default:"0x_mesh/keys/privkey"` 17 | } 18 | 19 | func main() { 20 | env := envVars{} 21 | if err := envvar.Parse(&env); err != nil { 22 | log.Fatal(err) 23 | } 24 | if _, err := os.Stat(env.PrivateKeyPath); !os.IsNotExist(err) { 25 | log.Fatalf("Key file: %s already exists. If you really want to overwrite it, delete the file and try again.", env.PrivateKeyPath) 26 | } 27 | if _, err := keys.GenerateAndSavePrivateKey(env.PrivateKeyPath); err != nil { 28 | log.Fatal(err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cmd/mesh/graphql_server.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/0xProject/0x-mesh/core" 11 | "github.com/0xProject/0x-mesh/graphql" 12 | "github.com/0xProject/0x-mesh/graphql/generated" 13 | 14 | gqlserver "github.com/99designs/gqlgen/graphql/handler" 15 | ) 16 | 17 | // gracefulShutdownTimeout is the maximum amount of time to allow 18 | // responding to any incoming requests after the server receives 19 | // the signal to shutdown. 20 | const gracefulShutdownTimeout = 10 * time.Second 21 | 22 | func serveGraphQL(ctx context.Context, app *core.App, config *standaloneConfig) error { 23 | handler := http.NewServeMux() 24 | 25 | // Set up handler for GraphiQL 26 | if config.EnableGraphQLServer { 27 | handler.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 28 | _, _ = w.Write(graphiQLPage) 29 | })) 30 | } 31 | 32 | // Set up handler for GrqphQL queries 33 | resolver := graphql.NewResolver(app, &graphql.ResolverConfig{ 34 | SlowSubscriberTimeout: config.GraphQLSlowSubscriberTimeout, 35 | }) 36 | graphQLServer := gqlserver.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: resolver})) 37 | handler.Handle("/graphql", graphQLServer) 38 | 39 | // Start the server 40 | server := &http.Server{Addr: config.GraphQLServerAddr, Handler: handler} 41 | go func() { 42 | <-ctx.Done() 43 | shutdownContext, cancel := context.WithTimeout(context.Background(), gracefulShutdownTimeout) 44 | defer cancel() 45 | _ = server.Shutdown(shutdownContext) 46 | }() 47 | return server.ListenAndServe() 48 | } 49 | 50 | var graphiQLPage = []byte(` 51 | 52 | 53 | 0x Mesh GraphQL Playground 54 | 55 | 56 | 57 |
58 | 59 | 63 | 67 | 71 | 72 | 86 | 87 | 88 | `) 89 | -------------------------------------------------------------------------------- /cmd/peer-id-to-pub-key/README.md: -------------------------------------------------------------------------------- 1 | `peer-id-to-pub-key` is a command line tool that extracts a public key from a 2 | peer ID. It expects exactly one argument, the peer ID. 3 | 4 | Example usage: 5 | 6 | ``` 7 | > peer-id-to-pub-key 16Uiu2HAmB6yLoF4hjCdUKHqJa5XqBg3K8q7AxtBhboAomRG8df9x 8 | 02e8f0963e96c881b4c90b91f03910896e7ef0dc4392e21d13ba87c8335912e6cf 9 | ``` 10 | -------------------------------------------------------------------------------- /cmd/peer-id-to-pub-key/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | peer "github.com/libp2p/go-libp2p-core/peer" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) != 2 { 14 | log.Fatal("expects exactly one argument") 15 | } 16 | peerIDString := os.Args[1] 17 | peerID, err := peer.Decode(peerIDString) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | pubKey, err := peerID.ExtractPublicKey() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | rawPubKey, err := pubKey.Raw() 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | fmt.Println(hex.EncodeToString(rawPubKey)) 30 | } 31 | -------------------------------------------------------------------------------- /common/types/types_js.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package types 4 | 5 | import ( 6 | "encoding/json" 7 | "syscall/js" 8 | 9 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/jsutil" 10 | ) 11 | 12 | func (r GetOrdersResponse) JSValue() js.Value { 13 | // TODO(albrow): Optimize this. Remove other uses of the JSON 14 | // encoding/decoding hack. 15 | encodedResponse, err := json.Marshal(r) 16 | if err != nil { 17 | panic(err) 18 | } 19 | responseJS := js.Global().Get("JSON").Call("parse", string(encodedResponse)) 20 | return responseJS 21 | } 22 | 23 | func (l LatestBlock) JSValue() js.Value { 24 | return js.ValueOf(map[string]interface{}{ 25 | "number": l.Number.String(), 26 | "hash": l.Hash.String(), 27 | }) 28 | } 29 | 30 | func (s Stats) JSValue() js.Value { 31 | secondaryRendezvous := make([]interface{}, len(s.SecondaryRendezvous)) 32 | for i, rendezvousPoint := range s.SecondaryRendezvous { 33 | secondaryRendezvous[i] = rendezvousPoint 34 | } 35 | return js.ValueOf(map[string]interface{}{ 36 | "version": s.Version, 37 | "pubSubTopic": s.PubSubTopic, 38 | "rendezvous": s.Rendezvous, 39 | "secondaryRendezvous": secondaryRendezvous, 40 | "peerID": s.PeerID, 41 | "ethereumChainID": s.EthereumChainID, 42 | "latestBlock": s.LatestBlock.JSValue(), 43 | "numPeers": s.NumPeers, 44 | "numOrders": s.NumOrders, 45 | "numOrdersIncludingRemoved": s.NumOrdersIncludingRemoved, 46 | "numPinnedOrders": s.NumPinnedOrders, 47 | "maxExpirationTime": s.MaxExpirationTime.String(), 48 | "startOfCurrentUTCDay": s.StartOfCurrentUTCDay.String(), 49 | "ethRPCRequestsSentInCurrentUTCDay": s.EthRPCRequestsSentInCurrentUTCDay, 50 | "ethRPCRateLimitExpiredRequests": s.EthRPCRateLimitExpiredRequests, 51 | }) 52 | } 53 | 54 | func (o OrderWithMetadata) JSValue() js.Value { 55 | value, _ := jsutil.InefficientlyConvertToJS(o) 56 | return value 57 | } 58 | -------------------------------------------------------------------------------- /core/eth_rpc_chain_id.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "math/big" 7 | 8 | "github.com/ethereum/go-ethereum/common/math" 9 | ) 10 | 11 | func (app *App) getEthRPCChainID(ctx context.Context) (*big.Int, error) { 12 | ctx, cancel := context.WithTimeout(ctx, ethereumRPCRequestTimeout) 13 | defer cancel() 14 | 15 | var chainIDRaw string 16 | err := app.ethRPCClient.CallContext(ctx, &chainIDRaw, "eth_chainId") 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | rpcChainID, ok := math.ParseBig256(chainIDRaw) 22 | if !ok { 23 | return nil, errors.New("Failed to parse big.Int value from hex-encoded chainID returned from eth_chainId") 24 | } 25 | 26 | return rpcChainID, nil 27 | } 28 | -------------------------------------------------------------------------------- /core/message_handler_v4.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | "github.com/0xProject/0x-mesh/common/types" 8 | "github.com/0xProject/0x-mesh/metrics" 9 | "github.com/0xProject/0x-mesh/p2p" 10 | "github.com/0xProject/0x-mesh/zeroex" 11 | "github.com/0xProject/0x-mesh/zeroex/ordervalidator" 12 | "github.com/ethereum/go-ethereum/common" 13 | log "github.com/sirupsen/logrus" 14 | ) 15 | 16 | func (app *App) HandleMessagesV4(ctx context.Context, messages []*p2p.Message) error { 17 | // First we validate the messages and decode them into orders. 18 | orders := []*zeroex.SignedOrderV4{} 19 | orderHashToMessage := map[common.Hash]*p2p.Message{} 20 | 21 | for _, msg := range messages { 22 | // Decode JSON 23 | var order zeroex.SignedOrderV4 24 | if err := json.Unmarshal(msg.Data, &order); err != nil { 25 | log.WithFields(map[string]interface{}{ 26 | "error": err, 27 | "from": msg.From, 28 | }).Trace("could not decode received message") 29 | app.handlePeerScoreEvent(msg.From, psInvalidMessage) 30 | continue 31 | } 32 | 33 | orderHash, err := order.ComputeOrderHash() 34 | if err != nil { 35 | return err 36 | } 37 | // Validate doesn't guarantee there are no duplicates so we keep track of 38 | // which orders we've already seen. 39 | if _, alreadySeen := orderHashToMessage[orderHash]; alreadySeen { 40 | continue 41 | } 42 | orders = append(orders, &order) 43 | orderHashToMessage[orderHash] = msg 44 | app.handlePeerScoreEvent(msg.From, psValidMessage) 45 | } 46 | 47 | // Next, we validate the orders. 48 | validationResults, err := app.orderWatcher.ValidateAndStoreValidOrdersV4(ctx, orders, app.chainID, false, &types.AddOrdersOpts{}) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | // Store any valid orders and update the peer scores. 54 | for _, acceptedOrderInfo := range validationResults.Accepted { 55 | // If the order isn't new, we don't log it's receipt or adjust peer scores 56 | if !acceptedOrderInfo.IsNew { 57 | continue 58 | } 59 | msg := orderHashToMessage[acceptedOrderInfo.OrderHash] 60 | // If we've reached this point, the message is valid, we were able to 61 | // decode it into an order and check that this order is valid. Update 62 | // peer scores accordingly. 63 | log.WithFields(map[string]interface{}{ 64 | "orderHash": acceptedOrderInfo.OrderHash.Hex(), 65 | "from": msg.From.String(), 66 | "protocol": "GossipSubV4", 67 | }).Info("received new valid order from peer") 68 | log.WithFields(map[string]interface{}{ 69 | "order": acceptedOrderInfo.SignedOrder, 70 | "orderHash": acceptedOrderInfo.OrderHash.Hex(), 71 | "from": msg.From.String(), 72 | "protocol": "GossipSubV4", 73 | }).Trace("all fields for new valid order received from peer") 74 | app.handlePeerScoreEvent(msg.From, psOrderStored) 75 | } 76 | 77 | // We don't store invalid orders, but in some cases still need to update peer 78 | // scores. 79 | for _, rejectedOrderInfo := range validationResults.Rejected { 80 | msg := orderHashToMessage[rejectedOrderInfo.OrderHash] 81 | log.WithFields(map[string]interface{}{ 82 | "rejectedOrderInfo": rejectedOrderInfo, 83 | "from": msg.From.String(), 84 | }).Trace("not storing rejected order received from peer") 85 | switch rejectedOrderInfo.Status { 86 | case ordervalidator.ROInternalError, ordervalidator.ROEthRPCRequestFailed, ordervalidator.RODatabaseFullOfOrders: 87 | // Don't incur a negative score for these status types 88 | // (it might not be their fault). 89 | default: 90 | // For other status types, we need to update the peer's score 91 | app.handlePeerScoreEvent(msg.From, psInvalidMessage) 92 | } 93 | } 94 | 95 | metrics.P2POrdersReceived. 96 | WithLabelValues(metrics.ProtocolV4, metrics.ValidationAccepted). 97 | Add(float64(len(validationResults.Accepted))) 98 | 99 | metrics.P2POrdersReceived. 100 | WithLabelValues(metrics.ProtocolV4, metrics.ValidationRejected). 101 | Add(float64(len(validationResults.Rejected))) 102 | 103 | return nil 104 | } 105 | -------------------------------------------------------------------------------- /core/new_db.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package core 4 | 5 | import ( 6 | "context" 7 | "path/filepath" 8 | 9 | "github.com/0xProject/0x-mesh/db" 10 | ) 11 | 12 | func newDB(ctx context.Context, config Config) (*db.DB, error) { 13 | meshDatabasePath := filepath.Join(config.DataDir, "db", "db.sqlite?_journal=WAL") 14 | peerStoreDatabasePath := filepath.Join(config.DataDir, "db", "peerstore.sqlite?_journal=WAL") 15 | dhtDatabasePath := filepath.Join(config.DataDir, "db", "dht.sqlite?_journal=WAL") 16 | 17 | return db.New(ctx, &db.Options{ 18 | DriverName: "sqlite3", 19 | DataSourceName: meshDatabasePath, 20 | DataSourcePeerStoreName: peerStoreDatabasePath, 21 | DataSourceDHTName: dhtDatabasePath, 22 | MaxOrders: config.MaxOrdersInStorage, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /core/new_db_js.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package core 4 | 5 | import ( 6 | "context" 7 | "path/filepath" 8 | 9 | "github.com/0xProject/0x-mesh/db" 10 | ) 11 | 12 | func newDB(ctx context.Context, config Config) (*db.DB, error) { 13 | databasePath := filepath.Join(config.DataDir, "mesh_dexie_db") 14 | return db.New(ctx, &db.Options{ 15 | DriverName: "dexie", 16 | DataSourceName: databasePath, 17 | MaxOrders: config.MaxOrdersInStorage, 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /core/ordersync/peer_score.go: -------------------------------------------------------------------------------- 1 | package ordersync 2 | 3 | import ( 4 | peer "github.com/libp2p/go-libp2p-core/peer" 5 | log "github.com/sirupsen/logrus" 6 | ) 7 | 8 | type peerScoreEvent uint 9 | 10 | const ( 11 | psInvalidMessage peerScoreEvent = iota 12 | psValidMessage 13 | psSubprotocolNegotiationFailed 14 | psUnexpectedDisconnect 15 | receivedOrders 16 | ) 17 | 18 | func (s *Service) handlePeerScoreEvent(id peer.ID, event peerScoreEvent) { 19 | // Note: for some events, we use `SetPeerScore` instead of `AddPeerScore` in 20 | // order to limit the maximum positive score associated with that event. 21 | // Without this, peers could be incentivized to artificially increase their 22 | // score in a way that doesn't benefit the network. (For example, they could 23 | // spam the network with valid messages). 24 | switch event { 25 | case psInvalidMessage: 26 | s.node.AddPeerScore(id, "ordersync/invalid-message", -5) 27 | case psValidMessage: 28 | s.node.SetPeerScore(id, "ordersync/valid-message", 5) 29 | case psSubprotocolNegotiationFailed: 30 | s.node.SetPeerScore(id, "ordersync/subprotocol-negotiation-failed", -5) 31 | case psUnexpectedDisconnect: 32 | s.node.AddPeerScore(id, "ordersync/unexpected-disconnect", -1) 33 | case receivedOrders: 34 | s.node.UnsetPeerScore(id, "ordersync/unexpected-disconnect") 35 | s.node.SetPeerScore(id, "ordersync/received-orders", 10) 36 | default: 37 | log.WithField("event", event).Error("unknown ordersync peerScoreEvent") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/ordersync_v4/peer_score.go: -------------------------------------------------------------------------------- 1 | package ordersync_v4 2 | 3 | import ( 4 | peer "github.com/libp2p/go-libp2p-core/peer" 5 | log "github.com/sirupsen/logrus" 6 | ) 7 | 8 | type peerScoreEvent uint 9 | 10 | const ( 11 | psInvalidMessage peerScoreEvent = iota 12 | psValidMessage 13 | psUnexpectedDisconnect 14 | receivedOrders 15 | ) 16 | 17 | func (s *Service) handlePeerScoreEvent(id peer.ID, event peerScoreEvent) { 18 | // Note: for some events, we use `SetPeerScore` instead of `AddPeerScore` in 19 | // order to limit the maximum positive score associated with that event. 20 | // Without this, peers could be incentivized to artificially increase their 21 | // score in a way that doesn't benefit the network. (For example, they could 22 | // spam the network with valid messages). 23 | switch event { 24 | case psInvalidMessage: 25 | s.app.Node().AddPeerScore(id, "ordersync/invalid-message", -5) 26 | case psValidMessage: 27 | s.app.Node().SetPeerScore(id, "ordersync/valid-message", 5) 28 | case psUnexpectedDisconnect: 29 | s.app.Node().AddPeerScore(id, "ordersync/unexpected-disconnect", -1) 30 | case receivedOrders: 31 | s.app.Node().UnsetPeerScore(id, "ordersync/unexpected-disconnect") 32 | s.app.Node().SetPeerScore(id, "ordersync/received-orders", 10) 33 | default: 34 | log.WithField("event", event).Error("unknown ordersync peerScoreEvent") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/peer_score.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | peer "github.com/libp2p/go-libp2p-core/peer" 5 | log "github.com/sirupsen/logrus" 6 | ) 7 | 8 | type peerScoreEvent uint 9 | 10 | const ( 11 | psInvalidMessage peerScoreEvent = iota 12 | psValidMessage 13 | psOrderStored 14 | psReceivedOrderDoesNotMatchFilter 15 | ) 16 | 17 | func (app *App) handlePeerScoreEvent(id peer.ID, event peerScoreEvent) { 18 | // Note: for some events, we use `SetPeerScore` instead of `AddPeerScore` in 19 | // order to limit the maximum positive score associated with that event. 20 | // Without this, peers could be incentivized to artificially increase their 21 | // score in a way that doesn't benefit the network. (For example, they could 22 | // spam the network with valid messages). 23 | switch event { 24 | case psInvalidMessage: 25 | app.node.AddPeerScore(id, "invalid-message", -5) 26 | case psValidMessage: 27 | app.node.SetPeerScore(id, "valid-message", 5) 28 | case psOrderStored: 29 | app.node.SetPeerScore(id, "order-stored", 10) 30 | case psReceivedOrderDoesNotMatchFilter: 31 | app.node.SetPeerScore(id, "received-order-does-not-match-filter", -10) 32 | default: 33 | log.WithField("event", event).Error("unknown peerScoreEvent") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /db/common_v4.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | type OrderFieldV4 string 4 | 5 | const ( 6 | OV4FHash OrderFieldV4 = "hash" 7 | OV4FChainID OrderFieldV4 = "chainID" 8 | OV4FExchangeAddress OrderFieldV4 = "exchangeAddress" 9 | OV4FMakerToken OrderFieldV4 = "makerToken" 10 | OV4FTakerToken OrderFieldV4 = "takerToken" 11 | OV4FMakerAmount OrderFieldV4 = "makerAmount" 12 | OV4FTakerAmount OrderFieldV4 = "takerAmount" 13 | OV4FTakerTokenFeeAmount OrderFieldV4 = "takerTokenFeeAmount" 14 | OV4FMaker OrderFieldV4 = "maker" 15 | OV4FTaker OrderFieldV4 = "taker" 16 | OV4FSender OrderFieldV4 = "sender" 17 | OV4FFeeRecipient OrderFieldV4 = "feeRecipient" 18 | OV4FPool OrderFieldV4 = "pool" 19 | OV4FExpiry OrderFieldV4 = "expiry" 20 | OV4FSalt OrderFieldV4 = "salt" 21 | OV4FSignature OrderFieldV4 = "signature" 22 | OV4FLastUpdated OrderFieldV4 = "lastUpdated" 23 | OV4FFillableTakerAssetAmount OrderFieldV4 = "fillableTakerAssetAmount" 24 | OV4FIsRemoved OrderFieldV4 = "isRemoved" 25 | OV4FIsPinned OrderFieldV4 = "isPinned" 26 | OV4FIsUnfillable OrderFieldV4 = "isUnfillable" 27 | OV4FIsExpired OrderFieldV4 = "isExpired" 28 | OV4FParsedMakerAssetData OrderFieldV4 = "parsedMakerAssetData" 29 | OV4FParsedMakerFeeAssetData OrderFieldV4 = "parsedMakerFeeAssetData" 30 | OV4FLastValidatedBlockNumber OrderFieldV4 = "lastValidatedBlockNumber" 31 | OV4FKeepCancelled OrderFieldV4 = "keepCancelled" 32 | OV4FKeepExpired OrderFieldV4 = "keepExpired" 33 | OV4FKeepFullyFilled OrderFieldV4 = "keepFullyFilled" 34 | OV4FKeepUnfunded OrderFieldV4 = "keepUnfunded" 35 | ) 36 | 37 | type OrderQueryV4 struct { 38 | Filters []OrderFilterV4 `json:"filters"` 39 | Sort []OrderSortV4 `json:"sort"` 40 | Limit uint `json:"limit"` 41 | Offset uint `json:"offset"` 42 | } 43 | 44 | type OrderSortV4 struct { 45 | Field OrderFieldV4 `json:"field"` 46 | Direction SortDirection `json:"direction"` 47 | } 48 | 49 | type OrderFilterV4 struct { 50 | Field OrderFieldV4 `json:"field"` 51 | Kind FilterKind `json:"kind"` 52 | Value interface{} `json:"value"` 53 | } 54 | -------------------------------------------------------------------------------- /db/dexietypes/dexietypes_test.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package dexietypes 4 | 5 | import ( 6 | "testing" 7 | "time" 8 | 9 | "github.com/0xProject/0x-mesh/common/types" 10 | "github.com/ethereum/go-ethereum/common" 11 | ethmath "github.com/ethereum/go-ethereum/common/math" 12 | ethtypes "github.com/ethereum/go-ethereum/core/types" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestMiniHeadersConversion(t *testing.T) { 17 | originalMiniHeader := &types.MiniHeader{ 18 | Hash: common.HexToHash("0x00a3ce0e9cbcb5c4d79c1c19df276a0db954a487b895dca1d4deb35e39859eb8"), 19 | Parent: common.HexToHash("0x302febf685d86eaa2339e6f9b226e36d69ebf48b1bfd10b44fc51fcaaefbf148"), 20 | Number: ethmath.MaxBig256, 21 | Timestamp: time.Date(1992, time.September, 29, 8, 45, 15, 1230, time.UTC), 22 | Logs: []ethtypes.Log{ 23 | { 24 | Address: common.HexToAddress("0x21ab6c9fac80c59d401b37cb43f81ea9dde7fe34"), 25 | Topics: []common.Hash{ 26 | common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), 27 | common.HexToHash("0x0000000000000000000000004d8a4aa1f304f9632cf3877473445d85c577fe5d"), 28 | common.HexToHash("0x0000000000000000000000004bdd0d16cfa18e33860470fc4d65c6f5cee60959"), 29 | }, 30 | Data: common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000337ad34c0"), 31 | BlockNumber: 30, 32 | TxHash: common.HexToHash("0xd9bb5f9e888ee6f74bedcda811c2461230f247c205849d6f83cb6c3925e54586"), 33 | TxIndex: 0, 34 | BlockHash: common.HexToHash("0x6bbf9b6e836207ab25379c20e517a89090cbbaf8877746f6ed7fb6820770816b"), 35 | Index: 0, 36 | Removed: false, 37 | }, 38 | { 39 | Address: common.HexToAddress("0x21ab6c9fac80c59d401b37cb43f81ea9dde7fe34"), 40 | Topics: []common.Hash{ 41 | common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), 42 | common.HexToHash("0x0000000000000000000000004d8a4aa1f304f9632cf3877473445d85c577fe5d"), 43 | common.HexToHash("0x0000000000000000000000004bdd0d16cfa18e33860470fc4d65c6f5cee60959"), 44 | }, 45 | Data: common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000deadbeef"), 46 | BlockNumber: 31, 47 | TxHash: common.HexToHash("0xd9bb5f9e888ee6f74bedcda811c2461230f247c205849d6f83cb6c3925e54586"), 48 | TxIndex: 1, 49 | BlockHash: common.HexToHash("0x6bbf9b6e836207ab25379c20e517a89090cbbaf8877746f6ed7fb6820770816b"), 50 | Index: 2, 51 | Removed: true, 52 | }, 53 | }, 54 | } 55 | 56 | // Convert to JS/Dexie type and back. Make sure we get back the same values that we started with. 57 | convertedMiniHeader := MiniHeaderToCommonType(MiniHeaderFromCommonType(originalMiniHeader)) 58 | assert.Equal(t, originalMiniHeader, convertedMiniHeader) 59 | } 60 | -------------------------------------------------------------------------------- /db/kv_store_queries.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package db 4 | 5 | import ( 6 | "database/sql" 7 | "fmt" 8 | 9 | "github.com/ipfs/go-ds-sql" 10 | _ "github.com/lib/pq" //postgres driver 11 | ) 12 | 13 | /// PostgreSQL 14 | 15 | // Options are the postgres datastore options, reexported here for convenience. 16 | type PostgreSQLOptions struct { 17 | Host string 18 | Port string 19 | User string 20 | Password string 21 | Database string 22 | Table string 23 | } 24 | 25 | type postgreSQLQueries struct { 26 | tableName string 27 | } 28 | 29 | func NewPostgreSQLQueriesForTable(tableName string) *postgreSQLQueries { 30 | return &postgreSQLQueries{tableName} 31 | } 32 | 33 | func (q postgreSQLQueries) Delete() string { 34 | return `DELETE FROM ` + q.tableName + ` WHERE key = $1` 35 | } 36 | 37 | func (q postgreSQLQueries) Exists() string { 38 | return `SELECT exists(SELECT 1 FROM ` + q.tableName + ` WHERE key=$1)` 39 | } 40 | 41 | func (q postgreSQLQueries) Get() string { 42 | return `SELECT data FROM ` + q.tableName + ` WHERE key = $1` 43 | } 44 | 45 | func (q postgreSQLQueries) Put() string { 46 | return `INSERT INTO ` + q.tableName + ` (key, data) SELECT $1, $2 ON CONFLICT(key) DO UPDATE SET data = $2 WHERE key = $1` 47 | } 48 | 49 | func (q postgreSQLQueries) Query() string { 50 | return `SELECT key, data FROM ` + q.tableName 51 | } 52 | 53 | func (q postgreSQLQueries) Prefix() string { 54 | return ` WHERE key LIKE '%s%%' ORDER BY key` 55 | } 56 | 57 | func (q postgreSQLQueries) Limit() string { 58 | return ` LIMIT %d` 59 | } 60 | 61 | func (q postgreSQLQueries) Offset() string { 62 | return ` OFFSET %d` 63 | } 64 | 65 | func (q postgreSQLQueries) GetSize() string { 66 | return `SELECT octet_length(data) FROM ` + q.tableName + ` WHERE key = $1` 67 | } 68 | 69 | // Create returns a datastore connected to postgres initialized with a table 70 | func (opts *PostgreSQLOptions) CreatePostgres() (*sqlds.Datastore, error) { 71 | opts.setDefaults() 72 | fmtstr := "postgresql:///%s?host=%s&port=%s&user=%s&password=%s&sslmode=disable" 73 | constr := fmt.Sprintf(fmtstr, opts.Database, opts.Host, opts.Port, opts.User, opts.Password) 74 | db, err := sql.Open("postgres", constr) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | createTable := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (key TEXT NOT NULL UNIQUE, data BYTEA NOT NULL)", opts.Table) 80 | _, err = db.Exec(createTable) 81 | 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | return sqlds.NewDatastore(db, NewPostgreSQLQueriesForTable(opts.Table)), nil 87 | } 88 | 89 | func (opts *PostgreSQLOptions) setDefaults() { 90 | if opts.Table == "" { 91 | opts.Table = "kv" 92 | } 93 | if opts.Host == "" { 94 | opts.Host = "postgres" 95 | } 96 | 97 | if opts.Port == "" { 98 | opts.Port = "5432" 99 | } 100 | 101 | if opts.User == "" { 102 | opts.User = "postgres" 103 | } 104 | 105 | if opts.Database == "" { 106 | opts.Database = "datastore" 107 | } 108 | } 109 | 110 | /// Sqlite 111 | 112 | type sqliteQueries struct { 113 | tableName string 114 | } 115 | 116 | func NewSqliteQueriesForTable(tableName string) *sqliteQueries { 117 | return &sqliteQueries{tableName} 118 | } 119 | 120 | func (q sqliteQueries) Delete() string { 121 | return `DELETE FROM ` + q.tableName + ` WHERE key = $1` 122 | } 123 | 124 | func (q sqliteQueries) Exists() string { 125 | return `SELECT exists(SELECT 1 FROM ` + q.tableName + ` WHERE key=$1)` 126 | } 127 | 128 | func (q sqliteQueries) Get() string { 129 | return `SELECT data FROM ` + q.tableName + ` WHERE key = $1` 130 | } 131 | 132 | func (q sqliteQueries) Put() string { 133 | return `INSERT INTO ` + q.tableName + ` (key, data) SELECT $1, $2 ON CONFLICT(key) DO UPDATE SET data = $2 WHERE key = $1` 134 | } 135 | 136 | func (q sqliteQueries) Query() string { 137 | return `SELECT key, data FROM ` + q.tableName 138 | } 139 | 140 | func (q sqliteQueries) Prefix() string { 141 | return ` WHERE key LIKE '%s%%' ORDER BY key` 142 | } 143 | 144 | func (q sqliteQueries) Limit() string { 145 | return ` LIMIT %d` 146 | } 147 | 148 | func (q sqliteQueries) Offset() string { 149 | return ` OFFSET %d` 150 | } 151 | 152 | func (q sqliteQueries) GetSize() string { 153 | return `SELECT length(data) FROM ` + q.tableName + ` WHERE key = $1` 154 | } 155 | -------------------------------------------------------------------------------- /db/sql_v4_queries.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | const v4OrdersSchema = ` 4 | CREATE TABLE IF NOT EXISTS ordersv4 ( 5 | hash TEXT UNIQUE NOT NULL, 6 | chainID TEXT NOT NULL, 7 | verifyingContract TEXT NOT NULL, 8 | makerToken TEXT NOT NULL, 9 | takerToken TEXT NOT NULL, 10 | makerAmount TEXT NOT NULL, 11 | takerAmount TEXT NOT NULL, 12 | takerTokenFeeAmount TEXT NOT NULL, 13 | maker TEXT NOT NULL, 14 | taker TEXT NOT NULL, 15 | sender TEXT NOT NULL, 16 | feeRecipient TEXT NOT NULL, 17 | pool TEXT NOT NULL, 18 | expiry TEXT NOT NULL, 19 | salt TEXT NOT NULL, 20 | signatureType TEXT NOT NULL, 21 | signatureV TEXT NOT NULL, 22 | signatureR TEXT NOT NULL, 23 | signatureS TEXT NOT NULL, 24 | lastUpdated DATETIME NOT NULL, 25 | fillableTakerAssetAmount TEXT NOT NULL, 26 | isRemoved BOOLEAN NOT NULL, 27 | isPinned BOOLEAN NOT NULL, 28 | isUnfillable BOOLEAN NOT NULL, 29 | isExpired BOOLEAN NOT NULL, 30 | lastValidatedBlockNumber TEXT NOT NULL, 31 | lastValidatedBlockHash TEXT NOT NULL, 32 | keepCancelled BOOLEAN NOT NULL, 33 | keepExpired BOOLEAN NOT NULL, 34 | keepFullyFilled BOOLEAN NOT NULL, 35 | keepUnfunded BOOLEAN NOT NULL 36 | ); 37 | ` 38 | const insertOrderQueryV4 = `INSERT INTO ordersv4 ( 39 | hash, 40 | chainID, 41 | verifyingContract, 42 | makerToken, 43 | takerToken, 44 | makerAmount, 45 | takerAmount, 46 | takerTokenFeeAmount, 47 | maker, 48 | taker, 49 | sender, 50 | feeRecipient, 51 | pool, 52 | expiry, 53 | salt, 54 | signatureType, 55 | signatureV, 56 | signatureR, 57 | signatureS, 58 | lastUpdated, 59 | fillableTakerAssetAmount, 60 | isRemoved, 61 | isPinned, 62 | isUnfillable, 63 | isExpired, 64 | lastValidatedBlockNumber, 65 | lastValidatedBlockHash, 66 | keepCancelled, 67 | keepExpired, 68 | keepFullyFilled, 69 | keepUnfunded 70 | ) VALUES ( 71 | :hash, 72 | :chainID, 73 | :verifyingContract, 74 | :makerToken, 75 | :takerToken, 76 | :makerAmount, 77 | :takerAmount, 78 | :takerTokenFeeAmount, 79 | :maker, 80 | :taker, 81 | :sender, 82 | :feeRecipient, 83 | :pool, 84 | :expiry, 85 | :salt, 86 | :signatureType, 87 | :signatureV, 88 | :signatureR, 89 | :signatureS, 90 | :lastUpdated, 91 | :fillableTakerAssetAmount, 92 | :isRemoved, 93 | :isPinned, 94 | :isUnfillable, 95 | :isExpired, 96 | :lastValidatedBlockNumber, 97 | :lastValidatedBlockHash, 98 | :keepCancelled, 99 | :keepExpired, 100 | :keepFullyFilled, 101 | :keepUnfunded 102 | ) ON CONFLICT DO NOTHING 103 | ` 104 | 105 | const updateOrderQueryV4 = `UPDATE ordersv4 SET 106 | chainID = :chainID, 107 | verifyingContract = :verifyingContract, 108 | maker = :maker, 109 | makerToken = :makerToken, 110 | makerAmount = :makerAmount, 111 | taker = :taker, 112 | takerToken = :takerToken, 113 | takerAmount = :takerAmount, 114 | sender = :sender, 115 | feeRecipient = :feeRecipient, 116 | expiry = :expiry, 117 | salt = :salt, 118 | pool = :pool, 119 | signatureType = :signatureType, 120 | signatureV = :signatureV, 121 | signatureR = :signatureR, 122 | signatureS = :signatureS, 123 | lastUpdated = :lastUpdated, 124 | fillableTakerAssetAmount = :fillableTakerAssetAmount, 125 | isRemoved = :isRemoved, 126 | isPinned = :isPinned, 127 | isUnfillable = :isUnfillable, 128 | isExpired = :isExpired, 129 | lastValidatedBlockNumber = :lastValidatedBlockNumber, 130 | lastValidatedBlockHash = :lastValidatedBlockHash, 131 | keepCancelled = :keepCancelled, 132 | keepExpired = :keepExpired, 133 | keepFullyFilled = :keepFullyFilled, 134 | keepUnfunded = :keepUnfunded 135 | WHERE ordersv4.hash = :hash 136 | ` 137 | -------------------------------------------------------------------------------- /db/types_js.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package db 4 | 5 | import ( 6 | "syscall/js" 7 | 8 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/jsutil" 9 | ) 10 | 11 | func (opts *Options) JSValue() js.Value { 12 | value, _ := jsutil.InefficientlyConvertToJS(opts) 13 | return value 14 | } 15 | 16 | func (query *OrderQuery) JSValue() js.Value { 17 | if query == nil { 18 | return js.Null() 19 | } 20 | value, err := jsutil.InefficientlyConvertToJS(query) 21 | if err != nil { 22 | // Should never happen 23 | panic(err) 24 | } 25 | return value 26 | } 27 | 28 | func (query *MiniHeaderQuery) JSValue() js.Value { 29 | if query == nil { 30 | return js.Null() 31 | } 32 | value, err := jsutil.InefficientlyConvertToJS(query) 33 | if err != nil { 34 | // Should never happen 35 | panic(err) 36 | } 37 | return value 38 | } 39 | -------------------------------------------------------------------------------- /dockerfiles/mesh-bootstrap/Dockerfile: -------------------------------------------------------------------------------- 1 | # Note: this must be built from the root of the project with: 2 | # 3 | # docker build . -f ./cmd/mesh-bootstrap/Dockerfile 4 | # 5 | 6 | # mesh-builder produces a statically linked binary 7 | FROM golang:1.15.2-alpine3.12 as mesh-builder 8 | 9 | 10 | RUN apk update && apk add ca-certificates nodejs-current npm make git dep gcc build-base musl linux-headers 11 | 12 | WORKDIR /0x-mesh 13 | 14 | ADD . ./ 15 | 16 | RUN go build ./cmd/mesh-bootstrap 17 | 18 | # Final Image 19 | FROM alpine:3.12 20 | 21 | RUN apk update && apk add ca-certificates --no-cache 22 | 23 | WORKDIR /usr/mesh 24 | 25 | COPY --from=mesh-builder /0x-mesh/mesh-bootstrap /usr/mesh/mesh-bootstrap 26 | 27 | # Default port for TCP multiaddr 28 | EXPOSE 60558 29 | # Default port for WebSockets multiaddr 30 | EXPOSE 60559 31 | 32 | RUN chmod +x ./mesh-bootstrap 33 | 34 | ENTRYPOINT ./mesh-bootstrap 35 | -------------------------------------------------------------------------------- /dockerfiles/mesh-bridge/Dockerfile: -------------------------------------------------------------------------------- 1 | # Note: this must be built from the root of the project with: 2 | # 3 | # docker build . -f ./cmd/mesh-bridge/Dockerfile 4 | # 5 | 6 | # mesh-builder produces a statically linked binary 7 | FROM golang:1.15.2-alpine3.12 as mesh-builder 8 | 9 | 10 | RUN apk update && apk add ca-certificates nodejs-current npm make git dep gcc build-base musl linux-headers 11 | 12 | WORKDIR /0x-mesh 13 | 14 | ADD . ./ 15 | 16 | RUN go build ./cmd/mesh-bridge 17 | 18 | # Final Image 19 | FROM alpine:3.12 20 | 21 | RUN apk update && apk add ca-certificates --no-cache 22 | 23 | WORKDIR /usr/mesh 24 | 25 | COPY --from=mesh-builder /0x-mesh/mesh-bridge /usr/mesh/mesh-bridge 26 | 27 | RUN chmod +x ./mesh-bridge 28 | 29 | ENTRYPOINT ./mesh-bridge 30 | -------------------------------------------------------------------------------- /dockerfiles/mesh-fluent-bit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fluent/fluent-bit:1.2.1 2 | 3 | COPY fluent-bit.conf /fluent-bit/etc/fluent-bit.conf 4 | COPY parsers.conf /fluent-bit/etc/parsers.conf 5 | -------------------------------------------------------------------------------- /dockerfiles/mesh-fluent-bit/fluent-bit.conf: -------------------------------------------------------------------------------- 1 | [SERVICE] 2 | Flush 5 3 | Daemon Off 4 | Log_Level debug 5 | Parsers_File /fluent-bit/etc/parsers.conf 6 | 7 | [INPUT] 8 | Name forward 9 | Listen fluentbit 10 | Port 24224 11 | 12 | [FILTER] 13 | Name parser 14 | Match docker.mesh 15 | Key_Name log 16 | Parser dockerCustom 17 | Unescape_Key On 18 | Preserve_Key On 19 | 20 | [OUTPUT] 21 | Name es 22 | Match * 23 | Host esproxy 24 | Port 3333 25 | Logstash_Format On 26 | Logstash_Prefix mesh_beta 27 | Type docker 28 | 29 | [OUTPUT] 30 | Name stdout 31 | Match * 32 | Format msgpack 33 | -------------------------------------------------------------------------------- /dockerfiles/mesh-fluent-bit/parsers.conf: -------------------------------------------------------------------------------- 1 | [PARSER] 2 | Name dockerCustom 3 | Format json 4 | Time_Key time 5 | Time_Format %Y-%m-%dT%H:%M:%S %z 6 | Decode_Field_As escaped_utf8 log do_next 7 | Decode_Field_As escaped log do_next 8 | Decode_Field_As json log 9 | -------------------------------------------------------------------------------- /dockerfiles/mesh/Dockerfile: -------------------------------------------------------------------------------- 1 | # Note: this must be built from the root of the project with: 2 | # 3 | # docker build . -f ./cmd/mesh/Dockerfile 4 | # 5 | 6 | # mesh-builder produces a statically linked binary 7 | FROM golang:1.15.2-alpine3.12 as mesh-builder 8 | 9 | 10 | RUN apk update && apk add ca-certificates nodejs-current npm make git dep gcc build-base musl linux-headers 11 | 12 | WORKDIR /0x-mesh 13 | 14 | ADD . ./ 15 | 16 | RUN go build ./cmd/mesh 17 | 18 | # Final Image 19 | FROM alpine:3.12 20 | 21 | RUN apk update && apk add ca-certificates --no-cache 22 | 23 | WORKDIR /usr/mesh 24 | 25 | COPY --from=mesh-builder /0x-mesh/mesh /usr/mesh/mesh 26 | 27 | ENV GRAPHQL_SERVER_ADDR=0.0.0.0:60557 28 | EXPOSE 60557 29 | 30 | ENV P2P_TCP_PORT=60558 31 | EXPOSE 60558 32 | ENV P2P_WEBSOCKETS_PORT=60559 33 | EXPOSE 60559 34 | 35 | RUN chmod +x ./mesh 36 | 37 | ENTRYPOINT ./mesh 38 | -------------------------------------------------------------------------------- /docs/browser-bindings/browser-lite/README.md: -------------------------------------------------------------------------------- 1 | # @0x/mesh-browser-lite - v11.1.0 2 | 3 | ## @0x/mesh-browser-lite 4 | 5 | This packages provides a set of Typescript and Javascript bindings for running a 6 | 0x-mesh node in the browser. The browser node's Wasm binary is not bundled in 7 | this package and is instead expected to be served by the consumer of the package. 8 | This package has a smaller bundle size than the `@0x/mesh-browser` package and 9 | may have faster load times. 10 | 11 | ## Installation 12 | 13 | ```bash 14 | yarn add @0x/mesh-browser-lite 15 | ``` 16 | 17 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 18 | 19 | ```json 20 | "compilerOptions": { 21 | "typeRoots": ["node_modules/@types"], 22 | } 23 | ``` 24 | 25 | ## Contributing 26 | 27 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 28 | 29 | ### Install dependencies 30 | 31 | ```bash 32 | yarn install 33 | ``` 34 | 35 | ### Build 36 | 37 | ```bash 38 | yarn build 39 | ``` 40 | 41 | ### Lint 42 | 43 | ```bash 44 | yarn lint 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/browser-bindings/browser/README.md: -------------------------------------------------------------------------------- 1 | # @0x/mesh-browser - v11.1.0 2 | 3 | ## @0x/mesh-browser 4 | 5 | This package provides an easy way to run a browser-based Mesh node. Specifically, it 6 | provides Typescript and Javascript bindings that can be used to interact with a Mesh 7 | node that is running in the browser and handles the process of loading the mesh node 8 | on the webpage. Because of the fact that this package handles Wasm loading, it is 9 | considerably heavier-weight and may take longer to load than the @0x/mesh-browser-lite 10 | package. 11 | 12 | ## Installation 13 | 14 | ```bash 15 | yarn add @0x/mesh-browser 16 | ``` 17 | 18 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 19 | 20 | ```json 21 | "compilerOptions": { 22 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], 23 | } 24 | ``` 25 | 26 | ## Contributing 27 | 28 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 29 | 30 | ### Install dependencies 31 | 32 | ```bash 33 | yarn install 34 | ``` 35 | 36 | ### Build 37 | 38 | ```bash 39 | yarn build 40 | ``` 41 | 42 | ### Clean 43 | 44 | ```bash 45 | yarn clean 46 | ``` 47 | 48 | ### Lint 49 | 50 | ```bash 51 | yarn lint 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/browser.md: -------------------------------------------------------------------------------- 1 | # 0x Mesh Browser Guide 2 | 3 | This guide will walk you through how to run 0x Mesh directly in the browser and 4 | discuss some of the advantages and drawbacks of doing so. 5 | 6 | ## Background 7 | 8 | Mesh is written in Go, but can be compiled to WebAssembly and run directly in 9 | the browser. This makes it possible for users to share orders and trade directly 10 | with one another without relying on any third-party server or database. This 11 | approach comes with a lot of advantages, but also has some drawbacks: 12 | 13 | ### Advantages 14 | 15 | - Increased decentralization 16 | - Little to no hosting costs 17 | - Ability to trade experimental/niche assets 18 | 19 | ### Drawbacks 20 | 21 | - Longer warm-up time 22 | - Increased risk of trade collisions 23 | - Consumes more end-user resources 24 | 25 | ## @0x/mesh-browser 26 | 27 | For your convenience, we've published an NPM package called `@0x/mesh-browser` 28 | which includes the WebAssembly bytecode and a lightweight wrapper around it. This 29 | package is used exactly the same way as any other NPM package and using it feels 30 | exactly like using a native TypeScript/JavaScript library. To find more information 31 | on optimizing the startup time of using Mesh in the browser, refer to the next 32 | section on the `@0x/mesh-browser-lite` package. 33 | 34 | ## @0x/mesh-browser-lite 35 | 36 | We've published a lightweight version of the `@0x/mesh-browser` package -- the 37 | `@0x/mesh-browser-lite` package -- that provides an identical abstraction around a 38 | browser-based Mesh node without requiring that Wasm bytecode be bundled with the 39 | rest of the webpage's code. Additionally, this package makes use of the 40 | WebAssembly's streaming functionality, which could provide a speedup to load 41 | times for users of browser-based Mesh. 42 | 43 | Using this package is a bit more complicated than using the `@0x/mesh-browser` package. 44 | WebAssembly binaries for each version of 0x-mesh that has an associated `@0x/mesh-browser-lite` 45 | package can be found in the [0x-mesh release notes](https://github.com/0xProject/0x-mesh/releases). 46 | The user will need to serve the appropriate binary on a server or CDN of their choice. 47 | The package gives users the option of providing a URL to the `loadMeshStreamingWithURLAsync` 48 | function or a `Response` object to the `loadMeshStreamingAsync` function in their 49 | application. The URL or `Response` option should be chosen in such a way that they 50 | load the Mesh Binary that is being served. 51 | 52 | ## Installation 53 | 54 | To install the `@0x/mesh-browser` NPM package, simply run: 55 | 56 | ``` 57 | npm install --save @0x/mesh-browser 58 | ``` 59 | 60 | Or if you are using `yarn`: 61 | 62 | ``` 63 | yarn add @0x/mesh-browser 64 | ``` 65 | 66 | Similarly, the `@0x/mesh-browser-lite` NPM package can be installed by running: 67 | 68 | ``` 69 | npm install --save @0x/mesh-browser-lite 70 | ``` 71 | 72 | Or if you are using `yarn`: 73 | 74 | ``` 75 | yarn add @0x/mesh-browser-lite 76 | ``` 77 | 78 | ## Documentation 79 | 80 | - Documentation for the `@0x/mesh-browser` package is available at 81 | [0x-org.gitbook.io/mesh/browser-bindings/browser](https://0x-org.gitbook.io/mesh/browser-bindings/browser). 82 | - Documentation for the `@0x/mesh-browser-lite` package is available at 83 | [0x-org.gitbook.io/mesh/browser-bindings/browser-lite](https://0x-org.gitbook.io/mesh/browser-bindings/browser-lite). 84 | 85 | ### Example usage 86 | 87 | [The webpack-example directory](../packages/mesh-webpack-example) and 88 | [the webpack-example-lite directory](../packages/mesh-webpack-example-lite) include 89 | examples of how to use the `@0x/mesh-browser` and the `@0x/mesh-browser-lite` packages, 90 | respectively. 91 | -------------------------------------------------------------------------------- /docs/graphql-client/README.md: -------------------------------------------------------------------------------- 1 | # @0x/mesh-graphql-client - v11.2.0 2 | 3 | ## @0x/mesh-graphql-client 4 | 5 | A client for the Mesh GraphQL API. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | yarn add @0x/mesh-graphql-client 11 | ``` 12 | 13 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 14 | 15 | ```json 16 | "compilerOptions": { 17 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], 18 | } 19 | ``` 20 | 21 | ## Contributing 22 | 23 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 24 | 25 | ### Install dependencies 26 | 27 | ```bash 28 | yarn install 29 | ``` 30 | 31 | ### Build 32 | 33 | ```bash 34 | yarn build 35 | ``` 36 | 37 | ### Clean 38 | 39 | ```bash 40 | yarn clean 41 | ``` 42 | 43 | ### Lint 44 | 45 | ```bash 46 | yarn lint 47 | ``` 48 | 49 | ### Run Tests 50 | 51 | ```bash 52 | yarn test 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/json_rpc_clients/typescript/README.md: -------------------------------------------------------------------------------- 1 | # @0x/mesh-rpc-client - v9.4.2 2 | 3 | ## @0x/mesh-rpc-client 4 | 5 | A Javascript library for interacting with the Mesh JSON RPC API over WebSockets. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | yarn add @0x/mesh-rpc-client 11 | ``` 12 | 13 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 14 | 15 | ```json 16 | "compilerOptions": { 17 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], 18 | } 19 | ``` 20 | 21 | ## Contributing 22 | 23 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 24 | 25 | ### Install dependencies 26 | 27 | ```bash 28 | yarn install 29 | ``` 30 | 31 | ### Build 32 | 33 | ```bash 34 | yarn build 35 | ``` 36 | 37 | ### Clean 38 | 39 | ```bash 40 | yarn clean 41 | ``` 42 | 43 | ### Lint 44 | 45 | ```bash 46 | yarn lint 47 | ``` 48 | 49 | ### Run Tests 50 | 51 | ```bash 52 | yarn test 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/summary.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | ## Getting started 4 | 5 | - [Deployment guide](deployment.md) 6 | - [Deploying a Telemetry-Enabled Mesh Node](deployment_with_telemetry.md) 7 | - [GraphQL API documentation](graphql_api.md) 8 | - [Browser API documentation](browser-bindings/browser/reference.md) 9 | - [Browser-Lite API documentation](browser-bindings/browser-lite/reference.md) 10 | - [Browser guide](browser.md) 11 | 12 | ## Advanced topics 13 | 14 | - [Custom order filters](custom_order_filters.md) 15 | - [Syncing an external DB with Mesh](db_syncing.md) 16 | 17 | ## GraphQL clients 18 | 19 | - [Golang client](https://godoc.org/github.com/0xProject/0x-mesh/rpc) 20 | - [TypeScript client](graphql_clients/typescript/README.md) 21 | - [Doc reference](graphql_clients/typescript/reference.md) 22 | 23 | ## Contributing 24 | 25 | - [Development and contribution guide](../CONTRIBUTING.md) 26 | - [CHANGELOG](../CHANGELOG.md) 27 | -------------------------------------------------------------------------------- /encoding/encoding.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/0xProject/0x-mesh/zeroex" 8 | ) 9 | 10 | type orderMessage struct { 11 | MessageType string `json:"messageType"` 12 | Order *zeroex.SignedOrder `json:"order"` 13 | Topics []string `json:"topics"` 14 | } 15 | 16 | // OrderToRawMessage encodes an order into an order message to be sent over the wire 17 | func OrderToRawMessage(topic string, order *zeroex.SignedOrder) ([]byte, error) { 18 | return json.Marshal(orderMessage{ 19 | MessageType: "order", 20 | Order: order, 21 | Topics: []string{topic}, 22 | }) 23 | } 24 | 25 | // RawMessageToOrder decodes an order message sent over the wire into an order 26 | func RawMessageToOrder(data []byte) (*zeroex.SignedOrder, error) { 27 | var orderMessage orderMessage 28 | if err := json.Unmarshal(data, &orderMessage); err != nil { 29 | return nil, err 30 | } 31 | if orderMessage.MessageType != "order" { 32 | return nil, fmt.Errorf("unexpected message type: %q", orderMessage.MessageType) 33 | } 34 | return orderMessage.Order, nil 35 | } 36 | -------------------------------------------------------------------------------- /ethereum/blockchain_lifecycle.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/rpc" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | // BlockchainLifecycle is a testing utility for taking snapshots of the blockchain 12 | // state on Ganache and reverting those snapshots at a later point in time. Ganache 13 | // supports performing multiple snapshots that can then be reverted in LIFO order. 14 | type BlockchainLifecycle struct { 15 | rpcClient *rpc.Client 16 | snapshotIdStack []string 17 | } 18 | 19 | // NewBlockchainLifecycle instantiates a new blockchainLifecycle instance 20 | func NewBlockchainLifecycle(rpcClient *rpc.Client) (*BlockchainLifecycle, error) { 21 | return &BlockchainLifecycle{ 22 | rpcClient: rpcClient, 23 | snapshotIdStack: []string{}, 24 | }, nil 25 | } 26 | 27 | // Start creates a snapshot of the blockchain state at that point in time 28 | // and adds it's snapshotId to a stack 29 | func (b *BlockchainLifecycle) Start(t *testing.T) { 30 | var snapshotId string 31 | err := b.rpcClient.Call(&snapshotId, "evm_snapshot") 32 | require.NoError(t, err) 33 | b.snapshotIdStack = append(b.snapshotIdStack, snapshotId) 34 | } 35 | 36 | // Revert reverts the latest snapshot of blockchain state created 37 | func (b *BlockchainLifecycle) Revert(t *testing.T) { 38 | latestSnapshot := b.snapshotIdStack[len(b.snapshotIdStack)-1] 39 | b.snapshotIdStack = b.snapshotIdStack[:len(b.snapshotIdStack)-1] 40 | var didRevert bool 41 | err := b.rpcClient.Call(&didRevert, "evm_revert", latestSnapshot) 42 | require.NoError(t, err) 43 | if !didRevert { 44 | t.Errorf("Failed to revert snapshot with ID: %s", latestSnapshot) 45 | } 46 | } 47 | 48 | // Mine force-mines a block with the specified block timestamp 49 | // WARNING(fabio): Using this method will brick `eth_getLogs` such that it always 50 | // returns the logs for the latest block, even if a specific blockHash is specified 51 | // Source: https://github.com/trufflesuite/ganache-cli/issues/708 52 | func (b *BlockchainLifecycle) Mine(t *testing.T, blockTimestamp time.Time) { 53 | var didForceMine string 54 | err := b.rpcClient.Call(&didForceMine, "evm_mine", blockTimestamp.Unix()) 55 | require.NoError(t, err) 56 | if didForceMine != "0x0" { 57 | t.Error("Failed to force mine a block") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ethereum/blockwatch/fake_log_client.go: -------------------------------------------------------------------------------- 1 | package blockwatch 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/big" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/0xProject/0x-mesh/common/types" 11 | "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/common" 13 | ethtypes "github.com/ethereum/go-ethereum/core/types" 14 | ) 15 | 16 | type filterLogsResponse struct { 17 | Logs []ethtypes.Log 18 | Err error 19 | } 20 | 21 | var _ Client = &fakeLogClient{} 22 | 23 | // fakeLogClient is a fake Client for testing code calling the `FilterLogs` method. 24 | // It allows the instatiator to specify `FilterLogs` responses for several block ranges. 25 | type fakeLogClient struct { 26 | count int64 27 | rangeToResponse map[string]filterLogsResponse 28 | } 29 | 30 | // newFakeLogClient instantiates a fakeLogClient for testing log fetching 31 | func newFakeLogClient(rangeToResponse map[string]filterLogsResponse) *fakeLogClient { 32 | return &fakeLogClient{count: 0, rangeToResponse: rangeToResponse} 33 | } 34 | 35 | // HeaderByNumber fetches a block header by its number 36 | func (fc *fakeLogClient) HeaderByNumber(number *big.Int) (*types.MiniHeader, error) { 37 | return nil, errors.New("NOT_IMPLEMENTED") 38 | } 39 | 40 | // HeaderByHash fetches a block header by its block hash 41 | func (fc *fakeLogClient) HeaderByHash(hash common.Hash) (*types.MiniHeader, error) { 42 | return nil, errors.New("NOT_IMPLEMENTED") 43 | } 44 | 45 | // FilterLogs returns the logs that satisfy the supplied filter query 46 | func (fc *fakeLogClient) FilterLogs(q ethereum.FilterQuery) ([]ethtypes.Log, error) { 47 | // Add a slight delay to simulate an actual network request. This also gives 48 | // BlockWatcher.getLogsInBlockRange multi-requests to hit the concurrent request 49 | // limit semaphore and simulate more realistic conditions. 50 | time.Sleep(5 * time.Millisecond) 51 | r := toRange(q.FromBlock, q.ToBlock) 52 | res, ok := fc.rangeToResponse[r] 53 | if !ok { 54 | return nil, fmt.Errorf("Didn't find response for range %s but was expecting it to exist", r) 55 | } 56 | atomic.AddInt64(&fc.count, 1) 57 | return res.Logs, res.Err 58 | } 59 | 60 | // Count returns the number of times FilterLogs was called 61 | func (fc *fakeLogClient) Count() int { 62 | return int(fc.count) 63 | } 64 | 65 | func toRange(from, to *big.Int) string { 66 | r := fmt.Sprintf("%s-%s", from, to) 67 | return r 68 | } 69 | -------------------------------------------------------------------------------- /ethereum/blockwatch/testdata/fake_client_basic_fixture.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "getLatestBlock": { 4 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 5 | "parent": "0x26b13ac89500f7fcdd141b7d1b30f3a82178431eca325d1cf10998f9d68ff5ba", 6 | "number": 5 7 | }, 8 | "getBlockByNumber": { 9 | "5": { 10 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 11 | "parent": "0x26b13ac89500f7fcdd141b7d1b30f3a82178431eca325d1cf10998f9d68ff5ba", 12 | "number": 5 13 | } 14 | }, 15 | "getBlockByHash": { 16 | "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f": { 17 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 18 | "parent": "0x26b13ac89500f7fcdd141b7d1b30f3a82178431eca325d1cf10998f9d68ff5ba", 19 | "number": 5 20 | } 21 | }, 22 | "getCorrectChain": [ 23 | { 24 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 25 | "parent": "0x26b13ac89500f7fcdd141b7d1b30f3a82178431eca325d1cf10998f9d68ff5ba", 26 | "number": 5 27 | } 28 | ], 29 | "blockEvents": [ 30 | { 31 | "type": 0, 32 | "blockHeader": { 33 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 34 | "parent": "0x26b13ac89500f7fcdd141b7d1b30f3a82178431eca325d1cf10998f9d68ff5ba", 35 | "number": 5 36 | } 37 | } 38 | ], 39 | "scenarioLabel": "FIND_NEXT_BLOCK" 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /ethereum/blockwatch/testdata/fake_client_reset_fixture.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "getLatestBlock": { 4 | "hash": "0x382b25dd926322722bba2575b5092be661a99f26472e2b7c2bb3d51e6bd2b09c", 5 | "parent": "0xb57233b65a0da953da19d685d10a65e3124207fd1cf4694e3fc0f7279373bf5f", 6 | "number": 133 7 | }, 8 | "getBlockByNumber": { 9 | "5": { 10 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 11 | "parent": "0x26b13ac89500f7fcdd141b7d1b135f3a82178431eca325d1cf10998f9d68ff5ba", 12 | "number": 5 13 | }, 14 | "133": { 15 | "hash": "0x382b25dd926322722bba2575b5092be661a99f26472e2b7c2bb3d51e6bd2b09c", 16 | "parent": "0xb57233b65a0da953da19d685d10a65e3124207fd1cf4694e3fc0f7279373bf5f", 17 | "number": 133 18 | } 19 | }, 20 | "getBlockByHash": { 21 | "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f": { 22 | "hash": "0x293b9ea024055a3e9eddbf9b9383dc7731744111894af6aa038594dc1b61f87f", 23 | "parent": "0x26b13ac89500f7fcdd141b7d1b135f3a82178431eca325d1cf10998f9d68ff5ba", 24 | "number": 5 25 | }, 26 | "0x382b25dd926322722bba2575b5092be661a99f26472e2b7c2bb3d51e6bd2b09c": { 27 | "hash": "0x382b25dd926322722bba2575b5092be661a99f26472e2b7c2bb3d51e6bd2b09c", 28 | "parent": "0xb57233b65a0da953da19d685d10a65e3124207fd1cf4694e3fc0f7279373bf5f", 29 | "number": 133 30 | } 31 | }, 32 | "getCorrectChain": [], 33 | "blockEvents": [], 34 | "scenarioLabel": "RESET" 35 | } 36 | ] 37 | -------------------------------------------------------------------------------- /ethereum/ratelimit/fake_rate_limiter.go: -------------------------------------------------------------------------------- 1 | package ratelimit 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // fakeLimiter is a fake RateLimiter that always allows a request through 10 | type fakeLimiter struct { 11 | currentUTCCheckpoint time.Time // Start of current UTC 24hr period 12 | grantedInLast24hrsUTC int // Number of granted requests issued in last 24hr UTC 13 | mu sync.Mutex 14 | } 15 | 16 | // NewUnlimited returns a new RateLimiter without any limits. It will always 17 | // allow requests immediately. It still keeps track of the number of requests 18 | // that are allowed. 19 | func NewUnlimited() RateLimiter { 20 | return &fakeLimiter{ 21 | currentUTCCheckpoint: GetUTCMidnightOfDate(time.Now()), 22 | grantedInLast24hrsUTC: 0, 23 | } 24 | } 25 | 26 | // Start starts the fake rateLimiter 27 | func (f *fakeLimiter) Start(ctx context.Context, checkpointInterval time.Duration) error { 28 | return nil 29 | } 30 | 31 | // Wait blocks until the rateLimiter allows for another request to be sent 32 | func (f *fakeLimiter) Wait(ctx context.Context) error { 33 | f.mu.Lock() 34 | f.grantedInLast24hrsUTC++ 35 | f.mu.Unlock() 36 | return nil 37 | } 38 | 39 | func (f *fakeLimiter) getGrantedInLast24hrsUTC() int { 40 | return f.grantedInLast24hrsUTC 41 | } 42 | 43 | func (f *fakeLimiter) getCurrentUTCCheckpoint() time.Time { 44 | return f.currentUTCCheckpoint 45 | } 46 | -------------------------------------------------------------------------------- /ethereum/signer/sign_test.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package signer 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/0xProject/0x-mesh/constants" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/rpc" 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestEthRPCSigner(t *testing.T) { 16 | // Test parameters lifted from @0x/order-utils' `signature_utils_test.ts` 17 | signerAddress := constants.GanacheAccount0 18 | message := common.Hex2Bytes("6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0") 19 | expectedSignature := &ECSignature{ 20 | V: byte(27), 21 | R: common.HexToHash("61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33"), 22 | S: common.HexToHash("40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"), 23 | } 24 | 25 | rpcClient, err := rpc.Dial(constants.GanacheEndpoint) 26 | require.NoError(t, err) 27 | ethRPCSigner := NewEthRPCSigner(rpcClient) 28 | actualSignature, err := ethRPCSigner.EthSign(message, signerAddress) 29 | require.NoError(t, err) 30 | 31 | assert.Equal(t, expectedSignature, actualSignature) 32 | } 33 | 34 | func TestTestSigner(t *testing.T) { 35 | // Test parameters lifted from @0x/order-utils' `signature_utils_test.ts` 36 | signerAddress := constants.GanacheAccount0 37 | message := common.Hex2Bytes("6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0") 38 | expectedSignature := &ECSignature{ 39 | V: byte(27), 40 | R: common.HexToHash("61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33"), 41 | S: common.HexToHash("40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"), 42 | } 43 | 44 | testSigner := NewTestSigner() 45 | actualSignature, err := testSigner.EthSign(message, signerAddress) 46 | require.NoError(t, err) 47 | 48 | assert.Equal(t, expectedSignature, actualSignature) 49 | } 50 | -------------------------------------------------------------------------------- /gqlgen.yml: -------------------------------------------------------------------------------- 1 | # Where are all the schema files located? globs are supported eg src/**/*.graphqls 2 | schema: 3 | - graphql/*.graphql 4 | 5 | # Where should the generated server code go? 6 | exec: 7 | filename: graphql/generated/generated.go 8 | package: generated 9 | 10 | # Uncomment to enable federation 11 | # federation: 12 | # filename: graph/generated/federation.go 13 | # package: generated 14 | 15 | # Where should any generated models go? 16 | model: 17 | filename: graphql/gqltypes/types_generated.go 18 | package: gqltypes 19 | 20 | # Where should the resolver implementations go? 21 | resolver: 22 | layout: follow-schema 23 | dir: graphql 24 | package: graphql 25 | 26 | # Optional: turn on use `gqlgen:"fieldName"` tags in your models 27 | # struct_tag: json 28 | 29 | # Optional: turn on to use []Thing instead of []*Thing 30 | # omit_slice_element_pointers: false 31 | 32 | # Optional: set to speed up generation time by not performing a final validation pass. 33 | # skip_validation: true 34 | 35 | # gqlgen will search for any type names in the schema in these go packages 36 | # if they match it will use them, otherwise it will generate them. 37 | autobind: 38 | - 'github.com/0xProject/0x-mesh/graphql/gqltypes' 39 | 40 | # This section declares type mapping between the GraphQL and go type systems 41 | # 42 | # The first line in each type will be used as defaults for resolver arguments and 43 | # modelgen, the others will be allowed when binding to fields. Configure them to 44 | # your liking 45 | models: 46 | ID: 47 | model: 48 | - github.com/99designs/gqlgen/graphql.ID 49 | - github.com/99designs/gqlgen/graphql.Int 50 | - github.com/99designs/gqlgen/graphql.Int64 51 | - github.com/99designs/gqlgen/graphql.Int32 52 | Int: 53 | model: 54 | - github.com/99designs/gqlgen/graphql.Int 55 | - github.com/99designs/gqlgen/graphql.Int64 56 | - github.com/99designs/gqlgen/graphql.Int32 57 | -------------------------------------------------------------------------------- /graphql/constants.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | const ( 4 | orderEventBufferSize = 100 5 | ) 6 | 7 | //go:generate gqlgen generate 8 | -------------------------------------------------------------------------------- /graphql/gqltypes/conversion_v4.go: -------------------------------------------------------------------------------- 1 | package gqltypes 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/0xProject/0x-mesh/zeroex/ordervalidator" 8 | ) 9 | 10 | func AddOrdersResultsFromValidationResultsV4(validationResults *ordervalidator.ValidationResults) (*AddOrdersResultsV4, error) { 11 | rejected, err := RejectedOrderResultsFromOrderInfosV4(validationResults.Rejected) 12 | if err != nil { 13 | return nil, err 14 | } 15 | return &AddOrdersResultsV4{ 16 | Accepted: AcceptedOrderResultsFromOrderInfosV4(validationResults.Accepted), 17 | Rejected: rejected, 18 | }, nil 19 | } 20 | 21 | func RejectedOrderResultsFromOrderInfosV4(infos []*ordervalidator.RejectedOrderInfo) ([]*RejectedOrderResultV4, error) { 22 | result := make([]*RejectedOrderResultV4, len(infos)) 23 | for i, info := range infos { 24 | rejectedResult, err := RejectedOrderResultFromOrderInfoV4(info) 25 | if err != nil { 26 | return nil, err 27 | } 28 | result[i] = rejectedResult 29 | } 30 | return result, nil 31 | } 32 | 33 | func RejectedOrderResultFromOrderInfoV4(info *ordervalidator.RejectedOrderInfo) (*RejectedOrderResultV4, error) { 34 | var hash *string 35 | if hashString := info.OrderHash.Hex(); hashString != "0x" { 36 | hash = &hashString 37 | } 38 | code, err := RejectedCodeFromValidatorStatus(info.Status) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return &RejectedOrderResultV4{ 43 | Hash: hash, 44 | Order: &OrderV4{ 45 | ChainID: info.SignedOrderV4.OrderV4.ChainID.String(), 46 | VerifyingContract: strings.ToLower(info.SignedOrderV4.OrderV4.VerifyingContract.Hex()), 47 | Maker: strings.ToLower(info.SignedOrderV4.OrderV4.Maker.Hex()), 48 | Taker: strings.ToLower(info.SignedOrderV4.OrderV4.Taker.Hex()), 49 | Sender: strings.ToLower(info.SignedOrderV4.OrderV4.Sender.Hex()), 50 | MakerAmount: info.SignedOrderV4.OrderV4.MakerAmount.String(), 51 | MakerToken: strings.ToLower(info.SignedOrderV4.OrderV4.MakerToken.Hex()), 52 | TakerAmount: info.SignedOrderV4.OrderV4.TakerAmount.String(), 53 | TakerToken: strings.ToLower(info.SignedOrderV4.OrderV4.TakerToken.Hex()), 54 | TakerTokenFeeAmount: info.SignedOrderV4.OrderV4.TakerTokenFeeAmount.String(), 55 | Pool: info.SignedOrderV4.OrderV4.Pool.String(), 56 | Expiry: info.SignedOrderV4.OrderV4.Expiry.String(), 57 | Salt: info.SignedOrderV4.OrderV4.Salt.String(), 58 | }, 59 | Code: code, 60 | Message: info.Status.Message, 61 | }, nil 62 | } 63 | 64 | func AcceptedOrderResultFromOrderInfoV4(info *ordervalidator.AcceptedOrderInfo) *AcceptedOrderResultV4 { 65 | return &AcceptedOrderResultV4{ 66 | Order: &OrderV4WithMetadata{ 67 | Hash: info.OrderHash.Hex(), 68 | ChainID: info.SignedOrderV4.ChainID.String(), 69 | VerifyingContract: strings.ToLower(info.SignedOrderV4.VerifyingContract.Hex()), 70 | Maker: strings.ToLower(info.SignedOrderV4.Maker.Hex()), 71 | Taker: strings.ToLower(info.SignedOrderV4.Taker.Hex()), 72 | Sender: strings.ToLower(info.SignedOrderV4.Sender.Hex()), 73 | MakerAmount: info.SignedOrderV4.MakerAmount.String(), 74 | MakerToken: strings.ToLower(info.SignedOrderV4.MakerToken.Hex()), 75 | TakerAmount: info.SignedOrderV4.TakerAmount.String(), 76 | TakerToken: strings.ToLower(info.SignedOrderV4.TakerToken.Hex()), 77 | TakerTokenFeeAmount: info.SignedOrderV4.TakerTokenFeeAmount.String(), 78 | Pool: info.SignedOrderV4.Pool.String(), 79 | Expiry: info.SignedOrderV4.Expiry.String(), 80 | Salt: info.SignedOrderV4.Salt.String(), 81 | SignatureType: info.SignedOrderV4.Signature.SignatureType.String(), 82 | SignatureV: strconv.FormatUint(uint64(info.SignedOrderV4.Signature.V), 10), 83 | SignatureR: info.SignedOrderV4.Signature.R.String(), 84 | SignatureS: info.SignedOrderV4.Signature.S.String(), 85 | FillableTakerAssetAmount: info.FillableTakerAssetAmount.String(), 86 | }, 87 | IsNew: info.IsNew, 88 | } 89 | } 90 | 91 | func AcceptedOrderResultsFromOrderInfosV4(infos []*ordervalidator.AcceptedOrderInfo) []*AcceptedOrderResultV4 { 92 | result := make([]*AcceptedOrderResultV4, len(infos)) 93 | for i, info := range infos { 94 | added := AcceptedOrderResultFromOrderInfoV4(info) 95 | result[i] = added 96 | } 97 | return result 98 | } 99 | -------------------------------------------------------------------------------- /graphql/gqltypes/utils.go: -------------------------------------------------------------------------------- 1 | package gqltypes 2 | 3 | import "strconv" 4 | 5 | func parseUint8FromStringOrPanic(s string) uint8 { 6 | val, err := strconv.ParseUint(s, 10, 8) 7 | if err != nil { 8 | panic(err) 9 | } 10 | return uint8(val) 11 | } 12 | -------------------------------------------------------------------------------- /graphql/resolver.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/0xProject/0x-mesh/core" 7 | ) 8 | 9 | type ResolverConfig struct { 10 | SlowSubscriberTimeout time.Duration 11 | } 12 | 13 | type Resolver struct { 14 | app *core.App 15 | config *ResolverConfig 16 | } 17 | 18 | func NewResolver(app *core.App, config *ResolverConfig) *Resolver { 19 | return &Resolver{ 20 | app: app, 21 | config: config, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/constants.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "time" 4 | 5 | const ( 6 | ethereumRPCURL = "http://localhost:8545" 7 | ethereumChainID = 1337 8 | 9 | graphQLServerAddr = "localhost:60501" 10 | graphQLServerURL = "http://localhost:60501/graphql" 11 | browserIntegrationTestDataDir = "./data/standalone-0" 12 | standaloneBlockPollingInterval = "200ms" 13 | standaloneEthereumRPCMaxRequestsPer24HrUtc = "550000" 14 | 15 | // Various config options/information for the bootstrap node. The private key 16 | // for the bootstrap node is checked in to version control so we know it's 17 | // peer ID ahead of time. 18 | bootstrapAddr = "/ip4/127.0.0.1/tcp/60500/ws" 19 | bootstrapList = "/ip4/127.0.0.1/tcp/60500/ws/ipfs/16Uiu2HAmGd949LwaV4KNvK2WDSiMVy7xEmW983VH75CMmefmMpP7" 20 | bootstrapDataDir = "./data/bootstrap-0" 21 | 22 | // serverStartWaitTime is the amount of time to wait after seeing the "starting GraphQL server" 23 | // log message before attempting to connect to the server. 24 | serverStartWaitTime = 100 * time.Millisecond 25 | 26 | // blockProcessingWaitTime is the amount of time to wait for blockwatcher and orderwatcher to process block 27 | // events. Creating a valid order involves transferring sufficient funds to the maker, and setting their allowance for 28 | // the maker asset. These transactions must be mined and Mesh's BlockWatcher poller must process these blocks 29 | // in order for the order validation run at order submission to occur at a block number equal or higher then 30 | // the one where these state changes were included. With the BlockWatcher poller configured to run every 200ms, 31 | // we wait 500ms to give it ample time to run before submitting the above order to the Mesh node. 32 | blockProcessingWaitTime = 500 * time.Millisecond 33 | ) 34 | -------------------------------------------------------------------------------- /integration-tests/data/bootstrap-0/keys/privkey: -------------------------------------------------------------------------------- 1 | CAISIPF3yWZ72zKSlTAWM1naqEaixUXjL2KHteOPKyyMghCr -------------------------------------------------------------------------------- /integration-tests/data/standalone-0/keys/privkey: -------------------------------------------------------------------------------- 1 | CAISII/icMM3OGlWZYp3NjgWSPWX4HIQHkWxKrDywtgpLnHw -------------------------------------------------------------------------------- /integration-tests/graphql_v4_integration_test.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package integrationtests 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "sync" 9 | "testing" 10 | "time" 11 | 12 | "github.com/0xProject/0x-mesh/scenario" 13 | "github.com/0xProject/0x-mesh/scenario/orderopts" 14 | "github.com/0xProject/0x-mesh/zeroex" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestAddOrdersSuccessV4(t *testing.T) { 20 | teardownSubTest := setupSubTest(t) 21 | defer teardownSubTest(t) 22 | 23 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 24 | defer cancel() 25 | wg := &sync.WaitGroup{} 26 | client, _ := buildAndStartGraphQLServer(t, ctx, wg) 27 | 28 | // Create a new valid order. 29 | signedTestOrder := scenario.NewSignedTestOrderV4(t, orderopts.SetupMakerState(true)) 30 | time.Sleep(blockProcessingWaitTime) 31 | 32 | fmt.Printf("%+v\n", signedTestOrder.Sender.Hex()) 33 | 34 | _, err := client.GetStats(ctx) 35 | require.NoError(t, err) 36 | 37 | // Send the "AddOrders" request to the GraphQL server. 38 | validationResponse, err := client.AddOrdersV4(ctx, []*zeroex.SignedOrderV4{signedTestOrder}) 39 | require.NoError(t, err) 40 | fmt.Printf("validation response: %+v\n", validationResponse) 41 | 42 | fmt.Println(validationResponse.Rejected) 43 | fmt.Println(validationResponse.Accepted) 44 | 45 | // Ensure that the validation results contain only the order that was 46 | // sent to the GraphQL server and that the order was marked as valid. 47 | require.Len(t, validationResponse.Accepted, 1) 48 | assert.Len(t, validationResponse.Rejected, 0) 49 | expectedFillableTakerAssetAmount := signedTestOrder.TakerAmount 50 | expectedOrderHash, err := signedTestOrder.ComputeOrderHash() 51 | require.NoError(t, err, "could not compute order hash for standalone order") 52 | fmt.Printf("the hash is: %+v and fillable is: %+v\n", expectedOrderHash, expectedFillableTakerAssetAmount) 53 | // expectedAcceptedOrder := &gqlclient.OrderWithMetadata{ 54 | // ChainID: signedTestOrder.ChainID, 55 | // ExchangeAddress: signedTestOrder.ExchangeAddress, 56 | // MakerAddress: signedTestOrder.MakerAddress, 57 | // MakerAssetData: signedTestOrder.MakerAssetData, 58 | // MakerAssetAmount: signedTestOrder.MakerAssetAmount, 59 | // MakerFeeAssetData: signedTestOrder.MakerFeeAssetData, 60 | // MakerFee: signedTestOrder.MakerFee, 61 | // TakerAddress: signedTestOrder.TakerAddress, 62 | // TakerAssetData: signedTestOrder.TakerAssetData, 63 | // TakerAssetAmount: signedTestOrder.TakerAssetAmount, 64 | // TakerFeeAssetData: signedTestOrder.TakerFeeAssetData, 65 | // TakerFee: signedTestOrder.TakerFee, 66 | // SenderAddress: signedTestOrder.SenderAddress, 67 | // FeeRecipientAddress: signedTestOrder.FeeRecipientAddress, 68 | // ExpirationTimeSeconds: signedTestOrder.ExpirationTimeSeconds, 69 | // Salt: signedTestOrder.Salt, 70 | // Signature: signedTestOrder.Signature, 71 | // Hash: expectedOrderHash, 72 | // FillableTakerAssetAmount: expectedFillableTakerAssetAmount, 73 | // } 74 | // assert.Equal(t, expectedAcceptedOrder, accepted.Order, "accepted.Order") 75 | // assert.Equal(t, true, accepted.IsNew, "accepted.IsNew") 76 | findResults, err := client.FindOrdersV4(ctx) 77 | require.NoError(t, err) 78 | fmt.Println(len(findResults)) 79 | 80 | cancel() 81 | wg.Wait() 82 | } 83 | 84 | func TestGetOrderV4(t *testing.T) { 85 | teardownSubTest := setupSubTest(t) 86 | defer teardownSubTest(t) 87 | 88 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 89 | defer cancel() 90 | wg := &sync.WaitGroup{} 91 | client, _ := buildAndStartGraphQLServer(t, ctx, wg) 92 | 93 | orderOptions := orderopts.SetupMakerState(true) 94 | signedTestOrder := scenario.NewSignedTestOrderV4(t, orderOptions) 95 | time.Sleep(blockProcessingWaitTime) 96 | 97 | validationResponse, err := client.AddOrdersV4(ctx, []*zeroex.SignedOrderV4{signedTestOrder}) 98 | require.NoError(t, err) 99 | assert.Len(t, validationResponse.Accepted, 1) 100 | assert.Len(t, validationResponse.Rejected, 0) 101 | 102 | acceptedOne := validationResponse.Accepted[0].Order.Hash 103 | fmt.Printf("the hash is: %+v\n", acceptedOne) 104 | 105 | expectedHash, err := signedTestOrder.ComputeOrderHash() 106 | require.NoError(t, err) 107 | actualOrder, err := client.GetOrderV4(ctx, expectedHash) 108 | require.NoError(t, err) 109 | 110 | fmt.Printf("%+v\n", actualOrder) 111 | 112 | cancel() 113 | wg.Wait() 114 | } 115 | -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | Please answer the following questions for yourself before submitting an issue. **YOU MAY DELETE THE PREREQUISITES SECTION.** 4 | 5 | - [ ] I checked the documentation and found no answer 6 | - [ ] I checked to make sure that this issue has not already been filed 7 | 8 | # Context 9 | 10 | Please provide any relevant information about your setup 11 | 12 | - Are you running Mesh in the browser or as a standalone server? Are running Mesh inside of Docker or directly running the binary? 13 | - What version of Mesh are you running? Be as specific as possible (e.g., `8.0.1` instead of `latest` or `8`). 14 | - Are you using one of our GraphQL clients and if so, which one? (e.g., Typescript or Golang client, the exact version) 15 | 16 | # Expected Behavior 17 | 18 | Please describe the behavior you are expecting. 19 | 20 | # Current Behavior 21 | 22 | What is the current behavior? 23 | 24 | # Failure Information (for bugs) 25 | 26 | Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template. 27 | 28 | ## Steps to Reproduce 29 | 30 | Please provide detailed steps for reproducing the issue. 31 | 32 | 1. step 1 33 | 2. step 2 34 | 3. you get it... 35 | 36 | ## Failure Logs 37 | 38 | Please include any relevant log snippets or files here. 39 | -------------------------------------------------------------------------------- /keys/fs.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package keys 4 | 5 | import ( 6 | "io/ioutil" 7 | "os" 8 | ) 9 | 10 | func readFile(path string) ([]byte, error) { 11 | return ioutil.ReadFile(path) 12 | } 13 | 14 | func mkdirAll(dir string) error { 15 | return os.MkdirAll(dir, os.ModePerm) 16 | } 17 | 18 | func writeFile(path string, data []byte) error { 19 | return ioutil.WriteFile(path, data, os.ModePerm) 20 | } 21 | -------------------------------------------------------------------------------- /keys/fs_js.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package keys 4 | 5 | import ( 6 | "encoding/base64" 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | "syscall/js" 11 | 12 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/jsutil" 13 | ) 14 | 15 | // keyPrefix is a prefix applied to all entries in localStorage. 16 | const keyPrefix = "0x-mesh-keys:" 17 | 18 | // getKey returns the localStorage key corresponding to the given path. 19 | func getKey(path string) string { 20 | return keyPrefix + path 21 | } 22 | 23 | func readFile(path string) ([]byte, error) { 24 | if isLocalStorageSupported() { 25 | return localStorageReadFile(path) 26 | } 27 | return ioutil.ReadFile(path) 28 | } 29 | 30 | func localStorageReadFile(path string) (data []byte, err error) { 31 | defer func() { 32 | if e := recover(); e != nil { 33 | err = convertRecoverErr(e) 34 | } 35 | }() 36 | key := getKey(path) 37 | rawData := js.Global().Get("localStorage").Call("getItem", key) 38 | if jsutil.IsNullOrUndefined(rawData) { 39 | return nil, os.ErrNotExist 40 | } 41 | return base64.StdEncoding.DecodeString(rawData.String()) 42 | } 43 | 44 | func mkdirAll(dir string) error { 45 | if isLocalStorageSupported() { 46 | return localStorageMkdirall(dir) 47 | } 48 | return os.MkdirAll(dir, os.ModePerm) 49 | } 50 | 51 | func localStorageMkdirall(dir string) (err error) { 52 | // We don't need to mkdir in localStorage because each path corresponds 53 | // exactly to one key. 54 | return nil 55 | } 56 | 57 | func writeFile(path string, data []byte) error { 58 | if isLocalStorageSupported() { 59 | return localStorageWriteFile(path, data) 60 | } 61 | return ioutil.WriteFile(path, data, os.ModePerm) 62 | } 63 | 64 | func localStorageWriteFile(path string, data []byte) (err error) { 65 | defer func() { 66 | if e := recover(); e != nil { 67 | err = convertRecoverErr(e) 68 | } 69 | }() 70 | key := getKey(path) 71 | encodedData := base64.StdEncoding.EncodeToString(data) 72 | js.Global().Get("localStorage").Call("setItem", key, encodedData) 73 | return nil 74 | } 75 | 76 | // isLocalStorageSupported returns true if localStorage is supported. It does 77 | // this by checking for the global "localStorage" object. 78 | func isLocalStorageSupported() bool { 79 | return !jsutil.IsNullOrUndefined(js.Global().Get("localStorage")) 80 | } 81 | 82 | func convertRecoverErr(e interface{}) error { 83 | switch e := e.(type) { 84 | case error: 85 | return e 86 | default: 87 | return fmt.Errorf("recovered with non-error: %v", e) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /keys/keys.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "crypto/rand" 5 | "path/filepath" 6 | 7 | p2pcrypto "github.com/libp2p/go-libp2p-core/crypto" 8 | ) 9 | 10 | func GetPrivateKeyFromPath(path string) (p2pcrypto.PrivKey, error) { 11 | keyBytes, err := readFile(path) 12 | if err != nil { 13 | return nil, err 14 | } 15 | decodedKey, err := p2pcrypto.ConfigDecodeKey(string(keyBytes)) 16 | if err != nil { 17 | return nil, err 18 | } 19 | priv, err := p2pcrypto.UnmarshalPrivateKey(decodedKey) 20 | if err != nil { 21 | return nil, err 22 | } 23 | return priv, nil 24 | } 25 | 26 | func GenerateAndSavePrivateKey(path string) (p2pcrypto.PrivKey, error) { 27 | dir := filepath.Dir(path) 28 | if err := mkdirAll(dir); err != nil { 29 | return nil, err 30 | } 31 | privKey, _, err := p2pcrypto.GenerateSecp256k1Key(rand.Reader) 32 | if err != nil { 33 | return nil, err 34 | } 35 | keyBytes, err := p2pcrypto.MarshalPrivateKey(privKey) 36 | if err != nil { 37 | return nil, err 38 | } 39 | encodedKey := p2pcrypto.ConfigEncodeKey(keyBytes) 40 | if err := writeFile(path, []byte(encodedKey)); err != nil { 41 | return nil, err 42 | } 43 | return privKey, nil 44 | } 45 | -------------------------------------------------------------------------------- /keys/keys_test.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/google/uuid" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestGenerateAndGetKey(t *testing.T) { 13 | path := "/tmp/keys/" + uuid.New().String() 14 | generatedKey, err := GenerateAndSavePrivateKey(path) 15 | require.NoError(t, err) 16 | gotKey, err := GetPrivateKeyFromPath(path) 17 | require.NoError(t, err) 18 | assert.Equal(t, generatedKey, gotKey) 19 | } 20 | 21 | func TestGetKeyNotExists(t *testing.T) { 22 | nonExistentPath := "/tmp/keys/" + uuid.New().String() 23 | _, err := GetPrivateKeyFromPath(nonExistentPath) 24 | assert.Error(t, err) 25 | assert.True(t, os.IsNotExist(err), "error should be a NotExist error, but got: (%T) %s", err, err) 26 | } 27 | -------------------------------------------------------------------------------- /loghooks/key_suffix_hook.go: -------------------------------------------------------------------------------- 1 | package loghooks 2 | 3 | import ( 4 | "bytes" 5 | "encoding" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "reflect" 10 | "strings" 11 | 12 | log "github.com/sirupsen/logrus" 13 | ) 14 | 15 | var errNestedMapType = errors.New("nested map types are not supported") 16 | 17 | // KeySuffixHook is a logger hook that adds suffixes to all keys based on their 18 | // type. 19 | type KeySuffixHook struct{} 20 | 21 | // NewKeySuffixHook creates and returns a new KeySuffixHook. 22 | func NewKeySuffixHook() *KeySuffixHook { 23 | return &KeySuffixHook{} 24 | } 25 | 26 | // Ensure that KeySuffixHook implements log.Hook. 27 | var _ log.Hook = &KeySuffixHook{} 28 | 29 | func (h *KeySuffixHook) Levels() []log.Level { 30 | return log.AllLevels 31 | } 32 | 33 | func (h *KeySuffixHook) Fire(entry *log.Entry) error { 34 | newFields := log.Fields{} 35 | for key, value := range entry.Data { 36 | typ, err := getTypeForValue(value) 37 | if err != nil { 38 | if err == errNestedMapType { 39 | // We can't safely log nested map types, so replace the value with a 40 | // string. 41 | newKey := fmt.Sprintf("%s_json_string", key) 42 | mapString, err := json.Marshal(value) 43 | if err != nil { 44 | return err 45 | } 46 | newFields[newKey] = string(mapString) 47 | continue 48 | } else { 49 | return err 50 | } 51 | } 52 | newKey := fmt.Sprintf("%s_%s", key, typ) 53 | newFields[newKey] = value 54 | } 55 | entry.Data = newFields 56 | return nil 57 | } 58 | 59 | const stringType = "string" 60 | 61 | // getTypeForValue returns a string representation of the type of the given val. 62 | func getTypeForValue(val interface{}) (string, error) { 63 | if val == nil { 64 | return "null", nil 65 | } 66 | if _, ok := val.(json.Marshaler); ok { 67 | // If val implements json.Marshaler, return the type of json.Marshal(val) 68 | // instead of the type of val. 69 | buf := &bytes.Buffer{} 70 | if err := json.NewEncoder(buf).Encode(val); err != nil { 71 | return "", err 72 | } 73 | var holder interface{} 74 | if err := json.NewDecoder(buf).Decode(&holder); err != nil { 75 | return "", err 76 | } 77 | return getTypeForValue(holder) 78 | } 79 | if _, ok := val.(encoding.TextMarshaler); ok { 80 | // The json package always encodes values that implement 81 | // encoding.TextMarshaler as a string. 82 | return stringType, nil 83 | } 84 | if _, ok := val.(error); ok { 85 | // The json package always encodes values that implement 86 | // error as a string. 87 | return stringType, nil 88 | } 89 | 90 | underlyingType := getUnderlyingType(reflect.TypeOf(val)) 91 | switch kind := underlyingType.Kind(); kind { 92 | case reflect.Ptr: 93 | reflectVal := reflect.ValueOf(val) 94 | if !reflectVal.IsNil() { 95 | return getTypeForValue(reflectVal.Elem()) 96 | } else { 97 | return "null", nil 98 | } 99 | case reflect.Bool: 100 | return "bool", nil 101 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: 102 | return "number", nil 103 | case reflect.String, reflect.Complex64, reflect.Complex128, reflect.Func, reflect.Chan: 104 | return stringType, nil 105 | case reflect.Array, reflect.Slice: 106 | return "array", nil 107 | case reflect.Map: 108 | // Nested map types can't be efficiently indexed because they allow for 109 | // arbitrary keys. We don't allow them. 110 | return "", errNestedMapType 111 | case reflect.Struct: 112 | return getSafeStructTypeName(underlyingType) 113 | default: 114 | return "", fmt.Errorf("cannot determine type suffix for kind: %s", kind) 115 | } 116 | } 117 | 118 | // getUnderlyingType returns the underlying type for the given type by 119 | // recursively dereferencing pointer types. 120 | func getUnderlyingType(typ reflect.Type) reflect.Type { 121 | if typ.Kind() == reflect.Ptr { 122 | return getUnderlyingType(typ.Elem()) 123 | } 124 | return typ 125 | } 126 | 127 | // getSafeStructTypeName replaces dots in the name of the given type with 128 | // underscores. Elasticsearch does not allow dots in key names. 129 | func getSafeStructTypeName(typ reflect.Type) (string, error) { 130 | unsafeTypeName := typ.String() 131 | safeTypeName := strings.ReplaceAll(unsafeTypeName, ".", "_") 132 | return safeTypeName, nil 133 | } 134 | -------------------------------------------------------------------------------- /loghooks/key_suffix_hook_test.go: -------------------------------------------------------------------------------- 1 | package loghooks 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "syscall" 7 | "testing" 8 | 9 | "github.com/0xProject/0x-mesh/constants" 10 | 11 | log "github.com/sirupsen/logrus" 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | type myStruct struct { 17 | } 18 | 19 | func TestGetTypeForValue(t *testing.T) { 20 | testCases := []struct { 21 | input interface{} 22 | expected string 23 | }{ 24 | { 25 | input: true, 26 | expected: "bool", 27 | }, 28 | { 29 | input: int(42), 30 | expected: "number", 31 | }, 32 | { 33 | input: int8(42), 34 | expected: "number", 35 | }, 36 | { 37 | input: int16(42), 38 | expected: "number", 39 | }, 40 | { 41 | input: int32(42), 42 | expected: "number", 43 | }, 44 | { 45 | input: int64(42), 46 | expected: "number", 47 | }, 48 | { 49 | input: uint(42), 50 | expected: "number", 51 | }, 52 | { 53 | input: uint8(42), 54 | expected: "number", 55 | }, 56 | { 57 | input: uint16(42), 58 | expected: "number", 59 | }, 60 | { 61 | input: uint32(42), 62 | expected: "number", 63 | }, 64 | { 65 | input: uint64(42), 66 | expected: "number", 67 | }, 68 | { 69 | input: float32(42), 70 | expected: "number", 71 | }, 72 | { 73 | input: float64(42), 74 | expected: "number", 75 | }, 76 | { 77 | input: "foo", 78 | expected: "string", 79 | }, 80 | { 81 | input: complex64(42i + 7), 82 | expected: "string", 83 | }, 84 | { 85 | input: complex128(42i + 7), 86 | expected: "string", 87 | }, 88 | { 89 | input: errors.New("this is an error"), 90 | expected: "string", 91 | }, 92 | { 93 | input: syscall.Errno(1), 94 | expected: "string", 95 | }, 96 | { 97 | input: func() {}, 98 | expected: "string", 99 | }, 100 | { 101 | input: make(chan struct{}), 102 | expected: "string", 103 | }, 104 | { 105 | input: []int{}, 106 | expected: "array", 107 | }, 108 | { 109 | input: [...]int{}, 110 | expected: "array", 111 | }, 112 | { 113 | input: myStruct{}, 114 | expected: "loghooks_myStruct", 115 | }, 116 | { 117 | input: &myStruct{}, 118 | expected: "loghooks_myStruct", 119 | }, 120 | { 121 | // Implements encoding.TextUnmarshaler but not json.Marshaler. 122 | input: constants.NullAddress, 123 | expected: "string", 124 | }, 125 | { 126 | // We don't expect the case of anonymous structs to come up often, but we 127 | // do handle it correcly. " ", "{", "}", and ";" are allowed in 128 | // Elasticsearch. The resulting string is ugly but at least it is 129 | // guaranteed to prevent field mapping conflicts. 130 | input: struct { 131 | myInt int 132 | myString string 133 | }{}, 134 | expected: "struct { myInt int; myString string }", 135 | }, 136 | } 137 | 138 | for _, testCase := range testCases { 139 | testCaseInfo := fmt.Sprintf("input: (%T) %v", testCase.input, testCase.input) 140 | actual, err := getTypeForValue(testCase.input) 141 | require.NoError(t, err, testCaseInfo) 142 | assert.Equal(t, testCase.expected, actual, testCaseInfo) 143 | } 144 | } 145 | 146 | func TestKeySuffixHookWithNestedMapType(t *testing.T) { 147 | hook := NewKeySuffixHook() 148 | entry := &log.Entry{ 149 | Data: log.Fields{ 150 | "myMap": map[string]int{"one": 1}, 151 | }, 152 | } 153 | require.NoError(t, hook.Fire(entry)) 154 | expectedData := log.Fields{ 155 | "myMap_json_string": `{"one":1}`, 156 | } 157 | assert.Equal(t, expectedData, entry.Data) 158 | } 159 | -------------------------------------------------------------------------------- /loghooks/peer_id_hook.go: -------------------------------------------------------------------------------- 1 | package loghooks 2 | 3 | import ( 4 | peer "github.com/libp2p/go-libp2p-core/peer" 5 | log "github.com/sirupsen/logrus" 6 | ) 7 | 8 | // PeerIDHook is a logger hook that injects the peer ID in all logs when 9 | // possible. 10 | type PeerIDHook struct { 11 | peerID string 12 | } 13 | 14 | // NewPeerIDHook creates and returns a new PeerIDHook with the given peer ID. 15 | func NewPeerIDHook(peerID peer.ID) *PeerIDHook { 16 | return &PeerIDHook{peerID: peerID.String()} 17 | } 18 | 19 | // Ensure that PeerIDHook implements log.Hook. 20 | var _ log.Hook = &PeerIDHook{} 21 | 22 | func (h *PeerIDHook) Levels() []log.Level { 23 | return log.AllLevels 24 | } 25 | 26 | func (h *PeerIDHook) Fire(entry *log.Entry) error { 27 | entry.Data["myPeerID"] = h.peerID 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/prometheus/client_golang/prometheus" 8 | "github.com/prometheus/client_golang/prometheus/promauto" 9 | "github.com/prometheus/client_golang/prometheus/promhttp" 10 | ) 11 | 12 | const ( 13 | ProtocolVersionLabel = "protocol_version" 14 | ProtocolV3 = "v3" 15 | ProtocolV4 = "v4" 16 | ValidationStatusLabel = "validation_status" 17 | ValidationAccepted = "accepted" 18 | ValidationRejected = "rejected" 19 | QueryLabel = "query" 20 | OrdersyncStatusLabel = "status" 21 | OrdersyncSuccess = "success" 22 | OrdersyncFailure = "failure" 23 | ) 24 | 25 | var ( 26 | OrdersShared = promauto.NewCounterVec(prometheus.CounterOpts{ 27 | Name: "mesh_orders_shared_total", 28 | Help: "The total number of shared orders", 29 | }, 30 | []string{ 31 | ProtocolVersionLabel, 32 | }) 33 | 34 | OrdersStored = promauto.NewGaugeVec(prometheus.GaugeOpts{ 35 | Name: "mesh_orders_total", 36 | Help: "Current total number of stored orders", 37 | }, 38 | []string{ 39 | ProtocolVersionLabel, 40 | }) 41 | PeersConnected = promauto.NewGauge(prometheus.GaugeOpts{ 42 | Name: "mesh_peers_connected_total", 43 | Help: "Current total number of connected peers", 44 | }) 45 | 46 | OrdersAddedViaGraphQl = promauto.NewCounterVec(prometheus.CounterOpts{ 47 | Name: "mesh_graphql_orders_added", 48 | Help: "Total number of orders added / rejected via graphql", 49 | }, 50 | []string{ 51 | ProtocolVersionLabel, 52 | ValidationStatusLabel, 53 | }) 54 | 55 | P2POrdersReceived = promauto.NewCounterVec(prometheus.CounterOpts{ 56 | Name: "mesh_p2p_orders_received", 57 | Help: "Number of orders received", 58 | }, []string{ 59 | ProtocolVersionLabel, 60 | ValidationStatusLabel, 61 | }) 62 | 63 | GraphqlQueries = promauto.NewCounterVec(prometheus.CounterOpts{ 64 | Name: "mesh_graphql_queries_total", 65 | Help: "Total number of GraphQL endpoint queries handled", 66 | }, []string{ 67 | QueryLabel, 68 | }) 69 | 70 | OrdersyncRequestsReceived = promauto.NewCounterVec(prometheus.CounterOpts{ 71 | Name: "mesh_ordersync_requests_received", 72 | Help: "Number of ordersync requests received", 73 | }, []string{ 74 | ProtocolVersionLabel, 75 | }) 76 | 77 | OrdersyncRequestsSent = promauto.NewCounterVec(prometheus.CounterOpts{ 78 | Name: "mesh_ordersync_requests_sent", 79 | Help: "Number of ordersync requests sent", 80 | }, []string{ 81 | ProtocolVersionLabel, 82 | OrdersyncStatusLabel, 83 | }) 84 | 85 | LatestBlock = promauto.NewGauge(prometheus.GaugeOpts{ 86 | Name: "mesh_latest_block", 87 | Help: "Latest block number recognized by mesh", 88 | }) 89 | ) 90 | 91 | func ServeMetrics(ctx context.Context, serveAddr string) error { 92 | http.Handle("/metrics", promhttp.Handler()) 93 | 94 | return http.ListenAndServe(serveAddr, nil) 95 | } 96 | -------------------------------------------------------------------------------- /orderfilter/filter.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package orderfilter 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/0xProject/0x-mesh/ethereum" 10 | "github.com/ethereum/go-ethereum/common" 11 | jsonschema "github.com/xeipuuv/gojsonschema" 12 | ) 13 | 14 | var ( 15 | // Built-in schemas 16 | addressSchemaLoader = jsonschema.NewStringLoader(addressSchema) 17 | wholeNumberSchemaLoader = jsonschema.NewStringLoader(wholeNumberSchema) 18 | hexSchemaLoader = jsonschema.NewStringLoader(hexSchema) 19 | orderSchemaLoader = jsonschema.NewStringLoader(orderSchema) 20 | orderV4SchemaLoader = jsonschema.NewStringLoader(orderV4Schema) 21 | signedOrderSchemaLoader = jsonschema.NewStringLoader(signedOrderSchema) 22 | signedOrderV4SchemaLoader = jsonschema.NewStringLoader(signedOrderV4Schema) 23 | 24 | // Root schemas 25 | rootOrderV4SchemaLoader = jsonschema.NewStringLoader(rootOrderV4Schema) 26 | rootOrderSchemaLoader = jsonschema.NewStringLoader(rootOrderSchema) 27 | rootOrderMessageSchemaLoader = jsonschema.NewStringLoader(rootOrderMessageSchema) 28 | ) 29 | 30 | var builtInSchemas = []jsonschema.JSONLoader{ 31 | addressSchemaLoader, 32 | wholeNumberSchemaLoader, 33 | hexSchemaLoader, 34 | orderSchemaLoader, 35 | orderV4SchemaLoader, 36 | signedOrderSchemaLoader, 37 | signedOrderV4SchemaLoader, 38 | } 39 | 40 | type Filter struct { 41 | encodedSchema string 42 | chainID int 43 | rawCustomOrderSchema string 44 | orderSchema *jsonschema.Schema 45 | orderV4Schema *jsonschema.Schema 46 | messageSchema *jsonschema.Schema 47 | exchangeAddress common.Address 48 | } 49 | 50 | // TODO(jalextowle): We do not need `contractAddresses` since we only use `contractAddresses.Exchange`. 51 | // In a future refactor, we should update this interface. 52 | func New(chainID int, customOrderSchema string, contractAddresses ethereum.ContractAddresses) (*Filter, error) { 53 | orderLoader, err := newLoader(chainID, customOrderSchema, contractAddresses) 54 | if err != nil { 55 | return nil, err 56 | } 57 | orderLoaderV4, err := newLoader(chainID, customOrderSchema, contractAddresses) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | compiledRootOrderSchema, err := orderLoader.Compile(rootOrderSchemaLoader) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | compiledV4OrderSchema, err := orderLoaderV4.Compile(rootOrderV4SchemaLoader) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | messageLoader, err := newLoader(chainID, customOrderSchema, contractAddresses) 73 | if err != nil { 74 | return nil, err 75 | } 76 | if err := messageLoader.AddSchemas(rootOrderSchemaLoader); err != nil { 77 | return nil, err 78 | } 79 | compiledRootOrderMessageSchema, err := messageLoader.Compile(rootOrderMessageSchemaLoader) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | return &Filter{ 85 | chainID: chainID, 86 | rawCustomOrderSchema: customOrderSchema, 87 | orderSchema: compiledRootOrderSchema, 88 | orderV4Schema: compiledV4OrderSchema, 89 | messageSchema: compiledRootOrderMessageSchema, 90 | exchangeAddress: contractAddresses.Exchange, 91 | }, nil 92 | } 93 | 94 | func loadExchangeAddress(loader *jsonschema.SchemaLoader, contractAddresses ethereum.ContractAddresses) error { 95 | // Note that exchangeAddressSchema accepts both checksummed and 96 | // non-checksummed (i.e. all lowercase) addresses. 97 | exchangeAddressSchema := fmt.Sprintf(`{"enum":[%q,%q]}`, contractAddresses.Exchange.Hex(), strings.ToLower(contractAddresses.Exchange.Hex())) 98 | return loader.AddSchema("/exchangeAddress", jsonschema.NewStringLoader(exchangeAddressSchema)) 99 | } 100 | 101 | func loadChainID(loader *jsonschema.SchemaLoader, chainID int) error { 102 | chainIDSchema := fmt.Sprintf(`{"const":%d}`, chainID) 103 | return loader.AddSchema("/chainId", jsonschema.NewStringLoader(chainIDSchema)) 104 | } 105 | 106 | func newLoader(chainID int, customOrderSchema string, contractAddresses ethereum.ContractAddresses) (*jsonschema.SchemaLoader, error) { 107 | loader := jsonschema.NewSchemaLoader() 108 | if err := loadChainID(loader, chainID); err != nil { 109 | return nil, err 110 | } 111 | if err := loadExchangeAddress(loader, contractAddresses); err != nil { 112 | return nil, err 113 | } 114 | if err := loader.AddSchemas(builtInSchemas...); err != nil { 115 | return nil, err 116 | } 117 | if err := loader.AddSchema("/customOrder", jsonschema.NewStringLoader(customOrderSchema)); err != nil { 118 | return nil, err 119 | } 120 | return loader, nil 121 | } 122 | -------------------------------------------------------------------------------- /orderfilter/filter_bench_test.go: -------------------------------------------------------------------------------- 1 | package orderfilter 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/0xProject/0x-mesh/constants" 8 | "github.com/0xProject/0x-mesh/zeroex" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/common/math" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func BenchmarkValidateOrder(b *testing.B) { 15 | order := &zeroex.Order{ 16 | ChainID: big.NewInt(constants.TestChainID), 17 | MakerAddress: common.HexToAddress("0x5409ed021d9299bf6814279a6a1411a7e866a631"), 18 | MakerAssetData: common.FromHex("0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"), 19 | MakerAssetAmount: math.MustParseBig256("1000"), 20 | MakerFee: math.MustParseBig256("0"), 21 | TakerAddress: common.HexToAddress("0x0000000000000000000000000000000000000000"), 22 | TakerAssetData: common.FromHex("0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082"), 23 | TakerAssetAmount: math.MustParseBig256("2000"), 24 | TakerFee: math.MustParseBig256("0"), 25 | SenderAddress: common.HexToAddress("0x0000000000000000000000000000000000000000"), 26 | ExchangeAddress: common.HexToAddress("0x48bacb9266a570d521063ef5dd96e61686dbe788"), 27 | FeeRecipientAddress: common.HexToAddress("0xa258b39954cef5cb142fd567a46cddb31a670124"), 28 | ExpirationTimeSeconds: math.MustParseBig256("1574532801"), 29 | Salt: math.MustParseBig256("1548619145450"), 30 | } 31 | 32 | filter, err := New(constants.TestChainID, DefaultCustomOrderSchema, contractAddresses) 33 | require.NoError(b, err) 34 | signedOrder, err := zeroex.SignTestOrder(order) 35 | require.NoError(b, err) 36 | b.ResetTimer() 37 | 38 | for i := 0; i < b.N; i++ { 39 | _, err = filter.ValidateOrder(signedOrder) 40 | b.StopTimer() 41 | require.NoError(b, err) 42 | b.StartTimer() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /orderfilter/filter_js.go: -------------------------------------------------------------------------------- 1 | // +build js, wasm 2 | 3 | package orderfilter 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "strings" 9 | "syscall/js" 10 | 11 | "github.com/0xProject/0x-mesh/ethereum" 12 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/jsutil" 13 | "github.com/ethereum/go-ethereum/common" 14 | ) 15 | 16 | type Filter struct { 17 | orderValidator js.Value 18 | messageValidator js.Value 19 | encodedSchema string 20 | chainID int 21 | rawCustomOrderSchema string 22 | exchangeAddress common.Address 23 | } 24 | 25 | func New(chainID int, customOrderSchema string, contractAddresses ethereum.ContractAddresses) (*Filter, error) { 26 | chainIDSchema := fmt.Sprintf(`{"$id": "/chainId", "const":%d}`, chainID) 27 | exchangeAddressSchema := fmt.Sprintf(`{"$id": "/exchangeAddress", "enum":[%q,%q]}`, contractAddresses.Exchange.Hex(), strings.ToLower(contractAddresses.Exchange.Hex())) 28 | 29 | if jsutil.IsNullOrUndefined(js.Global().Get("__mesh_createSchemaValidator__")) { 30 | return nil, errors.New(`"__mesh_createSchemaValidator__" has not been set on the Javascript "global" object`) 31 | } 32 | // NOTE(jalextowle): The order of the schemas within the two arrays 33 | // defines their order of compilation. 34 | schemaValidator := js.Global().Call( 35 | "__mesh_createSchemaValidator__", 36 | customOrderSchema, 37 | []interface{}{ 38 | addressSchema, 39 | wholeNumberSchema, 40 | hexSchema, 41 | chainIDSchema, 42 | exchangeAddressSchema, 43 | orderSchema, 44 | signedOrderSchema, 45 | }, 46 | []interface{}{ 47 | rootOrderSchema, 48 | rootOrderMessageSchema, 49 | }) 50 | orderValidator := schemaValidator.Get("orderValidator") 51 | if jsutil.IsNullOrUndefined(orderValidator) { 52 | return nil, errors.New(`"orderValidator" has not been set on the provided "schemaValidator"`) 53 | } 54 | messageValidator := schemaValidator.Get("messageValidator") 55 | if jsutil.IsNullOrUndefined(messageValidator) { 56 | return nil, errors.New(`"messageValidator" has not been set on the provided "schemaValidator"`) 57 | } 58 | return &Filter{ 59 | orderValidator: orderValidator, 60 | messageValidator: messageValidator, 61 | chainID: chainID, 62 | rawCustomOrderSchema: customOrderSchema, 63 | exchangeAddress: contractAddresses.Exchange, 64 | }, nil 65 | } 66 | -------------------------------------------------------------------------------- /orderfilter/validate.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package orderfilter 4 | 5 | import ( 6 | "github.com/0xProject/0x-mesh/zeroex" 7 | jsonschema "github.com/xeipuuv/gojsonschema" 8 | ) 9 | 10 | func (f *Filter) ValidateOrderJSON(orderJSON []byte) (*jsonschema.Result, error) { 11 | return f.orderSchema.Validate(jsonschema.NewBytesLoader(orderJSON)) 12 | } 13 | 14 | func (f *Filter) ValidateOrderJSONV4(orderJSON []byte) (*jsonschema.Result, error) { 15 | return f.orderV4Schema.Validate(jsonschema.NewBytesLoader(orderJSON)) 16 | } 17 | 18 | func (f *Filter) MatchOrderMessageJSON(messageJSON []byte) (bool, error) { 19 | result, err := f.messageSchema.Validate(jsonschema.NewBytesLoader(messageJSON)) 20 | if err != nil { 21 | return false, err 22 | } 23 | return result.Valid(), nil 24 | } 25 | 26 | func (f *Filter) ValidateOrder(order *zeroex.SignedOrder) (*jsonschema.Result, error) { 27 | return f.orderSchema.Validate(jsonschema.NewGoLoader(order)) 28 | } 29 | -------------------------------------------------------------------------------- /orderfilter/validate_js.go: -------------------------------------------------------------------------------- 1 | // +build js, wasm 2 | 3 | package orderfilter 4 | 5 | import ( 6 | "errors" 7 | 8 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/jsutil" 9 | "github.com/0xProject/0x-mesh/zeroex" 10 | ) 11 | 12 | type SchemaValidationError struct { 13 | err error 14 | } 15 | 16 | func (s *SchemaValidationError) String() string { 17 | return s.err.Error() 18 | } 19 | 20 | type SchemaValidationResult struct { 21 | valid bool 22 | errors []*SchemaValidationError 23 | } 24 | 25 | func (s *SchemaValidationResult) Valid() bool { 26 | return s.valid 27 | } 28 | 29 | func (s *SchemaValidationResult) Errors() []*SchemaValidationError { 30 | return s.errors 31 | } 32 | 33 | // ValidateOrderJSON Validates a JSON encoded signed order using the AJV javascript library. 34 | // This libarary is used to increase the performance of Mesh nodes that run in the browser. 35 | func (f *Filter) ValidateOrderJSON(orderJSON []byte) (*SchemaValidationResult, error) { 36 | jsResult := f.orderValidator.Invoke(string(orderJSON)) 37 | fatal := jsResult.Get("fatal") 38 | if !jsutil.IsNullOrUndefined(fatal) { 39 | return nil, errors.New(fatal.String()) 40 | } 41 | valid := jsResult.Get("success").Bool() 42 | jsErrors := jsResult.Get("errors") 43 | var convertedErrors []*SchemaValidationError 44 | for i := 0; i < jsErrors.Length(); i++ { 45 | convertedErrors = append(convertedErrors, &SchemaValidationError{errors.New(jsErrors.Index(i).String())}) 46 | } 47 | return &SchemaValidationResult{valid: valid, errors: convertedErrors}, nil 48 | } 49 | 50 | func (f *Filter) MatchOrderMessageJSON(messageJSON []byte) (bool, error) { 51 | jsResult := f.messageValidator.Invoke(string(messageJSON)) 52 | fatal := jsResult.Get("fatal") 53 | if !jsutil.IsNullOrUndefined(fatal) { 54 | return false, errors.New(fatal.String()) 55 | } 56 | return jsResult.Get("success").Bool(), nil 57 | } 58 | 59 | func (f *Filter) ValidateOrder(order *zeroex.SignedOrder) (*SchemaValidationResult, error) { 60 | orderJSON, err := order.MarshalJSON() 61 | if err != nil { 62 | return nil, err 63 | } 64 | return f.ValidateOrderJSON(orderJSON) 65 | } 66 | -------------------------------------------------------------------------------- /orderfilter/validate_js_test.go: -------------------------------------------------------------------------------- 1 | // +build js, wasm 2 | 3 | package orderfilter 4 | 5 | var ( 6 | wrongExchangeAddressError = `{"keyword":"enum","dataPath":".exchangeAddress","schemaPath":"/exchangeAddress/enum","params":{"allowedValues":["0x48BaCB9266a570d521063EF5dD96e61686DbE788","0x48bacb9266a570d521063ef5dd96e61686dbe788"]},"message":"should be equal to one of the allowed values"}` 7 | mismatchedChainIDError = `{"keyword":"const","dataPath":".chainId","schemaPath":"/chainId/const","params":{"allowedValue":1337},"message":"should be equal to constant"}` 8 | mismatchedSenderAddressError = `{"keyword":"const","dataPath":".senderAddress","schemaPath":"/customOrder/properties/senderAddress/const","params":{"allowedValue":"0x00000000000000000000000000000000ba5eba11"},"message":"should be equal to constant"}` 9 | requiredMakerAddressError = `{"keyword":"required","dataPath":"","schemaPath":"#/required","params":{"missingProperty":"makerAddress"},"message":"should have required property 'makerAddress'"}` 10 | invalidTakerAddressError = `{"keyword":"pattern","dataPath":".takerAddress","schemaPath":"/address/pattern","params":{"pattern":"^0x[0-9a-fA-F]{40}$"},"message":"should match pattern \"^0x[0-9a-fA-F]{40}$\""}` 11 | ) 12 | -------------------------------------------------------------------------------- /orderfilter/validate_test.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package orderfilter 4 | 5 | var ( 6 | wrongExchangeAddressError = "exchangeAddress must be one of the following" 7 | mismatchedChainIDError = "chainId does not match" 8 | mismatchedSenderAddressError = "senderAddress does not match" 9 | requiredMakerAddressError = "makerAddress is required" 10 | invalidTakerAddressError = "takerAddress: Does not match pattern '^0x[0-9a-fA-F]{40}$" 11 | ) 12 | -------------------------------------------------------------------------------- /p2p/bandwidth_banning_test.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package p2p 4 | 5 | import ( 6 | "context" 7 | "testing" 8 | "time" 9 | 10 | "github.com/libp2p/go-libp2p-core/network" 11 | "github.com/libp2p/go-libp2p-core/peer" 12 | ma "github.com/multiformats/go-multiaddr" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestBandwidthChecker(t *testing.T) { 17 | t.Parallel() 18 | ctx, cancel := context.WithCancel(context.Background()) 19 | defer cancel() 20 | node0 := newTestNode(t, ctx, nil) 21 | node1 := newTestNode(t, ctx, nil) 22 | 23 | // For the purposes of this test, we use only the first multiaddress for each 24 | // peer. 25 | node0AddrInfo := peer.AddrInfo{ 26 | ID: node0.ID(), 27 | Addrs: node0.Multiaddrs()[0:1], 28 | } 29 | node1AddrInfo := peer.AddrInfo{ 30 | ID: node1.ID(), 31 | Addrs: node1.Multiaddrs()[0:1], 32 | } 33 | 34 | // At first, each node should be able to connect to the other. 35 | require.NoError(t, node0.Connect(node1AddrInfo, testConnectionTimeout)) 36 | require.NoError(t, node1.Connect(node0AddrInfo, testConnectionTimeout)) 37 | 38 | // Repeatedly send messages from node0 to node1 that would exceed the 39 | // bandwidth limit. 40 | newMaxBytesPerSecond := float64(1) 41 | message := make([]byte, int(newMaxBytesPerSecond*100)) 42 | ticker := time.NewTicker(500 * time.Millisecond) 43 | go func() { 44 | for { 45 | select { 46 | case <-ctx.Done(): 47 | // Break the loop and exit goroutine when context is canceled. 48 | return 49 | case <-ticker.C: 50 | require.NoError(t, node0.Send(message)) 51 | } 52 | } 53 | }() 54 | 55 | // Wait for node1 to receive the message. 56 | expectedMessage := &Message{ 57 | From: node0.ID(), 58 | Data: message, 59 | } 60 | expectMessage(t, node1, expectedMessage, 15*time.Second) 61 | 62 | // Manually change the bandwidth limit for node1. 63 | node1.banner.SetMaxBytesPerSecond(newMaxBytesPerSecond) 64 | 65 | // Wait for node1 to block node0 and for the connection to close. 66 | waitForNodeToBlockAddr(t, node1, node0AddrInfo.Addrs[0], 5*time.Second) 67 | waitForNodesToDisconect(t, node0, node1, 5*time.Second) 68 | } 69 | 70 | func waitForNodeToBlockAddr(t *testing.T, blocker *Node, addressToBlock ma.Multiaddr, timeout time.Duration) { 71 | blockedCheckTimeout := time.After(timeout) 72 | blockedCheckInterval := 250 * time.Millisecond 73 | 74 | for { 75 | select { 76 | case <-blockedCheckTimeout: 77 | t.Fatal("timed out waiting for node to block the given address") 78 | return 79 | default: 80 | } 81 | 82 | blocker.banner.CheckBandwidthUsage() 83 | isBlocked := blocker.banner.IsAddrBanned(addressToBlock) 84 | if isBlocked { 85 | // This is what we want. Return and continue the test. 86 | return 87 | } 88 | 89 | // Otherwise wait a bit and then check again. 90 | time.Sleep(blockedCheckInterval) 91 | continue 92 | } 93 | } 94 | 95 | func waitForNodesToDisconect(t *testing.T, node0 *Node, node1 *Node, timeout time.Duration) { 96 | disconnectTimeout := time.After(timeout) 97 | disconnectCheckInterval := 250 * time.Millisecond 98 | 99 | for { 100 | select { 101 | case <-disconnectTimeout: 102 | t.Fatal("timed out waiting for node0 and node1 to disconnect") 103 | return 104 | default: 105 | } 106 | 107 | // Check if node0 is connected to node1 108 | if node0.host.Network().Connectedness(node1.ID()) != network.NotConnected { 109 | time.Sleep(disconnectCheckInterval) 110 | continue 111 | } 112 | 113 | // Check if node1 is connected to node0 114 | if node1.host.Network().Connectedness(node0.ID()) != network.NotConnected { 115 | time.Sleep(disconnectCheckInterval) 116 | continue 117 | } 118 | 119 | // If neither node is connected to the other, we're done. 120 | return 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /p2p/banner/banner_test.go: -------------------------------------------------------------------------------- 1 | package banner 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | ma "github.com/multiformats/go-multiaddr" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestIPNetFromMaddr(t *testing.T) { 13 | testCases := []struct { 14 | maddr ma.Multiaddr 15 | expected net.IPNet 16 | }{ 17 | { 18 | maddr: newMaddr(t, "/ip4/159.65.4.82/tcp/60558"), 19 | expected: net.IPNet{ 20 | IP: net.IP{0x9f, 0x41, 0x4, 0x52}, 21 | Mask: ipv4AllMask, 22 | }, 23 | }, 24 | { 25 | maddr: newMaddr(t, "/ip4/159.65.4.82/tcp/60558/ipfs/16Uiu2HAm9brLYhoM1wCTRtGRR7ZqXhk8kfEt6a2rSFSZpeV8eB7L/p2p-circuit"), 26 | expected: net.IPNet{ 27 | IP: net.IP{0x9f, 0x41, 0x4, 0x52}, 28 | Mask: ipv4AllMask, 29 | }, 30 | }, 31 | { 32 | maddr: newMaddr(t, "/ip6/fe80:cd00:0000:0cde:1257:0000:211e:729c/tcp/60558"), 33 | expected: net.IPNet{ 34 | IP: net.IP{0xfe, 0x80, 0xcd, 0x0, 0x0, 0x0, 0xc, 0xde, 0x12, 0x57, 0x0, 0x0, 0x21, 0x1e, 0x72, 0x9c}, 35 | Mask: ipv6AllMask, 36 | }, 37 | }, 38 | { 39 | maddr: newMaddr(t, "/ip6/fe80:cd00:0000:0cde:1257:0000:211e:729c/tcp/60558/ipfs/16Uiu2HAm9brLYhoM1wCTRtGRR7ZqXhk8kfEt6a2rSFSZpeV8eB7L/p2p-circuit"), 40 | expected: net.IPNet{ 41 | IP: net.IP{0xfe, 0x80, 0xcd, 0x0, 0x0, 0x0, 0xc, 0xde, 0x12, 0x57, 0x0, 0x0, 0x21, 0x1e, 0x72, 0x9c}, 42 | Mask: ipv6AllMask, 43 | }, 44 | }, 45 | } 46 | 47 | for i, tc := range testCases { 48 | actual, err := ipNetFromMaddr(tc.maddr) 49 | require.NoError(t, err, "test case %d (%s)", i, tc.maddr.String()) 50 | assert.Equal(t, tc.expected, actual, "test case %d (%s)", i, tc.maddr.String()) 51 | } 52 | } 53 | 54 | func newMaddr(t *testing.T, s string) ma.Multiaddr { 55 | maddr, err := ma.NewMultiaddr(s) 56 | require.NoError(t, err) 57 | return maddr 58 | } 59 | -------------------------------------------------------------------------------- /p2p/banner/violations_tracker.go: -------------------------------------------------------------------------------- 1 | package banner 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/karlseguin/ccache" 7 | "github.com/libp2p/go-libp2p-core/peer" 8 | ) 9 | 10 | // violationsTracker is used to count how many times each peer has violated the 11 | // bandwidth limit. It is a workaround for a bug in libp2p's BandwidthCounter. 12 | // See: https://github.com/libp2p/go-libp2p-core/issues/65. 13 | // 14 | // TODO(albrow): Could potentially remove this if the issue is resolved. 15 | type violationsTracker struct { 16 | cache *ccache.Cache 17 | } 18 | 19 | // BUG(albrow): newViolationsTracker currently leaks goroutines due to a 20 | // limitation of the caching library used under the hood. 21 | //nolint:unparam // NOTE(jalextowle): Leave the function signature the same so 22 | // that things don't need to be changed when the bug is fixed 23 | func newViolationsTracker(ctx context.Context) *violationsTracker { 24 | cache := ccache.New(ccache.Configure().MaxSize(violationsCacheSize).ItemsToPrune(violationsCacheSize / 10)) 25 | // TODO(albrow): We should be calling Stop to cleanup any goroutines 26 | // started by ccache, but doing so now results in a race condition. Figure 27 | // out a workaround or use a different library, possibly one we write 28 | // ourselves. 29 | // go func() { 30 | // // Stop the cache when the context is done. 31 | // select { 32 | // case <-ctx.Done(): 33 | // cache.Stop() 34 | // } 35 | // }() 36 | return &violationsTracker{ 37 | cache: cache, 38 | } 39 | } 40 | 41 | // add increments the number of bandwidth violations by the given peer. It 42 | // returns the new count. 43 | func (v *violationsTracker) add(peerID peer.ID) int { 44 | newCount := 1 45 | if item := v.cache.Get(peerID.String()); item != nil { 46 | newCount = item.Value().(int) + 1 47 | } 48 | v.cache.Set(peerID.String(), newCount, violationsTTL) 49 | return newCount 50 | } 51 | -------------------------------------------------------------------------------- /p2p/message.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/libp2p/go-libp2p-core/peer" 7 | ) 8 | 9 | type Message struct { 10 | // From is the peer ID of the peer who sent the message. 11 | From peer.ID 12 | // Data is the underlying data for the message. 13 | Data []byte 14 | } 15 | 16 | // MessageHandler is an interface responsible for validating and storing 17 | // messages as well as selecting messages which are ready to be shared. 18 | type MessageHandler interface { 19 | // HandleMessages is called whenever new messages are received. It should only 20 | // return an error if there was a problem handling the messages. It should not 21 | // return an error for invalid or duplicate messages. 22 | HandleMessages(context.Context, []*Message) error 23 | HandleMessagesV4(context.Context, []*Message) error 24 | } 25 | -------------------------------------------------------------------------------- /p2p/node_v4.go: -------------------------------------------------------------------------------- 1 | // Implements the GossipSub / PubSub sharing of v4 orders 2 | package p2p 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | mathrand "math/rand" 8 | ) 9 | 10 | func (n *Node) SendV4(data []byte) error { 11 | var firstErr error 12 | for _, topic := range n.config.PublishTopicsV4 { 13 | err := n.pubsub.Publish(topic, data) //nolint:staticcheck 14 | if err != nil && firstErr == nil { 15 | firstErr = err 16 | } 17 | } 18 | return firstErr 19 | } 20 | 21 | func (n *Node) receiveAndHandleMessagesV4(ctx context.Context) error { 22 | // Subscribe to topic if we haven't already 23 | if n.subV4 == nil { 24 | var err error 25 | //nolint 26 | n.subV4, err = n.pubsub.Subscribe(n.config.SubscribeTopicV4) 27 | if err != nil { 28 | return err 29 | } 30 | } 31 | 32 | // Receive up to maxReceiveBatch messages. 33 | incoming, err := n.receiveBatchV4(ctx) 34 | if err != nil { 35 | return err 36 | } 37 | if len(incoming) == 0 { 38 | return nil 39 | } 40 | if err := n.messageHandler.HandleMessagesV4(ctx, incoming); err != nil { 41 | return fmt.Errorf("could not validate or store v4 messages: %s", err.Error()) 42 | } 43 | 44 | return nil 45 | } 46 | 47 | func (n *Node) receiveV4(ctx context.Context) (*Message, error) { 48 | msg, err := n.subV4.Next(ctx) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return &Message{From: msg.GetFrom(), Data: msg.Data}, nil 53 | } 54 | 55 | func (n *Node) receiveBatchV4(ctx context.Context) ([]*Message, error) { 56 | // Receive a batch of messages so we can handle them in bulk 57 | messages := []*Message{} 58 | for { 59 | if len(messages) >= maxReceiveBatch { 60 | return messages, nil 61 | } 62 | // If the parent context was canceled, break. 63 | select { 64 | case <-ctx.Done(): 65 | return messages, nil 66 | default: 67 | } 68 | 69 | // Receive message 70 | receiveCtx, receiveCancel := context.WithTimeout(n.ctx, receiveTimeout) 71 | msg, err := n.receiveV4(receiveCtx) 72 | receiveCancel() 73 | if err != nil { 74 | if err == context.Canceled || err == context.DeadlineExceeded { 75 | return messages, nil 76 | } else { 77 | return nil, err 78 | } 79 | } 80 | // Skip self messages 81 | if msg.From == n.host.ID() { 82 | continue 83 | } 84 | 85 | // Add to batch 86 | messages = append(messages, msg) 87 | } 88 | } 89 | 90 | func (n *Node) startMessageHandlerV4(ctx context.Context) error { 91 | for { 92 | select { 93 | case <-ctx.Done(): 94 | return nil 95 | default: 96 | } 97 | 98 | if err := n.receiveAndHandleMessagesV4(ctx); err != nil { 99 | return err 100 | } 101 | 102 | // Check bandwidth usage non-deterministically 103 | if mathrand.Float64() <= chanceToCheckBandwidthUsage { 104 | n.banner.CheckBandwidthUsage() 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /p2p/notifee.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | connmgr "github.com/libp2p/go-libp2p-connmgr" 8 | p2pnet "github.com/libp2p/go-libp2p-core/network" 9 | ma "github.com/multiformats/go-multiaddr" 10 | log "github.com/sirupsen/logrus" 11 | ) 12 | 13 | const ( 14 | // pubsubProtocolTag is the tag used for peers who speak our pubsub protocol. 15 | pubsubProtocolTag = "pubsub-protocol" 16 | // pubsubProtocolScore is the score to add to peers who speak our pubsub 17 | // protocol. 18 | pubsubProtocolScore = 10 19 | ) 20 | 21 | // notifee receives notifications for network-related events. 22 | type notifee struct { 23 | ctx context.Context 24 | connManager *connmgr.BasicConnMgr 25 | } 26 | 27 | var _ p2pnet.Notifiee = ¬ifee{} 28 | 29 | // Listen is called when network starts listening on an addr 30 | func (n *notifee) Listen(p2pnet.Network, ma.Multiaddr) {} 31 | 32 | // ListenClose is called when network stops listening on an addr 33 | func (n *notifee) ListenClose(p2pnet.Network, ma.Multiaddr) {} 34 | 35 | // Connected is called when a connection opened 36 | func (n *notifee) Connected(network p2pnet.Network, conn p2pnet.Conn) { 37 | log.WithFields(map[string]interface{}{ 38 | "remotePeerID": conn.RemotePeer(), 39 | "remoteMultiaddress": conn.RemoteMultiaddr(), 40 | }).Trace("connected to peer") 41 | } 42 | 43 | // Disconnected is called when a connection closed 44 | func (n *notifee) Disconnected(network p2pnet.Network, conn p2pnet.Conn) { 45 | log.WithFields(map[string]interface{}{ 46 | "remotePeerID": conn.RemotePeer(), 47 | "remoteMultiaddress": conn.RemoteMultiaddr(), 48 | }).Trace("disconnected from peer") 49 | } 50 | 51 | // OpenedStream is called when a stream opened 52 | func (n *notifee) OpenedStream(network p2pnet.Network, stream p2pnet.Stream) { 53 | go func() { 54 | ctx, cancel := context.WithTimeout(n.ctx, 5*time.Second) 55 | defer cancel() 56 | waitForStreamProtocol(ctx, stream) 57 | 58 | if stream.Protocol() == pubsubProtocolID { 59 | // When we find a peer who speaks our protocol, we give them a slight 60 | // positive score so the Connection Manager will be less likely to 61 | // disconnect them. 62 | log.WithFields(map[string]interface{}{ 63 | "remotePeerID": stream.Conn().RemotePeer(), 64 | "protocol": stream.Protocol(), 65 | "direction": stream.Stat().Direction, 66 | }).Debug("found peer who speaks our protocol") 67 | n.connManager.TagPeer(stream.Conn().RemotePeer(), pubsubProtocolTag, pubsubProtocolScore) 68 | } 69 | }() 70 | } 71 | 72 | // ClosedStream is called when a stream closed 73 | func (n *notifee) ClosedStream(network p2pnet.Network, stream p2pnet.Stream) {} 74 | 75 | // waitForStreamProtocol blocks until the context is canceled or stream.Protocol 76 | // is not empty. 77 | func waitForStreamProtocol(ctx context.Context, stream p2pnet.Stream) { 78 | // HACK(albrow): When the stream is initially opened, the protocol is not 79 | // set. For now, we have to manually poll until it is set. 80 | // https://github.com/libp2p/go-libp2p/issues/467 mentions an internal 81 | // event bus which could potentially be used to detect when the protocol is 82 | // set or offer a different way to detect peers who speak the protocol we're 83 | // looking for. 84 | ticker := time.NewTicker(100 * time.Millisecond) 85 | defer ticker.Stop() 86 | for stream.Protocol() == "" { 87 | select { 88 | case <-ctx.Done(): 89 | return 90 | case <-ticker.C: 91 | continue 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /p2p/opts_js.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package p2p 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/0xProject/0x-mesh/db" 9 | libp2p "github.com/libp2p/go-libp2p" 10 | "github.com/libp2p/go-libp2p-core/host" 11 | dht "github.com/libp2p/go-libp2p-kad-dht" 12 | pubsub "github.com/libp2p/go-libp2p-pubsub" 13 | ws "github.com/libp2p/go-ws-transport" 14 | ) 15 | 16 | const ( 17 | // maxReceiveBatch is the maximum number of new messages to receive at once. 18 | maxReceiveBatch = 100 19 | // maxShareBatch is the maximum number of messages to share at once. 20 | maxShareBatch = 50 21 | // peerCountLow is the target number of peers to connect to at any given time. 22 | peerCountLow = 50 23 | // peerCountHigh is the maximum number of peers to be connected to. If the 24 | // number of connections exceeds this number, we will prune connections until 25 | // we reach peerCountLow. 26 | peerCountHigh = 60 27 | ) 28 | 29 | func getHostOptions(ctx context.Context, config Config) ([]libp2p.Option, error) { 30 | return []libp2p.Option{ 31 | libp2p.Transport(ws.New), 32 | // Don't listen on any addresses by default. We can't accept incoming 33 | // connections in the browser. 34 | libp2p.ListenAddrs(), 35 | }, nil 36 | } 37 | 38 | func getPubSubOptions() []pubsub.Option { 39 | return []pubsub.Option{ 40 | pubsub.WithValidateThrottle(64), 41 | pubsub.WithValidateWorkers(1), 42 | } 43 | } 44 | 45 | // NewDHT returns a new Kademlia DHT instance configured to work with 0x Mesh 46 | // in browser environments. 47 | func NewDHT(ctx context.Context, db *db.DB, host host.Host) (*dht.IpfsDHT, error) { 48 | return dht.New(ctx, host, dht.Datastore(db.DHTStore()), dht.V1ProtocolOverride(DHTProtocolID), dht.Mode(dht.ModeClient)) 49 | } 50 | -------------------------------------------------------------------------------- /p2p/ratevalidator/tracking_rate_limiter.go: -------------------------------------------------------------------------------- 1 | package ratevalidator 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "golang.org/x/time/rate" 7 | ) 8 | 9 | // trackingRateLimiter is a wrapper around rate.Limiter that tracks the number 10 | // of violations (i.e. the number of times that a request is not allowed). 11 | type trackingRateLimiter struct { 12 | limiter *rate.Limiter 13 | violations uint64 14 | } 15 | 16 | func newTrackingRateLimiter(r rate.Limit, b int) *trackingRateLimiter { 17 | return &trackingRateLimiter{ 18 | limiter: rate.NewLimiter(r, b), 19 | } 20 | } 21 | 22 | func (l *trackingRateLimiter) resetViolations() { 23 | atomic.StoreUint64(&l.violations, 0) 24 | } 25 | 26 | func (l *trackingRateLimiter) allow() bool { 27 | allowed := l.limiter.Allow() 28 | if !allowed { 29 | atomic.AddUint64(&l.violations, 1) 30 | } 31 | return allowed 32 | } 33 | -------------------------------------------------------------------------------- /p2p/ratevalidator/validator_test.go: -------------------------------------------------------------------------------- 1 | package ratevalidator 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/libp2p/go-libp2p-core/peer" 9 | pubsub "github.com/libp2p/go-libp2p-pubsub" 10 | pb "github.com/libp2p/go-libp2p-pubsub/pb" 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | "golang.org/x/time/rate" 14 | ) 15 | 16 | var peerIDStrings = []string{ 17 | "16Uiu2HAmGd949LwaV4KNvK2WDSiMVy7xEmW983VH75CMmefmMpP7", 18 | "16Uiu2HAmVqV4kepwSiNRmvKiBxwpt4EQJi3pAe9auSMyGjzA1eBZ", 19 | "16Uiu2HAmAmmoyR4M492Aq8vWFh4gyVr9Gz2uEGAWjdpGPfKpcw5F", 20 | } 21 | 22 | var peerIDs []peer.ID 23 | 24 | func init() { 25 | for _, peerIDString := range peerIDStrings { 26 | peerID, _ := peer.Decode(peerIDString) 27 | peerIDs = append(peerIDs, peerID) 28 | } 29 | } 30 | 31 | func TestValidatorPerPeer(t *testing.T) { 32 | t.Parallel() 33 | 34 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 35 | defer cancel() 36 | 37 | validator, err := New(ctx, Config{ 38 | MyPeerID: peerIDs[0], 39 | GlobalLimit: rate.Inf, 40 | PerPeerLimit: 1, 41 | PerPeerBurst: 5, 42 | MaxMessageSize: 1024, 43 | }) 44 | require.NoError(t, err) 45 | 46 | for _, peerID := range peerIDs[1:] { 47 | // All messages should be valid until we hit GlobalBurst. 48 | for i := 0; i < validator.config.PerPeerBurst; i++ { 49 | valid := validator.Validate(ctx, peerID, &pubsub.Message{}) 50 | assert.True(t, valid, "message should be valid") 51 | } 52 | // Next message should be invalid. 53 | valid := validator.Validate(ctx, peerID, &pubsub.Message{}) 54 | assert.False(t, valid, "message should be invalid") 55 | } 56 | 57 | // Wait one second. Limiter should now allow each peer to send one additional 58 | // message. 59 | time.Sleep(1 * time.Second) 60 | 61 | for _, peerID := range peerIDs[1:] { 62 | // First message should be valid. 63 | valid := validator.Validate(ctx, peerID, &pubsub.Message{}) 64 | assert.True(t, valid, "message should be valid") 65 | 66 | // Next message should be invalid. 67 | valid = validator.Validate(ctx, peerID, &pubsub.Message{}) 68 | assert.False(t, valid, "message should be invalid") 69 | } 70 | } 71 | 72 | func TestValidatorWithOwnPeerID(t *testing.T) { 73 | t.Parallel() 74 | 75 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 76 | defer cancel() 77 | 78 | validator, err := New(ctx, Config{ 79 | MyPeerID: peerIDs[0], 80 | GlobalLimit: 1, 81 | GlobalBurst: 1, 82 | PerPeerLimit: 1, 83 | PerPeerBurst: 1, 84 | MaxMessageSize: 1024, 85 | }) 86 | require.NoError(t, err) 87 | 88 | // All messages should sent by us should be valid. 89 | messagesToSend := validator.config.PerPeerBurst + validator.config.GlobalBurst + 5 90 | for i := 0; i < messagesToSend; i++ { 91 | valid := validator.Validate(ctx, peerIDs[0], &pubsub.Message{}) 92 | assert.True(t, valid, "message should be valid") 93 | } 94 | } 95 | 96 | func TestValidatorMaxMessageSize(t *testing.T) { 97 | t.Parallel() 98 | 99 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 100 | defer cancel() 101 | 102 | maxSize := 48 103 | validator, err := New(ctx, Config{ 104 | MyPeerID: peerIDs[0], 105 | GlobalLimit: rate.Inf, 106 | PerPeerLimit: rate.Inf, 107 | MaxMessageSize: maxSize, 108 | }) 109 | require.NoError(t, err) 110 | 111 | { 112 | isValid := validator.Validate(ctx, peerIDs[1], &pubsub.Message{ 113 | Message: &pb.Message{ 114 | Data: make([]byte, maxSize), 115 | }, 116 | }) 117 | assert.True(t, isValid, "message should be valid") 118 | } 119 | 120 | { 121 | isValid := validator.Validate(ctx, peerIDs[1], &pubsub.Message{ 122 | Message: &pb.Message{ 123 | Data: make([]byte, maxSize+1), 124 | }, 125 | }) 126 | assert.False(t, isValid, "message should be invalid") 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /p2p/validatorset/set.go: -------------------------------------------------------------------------------- 1 | // Package validatorset offers a way to combine a set of libp2p.Validators into 2 | // a single validator. The combined validator set only passes if *all* of its 3 | // constituent validators pass. 4 | package validatorset 5 | 6 | import ( 7 | "context" 8 | "sync" 9 | 10 | peer "github.com/libp2p/go-libp2p-core/peer" 11 | pubsub "github.com/libp2p/go-libp2p-pubsub" 12 | log "github.com/sirupsen/logrus" 13 | ) 14 | 15 | // Set is a set of libp2p.Validators. 16 | type Set struct { 17 | mu sync.RWMutex 18 | validators []*namedValidator 19 | } 20 | 21 | type namedValidator struct { 22 | name string 23 | validator pubsub.Validator 24 | } 25 | 26 | // New creates a new validator set 27 | func New() *Set { 28 | return &Set{} 29 | } 30 | 31 | // Add adds a new validator to the set with the given name. The name is used 32 | // in error messages. 33 | func (s *Set) Add(name string, validator pubsub.Validator) { 34 | s.mu.Lock() 35 | defer s.mu.Unlock() 36 | named := &namedValidator{ 37 | name: name, 38 | validator: validator, 39 | } 40 | s.validators = append(s.validators, named) 41 | } 42 | 43 | // Validate validates the message. It returns true if all of the constituent 44 | // validators in the set also return true. If one or more of them return false, 45 | // Validate returns false. 46 | func (s *Set) Validate(ctx context.Context, sender peer.ID, msg *pubsub.Message) bool { 47 | s.mu.RLock() 48 | defer s.mu.RUnlock() 49 | for _, validator := range s.validators { 50 | // If the context is done return immediately. 51 | select { 52 | case <-ctx.Done(): 53 | return false 54 | default: 55 | } 56 | 57 | // Otherwise continue by running this validator. 58 | isValid := validator.validator(ctx, sender, msg) 59 | if !isValid { 60 | // TODO(albrow): Should we reduce a peer's score as a penalty for invalid 61 | // messages? 62 | log.WithField("validatorName", validator.name).Trace("pubsub message validation failed") 63 | return false 64 | } 65 | } 66 | return true 67 | } 68 | -------------------------------------------------------------------------------- /p2p/validatorset/set_test.go: -------------------------------------------------------------------------------- 1 | package validatorset 2 | 3 | import ( 4 | "context" 5 | "crypto/rand" 6 | "fmt" 7 | "testing" 8 | "time" 9 | 10 | p2pcrypto "github.com/libp2p/go-libp2p-core/crypto" 11 | "github.com/libp2p/go-libp2p-core/peer" 12 | pubsub "github.com/libp2p/go-libp2p-pubsub" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | // alwaysFalseValidator is a pubsub.Validator that always returns false. 18 | func alwaysFalseValidator(ctx context.Context, sender peer.ID, msg *pubsub.Message) bool { 19 | return false 20 | } 21 | 22 | // alwaysTrueValidator is a pubsub.Validator that always returns true. 23 | func alwaysTrueValidator(ctx context.Context, sender peer.ID, msg *pubsub.Message) bool { 24 | return true 25 | } 26 | 27 | func TestValidatorSet(t *testing.T) { 28 | t.Parallel() 29 | 30 | // For each test case, we construct a new validator set with the given 31 | // validators. Then we check that the actual results of set.Validate matches 32 | // the expected result. 33 | testCases := []struct { 34 | validators []pubsub.Validator 35 | expectedResult bool 36 | }{ 37 | { 38 | validators: []pubsub.Validator{alwaysTrueValidator}, 39 | expectedResult: true, 40 | }, 41 | { 42 | validators: []pubsub.Validator{alwaysFalseValidator}, 43 | expectedResult: false, 44 | }, 45 | { 46 | validators: []pubsub.Validator{alwaysTrueValidator, alwaysTrueValidator}, 47 | expectedResult: true, 48 | }, 49 | { 50 | validators: []pubsub.Validator{alwaysFalseValidator, alwaysFalseValidator}, 51 | expectedResult: false, 52 | }, 53 | { 54 | validators: []pubsub.Validator{alwaysTrueValidator, alwaysFalseValidator}, 55 | expectedResult: false, 56 | }, 57 | { 58 | validators: []pubsub.Validator{alwaysTrueValidator, alwaysFalseValidator, alwaysTrueValidator}, 59 | expectedResult: false, 60 | }, 61 | } 62 | 63 | for i, testCase := range testCases { 64 | testCaseName := fmt.Sprintf("validatorset test case %d", i) 65 | t.Run(testCaseName, func(t *testing.T) { 66 | set := New() 67 | for j, validator := range testCase.validators { 68 | set.Add(fmt.Sprintf("validator %d", j), validator) 69 | } 70 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 71 | defer cancel() 72 | actualResult := set.Validate(ctx, getRandomPeerID(t), &pubsub.Message{}) 73 | assert.Equal(t, testCase.expectedResult, actualResult) 74 | }) 75 | } 76 | } 77 | 78 | func getRandomPeerID(t *testing.T) peer.ID { 79 | privKey, _, err := p2pcrypto.GenerateSecp256k1Key(rand.Reader) 80 | require.NoError(t, err) 81 | id, err := peer.IDFromPrivateKey(privKey) 82 | require.NoError(t, err) 83 | return id 84 | } 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "0x-mesh", 3 | "version": "0.0.1", 4 | "engines": { 5 | "node": ">=11" 6 | }, 7 | "workspaces": [ 8 | "packages/mesh-graphql-client" 9 | ], 10 | "scripts": { 11 | "wsrun": "wsrun", 12 | "build": "wsrun --stages --fast-exit --exclude-missing build", 13 | "build:ts": "tsc -b", 14 | "watch:ts": "tsc -b -w", 15 | "clean": "wsrun --fast-exit --exclude-missing clean", 16 | "docs:md": "wsrun --fast-exit --exclude-missing docs:md", 17 | "lint": "wsrun lint", 18 | "prettier": "prettier --write '**/*.{ts,tsx,json,md}' --config .prettierrc", 19 | "prettier:ci": "prettier --list-different '**/*.{ts,tsx,json,md}' --config .prettierrc", 20 | "test": "wsrun --fast-exit --exclude-missing test " 21 | }, 22 | "description": "A peer-to-peer network for sharing orders", 23 | "main": "index.js", 24 | "repository": "git@github.com:0xProject/0x-mesh.git", 25 | "private": true, 26 | "devDependencies": { 27 | "@0x/ts-doc-gen": "^0.0.16", 28 | "@0x/tslint-config": "^4.0.0", 29 | "@types/base64-arraybuffer": "^0.1.0", 30 | "es6-promise": "^4.2.6", 31 | "ignore-loader": "^0.1.2", 32 | "isomorphic-fetch": "^3.0.0", 33 | "prettier": "^2.1.2", 34 | "ts-loader": "^8.0.5", 35 | "tslint": "6.1.3", 36 | "typescript": "^4.0.3", 37 | "webpack": "^4.41.5", 38 | "webpack-cli": "^3.3.10", 39 | "wrtc": "0.4.1", 40 | "wsrun": "^5.2.0" 41 | }, 42 | "resolutions": { 43 | "@0x/ts-doc-gen/typedoc-plugin-markdown": "2.2.x" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/README.md: -------------------------------------------------------------------------------- 1 | ## @0x/mesh-browser-lite 2 | 3 | This packages provides a set of Typescript and Javascript bindings for running a 4 | 0x-mesh node in the browser. The browser node's Wasm binary is not bundled in 5 | this package and is instead expected to be served by the consumer of the package. 6 | This package has a smaller bundle size than the `@0x/mesh-browser` package and 7 | may have faster load times. 8 | 9 | ## Installation 10 | 11 | ```bash 12 | yarn add @0x/mesh-browser-lite 13 | ``` 14 | 15 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 16 | 17 | ```json 18 | "compilerOptions": { 19 | "typeRoots": ["node_modules/@types"], 20 | } 21 | ``` 22 | 23 | ## Contributing 24 | 25 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 26 | 27 | ### Install dependencies 28 | 29 | ```bash 30 | yarn install 31 | ``` 32 | 33 | ### Build 34 | 35 | ```bash 36 | yarn build 37 | ``` 38 | 39 | ### Lint 40 | 41 | ```bash 42 | yarn lint 43 | ``` 44 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@0x/mesh-browser-lite", 3 | "version": "11.2.0", 4 | "description": "TypeScript and JavaScript bindings for running Mesh directly in the browser. To use this packages, you must use your own copy of the Mesh WebAssembly Binary", 5 | "main": "./lib/index.js", 6 | "license": "Apache-2.0", 7 | "scripts": { 8 | "build": "tsc -b", 9 | "clean": "shx rm -r ./lib && shx rm tsconfig.tsbuildinfo || exit 0", 10 | "watch:ts": "tsc -b -w", 11 | "docs:md": "ts-doc-gen --sourceDir=./src --output=${npm_package_config_docsPath}", 12 | "lint": "tslint --format stylish --project ." 13 | }, 14 | "config": { 15 | "docsPath": "../../docs/browser-bindings/browser-lite" 16 | }, 17 | "devDependencies": { 18 | "@types/dexie": "^1.3.1", 19 | "shx": "^0.3.2", 20 | "typedoc": "^0.15.0", 21 | "typescript": "^4.0.3" 22 | }, 23 | "dependencies": { 24 | "@0x/order-utils": "^10.2.0", 25 | "@0x/utils": "^6.2.0", 26 | "ajv": "^6.12.5", 27 | "dexie": "^3.0.1", 28 | "ethereum-types": "^3.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/src/datastore.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:max-file-line-count 2 | 3 | /** 4 | * @hidden 5 | */ 6 | 7 | /** 8 | * NOTE(jalextowle): This comment must be here so that typedoc knows that the above 9 | * comment is a module comment 10 | */ 11 | import Dexie from 'dexie'; 12 | 13 | interface Entry { 14 | key: string; 15 | value: Uint8Array; 16 | size: number; 17 | } 18 | 19 | enum OperationType { 20 | Addition, 21 | Deletion, 22 | } 23 | 24 | interface Operation { 25 | operationType: OperationType; 26 | key: string; 27 | value?: Uint8Array; 28 | } 29 | 30 | // This implements the subset of the ds.Batching interface that should be implemented 31 | // on the Dexie side. The Go bindings for this system can be found in db/dexie_datastore.go. 32 | // Some aspects of the ds.Batching interface make more sense to implement in Go 33 | // for performance or dependency reasons. The most important example of this is 34 | // that query filtering and ordering is performed on the Go side to avoid converting 35 | // Go functions into Javascript functions. 36 | export class BatchingDatastore { 37 | private readonly _db: Dexie; 38 | private readonly _table: Dexie.Table<{ key: string; value: Uint8Array }, string>; 39 | 40 | constructor(db: Dexie, tableName: string) { 41 | this._db = db; 42 | this._table = this._db._allTables[tableName] as Dexie.Table<{ key: string; value: Uint8Array }, string>; 43 | if (this._table === undefined) { 44 | throw new Error('BatchingDatastore: Attempting to use undefined table'); 45 | } 46 | } 47 | 48 | public async commitAsync(operations: Operation[]): Promise { 49 | await this._db.transaction('rw!', this._table, async () => { 50 | for (const operation of operations) { 51 | if (operation.operationType === OperationType.Addition) { 52 | if (!operation.value) { 53 | throw new Error('commitAsync: no value for key'); 54 | } 55 | await this._table.put({ key: operation.key, value: operation.value }); 56 | } else { 57 | await this._table.delete(operation.key); 58 | } 59 | } 60 | }); 61 | } 62 | 63 | public async putAsync(key: string, value: Uint8Array): Promise { 64 | await this._table.put({ key, value }); 65 | } 66 | 67 | public async deleteAsync(key: string): Promise { 68 | await this._table.delete(key); 69 | } 70 | 71 | public async getAsync(key: string): Promise { 72 | const result = await this._table.get(key); 73 | if (result === undefined) { 74 | throw new Error('datastore: key not found'); 75 | } 76 | return result.value; 77 | } 78 | 79 | public async getSizeAsync(key: string): Promise { 80 | const value = await this.getAsync(key); 81 | if (value === undefined) { 82 | throw new Error('datastore: key not found'); 83 | } 84 | return value.length; 85 | } 86 | 87 | public async hasAsync(key: string): Promise { 88 | const result = await this._table.get(key); 89 | return result !== undefined; 90 | } 91 | 92 | // NOTE(jalextowle): This function only filters the database based on prefix 93 | // and generates entries for each row of the database. The other query 94 | // operations (filtering, sorting, etc.) are implemented in 95 | // db/dexie_datastore.go for performance reasons. The prefixes are 96 | // interpreted as regular expressions to satisfy the ds.Datastore interface. 97 | public async queryAsync(prefix: string): Promise { 98 | return this._db.transaction('rw!', this._table, async () => { 99 | const prefixRegExp = new RegExp(prefix); 100 | const col = 101 | prefix !== '' 102 | ? this._table.filter((entry) => prefixRegExp.test(entry.key)) 103 | : this._table.toCollection(); 104 | return (await col.toArray()).map((entry) => { 105 | return { 106 | key: entry.key, 107 | value: entry.value, 108 | size: entry.value.length, 109 | }; 110 | }); 111 | }); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mesh'; 2 | 3 | // If needed, add a polyfill for instantiateStreaming 4 | if (!WebAssembly.instantiateStreaming) { 5 | WebAssembly.instantiateStreaming = async (resp: any, importObject: any) => { 6 | const source = await (await resp).arrayBuffer(); 7 | return WebAssembly.instantiate(source, importObject); 8 | }; 9 | } 10 | 11 | /** 12 | * Loads the Wasm module that is provided by fetching a url. 13 | * @param url The URL to query for the Wasm binary. 14 | */ 15 | export async function loadMeshStreamingWithURLAsync(url: string): Promise { 16 | return loadMeshStreamingAsync(fetch(url)); 17 | } 18 | 19 | /** 20 | * Loads the Wasm module that is provided by a response. 21 | * @param response The Wasm response that supplies the Wasm binary. 22 | */ 23 | export async function loadMeshStreamingAsync(response: Response | Promise): Promise { 24 | const go = new Go(); 25 | 26 | const module = await WebAssembly.instantiateStreaming(response, go.importObject); 27 | // NOTE(jalextowle): Wrapping the `go.run(module.instance)` statement in `setImmediate` 28 | // prevents the statement from blocking when `await` is used with this load function. 29 | setImmediate(() => { 30 | go.run(module.instance); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/src/schema_validator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @hidden 3 | */ 4 | 5 | /** 6 | * NOTE(jalextowle): This comment must be here so that typedoc knows that the above 7 | * comment is a module comment 8 | */ 9 | import * as ajv from 'ajv'; 10 | 11 | interface SynchronousValidationFunction { 12 | (data: any): boolean; 13 | schema?: object | boolean; 14 | errors?: null | ajv.ErrorObject[]; 15 | } 16 | 17 | export interface SchemaValidationResult { 18 | success: boolean; 19 | errors: string[]; 20 | fatal?: string; 21 | } 22 | 23 | export interface SchemaValidator { 24 | orderValidator: (input: string) => SchemaValidationResult; 25 | messageValidator: (input: string) => SchemaValidationResult; 26 | } 27 | 28 | /** 29 | * Creates a schema validator object for the provided schemas. 30 | * @param customOrderSchema The custom filter that will be used by the root 31 | * schemas. 32 | * @param schemas These are all of the schemas that can be compiled before the 33 | * customOrderSchema. 34 | * @param rootSchemas The root schemas. These must be compiled last. 35 | */ 36 | export function createSchemaValidator( 37 | customOrderSchemaString: string, 38 | schemas: string[], 39 | rootSchemas: string[], 40 | ): SchemaValidator { 41 | const AJV = new ajv(); 42 | for (const schema of schemas) { 43 | AJV.addSchema(JSON.parse(schema)); 44 | } 45 | const customOrderSchema = JSON.parse(customOrderSchemaString); 46 | AJV.addSchema({ 47 | ...customOrderSchema, 48 | $id: '/customOrder', 49 | }); 50 | for (const schema of rootSchemas) { 51 | AJV.addSchema(JSON.parse(schema)); 52 | } 53 | 54 | const orderValidate = AJV.getSchema('/rootOrder'); 55 | if (orderValidate === undefined) { 56 | throw new Error('Cannot find "/rootOrder" schema in AJV'); 57 | } 58 | if (orderValidate === undefined) { 59 | throw new Error('Cannot find "/rootOrder" schema in AJV'); 60 | } 61 | const messageValidate = AJV.getSchema('/rootOrderMessage'); 62 | if (messageValidate === undefined) { 63 | throw new Error('Cannot find "rootOrderMessage" schema in AJV'); 64 | } 65 | return { 66 | orderValidator: constructValidationFunctionWrapper(orderValidate as SynchronousValidationFunction), 67 | messageValidator: constructValidationFunctionWrapper(messageValidate as SynchronousValidationFunction), 68 | }; 69 | } 70 | 71 | function constructValidationFunctionWrapper( 72 | validationFunction: SynchronousValidationFunction, 73 | ): (input: string) => SchemaValidationResult { 74 | return (input: string) => { 75 | const result: SchemaValidationResult = { success: false, errors: [] }; 76 | try { 77 | result.success = validationFunction(JSON.parse(input)); 78 | if (validationFunction.errors) { 79 | result.errors = validationFunction.errors.map((error) => JSON.stringify(error)); 80 | } 81 | } catch (error) { 82 | result.fatal = JSON.stringify(error); 83 | } 84 | return result; 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": ["./src/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/mesh-browser-lite/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-browser-shim/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@0x/mesh-browser-shim", 4 | "description": "Bundled Javascript init files to use with `wasmbrowsertest` for testing Go WebAssembly binaries", 5 | "version": "1.0.0", 6 | "main": "./lib/index.js", 7 | "license": "Apache-2.0", 8 | "scripts": { 9 | "build": "tsc -b && ./node_modules/.bin/webpack --mode=development", 10 | "clean": "shx rm -r ./dist && shx rm -r ./lib || exit 0", 11 | "lint": "tslint --format stylish --project ." 12 | }, 13 | "devDependencies": { 14 | "@types/dexie": "^1.3.1", 15 | "shx": "^0.3.2", 16 | "ts-loader": "^8.0.5", 17 | "typescript": "^4.0.3", 18 | "webpack": "^4.43.0", 19 | "webpack-cli": "^3.3.10" 20 | }, 21 | "dependencies": { 22 | "@0x/mesh-browser-lite": "^11.2.0", 23 | "dexie": "^3.0.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/mesh-browser-shim/src/browser_shim.ts: -------------------------------------------------------------------------------- 1 | import { _setGlobals } from '@0x/mesh-browser-lite'; 2 | 3 | // Set the globals that are required for e.g. the `db` and `orderfilter` packages. 4 | _setGlobals(); 5 | -------------------------------------------------------------------------------- /packages/mesh-browser-shim/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": ["./src/**/*"], 8 | "references": [ 9 | { 10 | "path": "../mesh-browser-lite" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/mesh-browser-shim/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-browser-shim/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/browser_shim.ts', 5 | node: { 6 | fs: 'empty' 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.tsx?$/, 12 | use: [{ 13 | loader: 'ts-loader', 14 | }], 15 | exclude: /node_modules/ 16 | }, 17 | ], 18 | }, 19 | resolve: { 20 | extensions: ['.tsx', '.ts', '.js'], 21 | }, 22 | output: { 23 | filename: 'browser_shim.js', 24 | path: path.resolve(__dirname, 'dist'), 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /packages/mesh-browser/README.md: -------------------------------------------------------------------------------- 1 | ## @0x/mesh-browser 2 | 3 | This package provides an easy way to run a browser-based Mesh node. Specifically, it 4 | provides Typescript and Javascript bindings that can be used to interact with a Mesh 5 | node that is running in the browser and handles the process of loading the mesh node 6 | on the webpage. Because of the fact that this package handles Wasm loading, it is 7 | considerably heavier-weight and may take longer to load than the @0x/mesh-browser-lite 8 | package. 9 | 10 | ## Installation 11 | 12 | ```bash 13 | yarn add @0x/mesh-browser 14 | ``` 15 | 16 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 17 | 18 | ```json 19 | "compilerOptions": { 20 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], 21 | } 22 | ``` 23 | 24 | ## Contributing 25 | 26 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 27 | 28 | ### Install dependencies 29 | 30 | ```bash 31 | yarn install 32 | ``` 33 | 34 | ### Build 35 | 36 | ```bash 37 | yarn build 38 | ``` 39 | 40 | ### Clean 41 | 42 | ```bash 43 | yarn clean 44 | ``` 45 | 46 | ### Lint 47 | 48 | ```bash 49 | yarn lint 50 | ``` 51 | -------------------------------------------------------------------------------- /packages/mesh-browser/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Conversion tests for Mesh in the browser 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/mesh-browser/go/browserutil/browserutil.go: -------------------------------------------------------------------------------- 1 | // +build js, wasm 2 | 3 | // NOTE(jalextowle): This file contains utilities used by browser based mesh nodes 4 | // that need to be tested and would cause cyclic dependencies if they were moved 5 | // to jsutil. 6 | package browserutil 7 | 8 | import ( 9 | "errors" 10 | "syscall/js" 11 | "time" 12 | 13 | "github.com/0xProject/0x-mesh/core" 14 | "github.com/0xProject/0x-mesh/orderfilter" 15 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/jsutil" 16 | "github.com/0xProject/0x-mesh/packages/mesh-browser/go/providerwrapper" 17 | ) 18 | 19 | // ConvertConfig converts a JavaScript config object into a core.Config. It also 20 | // adds default values for any that are missing in the JavaScript object. 21 | func ConvertConfig(jsConfig js.Value) (core.Config, error) { 22 | if jsutil.IsNullOrUndefined(jsConfig) { 23 | return core.Config{}, errors.New("config is required") 24 | } 25 | 26 | // Default config options. Some might be overridden. 27 | config := core.Config{ 28 | Verbosity: 2, 29 | DataDir: "0x-mesh", 30 | P2PTCPPort: 0, 31 | P2PWebSocketsPort: 0, 32 | UseBootstrapList: true, 33 | BlockPollingInterval: 5 * time.Second, 34 | EthereumRPCMaxContentLength: 524288, 35 | EthereumRPCMaxRequestsPer24HrUTC: 100000, 36 | EthereumRPCMaxRequestsPerSecond: 30, 37 | EnableEthereumRPCRateLimiting: true, 38 | MaxOrdersInStorage: 100000, 39 | CustomOrderFilter: orderfilter.DefaultCustomOrderSchema, 40 | MaxBytesPerSecond: 5242880, // 5 MiB 41 | } 42 | 43 | // Required config options 44 | if ethereumChainID := jsConfig.Get("ethereumChainID"); jsutil.IsNullOrUndefined(ethereumChainID) { 45 | return core.Config{}, errors.New("ethereumChainID is required") 46 | } else { 47 | config.EthereumChainID = ethereumChainID.Int() 48 | } 49 | 50 | // Optional config options 51 | if verbosity := jsConfig.Get("verbosity"); !jsutil.IsNullOrUndefined(verbosity) { 52 | config.Verbosity = verbosity.Int() 53 | } 54 | if useBootstrapList := jsConfig.Get("useBootstrapList"); !jsutil.IsNullOrUndefined(useBootstrapList) { 55 | config.UseBootstrapList = useBootstrapList.Bool() 56 | } 57 | if bootstrapList := jsConfig.Get("bootstrapList"); !jsutil.IsNullOrUndefined(bootstrapList) { 58 | config.BootstrapList = bootstrapList.String() 59 | } 60 | if blockPollingIntervalSeconds := jsConfig.Get("blockPollingIntervalSeconds"); !jsutil.IsNullOrUndefined(blockPollingIntervalSeconds) { 61 | config.BlockPollingInterval = time.Duration(blockPollingIntervalSeconds.Int()) * time.Second 62 | } 63 | if ethereumRPCMaxContentLength := jsConfig.Get("ethereumRPCMaxContentLength"); !jsutil.IsNullOrUndefined(ethereumRPCMaxContentLength) { 64 | config.EthereumRPCMaxContentLength = ethereumRPCMaxContentLength.Int() 65 | } 66 | if ethereumRPCMaxRequestsPer24HrUTC := jsConfig.Get("ethereumRPCMaxRequestsPer24HrUTC"); !jsutil.IsNullOrUndefined(ethereumRPCMaxRequestsPer24HrUTC) { 67 | config.EthereumRPCMaxRequestsPer24HrUTC = ethereumRPCMaxRequestsPer24HrUTC.Int() 68 | } 69 | if ethereumRPCMaxRequestsPerSecond := jsConfig.Get("ethereumRPCMaxRequestsPerSecond"); !jsutil.IsNullOrUndefined(ethereumRPCMaxRequestsPerSecond) { 70 | config.EthereumRPCMaxRequestsPerSecond = ethereumRPCMaxRequestsPerSecond.Float() 71 | } 72 | if enableEthereumRPCRateLimiting := jsConfig.Get("enableEthereumRPCRateLimiting"); !jsutil.IsNullOrUndefined(enableEthereumRPCRateLimiting) { 73 | config.EnableEthereumRPCRateLimiting = enableEthereumRPCRateLimiting.Bool() 74 | } 75 | if customContractAddresses := jsConfig.Get("customContractAddresses"); !jsutil.IsNullOrUndefined(customContractAddresses) { 76 | config.CustomContractAddresses = customContractAddresses.String() 77 | } 78 | if maxOrdersInStorage := jsConfig.Get("maxOrdersInStorage"); !jsutil.IsNullOrUndefined(maxOrdersInStorage) { 79 | config.MaxOrdersInStorage = maxOrdersInStorage.Int() 80 | } 81 | if customOrderFilter := jsConfig.Get("customOrderFilter"); !jsutil.IsNullOrUndefined(customOrderFilter) { 82 | config.CustomOrderFilter = customOrderFilter.String() 83 | } 84 | if ethereumRPCURL := jsConfig.Get("ethereumRPCURL"); !jsutil.IsNullOrUndefined(ethereumRPCURL) && ethereumRPCURL.String() != "" { 85 | config.EthereumRPCURL = ethereumRPCURL.String() 86 | } 87 | if web3Provider := jsConfig.Get("web3Provider"); !jsutil.IsNullOrUndefined(web3Provider) { 88 | config.EthereumRPCClient = providerwrapper.NewRPCClient(web3Provider) 89 | } 90 | if maxBytesPerSecond := jsConfig.Get("maxBytesPerSecond"); !jsutil.IsNullOrUndefined(maxBytesPerSecond) { 91 | config.MaxBytesPerSecond = maxBytesPerSecond.Float() 92 | } 93 | 94 | return config, nil 95 | } 96 | -------------------------------------------------------------------------------- /packages/mesh-browser/go/jsutil/jsutil_bench_test.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package jsutil 4 | 5 | import ( 6 | "fmt" 7 | "math/cmplx" 8 | "math/rand" 9 | "testing" 10 | "time" 11 | 12 | "github.com/pborman/uuid" 13 | ) 14 | 15 | type testValue struct { 16 | Uint uint `json:"uint"` 17 | Uint8 uint8 `json:"uint8"` 18 | Uint16 uint16 `json:"uint16"` 19 | Uint32 uint32 `json:"uint32"` 20 | Uint64 uint64 `json:"uint64"` 21 | Int int `json:"int"` 22 | Int8 int8 `json:"int8"` 23 | Int16 int16 `json:"int16"` 24 | Int32 int32 `json:"int32"` 25 | Int64 int64 `json:"int64"` 26 | Float32 float32 `json:"float32"` 27 | Float64 float64 `json:"float64"` 28 | Byte byte `json:"byte"` 29 | Rune rune `json:"rune"` 30 | String string `json:"string"` 31 | Bool bool `json:"bool"` 32 | Time time.Time `json:"time"` 33 | } 34 | 35 | func newTestValue() *testValue { 36 | return &testValue{ 37 | Uint: uint(randomInt()), 38 | Uint8: uint8(randomInt()), 39 | Uint16: uint16(randomInt()), 40 | Uint32: uint32(randomInt()), 41 | Uint64: uint64(randomInt()), 42 | Int: randomInt(), 43 | Int8: int8(randomInt()), 44 | Int16: int16(randomInt()), 45 | Int32: int32(randomInt()), 46 | Int64: int64(randomInt()), 47 | Float32: float32(randomInt()), 48 | Float64: float64(randomInt()), 49 | Byte: []byte(randomString())[0], 50 | Rune: []rune(randomString())[0], 51 | String: randomString(), 52 | Bool: randomBool(), 53 | Time: time.Now(), 54 | } 55 | } 56 | 57 | func BenchmarkInefficientlyConvertToJS(b *testing.B) { 58 | counts := []int{ 59 | 1, 60 | 10, 61 | 100, 62 | 1000, 63 | } 64 | for _, count := range counts { 65 | name := fmt.Sprintf("BenchmarkInefficientlyConvertToJS_%d", count) 66 | b.Run(name, runBenchmarkInefficientlyConvertToJS(count)) 67 | } 68 | } 69 | 70 | func runBenchmarkInefficientlyConvertToJS(count int) func(*testing.B) { 71 | return func(b *testing.B) { 72 | values := []*testValue{} 73 | for i := 0; i < count; i++ { 74 | values = append(values, newTestValue()) 75 | } 76 | b.ResetTimer() 77 | 78 | for i := 0; i < b.N; i++ { 79 | _, _ = InefficientlyConvertToJS(values) 80 | } 81 | } 82 | } 83 | 84 | // randomInt returns a pseudo-random int between the minimum and maximum 85 | // possible values. 86 | func randomInt() int { 87 | return rand.Int() 88 | } 89 | 90 | // randomString returns a random string of length 16 91 | func randomString() string { 92 | return uuid.New() 93 | } 94 | 95 | // randomBool returns a random bool 96 | func randomBool() bool { 97 | return rand.Int()%2 == 0 98 | } 99 | 100 | // randomFloat returns a random float64 101 | func randomFloat() float64 { 102 | return rand.Float64() 103 | } 104 | 105 | // randomComplex returns a random complex128 106 | func randomComplex() complex128 { 107 | return cmplx.Rect(randomFloat(), randomFloat()) 108 | } 109 | -------------------------------------------------------------------------------- /packages/mesh-browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@0x/mesh-browser", 3 | "version": "11.2.0", 4 | "description": "TypeScript and JavaScript bindings for running Mesh directly in the browser.", 5 | "main": "./lib/index.js", 6 | "license": "Apache-2.0", 7 | "scripts": { 8 | "build": "yarn build:go && yarn build:generate && yarn build:ts && yarn build:bundle", 9 | "build:bundle": "node --max_old_space_size=3072 ./node_modules/.bin/webpack --mode=development", 10 | "build:ts": "tsc -b", 11 | "clean": "shx rm -r ./lib && shx rm tsconfig.tsbuildinfo || exit 0", 12 | "watch:ts": "tsc -b -w", 13 | "build:generate": "INPUT_PATH=./wasm/main.wasm OUTPUT_PATH=./src/generated/wasm_buffer.ts go run ./scripts/generate_wasm_buffer.go", 14 | "build:go": "yarn build:go:main && yarn build:go:conversion-test", 15 | "build:go:main": "GOOS=js GOARCH=wasm go build -o ./wasm/main.wasm ./go/mesh-browser/main.go", 16 | "build:go:conversion-test": "GOOS=js GOARCH=wasm go build -o ./dist/conversion_test.wasm ./go/conversion-test/main.go", 17 | "docs:md": "ts-doc-gen --sourceDir=./src --output=${npm_package_config_docsPath}", 18 | "lint": "tslint --format stylish --project ." 19 | }, 20 | "config": { 21 | "docsPath": "../../docs/browser-bindings/browser" 22 | }, 23 | "devDependencies": { 24 | "@0x/subproviders": "^6.0.5", 25 | "@0x/tslint-config": "^4.0.0", 26 | "@types/base64-arraybuffer": "^0.1.0", 27 | "ignore-loader": "^0.1.2", 28 | "prettier": "^2.1.2", 29 | "shx": "^0.3.2", 30 | "ts-loader": "^8.0.5", 31 | "tslint": "6.1.3", 32 | "typedoc": "^0.15.0", 33 | "typescript": "^4.0.3", 34 | "webpack": "^4.41.5", 35 | "webpack-cli": "^3.3.10" 36 | }, 37 | "dependencies": { 38 | "@0x/mesh-browser-lite": "^11.2.0", 39 | "base64-arraybuffer": "^0.2.0", 40 | "ethereum-types": "^3.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/mesh-browser/scripts/generate_wasm_buffer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/plaid/go-envvar/envvar" 11 | ) 12 | 13 | type EnvVars struct { 14 | InputPath string `envvar:"INPUT_PATH"` 15 | OutputPath string `envvar:"OUTPUT_PATH"` 16 | } 17 | 18 | var ( 19 | prefix = []byte("import * as base64 from 'base64-arraybuffer';\nexport const wasmBuffer = base64.decode('") 20 | suffix = []byte("');\n") 21 | ) 22 | 23 | func main() { 24 | env := EnvVars{} 25 | if err := envvar.Parse(&env); err != nil { 26 | panic(err) 27 | } 28 | 29 | wasmBytcode, err := ioutil.ReadFile(env.InputPath) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | encodedLen := base64.StdEncoding.EncodedLen(len(wasmBytcode)) 35 | encodedWasmBytcode := make([]byte, encodedLen) 36 | base64.StdEncoding.Encode(encodedWasmBytcode, wasmBytcode) 37 | 38 | outputDir := filepath.Dir(env.OutputPath) 39 | if err := os.MkdirAll(outputDir, os.ModePerm); err != nil { 40 | panic(err) 41 | } 42 | 43 | // HACK(albrow): We generate a TypeScript file that contains the Wasm output 44 | // encoded as a base64 string. This is the most reliable way to load Wasm such 45 | // that users just see a TypeScript/JavaScript package and without relying on 46 | // a third-party server. 47 | outputFile, err := os.OpenFile(env.OutputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) 48 | if err != nil { 49 | panic(err) 50 | } 51 | defer outputFile.Close() 52 | mustWrite(outputFile, prefix) 53 | mustWrite(outputFile, encodedWasmBytcode) 54 | mustWrite(outputFile, suffix) 55 | } 56 | 57 | func mustWrite(writer io.Writer, data []byte) { 58 | if _, err := writer.Write(data); err != nil { 59 | panic(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/mesh-browser/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@0x/mesh-browser-lite/lib/mesh'; 2 | 3 | import { wasmBuffer } from './generated/wasm_buffer'; 4 | 5 | // Start compiling the WebAssembly as soon as the script is loaded. This lets 6 | // us initialize as quickly as possible. 7 | const go = new Go(); 8 | WebAssembly.instantiate(wasmBuffer, go.importObject) 9 | .then((module) => { 10 | go.run(module.instance); 11 | }) 12 | .catch((err) => { 13 | // tslint:disable-next-line no-console 14 | console.error('Could not load Wasm'); 15 | // tslint:disable-next-line no-console 16 | console.error(err); 17 | // If the Wasm bytecode didn't compile, Mesh won't work. We have no 18 | // choice but to throw an error. 19 | setImmediate(() => { 20 | throw err; 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/mesh-browser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": ["./src/**/*"], 8 | "exclude": ["./conversion-tests/**/*"], 9 | "references": [ 10 | { 11 | "path": "../mesh-browser-lite" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/mesh-browser/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-browser/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './conversion-tests/conversion_test.ts', 5 | node: { 6 | fs: 'empty' 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.tsx?$/, 12 | use: [{ 13 | loader: 'ts-loader', 14 | options: { 15 | configFile: 'webpack.tsconfig.json', 16 | } 17 | }], 18 | exclude: /node_modules/ 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | filename: 'bundle.js', 27 | path: path.resolve(__dirname, 'dist'), 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /packages/mesh-browser/webpack.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { "outDir": "lib", "rootDir": "." }, 4 | "include": ["./src/**/*", "./conversion-tests/**/*"], 5 | "exclude": ["./src/index.ts"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-graphql-client/README.md: -------------------------------------------------------------------------------- 1 | ## @0x/mesh-graphql-client 2 | 3 | A client for the Mesh GraphQL API. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | yarn add @0x/mesh-graphql-client 9 | ``` 10 | 11 | If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: 12 | 13 | ```json 14 | "compilerOptions": { 15 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], 16 | } 17 | ``` 18 | 19 | ## Contributing 20 | 21 | If you would like to contribute bug fixes or new features to the client, checkout the [0xproject/0x-mesh](https://github.com/0xProject/0x-mesh) project and use the below commands to install the dependencies, build, lint and test your changes. 22 | 23 | ### Install dependencies 24 | 25 | ```bash 26 | yarn install 27 | ``` 28 | 29 | ### Build 30 | 31 | ```bash 32 | yarn build 33 | ``` 34 | 35 | ### Clean 36 | 37 | ```bash 38 | yarn clean 39 | ``` 40 | 41 | ### Lint 42 | 43 | ```bash 44 | yarn lint 45 | ``` 46 | 47 | ### Run Tests 48 | 49 | ```bash 50 | yarn test 51 | ``` 52 | -------------------------------------------------------------------------------- /packages/mesh-graphql-client/apollo.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | client: { 3 | service: { localSchemaFile: '../../graphql/schema.graphql' }, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/mesh-graphql-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@0x/mesh-graphql-client", 3 | "version": "11.2.0", 4 | "description": "A client for the Mesh GraphQL API", 5 | "main": "./lib/src/index.js", 6 | "license": "Apache-2.0", 7 | "scripts": { 8 | "build": "yarn build:ts", 9 | "build:ts": "tsc -b", 10 | "test": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --timeout 200000 --exit", 11 | "clean": "shx rm -r ./lib && shx rm tsconfig.tsbuildinfo || exit 0", 12 | "watch:ts": "tsc -b -w", 13 | "docs:md": "ts-doc-gen --sourceDir=./src --output=${npm_package_config_docsPath}", 14 | "lint": "tslint --format stylish --project .", 15 | "publish:private": "yarn build && gitpkg publish" 16 | }, 17 | "config": { 18 | "docsPath": "../../docs/graphql-client" 19 | }, 20 | "gitpkg": { 21 | "registry": "git@github.com:0xProject/gitpkg-registry.git" 22 | }, 23 | "devDependencies": { 24 | "@0x/contract-addresses": "^4.11.0", 25 | "@0x/contracts-erc20": "^3.2.1", 26 | "@0x/contracts-exchange": "^3.2.7", 27 | "@0x/contracts-test-utils": "^5.3.4", 28 | "@0x/dev-utils": "^3.3.0", 29 | "@0x/order-utils": "^10.3.0", 30 | "@0x/subproviders": "^6.1.1", 31 | "@0x/types": "^3.2.0", 32 | "@0x/web3-wrapper": "^7.2.0", 33 | "@types/jsonstream": "^0.8.30", 34 | "@types/ramda": "^0.27.20", 35 | "@types/rimraf": "^3.0.0", 36 | "@types/websocket": "^1.0.1", 37 | "chai": "^4.2.0", 38 | "chai-as-promised": "^7.1.1", 39 | "chai-bignumber": "^3.0.0", 40 | "dirty-chai": "^2.0.1", 41 | "gitpkg": "https://github.com/0xProject/gitpkg.git", 42 | "jsonstream": "^1.0.3", 43 | "mocha": "^8.1.3", 44 | "rimraf": "^3.0.2", 45 | "shx": "^0.3.2", 46 | "typedoc": "^0.15.0", 47 | "typescript": "^4.0.3", 48 | "websocket": "^1.0.31" 49 | }, 50 | "dependencies": { 51 | "@0x/protocol-utils": "^1.1.4", 52 | "@0x/types": "^3.2.0", 53 | "@0x/utils": "^6.2.0", 54 | "@apollo/client": "^3.3.6", 55 | "@types/ws": "^7.2.6", 56 | "graphql": "^15.3.0", 57 | "react": "*", 58 | "subscriptions-transport-ws": "^0.9.17", 59 | "ws": "^7.3.1", 60 | "zen-observable": "^0.8.15" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/mesh-graphql-client/test/utils/chai_setup.ts: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import chaiAsPromised = require('chai-as-promised'); 3 | import ChaiBigNumber = require('chai-bignumber'); 4 | import * as dirtyChai from 'dirty-chai'; 5 | 6 | export const chaiSetup = { 7 | configure(): void { 8 | chai.config.includeStack = true; 9 | chai.use(ChaiBigNumber()); 10 | chai.use(dirtyChai); 11 | chai.use(chaiAsPromised); 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/mesh-graphql-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "." 6 | }, 7 | "include": ["./src/**/*", "./test/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/mesh-graphql-client/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/graphql-dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Integration tests for Mesh GraphQL in the browser 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/legacy-dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Integration tests for Mesh in the browser 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mesh-integration-tests", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "Apache-2.0", 6 | "private": true, 7 | "scripts": { 8 | "build": "yarn build:ts && yarn build:legacy-webpack && yarn build:graphql-webpack", 9 | "build:ts": "tsc -b", 10 | "build:legacy-webpack": "node --max_old_space_size=3072 ./node_modules/.bin/webpack --mode=development --config webpack.legacy.config.js", 11 | "build:graphql-webpack": "node --max_old_space_size=3072 ./node_modules/.bin/webpack --mode=development --config webpack.graphql.config.js", 12 | "clean": "shx rm ./dist/bundle.js || exit 0", 13 | "postinstall:comment": "Remove the go and scripts directories of the mesh browser package to reduce the webpack bundle size", 14 | "postinstall": "yarn rimraf ./node_modules/@0x/mesh-browser/go ./node_modules/@0x/mesh-browser/scripts", 15 | "lint": "tslint --format stylish --project ." 16 | }, 17 | "devDependencies": { 18 | "rimraf": "^3.0.0", 19 | "ts-loader": "^8.0.5", 20 | "typescript": "^4.0.3", 21 | "webpack": "^4.39.2", 22 | "webpack-cli": "^3.3.7" 23 | }, 24 | "dependencies": { 25 | "@0x/mesh-browser": "^11.2.0", 26 | "@0x/mesh-graphql-client": "^11.2.0", 27 | "@0x/order-utils": "^10.0.1", 28 | "@0x/subproviders": "^6.0.2", 29 | "@0x/utils": "^6.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "rootDir": ".", 6 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"] 7 | }, 8 | "include": ["./src/**/*", "./test/**/*"], 9 | "references": [{ "path": "../mesh-browser" }, { "path": "../mesh-graphql-client" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/webpack.graphql.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './graphql/index.ts', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.tsx?$/, 9 | use: 'ts-loader', 10 | exclude: /node_modules/, 11 | }, 12 | ], 13 | }, 14 | resolve: { 15 | extensions: ['.tsx', '.ts', '.js'], 16 | }, 17 | output: { 18 | filename: 'bundle.js', 19 | path: path.resolve(__dirname, 'graphql-dist'), 20 | }, 21 | // This supports the ethereumjs-vm dependency in `@0x/base-contract` 22 | // The .wasm 'glue' code generated by Emscripten requires these node builtins, 23 | // but won't actually use them in a web environment. We tell Webpack to not resolve those 24 | // require statements since we know we won't need them. 25 | externals: { 26 | fs: true, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /packages/mesh-integration-tests/webpack.legacy.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './legacy/index.ts', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.tsx?$/, 9 | use: 'ts-loader', 10 | exclude: /node_modules/, 11 | }, 12 | ], 13 | }, 14 | resolve: { 15 | extensions: ['.tsx', '.ts', '.js'], 16 | }, 17 | output: { 18 | filename: 'bundle.js', 19 | path: path.resolve(__dirname, 'legacy-dist'), 20 | }, 21 | // This supports the ethereumjs-vm dependency in `@0x/base-contract` 22 | // The .wasm 'glue' code generated by Emscripten requires these node builtins, 23 | // but won't actually use them in a web environment. We tell Webpack to not resolve those 24 | // require statements since we know we won't need them. 25 | externals: { 26 | fs: true, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/README.md: -------------------------------------------------------------------------------- 1 | ## 0x Mesh Browser Lite Example 2 | 3 | This directory contains an example of how to run Mesh in the browser by serving 4 | raw WebAssembly bytecode directly. This manner of serving Mesh to the browser 5 | can have performance benefits and allow the bundle size of an application to remain 6 | much smaller. 7 | 8 | ### Running the Example 9 | 10 | To run the example, first build the monorepo by changing into the **../../** 11 | directory (the project's root directory) and then run: 12 | 13 | ``` 14 | yarn install && yarn build 15 | ``` 16 | 17 | Then simply serve the **./dist** directory using the web server 18 | of your choice and navigate to the page in your browser. For example, you could 19 | use `goexec`: 20 | 21 | ``` 22 | go get -u github.com/shurcooL/goexec 23 | goexec 'http.ListenAndServe(":8000", http.FileServer(http.Dir("./dist")))' 24 | ``` 25 | 26 | Then navigate to [localhost:8000](http://localhost:8000) in your browser (Chrome 27 | or Firefox are recommended). 28 | 29 | ### More Information 30 | 31 | - [Browser Guide](https://0x-org.gitbook.io/mesh/getting-started/browser) 32 | - [Browser Lite API Documentation](https://0x-org.gitbook.io/mesh/getting-started/browser-lite/reference) 33 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example of Mesh running in the browser via Webpack 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mesh-webpack-example-lite", 3 | "description": "An example of how to run Mesh directly in the browser using Webpack and the @0x/mesh-browser-lite package.", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "license": "Apache-2.0", 7 | "private": true, 8 | "scripts": { 9 | "build": "tsc -b && yarn build:webpack && yarn build:wasm", 10 | "build:wasm": "GOOS=js GOARCH=wasm go build -o ./dist/main.wasm ../mesh-browser/go/mesh-browser/main.go", 11 | "build:webpack": "node --max_old_space_size=3072 ./node_modules/.bin/webpack --mode=development", 12 | "clean": "shx rm ./dist/bundle.js || exit 0", 13 | "postinstall:comment": "Remove the go and scripts directories of the mesh browser package to reduce the webpack bundle size", 14 | "postinstall": "yarn rimraf ./node_modules/@0x/mesh-browser/go ./node_modules/@0x/mesh-browser/scripts", 15 | "lint": "tslint --format stylish --project ." 16 | }, 17 | "devDependencies": { 18 | "rimraf": "^3.0.0", 19 | "shx": "^0.3.2", 20 | "ts-loader": "^8.0.5", 21 | "typescript": "^4.0.3", 22 | "webpack": "^4.39.2", 23 | "webpack-cli": "^3.3.7" 24 | }, 25 | "dependencies": { 26 | "@0x/mesh-browser-lite": "^11.2.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BigNumber, 3 | loadMeshStreamingWithURLAsync, 4 | Mesh, 5 | OrderEvent, 6 | SignedOrder, 7 | SupportedProvider, 8 | } from '@0x/mesh-browser-lite'; 9 | 10 | // tslint:disable:no-console 11 | (async () => { 12 | // Load the WebAssembly bytecode. 13 | await loadMeshStreamingWithURLAsync('main.wasm'); 14 | 15 | // Configure Mesh to use web3.currentProvider (e.g. provided by MetaMask). 16 | const mesh = new Mesh({ 17 | verbosity: 4, 18 | ethereumChainID: 1, 19 | web3Provider: (window as any).web3.currentProvider as SupportedProvider, 20 | }); 21 | 22 | // This handler will be called whenever there is a critical error. 23 | mesh.onError((err: Error) => { 24 | console.error(err); 25 | }); 26 | 27 | // This handler will be called whenever an order is added, expired, 28 | // cancelled, or filled. 29 | mesh.onOrderEvents((events: OrderEvent[]) => { 30 | for (const event of events) { 31 | console.log(event); 32 | } 33 | }); 34 | 35 | // Start Mesh *after* we set up the handlers. 36 | await mesh.startAsync(); 37 | 38 | // This order is for demonstration purposes only and will likely be expired 39 | // by the time you run this example. If so, it will be rejected by Mesh. You 40 | // can replace it with a valid order. 41 | const order: SignedOrder = { 42 | signature: 43 | '0x1c68eb1e2577e9f51776bdb06ec51fcec9aec0ea1565eca5e243917cecaafaa46b3b9590ff6575bf1c048d0b4ec5773a2e3a8df3bf117e1613e2a7b57d6f95c95a02', 44 | senderAddress: '0x0000000000000000000000000000000000000000', 45 | makerAddress: '0x4418755f710468e223797a006603e29937e825bc', 46 | takerAddress: '0x0000000000000000000000000000000000000000', 47 | makerFee: new BigNumber('0'), 48 | takerFee: new BigNumber('0'), 49 | makerAssetAmount: new BigNumber('3000000000'), 50 | takerAssetAmount: new BigNumber('19500000000000000000'), 51 | makerAssetData: '0xf47261b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 52 | takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 53 | salt: new BigNumber('1579725034907'), 54 | exchangeAddress: '0x61935cbdd02287b511119ddb11aeb42f1593b7ef', 55 | feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', 56 | expirationTimeSeconds: new BigNumber('1580329834'), 57 | makerFeeAssetData: '0x', 58 | chainId: 1, 59 | takerFeeAssetData: '0x', 60 | }; 61 | 62 | // Add the order and log the result. 63 | const result = await mesh.addOrdersAsync([order]); 64 | console.log(result); 65 | })().catch((err) => { 66 | console.error(err); 67 | }); 68 | // tslint:enable:no-console 69 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": ["./src/**/*", "./test/**/*"], 8 | "references": [ 9 | { 10 | "path": "../mesh-browser-lite" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example-lite/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.ts', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.tsx?$/, 9 | use: 'ts-loader', 10 | exclude: /node_modules/, 11 | }, 12 | ], 13 | }, 14 | resolve: { 15 | extensions: ['.tsx', '.ts', '.js'], 16 | }, 17 | output: { 18 | filename: 'bundle.js', 19 | path: path.resolve(__dirname, 'dist'), 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/README.md: -------------------------------------------------------------------------------- 1 | ## 0x Mesh Browser Example 2 | 3 | This directory contains an example of how to run Mesh in the browser. 4 | 5 | ### Running the Example 6 | 7 | To run the example, first build the monorepo by changing into the **../../** 8 | directory (the project's root directory) and then run: 9 | 10 | ``` 11 | yarn install && yarn build 12 | ``` 13 | 14 | Then simply serve the **./dist** directory using the web server 15 | of your choice and navigate to the page in your browser. For example, you could 16 | use `goexec`: 17 | 18 | ``` 19 | go get -u github.com/shurcooL/goexec 20 | goexec 'http.ListenAndServe(":8000", http.FileServer(http.Dir("./dist")))' 21 | ``` 22 | 23 | Then navigate to [localhost:8000](http://localhost:8000) in your browser (Chrome 24 | or Firefox are recommended). 25 | 26 | ### More Information 27 | 28 | - [Browser Guide](https://0x-org.gitbook.io/mesh/getting-started/browser) 29 | - [Browser API Documentation](https://0x-org.gitbook.io/mesh/getting-started/browser/reference) 30 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example of Mesh running in the browser via Webpack 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mesh-webpack-example", 3 | "description": "An example of how to run Mesh directly in the browser using Webpack and the @0x/mesh-browser package.", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "license": "Apache-2.0", 7 | "private": true, 8 | "scripts": { 9 | "build": "tsc -b && node --max_old_space_size=3072 ./node_modules/.bin/webpack --mode=development", 10 | "clean": "shx rm ./dist/bundle.js || exit 0", 11 | "postinstall:comment": "Remove the go and scripts directories of the mesh browser package to reduce the webpack bundle size", 12 | "postinstall": "yarn rimraf ./node_modules/@0x/mesh-browser/go ./node_modules/@0x/mesh-browser/scripts", 13 | "lint": "tslint --format stylish --project ." 14 | }, 15 | "devDependencies": { 16 | "rimraf": "^3.0.0", 17 | "shx": "^0.3.2", 18 | "ts-loader": "^8.0.5", 19 | "typescript": "^4.0.3", 20 | "webpack": "^4.39.2", 21 | "webpack-cli": "^3.3.7" 22 | }, 23 | "dependencies": { 24 | "@0x/mesh-browser": "^11.2.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/src/index.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, Mesh, OrderEvent, SignedOrder, SupportedProvider } from '@0x/mesh-browser'; 2 | 3 | // tslint:disable:no-console 4 | (async () => { 5 | // Configure Mesh to use web3.currentProvider (e.g. provided by MetaMask). 6 | const mesh = new Mesh({ 7 | verbosity: 4, 8 | ethereumChainID: 1, 9 | web3Provider: (window as any).web3.currentProvider as SupportedProvider, 10 | }); 11 | 12 | // This handler will be called whenever there is a critical error. 13 | mesh.onError((err: Error) => { 14 | console.error(err); 15 | }); 16 | 17 | // This handler will be called whenever an order is added, expired, 18 | // cancelled, or filled. 19 | mesh.onOrderEvents((events: OrderEvent[]) => { 20 | for (const event of events) { 21 | console.log(event); 22 | } 23 | }); 24 | 25 | // Start Mesh *after* we set up the handlers. 26 | await mesh.startAsync(); 27 | 28 | // This order is for demonstration purposes only and will likely be expired 29 | // by the time you run this example. If so, it will be rejected by Mesh. You 30 | // can replace it with a valid order. 31 | const order: SignedOrder = { 32 | signature: 33 | '0x1c68eb1e2577e9f51776bdb06ec51fcec9aec0ea1565eca5e243917cecaafaa46b3b9590ff6575bf1c048d0b4ec5773a2e3a8df3bf117e1613e2a7b57d6f95c95a02', 34 | senderAddress: '0x0000000000000000000000000000000000000000', 35 | makerAddress: '0x4418755f710468e223797a006603e29937e825bc', 36 | takerAddress: '0x0000000000000000000000000000000000000000', 37 | makerFee: new BigNumber('0'), 38 | takerFee: new BigNumber('0'), 39 | makerAssetAmount: new BigNumber('3000000000'), 40 | takerAssetAmount: new BigNumber('19500000000000000000'), 41 | makerAssetData: '0xf47261b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 42 | takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 43 | salt: new BigNumber('1579725034907'), 44 | exchangeAddress: '0x61935cbdd02287b511119ddb11aeb42f1593b7ef', 45 | feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', 46 | expirationTimeSeconds: new BigNumber('1580329834'), 47 | makerFeeAssetData: '0x', 48 | chainId: 1, 49 | takerFeeAssetData: '0x', 50 | }; 51 | 52 | // Add the order and log the result. 53 | const result = await mesh.addOrdersAsync([order]); 54 | console.log(result); 55 | })().catch((err) => { 56 | console.error(err); 57 | }); 58 | // tslint:enable:no-console 59 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": ["./src/**/*", "./test/**/*"], 8 | "references": [ 9 | { 10 | "path": "../mesh-browser" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@0x/tslint-config"], 3 | "rules": { 4 | "arrow-parens": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/mesh-webpack-example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.ts', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.tsx?$/, 9 | use: 'ts-loader', 10 | exclude: /node_modules/, 11 | }, 12 | ], 13 | }, 14 | resolve: { 15 | extensions: ['.tsx', '.ts', '.js'], 16 | }, 17 | output: { 18 | filename: 'bundle.js', 19 | path: path.resolve(__dirname, 'dist'), 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /scenario/orderopts/orderopts.go: -------------------------------------------------------------------------------- 1 | package orderopts 2 | 3 | import ( 4 | "bytes" 5 | "math/big" 6 | 7 | "github.com/0xProject/0x-mesh/constants" 8 | "github.com/0xProject/0x-mesh/ethereum" 9 | "github.com/0xProject/0x-mesh/zeroex" 10 | "github.com/ethereum/go-ethereum/common" 11 | ) 12 | 13 | type Config struct { 14 | Order *zeroex.Order 15 | OrderV4 *zeroex.OrderV4 16 | SetupMakerState bool 17 | SetupTakerAddress common.Address 18 | } 19 | 20 | type Option func(order *Config) error 21 | 22 | // Apply applies the given options to the config, returning the first error 23 | // encountered (if any). 24 | func (cfg *Config) Apply(opts ...Option) error { 25 | for _, opt := range opts { 26 | if opt == nil { 27 | continue 28 | } 29 | if err := opt(cfg); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | 36 | func MakerAddress(address common.Address) Option { 37 | return func(cfg *Config) error { 38 | cfg.Order.MakerAddress = address 39 | cfg.OrderV4.Maker = address 40 | return nil 41 | } 42 | } 43 | 44 | func MakerAssetData(assetData []byte) Option { 45 | return func(cfg *Config) error { 46 | cfg.Order.MakerAssetData = assetData 47 | cfg.OrderV4.MakerToken = tokenFromAssetData(assetData) 48 | return nil 49 | } 50 | } 51 | 52 | func MakerAssetAmount(amount *big.Int) Option { 53 | return func(cfg *Config) error { 54 | cfg.Order.MakerAssetAmount = amount 55 | cfg.OrderV4.MakerAmount = amount 56 | return nil 57 | } 58 | } 59 | 60 | func TakerAssetData(assetData []byte) Option { 61 | return func(cfg *Config) error { 62 | cfg.Order.TakerAssetData = assetData 63 | cfg.OrderV4.TakerToken = tokenFromAssetData(assetData) 64 | return nil 65 | } 66 | } 67 | 68 | func TakerAssetAmount(amount *big.Int) Option { 69 | return func(cfg *Config) error { 70 | cfg.Order.TakerAssetAmount = amount 71 | cfg.OrderV4.TakerAmount = amount 72 | return nil 73 | } 74 | } 75 | 76 | func ExpirationTimeSeconds(expirationTimeSeconds *big.Int) Option { 77 | return func(cfg *Config) error { 78 | cfg.Order.ExpirationTimeSeconds = expirationTimeSeconds 79 | cfg.OrderV4.Expiry = expirationTimeSeconds 80 | return nil 81 | } 82 | } 83 | 84 | func MakerFeeAssetData(assetData []byte) Option { 85 | return func(cfg *Config) error { 86 | cfg.Order.MakerFeeAssetData = assetData 87 | // V4 has no separate fee tokens 88 | return nil 89 | } 90 | } 91 | 92 | func MakerFee(amount *big.Int) Option { 93 | return func(cfg *Config) error { 94 | cfg.Order.MakerFee = amount 95 | // V4 has no maker fee 96 | return nil 97 | } 98 | } 99 | 100 | func SenderAddress(address common.Address) Option { 101 | return func(cfg *Config) error { 102 | cfg.Order.SenderAddress = address 103 | cfg.OrderV4.Sender = address 104 | return nil 105 | } 106 | } 107 | 108 | func TakerAddress(address common.Address) Option { 109 | return func(cfg *Config) error { 110 | cfg.Order.TakerAddress = address 111 | cfg.OrderV4.Taker = address 112 | return nil 113 | } 114 | } 115 | 116 | func FeeRecipientAddress(address common.Address) Option { 117 | return func(cfg *Config) error { 118 | cfg.Order.FeeRecipientAddress = address 119 | cfg.OrderV4.FeeRecipient = address 120 | return nil 121 | } 122 | } 123 | 124 | func SetupMakerState(b bool) Option { 125 | return func(cfg *Config) error { 126 | cfg.SetupMakerState = b 127 | return nil 128 | } 129 | } 130 | 131 | func SetupTakerAddress(takerAddress common.Address) Option { 132 | return func(cfg *Config) error { 133 | cfg.SetupTakerAddress = takerAddress 134 | return nil 135 | } 136 | } 137 | 138 | func tokenFromAssetData(assetData []byte) common.Address { 139 | if bytes.Equal(assetData, constants.ZRXAssetData) { 140 | return ethereum.GanacheAddresses.ZRXToken 141 | } else if bytes.Equal(assetData, constants.WETHAssetData) { 142 | return ethereum.GanacheAddresses.WETH9 143 | } else { 144 | // No other tokens exist in test and only ERC20 is supported 145 | return constants.NullAddress 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /scenario/scenario_v4.go: -------------------------------------------------------------------------------- 1 | // Package scenario allows creating orders for testing purposes with a variety of options. 2 | // It also supports setting up the necessary on-chain state for both the taker and maker. 3 | package scenario 4 | 5 | import ( 6 | "math/big" 7 | "testing" 8 | "time" 9 | 10 | "github.com/0xProject/0x-mesh/constants" 11 | "github.com/0xProject/0x-mesh/ethereum/signer" 12 | "github.com/0xProject/0x-mesh/scenario/orderopts" 13 | "github.com/0xProject/0x-mesh/zeroex" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func defaultTestOrderV4() *zeroex.OrderV4 { 18 | return &zeroex.OrderV4{ 19 | ChainID: big.NewInt(constants.TestChainID), 20 | VerifyingContract: ganacheAddresses.ExchangeProxy, 21 | // FIXME: Ganache snapshot currently requires valid token addresses or it will revert 22 | MakerToken: ganacheAddresses.WETH9, 23 | TakerToken: ganacheAddresses.ZRXToken, 24 | MakerAmount: big.NewInt(100), 25 | TakerAmount: big.NewInt(42), 26 | TakerTokenFeeAmount: big.NewInt(0), 27 | Maker: constants.GanacheAccount1, 28 | Taker: constants.NullAddress, 29 | Sender: constants.NullAddress, 30 | FeeRecipient: constants.NullAddress, 31 | Pool: zeroex.HexToBytes32("0000000000000000000000000000000000000000000000000000000000000000"), 32 | Expiry: big.NewInt(time.Now().Add(24 * time.Hour).Unix()), 33 | Salt: big.NewInt(int64(time.Now().Nanosecond())), 34 | } 35 | } 36 | 37 | func NewSignedTestOrderV4(t *testing.T, opts ...orderopts.Option) *zeroex.SignedOrderV4 { 38 | // Generate v4 order with options applied 39 | cfg := defaultConfig() 40 | require.NoError(t, cfg.Apply(opts...)) 41 | order := cfg.OrderV4 42 | 43 | // Sign order 44 | testSigner := signer.NewTestSigner() 45 | signedOrder, err := zeroex.SignOrderV4(testSigner, order) 46 | require.NoError(t, err, "could not sign order") 47 | 48 | if cfg.SetupMakerState { 49 | // sets up all the on-chain state in order to make the order fillable for maker 50 | requiredMakerBalances := newTokenBalances() 51 | requiredMakerBalances.addTokenAmount(t, order.MakerToken, order.MakerAmount) 52 | setupBalanceAndAllowance(t, order.Maker, requiredMakerBalances) 53 | } 54 | if cfg.SetupTakerAddress != constants.NullAddress { 55 | // sets up all the on-chain state in order to make the order fillable taker 56 | requiredMakerBalances := newTokenBalances() 57 | requiredMakerBalances.addTokenAmount(t, order.TakerToken, order.TakerAmount) 58 | requiredMakerBalances.addTokenAmount(t, order.TakerToken, order.TakerTokenFeeAmount) 59 | setupBalanceAndAllowance(t, cfg.SetupTakerAddress, requiredMakerBalances) 60 | } 61 | 62 | return signedOrder 63 | } 64 | 65 | // NewSignedTestOrdersBatchV4 creates numOrders orders with independent options. 66 | // 67 | // Unlike the V3 version it currently does not efficiently set balances. 68 | func NewSignedTestOrdersBatchV4(t *testing.T, numOrders int, optionsForIndex func(index int) []orderopts.Option) []*zeroex.SignedOrderV4 { 69 | allOrders := make([]*zeroex.SignedOrderV4, numOrders) 70 | for i := 0; i < numOrders; i++ { 71 | if optionsForIndex != nil { 72 | allOrders[i] = NewSignedTestOrderV4(t, optionsForIndex(i)...) 73 | } else { 74 | allOrders[i] = NewSignedTestOrderV4(t) 75 | } 76 | } 77 | return allOrders 78 | } 79 | -------------------------------------------------------------------------------- /tsconfig-base.json: -------------------------------------------------------------------------------- 1 | // This file is used as a base for all other tsconfig.json files. 2 | { 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "target": "es2016", 6 | "lib": ["es2017", "dom"], 7 | "experimentalDecorators": true, 8 | "downlevelIteration": true, 9 | "noImplicitReturns": true, 10 | "pretty": true, 11 | "skipLibCheck": true, 12 | "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], 13 | "strict": true, 14 | "allowJs": true, 15 | // These settings are required for TypeScript project references 16 | "composite": true, 17 | "declaration": true, 18 | "declarationMap": true, 19 | "sourceMap": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | // The root of the project is just a list of references and does not contain 2 | // any top-level TypeScript code. 3 | { 4 | "extends": "./tsconfig-base", 5 | "include": [], 6 | "references": [{ "path": "./packages/mesh-integration-tests" }, { "path": "./packages/mesh-graphql-client" }] 7 | } 8 | -------------------------------------------------------------------------------- /zeroex/order_js.go: -------------------------------------------------------------------------------- 1 | // +build js,wasm 2 | 3 | package zeroex 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | "syscall/js" 9 | "time" 10 | 11 | "github.com/ethereum/go-ethereum/common" 12 | ) 13 | 14 | func (o OrderEvent) JSValue() js.Value { 15 | contractEventsJS := make([]interface{}, len(o.ContractEvents)) 16 | for i, contractEvent := range o.ContractEvents { 17 | contractEventsJS[i] = contractEvent.JSValue() 18 | } 19 | return js.ValueOf(map[string]interface{}{ 20 | "timestamp": o.Timestamp.Format(time.RFC3339), 21 | "orderHash": o.OrderHash.Hex(), 22 | "signedOrder": o.SignedOrder.JSValue(), 23 | "endState": string(o.EndState), 24 | "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), 25 | "contractEvents": contractEventsJS, 26 | }) 27 | } 28 | 29 | func (s SignedOrder) JSValue() js.Value { 30 | makerAssetData := "0x" 31 | if len(s.MakerAssetData) != 0 { 32 | makerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(s.MakerAssetData)) 33 | } 34 | // Note(albrow): Because of how our smart contracts work, most fields of an 35 | // order cannot be null. However, makerAssetFeeData and takerAssetFeeData are 36 | // the exception. For these fields, "0x" is used to indicate a null value. 37 | makerFeeAssetData := "0x" 38 | if len(s.MakerFeeAssetData) != 0 { 39 | makerFeeAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(s.MakerFeeAssetData)) 40 | } 41 | takerAssetData := "0x" 42 | if len(s.TakerAssetData) != 0 { 43 | takerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(s.TakerAssetData)) 44 | } 45 | takerFeeAssetData := "0x" 46 | if len(s.TakerFeeAssetData) != 0 { 47 | takerFeeAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(s.TakerFeeAssetData)) 48 | } 49 | signature := "0x" 50 | if len(s.Signature) != 0 { 51 | signature = fmt.Sprintf("0x%s", common.Bytes2Hex(s.Signature)) 52 | } 53 | 54 | return js.ValueOf(map[string]interface{}{ 55 | "chainId": s.ChainID.Int64(), 56 | "exchangeAddress": strings.ToLower(s.ExchangeAddress.Hex()), 57 | "makerAddress": strings.ToLower(s.MakerAddress.Hex()), 58 | "makerAssetData": makerAssetData, 59 | "makerFeeAssetData": makerFeeAssetData, 60 | "makerAssetAmount": s.MakerAssetAmount.String(), 61 | "makerFee": s.MakerFee.String(), 62 | "takerAddress": strings.ToLower(s.TakerAddress.Hex()), 63 | "takerAssetData": takerAssetData, 64 | "takerFeeAssetData": takerFeeAssetData, 65 | "takerAssetAmount": s.TakerAssetAmount.String(), 66 | "takerFee": s.TakerFee.String(), 67 | "senderAddress": strings.ToLower(s.SenderAddress.Hex()), 68 | "feeRecipientAddress": strings.ToLower(s.FeeRecipientAddress.Hex()), 69 | "expirationTimeSeconds": s.ExpirationTimeSeconds.String(), 70 | "salt": s.Salt.String(), 71 | "signature": signature, 72 | }) 73 | } 74 | 75 | func (c ContractEvent) JSValue() js.Value { 76 | m := map[string]interface{}{ 77 | "address": c.Address.Hex(), 78 | "blockHash": c.BlockHash.Hex(), 79 | "txHash": c.TxHash.Hex(), 80 | "txIndex": c.TxIndex, 81 | "logIndex": c.LogIndex, 82 | "isRemoved": c.IsRemoved, 83 | "kind": c.Kind, 84 | "parameters": c.Parameters.(js.Wrapper).JSValue(), 85 | } 86 | return js.ValueOf(m) 87 | } 88 | -------------------------------------------------------------------------------- /zeroex/ordervalidator/order_validator_js.go: -------------------------------------------------------------------------------- 1 | // +build js, wasm 2 | 3 | package ordervalidator 4 | 5 | import "syscall/js" 6 | 7 | func (v ValidationResults) JSValue() js.Value { 8 | accepted := make([]interface{}, len(v.Accepted)) 9 | for i, info := range v.Accepted { 10 | accepted[i] = info 11 | } 12 | rejected := make([]interface{}, len(v.Rejected)) 13 | for i, info := range v.Rejected { 14 | rejected[i] = info 15 | } 16 | return js.ValueOf(map[string]interface{}{ 17 | "accepted": accepted, 18 | "rejected": rejected, 19 | }) 20 | } 21 | 22 | func (a AcceptedOrderInfo) JSValue() js.Value { 23 | return js.ValueOf(map[string]interface{}{ 24 | "orderHash": a.OrderHash.Hex(), 25 | "signedOrder": a.SignedOrder.JSValue(), 26 | "fillableTakerAssetAmount": a.FillableTakerAssetAmount.String(), 27 | "isNew": a.IsNew, 28 | }) 29 | } 30 | 31 | func (r RejectedOrderInfo) JSValue() js.Value { 32 | return js.ValueOf(map[string]interface{}{ 33 | "orderHash": r.OrderHash.String(), 34 | "signedOrder": r.SignedOrder.JSValue(), 35 | "kind": string(r.Kind), 36 | "status": r.Status.JSValue(), 37 | }) 38 | } 39 | 40 | func (s RejectedOrderStatus) JSValue() js.Value { 41 | return js.ValueOf(map[string]interface{}{ 42 | "code": s.Code, 43 | "message": s.Message, 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /zeroex/orderwatch/decoder/event_decoder_v4.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | type ExchangeFillEventV4 struct { 10 | OrderHash common.Hash 11 | Maker common.Address 12 | Taker common.Address 13 | FeeRecipient common.Address 14 | MakerToken common.Address 15 | TakerToken common.Address 16 | TakerTokenFilledAmount *big.Int 17 | MakerTokenFilledAmount *big.Int 18 | TakerTokenFeeFilledAmount *big.Int 19 | ProtocolFeePaid *big.Int 20 | Pool common.Hash // Decoder does not support bytes32 21 | } 22 | 23 | type ExchangeCancelEventV4 struct { 24 | Maker common.Address 25 | OrderHash common.Hash 26 | } 27 | 28 | type ExchangePairCancelledLimitOrdersEventV4 struct { 29 | Maker common.Address 30 | MakerToken common.Address 31 | TakerToken common.Address 32 | MinValidSalt *big.Int 33 | } 34 | -------------------------------------------------------------------------------- /zeroex/orderwatch/order_watcher_utils.go: -------------------------------------------------------------------------------- 1 | package orderwatch 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | type contractAddressesSeenCounter struct { 10 | contractAddressToSeenCount map[common.Address]uint 11 | 12 | mu sync.RWMutex 13 | } 14 | 15 | func NewContractAddressesSeenCounter() *contractAddressesSeenCounter { 16 | return &contractAddressesSeenCounter{ 17 | contractAddressToSeenCount: map[common.Address]uint{}, 18 | } 19 | } 20 | 21 | func (ca *contractAddressesSeenCounter) Inc(address common.Address) uint { 22 | ca.mu.Lock() 23 | defer ca.mu.Unlock() 24 | newValue := ca.contractAddressToSeenCount[address] + 1 25 | ca.contractAddressToSeenCount[address] = newValue 26 | return newValue 27 | } 28 | 29 | func (ca *contractAddressesSeenCounter) Dec(address common.Address) uint { 30 | ca.mu.Lock() 31 | defer ca.mu.Unlock() 32 | newValue := ca.contractAddressToSeenCount[address] - 1 33 | ca.contractAddressToSeenCount[address] = newValue 34 | return newValue 35 | } 36 | 37 | func (ca *contractAddressesSeenCounter) Get(address common.Address) uint { 38 | ca.mu.RLock() 39 | defer ca.mu.RUnlock() 40 | return ca.contractAddressToSeenCount[address] 41 | } 42 | -------------------------------------------------------------------------------- /zeroex/orderwatch/topics.go: -------------------------------------------------------------------------------- 1 | package orderwatch 2 | 3 | import ( 4 | "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/crypto" 7 | ) 8 | 9 | // GetRelevantTopics returns the OrderWatcher-relevant topics that should be used when filtering 10 | // the logs retrieved for Ethereum blocks 11 | func GetRelevantTopics() []common.Hash { 12 | topics := []common.Hash{} 13 | for _, signature := range decoder.EVENT_SIGNATURES { 14 | topic := common.BytesToHash(crypto.Keccak256([]byte(signature))) 15 | topics = append(topics, topic) 16 | } 17 | 18 | return topics 19 | } 20 | -------------------------------------------------------------------------------- /zeroex/utils.go: -------------------------------------------------------------------------------- 1 | package zeroex 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "golang.org/x/crypto/sha3" 9 | ) 10 | 11 | // keccak256 calculates and returns the Keccak256 hash of the input data. 12 | func keccak256(data ...[]byte) []byte { 13 | d := sha3.NewLegacyKeccak256() 14 | for _, b := range data { 15 | _, _ = d.Write(b) 16 | } 17 | return d.Sum(nil) 18 | } 19 | 20 | // Bytes32 represents the Solidity `bytes32` type. 21 | // It is mostly copied from go-ethereum's Hash type. 22 | // See 23 | type Bytes32 [32]byte 24 | 25 | // BytesToBytes32 converts []byte to Bytes32 26 | func BytesToBytes32(b []byte) Bytes32 { 27 | var result Bytes32 28 | result.SetBytes(b) 29 | return result 30 | } 31 | 32 | // HexToBytes32 creates a Bytes32 from a hex string. 33 | func HexToBytes32(s string) Bytes32 { 34 | return BytesToBytes32(common.FromHex(s)) 35 | } 36 | 37 | // BigToBytes32 creates a Bytes32 from a big.Int. 38 | func BigToBytes32(i *big.Int) Bytes32 { 39 | return BytesToBytes32(i.Bytes()) 40 | } 41 | 42 | // HashToBytes32 creates a Bytes32 from a Hash. 43 | func HashToBytes32(h common.Hash) Bytes32 { 44 | return BytesToBytes32(h.Bytes()) 45 | } 46 | 47 | // SetBytes sets the Bytes32 to the value of bytes. 48 | // If bytes is larger than 32, bytes will be cropped from the left. 49 | func (b *Bytes32) SetBytes(bytes []byte) { 50 | if len(bytes) > 32 { 51 | bytes = bytes[len(bytes)-32:] 52 | } 53 | 54 | copy(b[32-len(bytes):], bytes) 55 | } 56 | 57 | // Hex converts a Bytes32 to a hex string. 58 | func (b Bytes32) Hex() string { 59 | return hexutil.Encode(b[:]) 60 | } 61 | 62 | // Bytes converts a Bytes32 to a []bytes. 63 | func (b Bytes32) Bytes() []byte { 64 | return b[:] 65 | } 66 | 67 | // Raw converts a Bytes32 to a [32]bytes. 68 | func (b Bytes32) Raw() [32]byte { 69 | return b 70 | } 71 | 72 | // Raw converts a Bytes32 to a big.Int 73 | func (b Bytes32) Big() *big.Int { 74 | return new(big.Int).SetBytes(b[:]) 75 | } 76 | 77 | // String prints the value in hex 78 | func (b Bytes32) String() string { 79 | return b.Hex() 80 | } 81 | --------------------------------------------------------------------------------