├── .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 |
--------------------------------------------------------------------------------