├── .config ├── smoldot.dic └── spellcheck.toml ├── .dockerignore ├── .github ├── CODEOWNERS ├── cargo-deny.toml ├── dependabot.yml ├── mergify.yml └── workflows │ ├── audit.yml │ ├── ci-post-workflow.yml │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── CODING_GUIDE.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── header-polkadot-512271 └── header.rs ├── bin ├── dev.json ├── flaming-fir.json ├── full-node │ ├── Cargo.toml │ ├── Dockerfile │ └── src │ │ ├── cli.rs │ │ ├── main.rs │ │ ├── run.rs │ │ └── run │ │ ├── consensus_service.rs │ │ ├── database_thread.rs │ │ ├── jaeger_service.rs │ │ ├── json_rpc_service.rs │ │ ├── network_service.rs │ │ └── network_service │ │ └── tasks.rs ├── fuzz │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── fuzz_targets │ │ ├── chain-spec.rs │ │ ├── fork-tree.rs │ │ ├── grandpa-justification-parse.rs │ │ ├── header-parse.rs │ │ ├── json-rpc-call.rs │ │ ├── multiaddr-bytes.rs │ │ ├── multiaddr-text.rs │ │ ├── multihash.rs │ │ ├── network-connection-encrypted.rs │ │ ├── network-connection-raw.rs │ │ ├── peer-id.rs │ │ ├── proof-node-codec.rs │ │ ├── protocol-block-announce-decode.rs │ │ ├── protocol-block-announces-handshake-decode.rs │ │ ├── protocol-blocks-request-decode.rs │ │ ├── protocol-blocks-response-decode.rs │ │ ├── protocol-grandpa-notification-decode.rs │ │ ├── protocol-grandpa-warp-sync-response-decode.rs │ │ ├── protocol-identify-response-decode.rs │ │ ├── protocol-state-response-decode.rs │ │ ├── protocol-storage-call-proof-response-decode.rs │ │ ├── wasm-module-wasmi.rs │ │ └── wasm-module-wasmtime.rs ├── generate_chain_spec.sh ├── kusama-karura.json ├── kusama-statemine.json ├── kusama.json ├── light-base │ ├── Cargo.toml │ ├── examples │ │ └── basic.rs │ └── src │ │ ├── database.rs │ │ ├── json_rpc_service.rs │ │ ├── json_rpc_service │ │ ├── chain_head.rs │ │ ├── getters.rs │ │ ├── state_chain.rs │ │ ├── state_chain │ │ │ └── sub_utils.rs │ │ └── transactions.rs │ │ ├── lib.rs │ │ ├── network_service.rs │ │ ├── network_service │ │ └── tasks.rs │ │ ├── platform.rs │ │ ├── platform │ │ └── async_std.rs │ │ ├── runtime_service.rs │ │ ├── sync_service.rs │ │ ├── sync_service │ │ ├── parachain.rs │ │ └── standalone.rs │ │ ├── transactions_service.rs │ │ └── util.rs ├── polkadot-acala.json ├── polkadot.json ├── rococo-canvas.json ├── rococo.json ├── substrate-node-template.json ├── tick.json ├── track.json ├── trick.json ├── wasm-node │ ├── CHANGELOG.md │ ├── README.md │ ├── javascript │ │ ├── .gitignore │ │ ├── README.md │ │ ├── demo │ │ │ ├── demo-deno.ts │ │ │ └── demo.mjs │ │ ├── fix-package-type.sh │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── prepare.mjs │ │ ├── src │ │ │ ├── base64.ts │ │ │ ├── client.ts │ │ │ ├── index-browser.ts │ │ │ ├── index-deno.ts │ │ │ ├── index-nodejs.ts │ │ │ └── instance │ │ │ │ ├── autogen │ │ │ │ └── .gitignore │ │ │ │ ├── bindings-smoldot-light.ts │ │ │ │ ├── bindings-wasi.ts │ │ │ │ ├── bindings.ts │ │ │ │ ├── buffer.ts │ │ │ │ ├── instance.ts │ │ │ │ └── raw-instance.ts │ │ ├── test │ │ │ ├── chainHead.mjs │ │ │ ├── chainSpec.mjs │ │ │ ├── misc.mjs │ │ │ ├── sudo.mjs │ │ │ ├── test.mjs │ │ │ └── westend.json │ │ ├── tsconfig-cjs.json │ │ ├── tsconfig-mjs.json │ │ └── tsconfig.json │ └── rust │ │ ├── Cargo.toml │ │ └── src │ │ ├── alloc.rs │ │ ├── bindings.rs │ │ ├── cpu_rate_limiter.rs │ │ ├── init.rs │ │ ├── lib.rs │ │ ├── platform.rs │ │ └── timers.rs ├── westend-westmint.json └── westend.json └── src ├── author.rs ├── author ├── aura.rs ├── build.rs ├── runtime.rs └── runtime │ ├── example-chain-specs.json │ └── tests.rs ├── chain.rs ├── chain ├── async_tree.rs ├── blocks_tree.rs ├── blocks_tree │ ├── best_block.rs │ ├── finality.rs │ └── verify.rs ├── chain_information.rs ├── chain_information │ └── build.rs └── fork_tree.rs ├── chain_spec.rs ├── chain_spec ├── example.json ├── light_sync_state.rs └── structs.rs ├── database.rs ├── database ├── finalized_serialize.rs ├── finalized_serialize │ └── defs.rs ├── full_sqlite.rs └── full_sqlite │ └── open.rs ├── executor.rs ├── executor ├── allocator.rs ├── host.rs ├── host │ ├── runtime_version.rs │ ├── zstd.rs │ └── zstd │ │ ├── example-runtime.wasm.zstd │ │ ├── polkadot-runtime-v9160.wasm.zstd │ │ └── tests.rs ├── read_only_runtime_host.rs ├── runtime_host.rs ├── storage_diff.rs ├── vm.rs └── vm │ ├── interpreter.rs │ ├── jit.rs │ ├── test-polkadot-runtime-v9160.wasm │ └── tests.rs ├── finality.rs ├── finality ├── grandpa.rs ├── grandpa │ ├── commit.rs │ ├── commit │ │ ├── decode.rs │ │ └── verify.rs │ └── warp_sync.rs ├── justification.rs └── justification │ ├── decode.rs │ └── verify.rs ├── header.rs ├── header ├── aura.rs ├── babe.rs ├── grandpa.rs ├── tests-header-kusama-7472481 ├── tests-header-polkadot-512271 └── tests.rs ├── identity.rs ├── identity ├── keystore.rs └── seed_phrase.rs ├── informant.rs ├── json_rpc.rs ├── json_rpc ├── methods.rs ├── parse.rs ├── payment_info.rs ├── requests_subscriptions.rs ├── websocket_server.rs └── websocket_server │ └── tests.rs ├── lib.rs ├── libp2p.rs ├── libp2p ├── async_std_connection.rs ├── async_std_connection │ └── with_buffers.rs ├── collection.rs ├── collection │ ├── multi_stream.rs │ └── single_stream.rs ├── connection.rs ├── connection │ ├── established.rs │ ├── established │ │ ├── multi_stream.rs │ │ ├── single_stream.rs │ │ ├── substream.rs │ │ └── tests.rs │ ├── multistream_select.rs │ ├── noise.rs │ ├── single_stream_handshake.rs │ ├── single_stream_handshake │ │ └── tests.rs │ ├── yamux.rs │ └── yamux │ │ └── header.rs ├── multiaddr.rs ├── multihash.rs ├── peer_id.rs ├── peers.rs ├── read_write.rs └── websocket.rs ├── network.rs ├── network ├── kademlia.rs ├── kademlia │ └── kbuckets.rs ├── protocol.rs ├── protocol │ ├── block_announces.rs │ ├── block_request.rs │ ├── grandpa.rs │ ├── grandpa_warp_sync.rs │ ├── identify.rs │ ├── kademlia.rs │ ├── state_request.rs │ └── storage_call_proof.rs ├── service.rs └── service │ ├── addresses.rs │ ├── notifications.rs │ └── requests_responses.rs ├── sync.rs ├── sync ├── all.rs ├── all_forks.rs ├── all_forks │ ├── disjoint.rs │ ├── pending_blocks.rs │ └── sources.rs ├── optimistic.rs ├── optimistic │ └── verification_queue.rs ├── para.rs └── warp_sync.rs ├── transactions.rs ├── transactions ├── light_pool.rs ├── light_pool │ └── tests.rs ├── pool.rs └── validate.rs ├── trie.rs ├── trie ├── calculate_root.rs ├── nibble.rs ├── node_value.rs ├── prefix_proof.rs ├── proof_decode.rs ├── proof_encode.rs ├── proof_node_codec.rs ├── trie_structure.rs └── trie_structure │ └── tests.rs ├── util.rs ├── util ├── leb128.rs └── protobuf.rs ├── verify.rs └── verify ├── aura.rs ├── babe.rs ├── header_body.rs ├── header_only.rs └── inherents.rs /.config/smoldot.dic: -------------------------------------------------------------------------------- 1 | 153 2 | ABI 3 | ACK 4 | API 5 | APIs 6 | BIP39 7 | BLAKE2 8 | Base58 9 | CLI 10 | ChaCha20 11 | Deno 12 | Deserialize/S 13 | DoS 14 | DTLS 15 | ECDH 16 | Ed25519 17 | Endian 18 | Enqueue/S 19 | FFI 20 | FNV 21 | GrandPa 22 | HMAC 23 | HashDoS 24 | IP 25 | ISP/S 26 | JIT 27 | JITs 28 | JSON 29 | Jaeger 30 | Kademlia 31 | Kusama 32 | LEB128 33 | LLVM 34 | LRU 35 | MERCHANTABILITY 36 | Merkle 37 | NPM 38 | Namespace 39 | POV 40 | Polkadot 41 | PolkadotJS 42 | PRNG 43 | Protobuf 44 | QUIC 45 | RPC 46 | RST 47 | SHA-256 48 | SQLite 49 | SS58 50 | Sr25519 51 | TCP 52 | TLS 53 | TODO 54 | Trie 55 | UDP 56 | UI/S 57 | UTC 58 | Unencrypt/D 59 | VM 60 | VM's 61 | VRF 62 | Wasi 63 | Wasm 64 | WebAssembly 65 | WebRTC 66 | WebSocket/M 67 | Westend 68 | Yamux 69 | aborter 70 | allocator 71 | amongst 72 | authorable 73 | backend/S 74 | benchmarked 75 | blockchain 76 | boolean/S 77 | bootnode 78 | borked 79 | broadcasted 80 | bytecode 81 | checksum 82 | combinator/S 83 | config 84 | cryptographic 85 | datas 86 | deallocate/NXS 87 | decodable 88 | decrementation 89 | decrypt 90 | dependant 91 | dereferences 92 | deserialization 93 | deserialize/G 94 | deterministically 95 | dialer 96 | durations 97 | enqueue/D 98 | enum/SM 99 | externalities 100 | extrinsics 101 | frontend 102 | functionalities 103 | hasher 104 | iff 105 | includable 106 | incrementing 107 | inherents 108 | instantiation 109 | intrinsics 110 | keystore 111 | lexicographically 112 | libp2p 113 | lookups 114 | metadata 115 | multiaddress/S 116 | multihash 117 | mutex/SM 118 | namespace/S 119 | noncomprehending 120 | opcode 121 | orthogonally 122 | parachain/S 123 | parathread/S 124 | parsers 125 | pluggable 126 | precommit/S 127 | reachability 128 | reannounce/D 129 | redownloading 130 | reorg/S 131 | responder 132 | revalidated 133 | runtime/S 134 | rustup 135 | smoldot 136 | startup 137 | stdout 138 | struct/S 139 | submodule 140 | substream/S 141 | syscall 142 | testability 143 | timestamp 144 | trie 145 | trustless 146 | tuple/DSM 147 | unassign 148 | unbypassable 149 | undecoded 150 | unencrypted 151 | unfinalized 152 | unhashed 153 | unpulled 154 | unregister 155 | untrusted 156 | v2 157 | validator/S 158 | verifications 159 | versa 160 | wasi 161 | wasm 162 | -------------------------------------------------------------------------------- /.config/spellcheck.toml: -------------------------------------------------------------------------------- 1 | [Hunspell] 2 | lang = "en_US" 3 | 4 | # Additional search paths, which take presedence over the default os specific 5 | # search dirs, searched in order, defaults last 6 | search_dirs = ["."] 7 | 8 | # Adds additional dictionaries, can be specified as absolute paths or relative 9 | # in the search dirs (in this order). Relative paths are resolved relative to 10 | # the configuration file which is used. 11 | # 12 | # Refer to `man 5 hunspell` or 13 | # https://www.systutorials.com/docs/linux/man/4-hunspell/#lbAE on how to define 14 | # a custom dictionary file. 15 | # 16 | # AFF rules: 17 | extra_dictionaries = ["smoldot.dic"] 18 | 19 | # If set to `true`, the OS specific default search paths are skipped and only 20 | # explicitly specified ones are used. 21 | skip_os_lookups = true 22 | 23 | # Use the builtin dictionaries if none were found in in the configured lookup 24 | # paths. 25 | # 26 | # Usually combined with `skip_os_lookups=true` to enforce the `builtin` usage 27 | # for consistent results across distributions and CI runs. 28 | # 29 | # Setting this will still use the dictionaries specified in `extra_dictionaries 30 | # = [..]` for topic specific lingo. 31 | use_builtin = true 32 | 33 | 34 | [Hunspell.quirks] 35 | # Transforms words that are provided by the tokenizer into word fragments based 36 | # on the capture groups which are to be checked. 37 | # 38 | # If no capture groups are present, the matched word is whitelisted. 39 | transform_regex = ["^'([^\\s])'$", "^[0-9]+x$", "^\\#[0-9]+$", "^[0-9]+$", "^.+\\+$", "\\+"] 40 | 41 | # Accepts `alphabeta` variants if the checker provides a replacement suggestion 42 | # of `alpha-beta`. 43 | allow_concatenation = true 44 | 45 | # And the counterpart, which accepts words with dashes, when the suggestion has 46 | # recommendations without the dashes. This is less common. 47 | allow_dashed = true 48 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | .github 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. 6 | * @tomaka 7 | -------------------------------------------------------------------------------- /.github/cargo-deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | unlicensed = "deny" 3 | copyleft = "allow" 4 | allow-osi-fsf-free = "either" 5 | default = "deny" 6 | 7 | [bans] 8 | multiple-versions = "warn" 9 | wildcards = "deny" 10 | 11 | # The `openssl` and `ring` libraries are easy to accidentally pull in, and are too annoying to 12 | # compile on a wide range of platforms. 13 | [[bans.deny]] 14 | name = "openssl" 15 | [[bans.deny]] 16 | name = "ring" 17 | 18 | [sources] 19 | unknown-git = "deny" 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | labels: [] 6 | schedule: 7 | interval: "daily" 8 | rebase-strategy: disabled # Redundant with mergify 9 | open-pull-requests-limit: 4 10 | 11 | - package-ecosystem: cargo 12 | directory: "/bin/fuzz" 13 | labels: [] 14 | schedule: 15 | interval: "daily" 16 | rebase-strategy: disabled # Redundant with mergify 17 | open-pull-requests-limit: 4 18 | 19 | - package-ecosystem: "npm" 20 | directory: "/bin/wasm-node/javascript" 21 | labels: [] 22 | schedule: 23 | interval: "daily" 24 | rebase-strategy: disabled # Redundant with mergify 25 | open-pull-requests-limit: 2 26 | 27 | - package-ecosystem: github-actions 28 | directory: "/" 29 | labels: [] 30 | schedule: 31 | interval: "daily" 32 | rebase-strategy: disabled # Redundant with mergify 33 | open-pull-requests-limit: 2 34 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | queue_rules: 2 | - name: default 3 | conditions: 4 | - check-success=all-ci 5 | - check-success=all-deploy 6 | - label=automerge 7 | - base=main 8 | - "#changes-requested-reviews-by=0" 9 | - "#approved-reviews-by>=1" 10 | 11 | pull_request_rules: 12 | - name: automatic merge when CI passes on main 13 | conditions: 14 | - check-success=all-ci 15 | - check-success=all-deploy 16 | - label=automerge 17 | - base=main 18 | - "#changes-requested-reviews-by=0" 19 | - "#approved-reviews-by>=1" 20 | actions: 21 | queue: 22 | name: default 23 | method: squash 24 | 25 | - name: automatic approval for Dependabot pull requests 26 | conditions: 27 | - author~=^dependabot(|-preview)\[bot\]$ 28 | actions: 29 | review: 30 | type: APPROVE 31 | message: Automatically approving dependabot 32 | 33 | # At the moment, tomaka is the only active maintainer of the Rust code, and nobody is reviewing his code. This rule is meant to be removed if that situation changes. 34 | - name: automatic approval for tomaka pull requests 35 | conditions: 36 | - author~=^tomaka$ 37 | actions: 38 | review: 39 | type: APPROVE 40 | message: Automatically approving tomaka's pull requests. This auto-approval will be removed once more maintainers are active. 41 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: recurrent-audit 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | jobs: 6 | audit: 7 | runs-on: ubuntu-latest 8 | container: 9 | image: rust 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions-rs/audit-check@v1 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/ci-post-workflow.yml: -------------------------------------------------------------------------------- 1 | name: continuous-integration-after 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["continuous-integration"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | # This job is run after a pull request workflow has completed, potentially from a fork 11 | # repository. It downloads the artifact output of the workflow, then comments in the pull 12 | # request accordingly. 13 | # 14 | # The reason why this is necessary is that the workflow of the pull request itself doesn't 15 | # have the authorization to post comments (or any other "write" access action) to avoid people 16 | # opening pull requests that modify workflows and execute arbitrary code. 17 | # Instead, this workflow here is always taken from the base branch. 18 | wasm-node-size-diff: 19 | runs-on: ubuntu-latest 20 | if: ${{ github.event.workflow_run.event == 'pull_request' }} 21 | steps: 22 | # TODO: use download-artifact action 23 | - uses: actions/github-script@v3.1.0 24 | with: 25 | script: | 26 | var artifacts = await github.actions.listWorkflowRunArtifacts({ 27 | owner: context.repo.owner, 28 | repo: context.repo.repo, 29 | run_id: ${{github.event.workflow_run.id }}, 30 | }); 31 | var matchArtifact = artifacts.data.artifacts.filter((artifact) => { 32 | return artifact.name == "pr" 33 | })[0]; 34 | var download = await github.actions.downloadArtifact({ 35 | owner: context.repo.owner, 36 | repo: context.repo.repo, 37 | artifact_id: matchArtifact.id, 38 | archive_format: 'zip', 39 | }); 40 | var fs = require('fs'); 41 | fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); 42 | - run: unzip pr.zip 43 | - id: twiggy-diff 44 | # This step is purely meant to turn the diff into an GitHub action output that can be picked up by the further steps. 45 | # This code has been found here: https://github.community/t/set-output-truncates-multiline-strings/16852/16 46 | uses: actions/github-script@v4 47 | with: 48 | script: | 49 | const fs = require('fs'); 50 | return fs.readFileSync('./twiggy-diff', 'utf8').toString(); 51 | result-encoding: string 52 | - id: pr-num 53 | uses: actions/github-script@v4 54 | # This step grabs the pull request number from the artifacts to know where to comment. 55 | # This pull request number is untrusted and could be malicious. However the harm that 56 | # could be done is very limited (spammy comments), and so this is not considered a 57 | # problem. 58 | with: 59 | script: | 60 | const fs = require('fs'); 61 | return fs.readFileSync('./number', 'utf8').toString(); 62 | result-encoding: string 63 | - uses: peter-evans/find-comment@v2 64 | id: find-comment 65 | with: 66 | issue-number: ${{ steps.pr-num.outputs.result }} 67 | comment-author: 'github-actions[bot]' 68 | body-includes: twiggy diff report 69 | - uses: peter-evans/create-or-update-comment@v2 70 | with: 71 | comment-id: ${{ steps.find-comment.outputs.comment-id }} 72 | issue-number: ${{ steps.pr-num.outputs.result }} 73 | body: | 74 | # twiggy diff report 75 | 76 | Difference in .wasm size before and after this pull request. 77 | 78 | --- 79 | 80 | ``` 81 | ${{ steps.twiggy-diff.outputs.result }} 82 | ``` 83 | edit-mode: replace 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | This repository is deprecated in favor of: 4 | 5 | 6 | 7 | Lightweight Substrate and Polkadot client. 8 | 9 | # Introduction 10 | 11 | `smoldot` is an alternative client of [Substrate](https://github.com/paritytech/substrate)-based chains, including [Polkadot](https://github.com/paritytech/polkadot/). 12 | 13 | There exists two clients: the full client and the wasm light node. 14 | The full client is currently a work in progress and doesn't support many features that the official client supports. 15 | 16 | The main development focus is currently around the wasm light node. Using https://github.com/polkadot-js/api/ and https://github.com/paritytech/substrate-connect/ (which uses smoldot as an implementation detail), one can easily connect to a chain and interact in a fully trust-less way with it, from JavaScript. 17 | 18 | The Wasm light node is published: 19 | 20 | - On NPM: 21 | - On Deno.land/x: (URL to import: `https://deno.land/x/smoldot/index-deno.js`) 22 | 23 | # Objectives 24 | 25 | There exists multiple objectives behind this repository: 26 | 27 | - Write a client implementation that is as comprehensive as possible, to make it easier to understand the various components of a Substrate/Polkadot client. A large emphasis is put on documentation. 28 | - Implement a client that is lighter than Substrate, in terms of memory consumption, number of threads, and code size, in order to compile it to WebAssembly and distribute it in web pages. 29 | - Experiment with a new code architecture, to maybe upstream some components to Substrate and Polkadot. 30 | 31 | # Trade-offs 32 | 33 | In order to simplify the code, two main design decisions have been made compared to Substrate: 34 | 35 | - No native runtime. The execution time of the `wasmtime` library is satisfying enough that having a native runtime isn't critical anymore. 36 | 37 | - No pluggable architecture. `smoldot` supports a certain hard coded list of consensus algorithms, at the moment Babe, Aura, and GrandPa. Support for other algorithms can only be added by modifying the code of smoldot, and it is not possible to plug a custom algorithm from outside. 38 | 39 | # Building manually 40 | 41 | ## Wasm light node 42 | 43 | In order to run the wasm light node, you must have installed [rustup](https://rustup.rs/). 44 | 45 | The wasm light node can be tested with `cd bin/wasm-node/javascript` and `npm install; npm start`. This will compile the smoldot wasm light node and start a WebSocket server capable of answering JSON-RPC requests. This demo will print a list of URLs that you can navigate to in order to connect to a certain chain. For example you can navigate to in order to interact with the Westend chain. 46 | 47 | > Note: The `npm start` command starts a small JavaScript shim, on top of the wasm light node, that hard codes the chain to Westend and starts the WebSocket server. The wasm light node itself can connect to a variety of different chains (not only Westend) and doesn't start any server. 48 | 49 | ## Full client 50 | 51 | The full client is a binary similar to the official Polkadot client, and can be tested with `cargo run`. 52 | 53 | > Note: The `Cargo.toml` contains a section `[profile.dev] opt-level = 2`, and as such `cargo run` alone should give performances close to the ones in release mode. 54 | 55 | The following list is a best-effort list of packages that must be available on the system in order to compile the full node: 56 | 57 | - `clang` or `gcc` 58 | - `pkg-config` 59 | - `sqlite` 60 | -------------------------------------------------------------------------------- /benches/header-polkadot-512271: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/smoldot/938055a638ec201c022f680c8e8cbd0349e70ed1/benches/header-polkadot-512271 -------------------------------------------------------------------------------- /benches/header.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; 19 | 20 | fn benchmark_headers(c: &mut Criterion) { 21 | let mut group = c.benchmark_group("headers"); 22 | 23 | let headers = [ 24 | &[ 25 | 5, 35, 55, 218, 117, 209, 29, 117, 103, 130, 55, 39, 55, 132, 95, 54, 138, 185, 89, 79, 26 | 123, 161, 124, 51, 67, 40, 71, 126, 0, 210, 240, 78, 57, 177, 102, 97, 175, 183, 124, 27 | 206, 195, 77, 217, 117, 83, 14, 134, 50, 246, 163, 138, 196, 199, 78, 108, 145, 187, 28 | 240, 123, 5, 18, 219, 158, 44, 174, 132, 41, 70, 121, 181, 160, 189, 104, 253, 173, 29 | 135, 222, 15, 45, 68, 248, 23, 46, 6, 140, 247, 18, 52, 37, 9, 32, 38, 102, 12, 190, 8, 30 | 212, 237, 12, 6, 66, 65, 66, 69, 181, 1, 1, 0, 0, 0, 0, 253, 121, 18, 16, 0, 0, 0, 0, 31 | 182, 14, 80, 77, 46, 39, 209, 60, 81, 14, 141, 206, 160, 50, 106, 233, 35, 123, 4, 185, 32 | 66, 182, 193, 156, 19, 45, 137, 155, 123, 186, 11, 120, 251, 123, 81, 117, 113, 108, 33 | 169, 115, 142, 208, 243, 50, 102, 4, 117, 254, 247, 226, 199, 113, 132, 25, 141, 90, 34 | 247, 19, 211, 5, 152, 96, 121, 6, 40, 217, 92, 0, 33, 38, 199, 73, 36, 129, 161, 159, 35 | 184, 208, 215, 110, 150, 127, 221, 158, 50, 102, 118, 40, 146, 24, 8, 98, 7, 56, 144, 36 | 0, 4, 66, 69, 69, 70, 132, 3, 39, 11, 33, 224, 56, 100, 17, 18, 118, 159, 167, 103, 10, 37 | 86, 125, 222, 20, 189, 120, 236, 48, 202, 89, 180, 71, 31, 56, 185, 23, 33, 23, 87, 5, 38 | 66, 65, 66, 69, 1, 1, 180, 253, 231, 90, 196, 206, 208, 183, 14, 97, 124, 243, 43, 160, 39 | 133, 94, 19, 162, 126, 19, 7, 15, 222, 73, 114, 113, 104, 78, 24, 52, 113, 47, 39, 154, 40 | 108, 148, 28, 146, 180, 232, 199, 20, 52, 170, 93, 214, 0, 109, 168, 175, 162, 91, 234, 41 | 195, 228, 139, 236, 170, 251, 200, 178, 123, 26, 130, 42 | ][..], 43 | &include_bytes!("./header-polkadot-512271")[..], 44 | ]; 45 | 46 | for header in &headers { 47 | group.throughput(Throughput::Bytes(header.len() as u64)); 48 | group.bench_with_input(BenchmarkId::new("decode", header.len()), header, |b, i| { 49 | b.iter(|| smoldot::header::decode(i, 4).unwrap()) 50 | }); 51 | group.bench_with_input( 52 | BenchmarkId::new("decode_and_to_owned", header.len()), 53 | header, 54 | |b, i| { 55 | b.iter(|| { 56 | let as_ref = smoldot::header::decode(i, 4).unwrap(); 57 | smoldot::header::Header::from(as_ref) 58 | }) 59 | }, 60 | ); 61 | } 62 | 63 | group.finish() 64 | } 65 | 66 | criterion_group!(benches, benchmark_headers); 67 | criterion_main!(benches); 68 | -------------------------------------------------------------------------------- /bin/full-node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smoldot-full-node" 3 | version = "0.1.0" 4 | authors = ["Parity Technologies ", "Pierre Krieger "] 5 | description = "Substrate/Polkadot full node using smoldot" 6 | repository = "https://github.com/paritytech/smoldot" 7 | license = "GPL-3.0-or-later WITH Classpath-exception-2.0" 8 | edition = "2021" 9 | publish = false 10 | default-run = "full-node" 11 | 12 | [[bin]] 13 | name = "full-node" 14 | path = "src/main.rs" 15 | 16 | [dependencies] 17 | async-std = "1.12.0" 18 | atty = "0.2.14" 19 | blake2-rfc = { version = "0.2.18", default-features = false } 20 | clap = { version = "4.0.27", default-features = false, features = ["color", "derive", "help", "std", "suggestions", "usage"] } # Note: enabling/disabling some features modifies the internal behavior of clap, be careful 21 | ctrlc = "3.2.2" 22 | derive_more = "0.99.17" 23 | directories = "4.0.1" 24 | either = { version = "1.8.0", default-features = false } 25 | event-listener = { version = "2.5.3" } 26 | fnv = { version = "1.0.7", default-features = false } 27 | futures = { version = "0.3.25", default-features = false, features = ["std", "thread-pool"] } 28 | futures-timer = "3.0" 29 | hashbrown = { version = "0.13.1", default-features = false } 30 | hex = { version = "0.4.3", default-features = false } 31 | mick-jaeger = "0.1.8" 32 | rand = "0.8.5" 33 | smoldot = { version = "0.5.0", path = "../..", default-features = false, features = ["database-sqlite", "std"] } 34 | terminal_size = "0.2.3" 35 | tracing = { version = "0.1.37", features = ["attributes"] } 36 | tracing-subscriber = { version = "0.2.25", default-features = false, features = ["ansi", "chrono", "env-filter", "json", "fmt", "parking_lot", "smallvec"] } 37 | -------------------------------------------------------------------------------- /bin/full-node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1 AS builder 2 | LABEL maintainer "Pierre Krieger " 3 | 4 | COPY ./../.. /build 5 | WORKDIR /build 6 | 7 | RUN apt-get update && apt-get install -y musl-tools 8 | RUN rustup target add x86_64-unknown-linux-musl 9 | RUN cargo build --target x86_64-unknown-linux-musl --package smoldot-full-node --release --verbose 10 | 11 | 12 | FROM alpine:latest 13 | LABEL maintainer "Pierre Krieger " 14 | COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/full-node /usr/local/bin 15 | 16 | EXPOSE 30333 17 | CMD ["/usr/local/bin/full-node"] 18 | -------------------------------------------------------------------------------- /bin/full-node/src/main.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![recursion_limit = "1024"] 19 | #![deny(rustdoc::broken_intra_doc_links)] 20 | #![deny(unused_crate_dependencies)] 21 | 22 | mod cli; 23 | mod run; 24 | 25 | fn main() { 26 | futures::executor::block_on(async_main()) 27 | } 28 | 29 | async fn async_main() { 30 | match ::parse().command { 31 | cli::CliOptionsCommand::Run(r) => run::run(*r).await, 32 | cli::CliOptionsCommand::Blake264BitsHash(opt) => { 33 | let hash = blake2_rfc::blake2b::blake2b(8, &[], opt.payload.as_bytes()); 34 | println!("0x{}", hex::encode(hash)); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bin/full-node/src/run/database_thread.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! As explained in the documentation of smoldot, the database uses synchronous I/O operations. 19 | //! For this reason, it is undesirable to access it from an asynchronous context. 20 | 21 | use futures::{ 22 | channel::{mpsc, oneshot}, 23 | lock::Mutex, 24 | prelude::*, 25 | }; 26 | use smoldot::database::full_sqlite::SqliteFullDatabase; 27 | use std::thread; 28 | 29 | /// Handle to the thread were the database accesses are performed. 30 | /// 31 | /// Destroying this object stops the thread. 32 | /// 33 | /// Use the `From` trait implementation to build a [`DatabaseThread`]. 34 | pub struct DatabaseThread { 35 | sender: Mutex>>, 36 | } 37 | 38 | impl DatabaseThread { 39 | /// Sends a closure to the database thread, executes it, then returns the value that the 40 | /// closure returned. 41 | pub async fn with_database( 42 | &self, 43 | closure: impl FnOnce(&SqliteFullDatabase) -> T + Send + 'static, 44 | ) -> T { 45 | let (tx, rx) = oneshot::channel(); 46 | self.sender 47 | .lock() 48 | .await 49 | .send(Box::new(move |db| { 50 | let _ = tx.send(closure(db)); 51 | })) 52 | .await 53 | .unwrap(); 54 | rx.await.unwrap() 55 | } 56 | 57 | /// Similar to [`DatabaseThread::with_database`], but without any return value. This function 58 | /// is slightly more optimized for this use case. 59 | pub async fn with_database_detached( 60 | &self, 61 | closure: impl FnOnce(&SqliteFullDatabase) + Send + 'static, 62 | ) { 63 | self.sender 64 | .lock() 65 | .await 66 | .send(Box::new(move |db| { 67 | closure(db); 68 | })) 69 | .await 70 | .unwrap(); 71 | } 72 | } 73 | 74 | impl From for DatabaseThread { 75 | fn from(db: SqliteFullDatabase) -> DatabaseThread { 76 | let (sender, mut rx) = mpsc::channel::>(256); 77 | 78 | thread::Builder::new() 79 | .name("sqlite-database".into()) 80 | .spawn(move || { 81 | // When the `DatabaseThread` is dropped, the sender will close, `rx.next()` 82 | // will return `None`, and the closure here will finish, ending the thread. 83 | while let Some(closure) = futures::executor::block_on(rx.next()) { 84 | closure(&db) 85 | } 86 | }) 87 | .unwrap(); 88 | 89 | DatabaseThread { 90 | sender: Mutex::new(sender), 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /bin/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | -------------------------------------------------------------------------------- /bin/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smoldot-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | arbitrary = "1.2.0" 13 | fnv = { version = "1.0.7", default-features = false } 14 | hashbrown = { version = "0.13.1", default-features = false } 15 | libfuzzer-sys = "0.4" 16 | smoldot = { path = "../.." } 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "chain-spec" 24 | path = "fuzz_targets/chain-spec.rs" 25 | test = false 26 | doc = false 27 | 28 | [[bin]] 29 | name = "fork-tree" 30 | path = "fuzz_targets/fork-tree.rs" 31 | test = false 32 | doc = false 33 | 34 | [[bin]] 35 | name = "grandpa-justification-parse" 36 | path = "fuzz_targets/grandpa-justification-parse.rs" 37 | test = false 38 | doc = false 39 | 40 | [[bin]] 41 | name = "header-parse" 42 | path = "fuzz_targets/header-parse.rs" 43 | test = false 44 | doc = false 45 | 46 | [[bin]] 47 | name = "json-rpc-call" 48 | path = "fuzz_targets/json-rpc-call.rs" 49 | test = false 50 | doc = false 51 | 52 | [[bin]] 53 | name = "multiaddr-bytes" 54 | path = "fuzz_targets/multiaddr-bytes.rs" 55 | test = false 56 | doc = false 57 | 58 | [[bin]] 59 | name = "multiaddr-text" 60 | path = "fuzz_targets/multiaddr-text.rs" 61 | test = false 62 | doc = false 63 | 64 | [[bin]] 65 | name = "multihash" 66 | path = "fuzz_targets/multihash.rs" 67 | test = false 68 | doc = false 69 | 70 | [[bin]] 71 | name = "network-connection-encrypted" 72 | path = "fuzz_targets/network-connection-encrypted.rs" 73 | test = false 74 | doc = false 75 | 76 | [[bin]] 77 | name = "network-connection-raw" 78 | path = "fuzz_targets/network-connection-raw.rs" 79 | test = false 80 | doc = false 81 | 82 | [[bin]] 83 | name = "peer-id" 84 | path = "fuzz_targets/peer-id.rs" 85 | test = false 86 | doc = false 87 | 88 | [[bin]] 89 | name = "proof-node-codec" 90 | path = "fuzz_targets/proof-node-codec.rs" 91 | test = false 92 | doc = false 93 | 94 | [[bin]] 95 | name = "protocol-block-announce-decode" 96 | path = "fuzz_targets/protocol-block-announce-decode.rs" 97 | test = false 98 | doc = false 99 | 100 | [[bin]] 101 | name = "protocol-block-announces-handshake-decode" 102 | path = "fuzz_targets/protocol-block-announces-handshake-decode.rs" 103 | test = false 104 | doc = false 105 | 106 | [[bin]] 107 | name = "protocol-blocks-request-decode" 108 | path = "fuzz_targets/protocol-blocks-request-decode.rs" 109 | test = false 110 | doc = false 111 | 112 | [[bin]] 113 | name = "protocol-blocks-response-decode" 114 | path = "fuzz_targets/protocol-blocks-response-decode.rs" 115 | test = false 116 | doc = false 117 | 118 | [[bin]] 119 | name = "protocol-grandpa-warp-sync-response-decode" 120 | path = "fuzz_targets/protocol-grandpa-warp-sync-response-decode.rs" 121 | test = false 122 | doc = false 123 | 124 | [[bin]] 125 | name = "protocol-grandpa-notification-decode" 126 | path = "fuzz_targets/protocol-grandpa-notification-decode.rs" 127 | test = false 128 | doc = false 129 | 130 | [[bin]] 131 | name = "protocol-identify-response-decode" 132 | path = "fuzz_targets/protocol-identify-response-decode.rs" 133 | test = false 134 | doc = false 135 | 136 | [[bin]] 137 | name = "protocol-state-response-decode" 138 | path = "fuzz_targets/protocol-state-response-decode.rs" 139 | test = false 140 | doc = false 141 | 142 | [[bin]] 143 | name = "protocol-storage-call-proof-response-decode" 144 | path = "fuzz_targets/protocol-storage-call-proof-response-decode.rs" 145 | test = false 146 | doc = false 147 | 148 | [[bin]] 149 | name = "wasm-module-wasmi" 150 | path = "fuzz_targets/wasm-module-wasmi.rs" 151 | test = false 152 | doc = false 153 | 154 | [[bin]] 155 | name = "wasm-module-wasmtime" 156 | path = "fuzz_targets/wasm-module-wasmtime.rs" 157 | test = false 158 | doc = false 159 | -------------------------------------------------------------------------------- /bin/fuzz/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzing 2 | 3 | These fuzzing targets can be used in conjunction with [`cargo fuzz`](https://github.com/rust-fuzz/cargo-fuzz). 4 | 5 | Setup: 6 | 7 | ```bash 8 | cargo install --force cargo-fuzz 9 | rustup install nightly 10 | ``` 11 | 12 | In order to start fuzzing: 13 | 14 | ```bash 15 | cargo +nightly fuzz run --fuzz-dir ./bin/fuzz 16 | ``` 17 | 18 | Where `` is one of the files in the `fuzz_targets` directory. 19 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/chain-spec.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | let _ = smoldot::chain_spec::ChainSpec::from_json_bytes(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/grandpa-justification-parse.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (&[u8], u8)| { 21 | let _ = smoldot::finality::justification::decode::decode_grandpa( 22 | params.0, 23 | usize::from(params.1) + 1, 24 | ); 25 | }); 26 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/header-parse.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (&[u8], u8)| { 21 | let _ = smoldot::header::decode(params.0, usize::from(params.1) + 1); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/json-rpc-call.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &str| { 21 | let _ = smoldot::json_rpc::methods::parse_json_call(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/multiaddr-bytes.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | use core::convert::TryFrom as _; 21 | 22 | libfuzzer_sys::fuzz_target!(|data: Vec| { 23 | let _ = smoldot::libp2p::multiaddr::Multiaddr::try_from(data); 24 | }); 25 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/multiaddr-text.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | use core::str::FromStr as _; 21 | 22 | libfuzzer_sys::fuzz_target!(|data: &str| { 23 | let _ = smoldot::libp2p::multiaddr::Multiaddr::from_str(data); 24 | }); 25 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/multihash.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | let _ = smoldot::libp2p::multihash::MultihashRef::from_bytes(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/network-connection-raw.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | use core::time::Duration; 21 | 22 | // This fuzzing target simulates an incoming or outgoing connection. The remote endpoint of that 23 | // connection sends the fuzzing data to smoldot. The data that smoldot sends back on that 24 | // connection is silently discarded and doesn't influence the behaviour of this fuzzing test. 25 | 26 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 27 | let mut data = data; 28 | 29 | // Note that the noise key and randomness are constant rather than being derived from the 30 | // fuzzing data. This is because we're not here to fuzz the cryptographic code (which we 31 | // assume is working well) but everything around it (decoding frames, allocating buffers, 32 | // etc.). 33 | let mut collection = 34 | smoldot::libp2p::collection::Network::new(smoldot::libp2p::collection::Config { 35 | randomness_seed: [0; 32], 36 | capacity: 0, 37 | max_inbound_substreams: 10, 38 | notification_protocols: Vec::new(), 39 | request_response_protocols: Vec::new(), 40 | // This timeout doesn't matter as we pass dummy time values. 41 | handshake_timeout: Duration::from_secs(5), 42 | ping_protocol: "ping".into(), 43 | noise_key: smoldot::libp2p::connection::NoiseKey::new(&[0; 32]), 44 | }); 45 | 46 | // We use the first element of ̀`data` to determine whether we have opened the connection 47 | // or whether the remote has opened it. 48 | let is_initiator = { 49 | if data.is_empty() { 50 | return; 51 | } 52 | let is_initiator = (data[0] % 2) == 0; 53 | data = &data[1..]; 54 | is_initiator 55 | }; 56 | 57 | let (_id, mut task) = collection.insert_single_stream( 58 | Duration::new(0, 0), 59 | smoldot::libp2p::collection::SingleStreamHandshakeKind::MultistreamSelectNoiseYamux, 60 | is_initiator, 61 | (), 62 | ); 63 | 64 | let mut out_buffer = vec![0; 4096]; 65 | 66 | loop { 67 | let mut read_write = smoldot::libp2p::read_write::ReadWrite { 68 | now: Duration::new(0, 0), 69 | incoming_buffer: Some(data), 70 | outgoing_buffer: Some((&mut out_buffer, &mut [])), 71 | read_bytes: 0, 72 | written_bytes: 0, 73 | wake_up_after: None, 74 | }; 75 | task.read_write(&mut read_write); 76 | 77 | let read_bytes = read_write.read_bytes; 78 | let written_bytes = read_write.written_bytes; 79 | data = &data[read_bytes..]; 80 | 81 | // We need to call `pull_message_to_coordinator()`, as the connection state machine might 82 | // refuse to process more incoming data before events have been pulled. 83 | let (task_update, event) = task.pull_message_to_coordinator(); 84 | match task_update { 85 | Some(t) => task = t, 86 | None => break, 87 | } 88 | 89 | if event.is_none() && read_bytes == 0 && written_bytes == 0 { 90 | // Stop the test. 91 | break; 92 | } 93 | } 94 | }); 95 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/peer-id.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: Vec| { 21 | let _ = smoldot::libp2p::peer_id::PeerId::from_bytes(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/proof-node-codec.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | if let Ok(decoded) = smoldot::trie::proof_node_codec::decode(data) { 22 | assert_eq!( 23 | smoldot::trie::proof_node_codec::encode(decoded.clone()).fold( 24 | Vec::new(), 25 | |mut a, b| { 26 | a.extend_from_slice(b.as_ref()); 27 | a 28 | } 29 | ), 30 | data, 31 | "{:?}", 32 | decoded 33 | ); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-block-announce-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (&[u8], u8)| { 21 | let _ = smoldot::network::protocol::decode_block_announce(params.0, usize::from(params.1) + 1); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-block-announces-handshake-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (u8, &[u8])| { 21 | let _ = smoldot::network::protocol::decode_block_announces_handshake( 22 | usize::from(params.0) + 1, 23 | params.1, 24 | ); 25 | }); 26 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-blocks-request-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (u8, &[u8])| { 21 | let _ = smoldot::network::protocol::decode_block_request(usize::from(params.0) + 1, params.1); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-blocks-response-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | let _ = smoldot::network::protocol::decode_block_response(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-grandpa-notification-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (&[u8], u8)| { 21 | let _ = smoldot::network::protocol::decode_grandpa_notification( 22 | params.0, 23 | usize::from(params.1) + 1, 24 | ); 25 | }); 26 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-grandpa-warp-sync-response-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|params: (&[u8], u8)| { 21 | let _ = smoldot::network::protocol::decode_grandpa_warp_sync_response( 22 | params.0, 23 | usize::from(params.1) + 1, 24 | ); 25 | }); 26 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-identify-response-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | let _ = smoldot::network::protocol::decode_identify_response(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-state-response-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | let _ = smoldot::network::protocol::decode_state_response(data); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/protocol-storage-call-proof-response-decode.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | // Note that the type of response has no influence on the code of the implementation. 22 | let _ = smoldot::network::protocol::decode_storage_or_call_proof_response( 23 | smoldot::network::protocol::StorageOrCallProof::CallProof, 24 | data, 25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/wasm-module-wasmi.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | let _ = smoldot::executor::host::HostVmPrototype::new(smoldot::executor::host::Config { 22 | module: data, 23 | heap_pages: smoldot::executor::DEFAULT_HEAP_PAGES, 24 | exec_hint: smoldot::executor::vm::ExecHint::ForceWasmi, 25 | allow_unresolved_imports: true, 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /bin/fuzz/fuzz_targets/wasm-module-wasmtime.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![no_main] 19 | 20 | libfuzzer_sys::fuzz_target!(|data: &[u8]| { 21 | // Note that this fuzzing test will not compile on platforms where wasmtime isn't available. 22 | // This is intentional. 23 | let _ = smoldot::executor::host::HostVmPrototype::new(smoldot::executor::host::Config { 24 | module: data, 25 | heap_pages: smoldot::executor::DEFAULT_HEAP_PAGES, 26 | exec_hint: smoldot::executor::vm::ExecHint::ForceWasmtime, 27 | allow_unresolved_imports: true, 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /bin/generate_chain_spec.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Small script to grab the chain specification of a running node. 4 | 5 | echo '{"id":53,"jsonrpc":"2.0","method":"sync_state_genSyncSpec","params":[true]}' | 6 | websocat -n1 -B 99999999 wss://node-address-here | 7 | jq .result > chain_spec.json 8 | -------------------------------------------------------------------------------- /bin/light-base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smoldot-light" 3 | version = "0.3.0" 4 | authors = ["Parity Technologies ", "Pierre Krieger "] 5 | description = "Browser bindings to a light client for Substrate-based blockchains" 6 | repository = "https://github.com/paritytech/smoldot" 7 | license = "GPL-3.0-or-later WITH Classpath-exception-2.0" 8 | edition = "2021" 9 | 10 | [[example]] 11 | name = "basic" 12 | required-features = ["std"] 13 | 14 | [dependencies] 15 | blake2-rfc = { version = "0.2.18", default-features = false } 16 | derive_more = "0.99.17" 17 | either = { version = "1.8.0", default-features = false } 18 | event-listener = { version = "2.5.3" } 19 | fnv = { version = "1.0.7", default-features = false } 20 | futures = "0.3.25" 21 | hashbrown = { version = "0.13.1", default-features = false } 22 | hex = { version = "0.4.3", default-features = false } 23 | itertools = "0.10.5" 24 | log = { version = "0.4.17", default-features = false } 25 | lru = { version = "0.8.1", default-features = false } # TODO: there's no way to use a custom hasher; remove this dependency 26 | rand = "0.8.5" 27 | serde = { version = "1.0.148", default-features = false, features = ["alloc", "derive"] } 28 | serde_json = "1.0.89" 29 | slab = { version = "0.4.7", default-features = false } 30 | smoldot = { version = "0.5.0", path = "../..", default-features = false } 31 | 32 | # `std` feature 33 | # Add here the crates that cannot function without the help of the operating system or environment. 34 | async-std = { version = "1.12.0", optional = true } 35 | parking_lot = { version = "0.12.1", optional = true } 36 | 37 | [features] 38 | default = ["std"] 39 | std = ["async-std", "parking_lot", "smoldot/std"] 40 | 41 | [dev-dependencies] 42 | env_logger = "0.10.0" 43 | -------------------------------------------------------------------------------- /bin/light-base/examples/basic.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use core::iter; 19 | 20 | fn main() { 21 | // The `smoldot_light` library uses the `log` crate to emit logs. 22 | // We need to register some kind of logs listener, in this example `env_logger`. 23 | // See also . 24 | env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); 25 | 26 | // Now initialize the client. This does nothing except allocate resources. 27 | // The `Client` struct requires a generic parameter that provides platform bindings. In this 28 | // example, we provide `AsyncStdTcpWebSocket`, which are the "plug and play" default platform. 29 | // Any advance usage, such as embedding a client in WebAssembly, will likely require a custom 30 | // implementation of these bindings. 31 | let mut client = smoldot_light::Client::< 32 | smoldot_light::platform::async_std::AsyncStdTcpWebSocket, 33 | >::new(smoldot_light::ClientConfig { 34 | // The smoldot client will need to spawn tasks that run in the background. In order to do 35 | // so, we need to provide a "tasks spawner". 36 | tasks_spawner: Box::new(move |_name, task| { 37 | async_std::task::spawn(task); 38 | }), 39 | system_name: env!("CARGO_PKG_NAME").into(), 40 | system_version: env!("CARGO_PKG_VERSION").into(), 41 | }); 42 | 43 | // Ask the client to connect to a chain. 44 | let smoldot_light::AddChainSuccess { 45 | chain_id, 46 | json_rpc_responses, 47 | } = client 48 | .add_chain(smoldot_light::AddChainConfig { 49 | // The most important field of the configuration is the chain specification. This is a 50 | // JSON document containing all the information necessary for the client to connect to said 51 | // chain. 52 | specification: include_str!("../../polkadot.json"), 53 | 54 | // If `true`, the chain will not be able to handle JSON-RPC requests. This can be used 55 | // to save up some resources. 56 | disable_json_rpc: false, 57 | 58 | // This field is necessary only if adding a parachain. 59 | potential_relay_chains: iter::empty(), 60 | 61 | // After a chain has been added, it is possible to extract a "database" (in the form of a 62 | // simple string). This database can later be passed back the next time the same chain is 63 | // added again. 64 | // A database with an invalid format is simply ignored by the client. 65 | // In this example, we don't use this feature, and as such we simply pass an empty string, 66 | // which is intentionally an invalid database content. 67 | database_content: "", 68 | 69 | // The client gives the possibility to insert an opaque "user data" alongside each chain. 70 | // This avoids having to create a separate `HashMap` in parallel of the 71 | // client. 72 | // In this example, this feature isn't used. The chain simply has `()`. 73 | user_data: (), 74 | }) 75 | .unwrap(); 76 | 77 | // The chain is now properly initialized. 78 | 79 | // `json_rpc_responses` can only be `None` if we had passed `disable_json_rpc: true` in the 80 | // configuration. 81 | let mut json_rpc_responses = json_rpc_responses.unwrap(); 82 | 83 | // Send a JSON-RPC request to the chain. 84 | // The example here asks the client to send us notifications whenever the new best block has 85 | // changed. 86 | // Calling this function only queues the request. It is not processed immediately. 87 | // An `Err` is returned immediately if and only if the request isn't a proper JSON-RPC request 88 | // or if the channel of JSON-RPC responses is clogged. 89 | client 90 | .json_rpc_request( 91 | r#"{"id":1,"jsonrpc":"2.0","method":"chain_subscribeNewHeads","params":[]}"#, 92 | chain_id, 93 | ) 94 | .unwrap(); 95 | 96 | // Now block the execution forever and print the responses received on the channel of 97 | // JSON-RPC responses. 98 | async_std::task::block_on(async move { 99 | loop { 100 | let response = json_rpc_responses.next().await.unwrap(); 101 | println!("JSON-RPC response: {}", response); 102 | } 103 | }) 104 | } 105 | -------------------------------------------------------------------------------- /bin/light-base/src/util.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | /// Iterator combinator. Truncates the given `char`-yielding iterator to the given number of 19 | /// elements, and if the limit is reached adds a `…` at the end. 20 | pub fn truncate_str_iter( 21 | input: impl Iterator, 22 | limit: usize, 23 | ) -> impl Iterator { 24 | struct Iter(I, usize, bool, usize); 25 | 26 | impl> Iterator for Iter { 27 | type Item = char; 28 | 29 | fn next(&mut self) -> Option { 30 | if self.2 { 31 | return None; 32 | } 33 | 34 | if self.1 >= self.3 { 35 | self.2 = true; 36 | if self.0.next().is_some() { 37 | return Some('…'); 38 | } else { 39 | return None; 40 | } 41 | } 42 | 43 | self.1 += 1; 44 | self.0.next() 45 | } 46 | 47 | fn size_hint(&self) -> (usize, Option) { 48 | // Returns `size_hint()` of the inner iterator, after adding 1 to the maximum 49 | let (min, max) = self.0.size_hint(); 50 | let max = max.and_then(|m| m.checked_add(1)); 51 | (min, max) 52 | } 53 | } 54 | 55 | Iter(input, 0, false, limit) 56 | } 57 | -------------------------------------------------------------------------------- /bin/wasm-node/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This directory contains the source code of a JavaScript package (in the `javascript` subdirectory) 4 | that makes it possible to connect to Substrate-based chains (such as Polkadot). 5 | 6 | The JavaScript package works by instantiating a WebAssembly virtual machine. The `.wasm` file 7 | containing the WebAssembly bytecode is generated by compiling the Rust library found in the 8 | `rust` subdirectory. 9 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/README.md: -------------------------------------------------------------------------------- 1 | # Light client for Polkadot and Substrate-based chains 2 | 3 | This JavaScript library provides a light client for 4 | [the Polkadot blockchain](https://polkadot.network/) and for chains built 5 | using [the Substrate blockchain framework](https://substrate.io/). 6 | 7 | It is an "actual" light client, in the sense that it is byzantine-resilient. 8 | It does not rely on the presence of an RPC server, but directly connects to 9 | the full nodes of the network. 10 | 11 | ## Example 12 | 13 | ``` 14 | import * as smoldot from '@substrate/smoldot-light'; 15 | 16 | // Load a string chain specification. 17 | const chainSpec = fs.readFileSync('./westend.json', 'utf8'); 18 | 19 | // A single client can be used to initialize multiple chains. 20 | const client = smoldot.start(); 21 | 22 | const chain = await client.addChain({ chainSpec }); 23 | 24 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"system_name","params":[]}'); 25 | 26 | // Wait for a JSON-RPC response to come back. This is typically done in a loop in the background. 27 | const jsonRpcResponse = await chain.nextJsonRpcResponse(); 28 | console.log(jsonRpcResponse) 29 | 30 | // Later: 31 | // chain.remove(); 32 | ``` 33 | 34 | ## Usage 35 | 36 | The first thing to do is to initialize the client with the `start` function. 37 | 38 | Once initialized, the client can be used to connect to one or more chains. Use `addChain` to add 39 | a new chain that the client must be connected to. `addChain` must be passed the specification of 40 | the chain (commonly known as "chain spec"). 41 | 42 | The `addChain` function returns a `Promise` that yields a chain once the chain specification has 43 | been successfully parsed and basic initialization is finished, but before Internet connections 44 | are opened towards the chains. 45 | 46 | In order to de-initialize a chain, call `chain.remove()`. Any function called afterwards on this 47 | chain will throw an exception. 48 | In order to de-initialize a client, call `client.terminate()`. Any function called afterwards on 49 | any of the chains of the client will throw an exception. 50 | 51 | After having obtained a chain, use `sendJsonRpc` to send a JSON-RPC request towards the node. 52 | The function accepts as parameter a string request. See 53 | [the specification of the JSON-RPC protocol](https://www.jsonrpc.org/specification), 54 | and [the list of requests that smoldot is capable of serving](https://polkadot.js.org/docs/substrate/rpc/). 55 | Smoldot also has experimental support for an extra (still experimental at the time of writing of 56 | this comment) set of JSON-RPC functions [found here](https://github.com/paritytech/json-rpc-interface-spec/). 57 | 58 | If the request is well formatted, the client will generate a response. This response can be pulled 59 | using the `nextJsonRpcResponse` asynchronous function. Calling this function waits until a response 60 | is available and returns it. 61 | 62 | If the request is a subscription, the notifications will also be sent back using the same mechanism 63 | and can be pulled using `nextJsonRpcResponse`. 64 | 65 | If the chain specification passed to `addChain` is a parachain, then the list of potential relay 66 | chains must be passed as parameter to `addChain` as well. In situations where the chain 67 | specifications passed to `addChain` are not trusted, it is important for security reasons to not 68 | establish a parachain-relay-chain link between two chains that aren't part of the same "trust 69 | sandbox". 70 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/demo/demo-deno.ts: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // This file launches a WebSocket server that exposes JSON-RPC functions. 19 | 20 | import * as smoldot from '../dist/mjs/index-deno.js'; 21 | 22 | // Load the chain spec file. 23 | const chainSpec = new TextDecoder("utf-8").decode(await Deno.readFile("../../westend.json")); 24 | 25 | const client = smoldot.start({ 26 | maxLogLevel: 3, // Can be increased for more verbosity 27 | forbidTcp: false, 28 | forbidWs: false, 29 | forbidNonLocalWs: false, 30 | forbidWss: false, 31 | cpuRateLimit: 0.5, 32 | logCallback: (_level, target, message) => { 33 | // As incredible as it seems, there is currently no better way to print the current time 34 | // formatted in a certain way. 35 | const now = new Date(); 36 | const hours = ("0" + now.getHours()).slice(-2); 37 | const minutes = ("0" + now.getMinutes()).slice(-2); 38 | const seconds = ("0" + now.getSeconds()).slice(-2); 39 | const milliseconds = ("00" + now.getMilliseconds()).slice(-3); 40 | console.log( 41 | "[%s:%s:%s.%s] [%s] %s", 42 | hours, minutes, seconds, milliseconds, target, message 43 | ); 44 | } 45 | }); 46 | 47 | // We add the chain ahead of time in order to preload it. 48 | // Once a client connects, the chain is added again, but smoldot is smart enough to not connect 49 | // a second time. 50 | client.addChain({ chainSpec, disableJsonRpc: true }); 51 | 52 | // Now spawn a WebSocket server in order to handle JSON-RPC clients. 53 | console.log('JSON-RPC server now listening on port 9944'); 54 | console.log('Please visit: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944'); 55 | 56 | const conn = Deno.listen({ port: 9944 }); 57 | const httpConn = Deno.serveHttp(await conn.accept()); 58 | 59 | while(true) { 60 | const event = await httpConn.nextRequest(); 61 | if (!event) 62 | break; 63 | 64 | console.log('(demo) New JSON-RPC client connected.'); 65 | 66 | const { socket, response } = Deno.upgradeWebSocket(event.request); 67 | 68 | const chain = await client.addChain({ chainSpec }); 69 | 70 | (async () => { 71 | try { 72 | while(true) { 73 | const response = await chain.nextJsonRpcResponse(); 74 | socket.send(response); 75 | } 76 | } catch(_error) {} 77 | })() 78 | 79 | socket.onclose = () => { 80 | console.log("(demo) JSON-RPC client disconnected."); 81 | chain.remove(); 82 | }; 83 | 84 | socket.onmessage = (event: Deno.MessageEvent) => { 85 | if (typeof event.data === 'string') { 86 | chain.sendJsonRpc(event.data); 87 | } else { 88 | socket.close(1002); // Protocol error 89 | } 90 | }; 91 | 92 | event.respondWith(response); 93 | } 94 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/fix-package-type.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Adds package.json files to cjs/mjs subtrees with the respective type needed for 3 | # the target environment. 4 | # 5 | 6 | cat >dist/cjs/package.json <dist/mjs/package.json <", 6 | "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", 7 | "homepage": "https://github.com/paritytech/smoldot", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/paritytech/smoldot.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/paritytech/smoldot/issues" 14 | }, 15 | "files": [ 16 | "dist" 17 | ], 18 | "main": "dist/cjs/index-browser.js", 19 | "types": "dist/mjs/index-browser.d.ts", 20 | "module": "dist/mjs/index-browser.js", 21 | "exports": { 22 | "node": { 23 | "import": "./dist/mjs/index-nodejs.js", 24 | "require": "./dist/cjs/index-nodejs.js" 25 | }, 26 | "default": { 27 | "import": "./dist/mjs/index-browser.js", 28 | "require": "./dist/cjs/index-browser.js" 29 | } 30 | }, 31 | "scripts": { 32 | "buildModules": "tsc -p tsconfig-mjs.json && tsc -p tsconfig-cjs.json && bash fix-package-type.sh", 33 | "prepublishOnly": "node prepare.mjs --release && rimraf ./dist && npm run buildModules", 34 | "build": "node prepare.mjs --release && rimraf ./dist && npm run buildModules", 35 | "start": "node prepare.mjs --debug && rimraf ./dist && npm run buildModules && node demo/demo.mjs", 36 | "test": "node prepare.mjs --debug && rimraf ./dist && npm run buildModules && deno run ./dist/mjs/index-deno.js && ava --timeout=2m --concurrency 2 --no-worker-threads", 37 | "doc": "node prepare.mjs --debug && typedoc --basePath ./src --out ./dist/doc --treatWarningsAsErrors ./src/index-browser.ts" 38 | }, 39 | "dependencies": { 40 | "pako": "^2.0.4", 41 | "ws": "^8.8.1" 42 | }, 43 | "devDependencies": { 44 | "@types/node": "^18.0.0", 45 | "@types/pako": "^2.0.0", 46 | "@types/ws": "^8.5.3", 47 | "ava": "^5.0.1", 48 | "dtslint": "^4.0.6", 49 | "rimraf": "^3.0.2", 50 | "typedoc": "^0.23.15", 51 | "typescript": "^4.5.4" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/src/base64.ts: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | let rfc4648Alphabet: Map = new Map(); 19 | const rfc4648AlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 20 | for (let i = 0; i < rfc4648AlphabetAsStr.length; ++i) { 21 | rfc4648Alphabet.set(rfc4648AlphabetAsStr[i]!, i) 22 | } 23 | 24 | let urlSafeAlphabet: Map = new Map(); 25 | const urlSafeAlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 26 | for (let i = 0; i < urlSafeAlphabetAsStr.length; ++i) { 27 | urlSafeAlphabet.set(urlSafeAlphabetAsStr[i]!, i) 28 | } 29 | 30 | /** 31 | * Decodes a multibase-encoded string. 32 | * 33 | * Throws an exception if the encoding isn't base64 or one of its variants. 34 | */ 35 | export function multibaseBase64Decode(input: string): Uint8Array { 36 | if (input.length === 0) 37 | throw new Error("Invalid multibase"); 38 | 39 | switch (input[0]) { 40 | case 'm': 41 | case 'M': 42 | return classicDecode(input.slice(1)) 43 | case 'u': 44 | case 'U': 45 | return urlSafeDecode(input.slice(1)) 46 | default: 47 | throw new Error('Unknown multibase prefix: ' + input[0]); 48 | } 49 | } 50 | 51 | /** 52 | * Decodes a base64-encoded string into bytes using the original alphabet from RFC4648. 53 | * 54 | * See . 55 | */ 56 | export function classicDecode(input: string): Uint8Array { 57 | return base64Decode(input, rfc4648Alphabet); 58 | } 59 | 60 | /** 61 | * Decodes a base64-encoded string into bytes using the URL-safe alphabet. 62 | * 63 | * See . 64 | */ 65 | export function urlSafeDecode(input: string): Uint8Array { 66 | return base64Decode(input, urlSafeAlphabet); 67 | } 68 | 69 | /** 70 | * Decodes a base64-encoded string into bytes using the given alphabet. 71 | */ 72 | export function base64Decode(input: string, alphabet: Map): Uint8Array { 73 | // Remove the padding bytes at the end of the string. We don't check whether the padding is 74 | // accurate. 75 | while (input.length !== 0 && input[input.length - 1] === '=') 76 | input = input.slice(0, -1) 77 | 78 | // Contains the output data. 79 | const out = new Uint8Array(Math.floor(input.length * 6 / 8)); 80 | // Position within `out` of the next byte to write. 81 | let outPos = 0; 82 | 83 | // The bits decoded from the input are added to the right of this value. 84 | let currentByte = 0; 85 | // The left-most `validBitsInCurrentByte` bits of `currentByte` must be written out. 86 | let validBitsInCurrentByte = 0; 87 | 88 | for (let i = 0; i < input.length; ++i) { 89 | const inputChr = input[i]!; 90 | 91 | const bitsToAppend = alphabet.get(inputChr); 92 | if (bitsToAppend === undefined) 93 | throw new Error('Invalid base64 character: ' + inputChr); 94 | console.assert(bitsToAppend < (1 << 6)); 95 | 96 | currentByte = (currentByte << 6) | bitsToAppend; 97 | validBitsInCurrentByte += 6; 98 | 99 | if (validBitsInCurrentByte >= 8) { 100 | let outByte = currentByte >> (validBitsInCurrentByte - 8); 101 | out[outPos] = outByte; 102 | outPos += 1; 103 | validBitsInCurrentByte -= 8; 104 | } 105 | console.assert(validBitsInCurrentByte < 8); 106 | currentByte &= 0xff; 107 | } 108 | 109 | if ((currentByte & ((1 << validBitsInCurrentByte) - 1)) !== 0) 110 | throw new Error("Unexpected EOF"); 111 | if (validBitsInCurrentByte >= 6) 112 | throw new Error("Unexpected EOF"); 113 | 114 | return out; 115 | } 116 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/src/instance/autogen/.gitignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/src/instance/bindings.ts: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | /** 19 | * Interface that the Wasm module exports. Contains the functions that are exported by the Rust 20 | * code. 21 | * 22 | * Must match the bindings found in the Rust code. 23 | */ 24 | export interface SmoldotWasmExports extends WebAssembly.Exports { 25 | memory: WebAssembly.Memory, 26 | init: (maxLogLevel: number, enableCurrentTask: number, cpuRateLimit: number, periodicallyYield: number) => void, 27 | set_periodically_yield: (periodicallyYield: number) => void, 28 | start_shutdown: () => void, 29 | alloc: (len: number) => number, 30 | add_chain: (chainSpecPointer: number, chainSpecLen: number, databaseContentPointer: number, databaseContentLen: number, jsonRpcRunning: number, potentialRelayChainsPtr: number, potentialRelayChainsLen: number) => number; 31 | remove_chain: (chainId: number) => void, 32 | chain_is_ok: (chainId: number) => number, 33 | chain_error_len: (chainId: number) => number, 34 | chain_error_ptr: (chainId: number) => number, 35 | json_rpc_send: (textPtr: number, textLen: number, chainId: number) => number, 36 | json_rpc_responses_peek: (chainId: number) => number, 37 | json_rpc_responses_pop: (chainId: number) => void, 38 | timer_finished: (timerId: number) => void, 39 | connection_open_single_stream: (connectionId: number, handshakeTy: number) => void, 40 | connection_open_multi_stream: (connectionId: number, handshakeTyPtr: number, handshakeTyLen: number) => void, 41 | stream_message: (connectionId: number, streamId: number, ptr: number, len: number) => void, 42 | connection_stream_opened: (connectionId: number, streamId: number, outbound: number) => void, 43 | connection_reset: (connectionId: number, ptr: number, len: number) => void, 44 | stream_reset: (connectionId: number, streamId: number) => void, 45 | } 46 | 47 | export interface SmoldotWasmInstance extends WebAssembly.Instance { 48 | readonly exports: SmoldotWasmExports; 49 | } 50 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/src/instance/buffer.ts: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | export function utf8BytesToString(buffer: Uint8Array, offset: number, length: number): string { 19 | checkRange(buffer, offset, length) 20 | 21 | // The `TextDecoder` API is supported by all major browsers and by NodeJS. 22 | // 23 | return new TextDecoder().decode(buffer.slice(offset, offset + length)) 24 | } 25 | 26 | export function readUInt32LE(buffer: Uint8Array, offset: number): number { 27 | checkRange(buffer, offset, 4) 28 | return (buffer[offset]! | (buffer[offset + 1]! << 8) | (buffer[offset + 2]! << 16)) + (buffer[offset + 3]! * 0x1000000) 29 | } 30 | 31 | /** 32 | * Sets the value of a given byte in the buffer. 33 | * 34 | * This function is equivalent to `buffer[offset] = value`, except that an exception is thrown 35 | * if `offset` is out of range. 36 | */ 37 | export function writeUInt8(buffer: Uint8Array, offset: number, value: number) { 38 | checkRange(buffer, offset, 1) 39 | buffer[offset] = value & 0xff 40 | } 41 | 42 | export function writeUInt32LE(buffer: Uint8Array, offset: number, value: number) { 43 | checkRange(buffer, offset, 4); 44 | buffer[offset + 3] = (value >>> 24) & 0xff 45 | buffer[offset + 2] = (value >>> 16) & 0xff 46 | buffer[offset + 1] = (value >>> 8) & 0xff 47 | buffer[offset] = value & 0xff 48 | } 49 | 50 | function checkRange(buffer: Uint8Array, offset: number, length: number) { 51 | if (!Number.isInteger(offset) || offset < 0) 52 | throw new RangeError() 53 | if (offset + length > buffer.length) 54 | throw new RangeError() 55 | } 56 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/test/chainHead.mjs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | import test from 'ava'; 19 | import * as fs from 'node:fs'; 20 | import { start } from "../dist/mjs/index-nodejs.js"; 21 | 22 | const westendSpec = fs.readFileSync('./test/westend.json', 'utf8'); 23 | 24 | test('chainHead_unstable_follow works', async t => { 25 | const client = start({ logCallback: () => { } }); 26 | await client 27 | .addChain({ chainSpec: westendSpec }) 28 | .then((chain) => { 29 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"chainHead_unstable_follow","params":[false]}'); 30 | return chain; 31 | }) 32 | .then(async (chain) => { 33 | // Subscription response. 34 | await chain.nextJsonRpcResponse(); 35 | return chain; 36 | }) 37 | .then(async (chain) => { 38 | const parsed = JSON.parse(await chain.nextJsonRpcResponse()); 39 | if (parsed.params.result.event == "initialized") { 40 | if (parsed.params.result.finalizedBlockHash.toLowerCase() == "0x9d34c5a7a8ad8d73c7690a41f7a9d1a7c46e21dc8fb1638aee6ef07f45b65158" && !parsed.params.result.finalizedBlockRuntime) 41 | t.pass(); 42 | else 43 | t.fail(response); 44 | } 45 | return chain; 46 | }) 47 | .then(() => client.terminate()); 48 | }); 49 | 50 | test('chainHead_unstable_unfollow works', async t => { 51 | const client = start({ logCallback: () => { } }); 52 | await client 53 | .addChain({ chainSpec: westendSpec }) 54 | .then((chain) => { 55 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"chainHead_unstable_follow","params":[false]}'); 56 | return chain; 57 | }) 58 | .then(async (chain) => { 59 | const parsed = JSON.parse(await chain.nextJsonRpcResponse()); 60 | t.assert(parsed.id === 1); 61 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":2,"method":"chainHead_unstable_unfollow","params":[' + JSON.stringify(parsed.result) + ']}'); 62 | return chain; 63 | }) 64 | .then(async (chain) => { 65 | while (true) { 66 | const parsed = JSON.parse(await chain.nextJsonRpcResponse()); 67 | if (parsed.id === 2 && parsed.result === null) { 68 | t.pass(); 69 | break; 70 | } 71 | } 72 | }) 73 | .then(() => client.terminate()); 74 | }); 75 | 76 | test('chainHead_unstable_body works', async t => { 77 | const client = start({ logCallback: () => { } }); 78 | await client 79 | .addChain({ chainSpec: westendSpec }) 80 | .then((chain) => { 81 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"chainHead_unstable_follow","params":[false]}'); 82 | return chain; 83 | }) 84 | .then(async (chain) => { 85 | const parsed = JSON.parse(await chain.nextJsonRpcResponse()); 86 | t.assert(parsed.id === 1); 87 | return [chain, parsed.result]; 88 | }) 89 | .then(async ([chain, followSubscription]) => { 90 | const parsed = JSON.parse(await chain.nextJsonRpcResponse()); 91 | t.assert(parsed.method == "chainHead_unstable_followEvent" && parsed.params.subscription == followSubscription); 92 | if (parsed.params.result.event == "initialized") { 93 | if (parsed.params.result.finalizedBlockHash.toLowerCase() != "0x9d34c5a7a8ad8d73c7690a41f7a9d1a7c46e21dc8fb1638aee6ef07f45b65158") 94 | t.fail(parsed); 95 | chain.sendJsonRpc(JSON.stringify({ "jsonrpc": "2.0", "id": 1, "method": "chainHead_unstable_body", "params": [followSubscription, parsed.params.result.finalizedBlockHash] })); 96 | } 97 | return chain; 98 | }) 99 | .then(async (chain) => { 100 | while (true) { 101 | const parsed = JSON.parse(await chain.nextJsonRpcResponse()); 102 | if (parsed.method == "chainHead_unstable_bodyEvent" && parsed.params.result.event == "inaccessible") { 103 | t.pass(); 104 | break; 105 | } 106 | } 107 | }) 108 | .then(() => client.terminate()); 109 | }); 110 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/test/chainSpec.mjs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | import test from 'ava'; 19 | import * as fs from 'node:fs'; 20 | import { start } from "../dist/mjs/index-nodejs.js"; 21 | 22 | const westendSpec = fs.readFileSync('./test/westend.json', 'utf8'); 23 | 24 | test('chainSpec_chainName works', async t => { 25 | const client = start({ logCallback: () => { } }); 26 | await client 27 | .addChain({ chainSpec: westendSpec }) 28 | .then((chain) => { 29 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"chainSpec_unstable_chainName","params":[]}'); 30 | return chain; 31 | }) 32 | .then(async (chain) => { 33 | const response = await chain.nextJsonRpcResponse(); 34 | const parsed = JSON.parse(response); 35 | if (parsed.result == "Westend") 36 | t.pass(); 37 | else 38 | t.fail(response); 39 | }) 40 | .then(() => client.terminate()); 41 | }); 42 | 43 | test('chainSpec_unstable_genesisHash works', async t => { 44 | const client = start({ logCallback: () => { } }); 45 | await client 46 | .addChain({ chainSpec: westendSpec }) 47 | .then((chain) => { 48 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"chainSpec_unstable_genesisHash","params":[]}'); 49 | return chain; 50 | }) 51 | .then(async (chain) => { 52 | const response = await chain.nextJsonRpcResponse(); 53 | const parsed = JSON.parse(response); 54 | if (parsed.result.toLowerCase() == "0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e") 55 | t.pass(); 56 | else 57 | t.fail(response); 58 | }) 59 | .then(() => client.terminate()); 60 | }); 61 | 62 | test('chainSpec_unstable_properties works', async t => { 63 | const client = start({ logCallback: () => { } }); 64 | await client 65 | .addChain({ chainSpec: westendSpec }) 66 | .then((chain) => { 67 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"chainSpec_unstable_properties","params":[]}'); 68 | return chain; 69 | }) 70 | .then(async (chain) => { 71 | const response = await chain.nextJsonRpcResponse(); 72 | const parsed = JSON.parse(response); 73 | if (parsed.result.ss58Format == 42 && parsed.result.tokenDecimals == 12 && parsed.result.tokenSymbol == "WND") 74 | t.pass(); 75 | else 76 | t.fail(response); 77 | }) 78 | .then(() => client.terminate()); 79 | }); 80 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/test/misc.mjs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | import test from 'ava'; 19 | import * as fs from 'node:fs'; 20 | import { start, JsonRpcDisabledError, MalformedJsonRpcError } from "../dist/mjs/index-nodejs.js"; 21 | 22 | const westendSpec = fs.readFileSync('./test/westend.json', 'utf8'); 23 | 24 | test('malformed json-rpc requests rejected', async t => { 25 | const client = start({ logCallback: () => { } }); 26 | await client 27 | .addChain({ chainSpec: westendSpec }) 28 | .then((chain) => { 29 | try { 30 | chain.sendJsonRpc("this is not a valid JSON-RPC request"); 31 | } catch(error) { 32 | t.assert(error instanceof MalformedJsonRpcError); 33 | t.pass(); 34 | } 35 | }) 36 | .then(() => client.terminate()); 37 | }); 38 | 39 | test('too large json-rpc requests rejected', async t => { 40 | // Generate a very long string. We start with a length of 1 and double for every iteration. 41 | // Thus the final length of the string is `2^i` where `i` is the number of iterations. 42 | let veryLongString = 'a'; 43 | for (let i = 0; i < 27; ++i) { 44 | veryLongString += veryLongString; 45 | } 46 | 47 | const client = start({ logCallback: () => { } }); 48 | await client 49 | .addChain({ chainSpec: westendSpec }) 50 | .then((chain) => { 51 | try { 52 | // We use `JSON.stringify` in order to be certain that the request is valid JSON. 53 | chain.sendJsonRpc(JSON.stringify({ "jsonrpc": "2.0", "id": 1, "method": "foo", "params": [veryLongString] })); 54 | } catch(error) { 55 | t.assert(error instanceof MalformedJsonRpcError); 56 | t.pass(); 57 | } 58 | }) 59 | .then(() => client.terminate()); 60 | }); 61 | 62 | test('disableJsonRpc option forbids sendJsonRpc', async t => { 63 | const client = start({ logCallback: () => { } }); 64 | await client 65 | .addChain({ chainSpec: westendSpec, disableJsonRpc: true }) 66 | .then((chain) => { 67 | try { 68 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"system_name","params":[]}'); 69 | } catch(error) { 70 | t.assert(error instanceof JsonRpcDisabledError); 71 | t.pass(); 72 | } 73 | }) 74 | .then(() => client.terminate()); 75 | }); 76 | 77 | test('disableJsonRpc option forbids nextJsonRpcResponse', async t => { 78 | const client = start({ logCallback: () => { } }); 79 | await client 80 | .addChain({ chainSpec: westendSpec, disableJsonRpc: true }) 81 | .then(async (chain) => { 82 | try { 83 | await chain.nextJsonRpcResponse(); 84 | } catch(error) { 85 | t.assert(error instanceof JsonRpcDisabledError); 86 | t.pass(); 87 | } 88 | }) 89 | .then(() => client.terminate()); 90 | }); 91 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/test/sudo.mjs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | import test from 'ava'; 19 | import * as fs from 'node:fs'; 20 | import { start } from "../dist/mjs/index-nodejs.js"; 21 | 22 | const westendSpec = fs.readFileSync('./test/westend.json', 'utf8'); 23 | 24 | test('sudo_unstable_p2pDiscover is available', async t => { 25 | const client = start({ logCallback: () => { } }); 26 | await client 27 | .addChain({ chainSpec: westendSpec }) 28 | .then((chain) => { 29 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"sudo_unstable_p2pDiscover","params":["/ip4/1.2.3.4/tcp/30333/p2p/12D3KooWDmQPkBvQGg9wjBdFThtWj3QCDVQyHJ1apfWrHvjwbYS8"]}'); 30 | return chain; 31 | }) 32 | .then(async (chain) => { 33 | const response = await chain.nextJsonRpcResponse(); 34 | const parsed = JSON.parse(response); 35 | if (parsed.id == 1 && parsed.result === null) 36 | t.pass(); 37 | else 38 | t.fail(response); 39 | }) 40 | .then(() => client.terminate()); 41 | }); 42 | 43 | test('sudo_unstable_p2pDiscover returns error on invalid multiaddr', async t => { 44 | const client = start({ logCallback: () => { } }); 45 | await client 46 | .addChain({ chainSpec: westendSpec }) 47 | .then((chain) => { 48 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"sudo_unstable_p2pDiscover","params":["/hello/world"]}'); 49 | return chain; 50 | }) 51 | .then(async (chain) => { 52 | const response = await chain.nextJsonRpcResponse(); 53 | const parsed = JSON.parse(response); 54 | if (parsed.id == 1 && !!parsed.error) 55 | t.pass(); 56 | else 57 | t.fail(response); 58 | }) 59 | .then(() => client.terminate()); 60 | }); 61 | 62 | test('sudo_unstable_p2pDiscover returns error if multiaddr doesn\'t end with /p2p', async t => { 63 | const client = start({ logCallback: () => { } }); 64 | await client 65 | .addChain({ chainSpec: westendSpec }) 66 | .then((chain) => { 67 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"sudo_unstable_p2pDiscover","params":["/ip4/127.0.0.1/tcp/30333/ws"]}'); 68 | return chain; 69 | }) 70 | .then(async (chain) => { 71 | const response = await chain.nextJsonRpcResponse(); 72 | const parsed = JSON.parse(response); 73 | if (parsed.id == 1 && !!parsed.error) 74 | t.pass(); 75 | else 76 | t.fail(response); 77 | }) 78 | .then(() => client.terminate()); 79 | }); 80 | 81 | test('sudo_unstable_version works', async t => { 82 | const client = start({ logCallback: () => { } }); 83 | await client 84 | .addChain({ chainSpec: westendSpec }) 85 | .then((chain) => { 86 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"sudo_unstable_version","params":[]}'); 87 | return chain; 88 | }) 89 | .then(async (chain) => { 90 | const response = await chain.nextJsonRpcResponse(); 91 | const parsed = JSON.parse(response); 92 | if (parsed.result.includes("smoldot")) 93 | t.pass(); 94 | else 95 | t.fail(response); 96 | }) 97 | .then(() => client.terminate()); 98 | }); 99 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/test/test.mjs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | import test from 'ava'; 19 | import * as fs from 'node:fs'; 20 | import { start } from "../dist/mjs/index-nodejs.js"; 21 | 22 | const westendSpec = fs.readFileSync('./test/westend.json', 'utf8'); 23 | 24 | test('invalid chain spec throws error', async t => { 25 | const client = start({ logCallback: () => { } }); 26 | await client 27 | .addChain({ 28 | chainSpec: "invalid chain spec", 29 | }) 30 | .then((chain) => t.fail()) 31 | .catch(() => t.pass()) 32 | .then(() => client.terminate()); 33 | }); 34 | 35 | test('system_name works', async t => { 36 | const client = start({ logCallback: () => { } }); 37 | await client 38 | .addChain({ chainSpec: westendSpec }) 39 | .then((chain) => { 40 | chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"system_name","params":[]}'); 41 | return chain; 42 | }) 43 | .then(async (chain) => { 44 | const response = await chain.nextJsonRpcResponse(); 45 | t.assert(response === '{"jsonrpc":"2.0","id":1,"result":"smoldot-light-wasm"}'); 46 | t.pass(); 47 | }) 48 | .then(() => client.terminate()); 49 | }); 50 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/tsconfig-cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "es6" 6 | ], 7 | "target": "es6", 8 | "module": "commonjs", 9 | "downlevelIteration": true, 10 | "outDir": "./dist/cjs", 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/tsconfig-mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "es6", 6 | ], 7 | "target": "es6", 8 | "module": "es6", 9 | "downlevelIteration": true, 10 | "outDir": "./dist/mjs", 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bin/wasm-node/javascript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "strict": true, 5 | "allowUnreachableCode": false, 6 | "allowUnusedLabels": false, 7 | "noImplicitReturns": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noUncheckedIndexedAccess": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true 12 | }, 13 | "include": [ 14 | "src/**/*" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /bin/wasm-node/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smoldot-light-wasm" 3 | version = "0.7.9" 4 | authors = ["Parity Technologies ", "Pierre Krieger "] 5 | description = "Browser bindings to a light client for Substrate-based blockchains" 6 | repository = "https://github.com/paritytech/smoldot" 7 | license = "GPL-3.0-or-later WITH Classpath-exception-2.0" 8 | edition = "2021" 9 | publish = false 10 | 11 | [lib] 12 | crate-type = ["cdylib", "rlib"] 13 | 14 | [dependencies] 15 | event-listener = { version = "2.5.3" } 16 | fnv = { version = "1.0.7", default-features = false } 17 | futures = "0.3.25" 18 | hashbrown = { version = "0.13.1", default-features = false } 19 | lazy_static = "1.4.0" 20 | log = { version = "0.4.17", features = ["std"] } 21 | nom = { version = "7.1.1", default-features = false } 22 | pin-project = "1.0.12" 23 | rand = "0.8.5" 24 | slab = { version = "0.4.7", default-features = false } 25 | smoldot = { version = "0.5.0", path = "../../..", default-features = false } 26 | smoldot-light = { version = "0.3.0", path = "../../light-base", default-features = false } 27 | -------------------------------------------------------------------------------- /bin/wasm-node/rust/src/alloc.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! This module contains the `#[global_allocator]` used by the wasm node. This allocator is very 19 | //! simple, apart from the fact that it counts the total number of bytes that have been allocated. 20 | //! This value can then be retrieved by calling [`total_alloc_bytes`]. 21 | 22 | use std::{alloc, sync::atomic}; 23 | 24 | /// Returns the total number of bytes that have been allocated through the Rust `alloc` crate 25 | /// throughout the entire Wasm node. 26 | pub fn total_alloc_bytes() -> usize { 27 | ALLOCATOR.total.load(atomic::Ordering::Relaxed) 28 | } 29 | 30 | struct AllocCounter { 31 | /// Use the default "system" allocator. In the context of Wasm, this uses the `dlmalloc` 32 | /// library. See . 33 | /// 34 | /// While the `wee_alloc` crate is usually the recommended choice in WebAssembly, testing has 35 | /// shown that using it makes memory usage explode from `~100MiB` to `~2GiB` and more (the 36 | /// environment then refuses to allocate `4GiB`). 37 | inner: alloc::System, 38 | 39 | /// Total number of bytes allocated. 40 | total: atomic::AtomicUsize, 41 | } 42 | 43 | #[global_allocator] 44 | static ALLOCATOR: AllocCounter = AllocCounter { 45 | inner: alloc::System, 46 | total: atomic::AtomicUsize::new(0), 47 | }; 48 | 49 | unsafe impl alloc::GlobalAlloc for AllocCounter { 50 | unsafe fn alloc(&self, layout: alloc::Layout) -> *mut u8 { 51 | let ret = self.inner.alloc(layout); 52 | if !ret.is_null() { 53 | self.total 54 | .fetch_add(layout.size(), atomic::Ordering::Relaxed); 55 | } 56 | ret 57 | } 58 | 59 | unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::Layout) { 60 | self.total 61 | .fetch_sub(layout.size(), atomic::Ordering::Relaxed); 62 | self.inner.dealloc(ptr, layout); 63 | } 64 | 65 | unsafe fn alloc_zeroed(&self, layout: alloc::Layout) -> *mut u8 { 66 | let ret = self.inner.alloc_zeroed(layout); 67 | if !ret.is_null() { 68 | self.total 69 | .fetch_add(layout.size(), atomic::Ordering::Relaxed); 70 | } 71 | ret 72 | } 73 | 74 | unsafe fn realloc(&self, ptr: *mut u8, layout: alloc::Layout, new_size: usize) -> *mut u8 { 75 | let ret = self.inner.realloc(ptr, layout, new_size); 76 | if !ret.is_null() { 77 | if new_size >= layout.size() { 78 | self.total 79 | .fetch_add(new_size - layout.size(), atomic::Ordering::Relaxed); 80 | } else { 81 | self.total 82 | .fetch_sub(layout.size() - new_size, atomic::Ordering::Relaxed); 83 | } 84 | } 85 | ret 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /bin/wasm-node/rust/src/cpu_rate_limiter.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use core::{ 19 | future::Future, 20 | pin::Pin, 21 | task::{Context, Poll}, 22 | time::Duration, 23 | }; 24 | 25 | /// Wraps around a `Future` and enforces an upper bound to the CPU consumed by the polling of 26 | /// this `Future`. 27 | /// 28 | /// This works by enforcing a delay after a polling operation has happened, so that the average 29 | /// polling time respects the upper bound. This struct doesn't protect against infinite loops or 30 | /// a single polling taking a long time. 31 | #[pin_project::pin_project] 32 | pub struct CpuRateLimiter { 33 | #[pin] 34 | inner: T, 35 | max_divided_by_rate_limit_minus_one: f64, 36 | 37 | /// Prevent `self.inner.poll` from being called before this `Delay` is ready. 38 | #[pin] 39 | prevent_poll_until: crate::timers::Delay, 40 | } 41 | 42 | impl CpuRateLimiter { 43 | /// Wraps around `inner`. The `rate_limit` represents the upper bound, where 44 | /// `u32::max_value()` represents "one CPU". For example passing `u32::max_value() / 2` 45 | /// represents "`50%` of one CPU". 46 | pub fn new(inner: T, rate_limit: u32) -> Self { 47 | let max_divided_by_rate_limit_minus_one = 48 | (f64::from(u32::max_value()) / f64::from(rate_limit)) - 1.0; 49 | // Note that it is legal for `max_divided_by_rate_limit_minus_one` to be +infinite 50 | debug_assert!( 51 | !max_divided_by_rate_limit_minus_one.is_nan() 52 | && max_divided_by_rate_limit_minus_one >= 0.0 53 | ); 54 | 55 | CpuRateLimiter { 56 | inner, 57 | max_divided_by_rate_limit_minus_one, 58 | prevent_poll_until: crate::timers::Delay::new(Duration::new(0, 0)), 59 | } 60 | } 61 | } 62 | 63 | impl Future for CpuRateLimiter { 64 | type Output = T::Output; 65 | 66 | fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { 67 | let mut this = self.project(); 68 | 69 | // Note that `crate::timers::Delay` is a `FusedFuture`, making it ok to call `poll` even 70 | // if it is possible for the `Delay` to already be resolved. 71 | // We add a small zero-cost shim to ensure at compile time that this is indeed the case. 72 | fn enforce_fused(_: &T) {} 73 | enforce_fused(&this.prevent_poll_until); 74 | if Future::poll(this.prevent_poll_until.as_mut(), cx).is_pending() { 75 | return Poll::Pending; 76 | } 77 | 78 | let before_polling = crate::Instant::now(); 79 | 80 | match this.inner.poll(cx) { 81 | Poll::Ready(value) => Poll::Ready(value), 82 | Poll::Pending => { 83 | let after_polling = crate::Instant::now(); 84 | 85 | // Time it took to execute `poll`. 86 | let poll_duration = after_polling - before_polling; 87 | 88 | // In order to enforce the rate limiting, we prevent `poll` from executing 89 | // for a certain amount of time. 90 | // The base equation here is: `(after_poll_sleep + poll_duration) * rate_limit == poll_duration * u32::max_value()`. 91 | // Because `Duration::mul_f64` and `Duration::from_secs_f64` panic in case of 92 | // overflow, we need to do the operation of multiplying and checking the bounds 93 | // manually. 94 | let mut after_poll_sleep = 95 | poll_duration.as_secs_f64() * *this.max_divided_by_rate_limit_minus_one; 96 | debug_assert!(after_poll_sleep >= 0.0 && !after_poll_sleep.is_nan()); 97 | let max_duration_float: f64 = Duration::MAX.as_secs_f64(); // TODO: turn this into a `const` once `as_secs_f64` is `const` 98 | if after_poll_sleep 99 | .partial_cmp(&max_duration_float) 100 | .map(|ord| ord.is_ge()) 101 | .unwrap_or(true) 102 | { 103 | after_poll_sleep = max_duration_float; 104 | } 105 | // TODO: use try_from_secs_f64 when it's stable https://github.com/rust-lang/rust/issues/83400 106 | 107 | this.prevent_poll_until.set(crate::timers::Delay::new_at( 108 | after_polling + Duration::from_secs_f64(after_poll_sleep), 109 | )); 110 | Poll::Pending 111 | } 112 | } 113 | } 114 | } 115 | 116 | impl futures::future::FusedFuture for CpuRateLimiter { 117 | fn is_terminated(&self) -> bool { 118 | self.inner.is_terminated() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/author.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // TODO: doc 19 | 20 | pub mod aura; 21 | pub mod build; 22 | pub mod runtime; 23 | -------------------------------------------------------------------------------- /src/author/aura.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::header; 19 | use core::{num::NonZeroU64, time::Duration}; 20 | 21 | /// Configuration for [`next_slot_claim`]. 22 | pub struct Config<'a, TLocAuth> { 23 | /// Time elapsed since [the Unix Epoch](https://en.wikipedia.org/wiki/Unix_time) (i.e. 24 | /// 00:00:00 UTC on 1 January 1970), ignoring leap seconds. 25 | pub now_from_unix_epoch: Duration, 26 | 27 | /// Duration, in milliseconds, of an Aura slot. 28 | pub slot_duration: NonZeroU64, 29 | 30 | /// List of the Aura authorities allowed to produce a block. This is either the same as the 31 | /// ones of the current best block, or a new list if the current best block contains an 32 | /// authorities list change digest item. 33 | pub current_authorities: header::AuraAuthoritiesIter<'a>, 34 | 35 | /// Iterator to the list of Sr25519 public keys available locally. 36 | /// 37 | /// Must implement `Iterator`. 38 | pub local_authorities: TLocAuth, 39 | } 40 | 41 | /// Calculates the earliest one of the authorities in [`Config::local_authorities`] is allowed to 42 | /// produce a block. 43 | /// 44 | /// Returns `None` if none of the local authorities are allowed to produce blocks. 45 | /// 46 | /// The value returned by this function is entirely deterministic based on the [`Config`] and 47 | /// never changes until [`Config::now_from_unix_epoch`] gets past the value returned in 48 | /// [`SlotClaim::slot_end_from_unix_epoch`]. 49 | /// 50 | /// However, keep in mind that, as the best block changes, the list of authorities 51 | /// ([`Config::current_authorities`]) might change, in which case this function should be 52 | /// called again. 53 | pub fn next_slot_claim<'a>( 54 | config: Config<'a, impl Iterator>, 55 | ) -> Option { 56 | let num_current_authorities = config.current_authorities.clone().count(); 57 | 58 | // Note that this calculation (and some other calculations down below) can overflow in the 59 | // very distant future. This is considered acceptable. 60 | let current_slot = u64::try_from( 61 | config.now_from_unix_epoch.as_millis() / u128::from(config.slot_duration.get()), 62 | ) 63 | .unwrap(); 64 | 65 | let current_slot_index = 66 | usize::try_from(current_slot.checked_rem(u64::try_from(num_current_authorities).unwrap())?) 67 | .unwrap(); 68 | debug_assert!(current_slot_index < num_current_authorities); 69 | 70 | let mut claim = None; 71 | 72 | for (pub_key_index, local_pub_key) in config.local_authorities.enumerate() { 73 | // TODO: O(n) complexity 74 | let mut index = match config 75 | .current_authorities 76 | .clone() 77 | .position(|pk| pk.public_key == local_pub_key) 78 | { 79 | Some(idx) => idx, 80 | None => continue, 81 | }; 82 | 83 | if index < current_slot_index { 84 | index += num_current_authorities; 85 | } 86 | debug_assert!(index >= current_slot_index); 87 | 88 | let claimable_slot = current_slot + u64::try_from(index - current_slot_index).unwrap(); 89 | 90 | match claim { 91 | Some((s, _)) if s <= claimable_slot => {} 92 | _ => claim = Some((claimable_slot, pub_key_index)), 93 | } 94 | } 95 | 96 | if let Some((slot_number, local_authorities_index)) = claim { 97 | let slot_start_from_unix_epoch = 98 | Duration::from_millis(slot_number.checked_mul(config.slot_duration.get()).unwrap()); 99 | let slot_end_from_unix_epoch = 100 | slot_start_from_unix_epoch + Duration::from_secs(config.slot_duration.get()); 101 | debug_assert!(slot_end_from_unix_epoch > config.now_from_unix_epoch); 102 | 103 | Some(SlotClaim { 104 | slot_start_from_unix_epoch, 105 | slot_end_from_unix_epoch, 106 | slot_number, 107 | local_authorities_index, 108 | }) 109 | } else { 110 | None 111 | } 112 | } 113 | 114 | /// Slot happening now or in the future and that can be attributed to one of the authorities in 115 | /// [`Config::local_authorities`]. 116 | /// 117 | /// See also [`next_slot_claim`]. 118 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 119 | pub struct SlotClaim { 120 | /// UNIX time when the slot starts. Can be inferior to the value passed to 121 | /// [`Config::now_from_unix_epoch`] if the slot has already started. 122 | pub slot_start_from_unix_epoch: Duration, 123 | /// UNIX time when the slot ends. Always superior to the value passed to 124 | /// [`Config::now_from_unix_epoch`]. 125 | pub slot_end_from_unix_epoch: Duration, 126 | /// Slot number of the claim. Used when building the block. 127 | pub slot_number: u64, 128 | /// Index within [`Config::local_authorities`] of the authority that can produce the block. 129 | pub local_authorities_index: usize, 130 | } 131 | -------------------------------------------------------------------------------- /src/author/runtime/tests.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![cfg(test)] 19 | 20 | use crate::verify::inherents; 21 | use core::iter; 22 | 23 | #[test] 24 | fn block_building_works() { 25 | let chain_specs = crate::chain_spec::ChainSpec::from_json_bytes( 26 | &include_bytes!("example-chain-specs.json")[..], 27 | ) 28 | .unwrap(); 29 | let genesis_storage = chain_specs.genesis_storage().into_genesis_items().unwrap(); 30 | 31 | let (chain_info, genesis_runtime) = chain_specs.as_chain_information().unwrap(); 32 | let genesis_hash = chain_info.as_ref().finalized_block_header.hash(4); 33 | 34 | let mut builder = super::build_block(super::Config { 35 | block_number_bytes: 4, 36 | parent_runtime: genesis_runtime, 37 | parent_hash: &genesis_hash, 38 | parent_number: 0, 39 | block_body_capacity: 0, 40 | consensus_digest_log_item: super::ConfigPreRuntime::Aura(crate::header::AuraPreDigest { 41 | slot_number: 1234u64, 42 | }), 43 | top_trie_root_calculation_cache: None, 44 | }); 45 | 46 | loop { 47 | match builder { 48 | super::BlockBuild::Finished(Ok(success)) => { 49 | let decoded = crate::header::decode(&success.scale_encoded_header, 4).unwrap(); 50 | assert_eq!(decoded.number, 1); 51 | assert_eq!(*decoded.parent_hash, genesis_hash); 52 | break; 53 | } 54 | super::BlockBuild::Finished(Err(err)) => panic!("{}", err), 55 | super::BlockBuild::ApplyExtrinsic(ext) => builder = ext.finish(), 56 | super::BlockBuild::ApplyExtrinsicResult { .. } => unreachable!(), 57 | super::BlockBuild::InherentExtrinsics(ext) => { 58 | builder = ext.inject_inherents(inherents::InherentData { timestamp: 1234 }); 59 | } 60 | super::BlockBuild::StorageGet(get) => { 61 | let value = genesis_storage 62 | .iter() 63 | .find(|(k, _)| *k == get.key().as_ref()) 64 | .map(|(_, v)| iter::once(v)); 65 | builder = get.inject_value(value); 66 | } 67 | super::BlockBuild::NextKey(_) => unimplemented!(), // Not needed for this test. 68 | super::BlockBuild::PrefixKeys(prefix) => { 69 | let p = prefix.prefix().as_ref().to_owned(); 70 | let list = genesis_storage 71 | .iter() 72 | .filter(move |(k, _)| k.starts_with(&p)) 73 | .map(|(k, _)| k); 74 | builder = prefix.inject_keys_ordered(list); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/chain.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Data structures describing a chain of blocks. 19 | //! 20 | //! A chain of blocks is composed of two parts: 21 | //! 22 | //! - A list of finalized blocks. Finalized blocks are blocks that are forever part of the chain 23 | //! and can never be reverted. 24 | //! - A tree of non-finalized blocks built on top of the last finalized block. 25 | //! 26 | //! When a block first appears it is verified and, on success, added to the tree of 27 | //! non-finalized blocks. Later, this block might get finalized. When a block is finalized, all 28 | //! the blocks that are not one of its ancestors or descendants is entirely discarded. 29 | //! 30 | //! Example chain: 31 | //! 32 | //! ```ignore 33 | //! +-> #5 34 | //! | 35 | //! +-> #4 +-> #5 36 | //! | 37 | //! #0 +> #1 +> #2 +> #3 +-> #4 +-> #5 +> #6 38 | //! ``` 39 | //! 40 | //! In this example, #3 is the latest finalized block. Before and including #3, the chain is 41 | //! always a simple list. After #3, the chain becomes a tree. 42 | //! 43 | //! > **Note**: This example is exaggerated, and in most situations the non-finalized blocks also 44 | //! > form a simple list with a few extra individual leaves. 45 | //! 46 | //! Amongst the non-finalized blocks, one block is chosen as the *best* block. When authoring 47 | //! blocks, the *best block* is the one upon new blocks will be built upon. When finalizing 48 | //! blocks, the *best block* is the one that will be voted for finalization. If there isn't any 49 | //! non-finalized block, the latest finalized block is also the best block. 50 | 51 | pub mod async_tree; 52 | pub mod blocks_tree; 53 | pub mod chain_information; 54 | pub mod fork_tree; 55 | -------------------------------------------------------------------------------- /src/chain/blocks_tree/best_block.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Extension module containing the best block determination. 19 | 20 | use super::{fork_tree, header, Block}; 21 | use core::{cmp::Ordering, iter}; 22 | 23 | /// Accepts as parameter a container of blocks and indices within this container. 24 | /// 25 | /// Returns `Ordering::Greater` if `maybe_new_best` on top of `maybe_new_best_parent` is a better 26 | /// block compared to `old_best`. 27 | /// 28 | /// `maybe_new_best_parent` is to be `None` if the parent is the finalized block that is the 29 | /// parent of all the leaves of the tree. 30 | /// 31 | /// The implementation assumes that all blocks of the chain use the same consensus algorithm. No 32 | /// output is guaranteed if this is not the case. 33 | pub(super) fn is_better_block( 34 | blocks: &fork_tree::ForkTree>, 35 | old_best: fork_tree::NodeIndex, 36 | maybe_new_best_parent: Option, 37 | maybe_new_best: header::HeaderRef, 38 | ) -> Ordering { 39 | debug_assert!( 40 | maybe_new_best_parent.map_or(true, |p_idx| blocks.get(p_idx).unwrap().hash 41 | == *maybe_new_best.parent_hash) 42 | ); 43 | 44 | // A descendant is always preferred to its ancestor. 45 | if maybe_new_best_parent.map_or(false, |p_idx| blocks.is_ancestor(old_best, p_idx)) { 46 | return Ordering::Greater; 47 | }; 48 | 49 | // In order to determine whether the new block is our new best: 50 | // 51 | // - Find the common ancestor between the current best and the new block's parent. 52 | // - Count the number of Babe primary slot claims between the common ancestor and the current 53 | // best. 54 | // - Count the number of Babe primary slot claims between the common ancestor and the new 55 | // block's parent. Add one if the new block has a Babe primary slot claim. 56 | // - If the number for the new block is strictly superior, then the new block is our new best. 57 | // 58 | // For algorithms other than Babe, all blocks simply count as one, such that the longest 59 | // chain is the preferred one. 60 | // 61 | // The code below assumes that all blocks use the same consensus algorithm. It is not 62 | // meaningful to compare the score of an Aura chain and the score of a Babe chain, for 63 | // example. 64 | let (ascend, descend) = if let Some(maybe_new_best_parent) = maybe_new_best_parent { 65 | let (asc, desc) = blocks.ascend_and_descend(old_best, maybe_new_best_parent); 66 | (either::Left(asc), either::Left(desc)) 67 | } else { 68 | ( 69 | either::Right(blocks.node_to_root_path(old_best)), 70 | either::Right(iter::empty()), 71 | ) 72 | }; 73 | 74 | let curr_best_chain_score: usize = ascend 75 | .map(|i| block_score(&blocks.get(i).unwrap().header)) 76 | .sum(); 77 | let candidate_score = block_score(maybe_new_best); 78 | let candidate_chain_score: usize = descend 79 | .map(|i| block_score(&blocks.get(i).unwrap().header)) 80 | .sum(); 81 | (candidate_chain_score + candidate_score).cmp(&curr_best_chain_score) 82 | } 83 | 84 | fn block_score<'a>(header: impl Into>) -> usize { 85 | let header = header.into(); 86 | if let Some(pr) = header.digest.babe_pre_runtime() { 87 | if pr.is_primary() { 88 | 1 89 | } else { 90 | 0 91 | } 92 | } else { 93 | 1 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/database.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Persistent data storage. 19 | //! 20 | //! This module contains sub-modules that provide different means of storing data in a 21 | //! persistent way. 22 | 23 | pub mod finalized_serialize; 24 | pub mod full_sqlite; 25 | -------------------------------------------------------------------------------- /src/database/finalized_serialize.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Serializing/deserializing a [`chain_information::ChainInformation`]. 19 | //! 20 | //! This module contains the [`encode_chain_storage`] and [`decode_chain`] functions that can turn 21 | //! a [`chain_information::ChainInformation`] into a string and back, with optionally the state of 22 | //! the finalized block. 23 | //! With no finalized block storage, the string is expected to be in the order of magnitude of a 24 | //! few dozens kilobytes. 25 | //! 26 | //! The string format designed to be stable even if the structure of 27 | //! [`chain_information::ChainInformation`] is later modified. 28 | //! 29 | //! This feature is expected to be used for example by light clients in order to easily (but 30 | //! inefficiently) store the state of the finalized chain somewhere and later reload it. 31 | 32 | use crate::chain::chain_information; 33 | 34 | use alloc::{string::String, vec::Vec}; 35 | use core::iter; 36 | use hashbrown::HashMap; 37 | 38 | mod defs; 39 | 40 | /// Serializes the given chain information as a JSON string. 41 | /// 42 | /// This is a shortcut for [`encode_chain_storage`] with no `finalized_storage`. 43 | pub fn encode_chain<'a>( 44 | information: impl Into>, 45 | block_number_bytes: usize, 46 | ) -> String { 47 | encode_chain_storage( 48 | information, 49 | block_number_bytes, 50 | None::, Vec)>>, 51 | ) 52 | } 53 | 54 | /// Serializes the given chain information and finalized block storage as a string. 55 | pub fn encode_chain_storage<'a>( 56 | information: impl Into>, 57 | block_number_bytes: usize, 58 | finalized_storage: Option, impl AsRef<[u8]>)>>, 59 | ) -> String { 60 | let information = information.into(); 61 | 62 | let decoded = defs::SerializedChainInformation::V1(defs::SerializedChainInformationV1::new( 63 | information.as_ref(), 64 | block_number_bytes, 65 | finalized_storage, 66 | )); 67 | 68 | serde_json::to_string(&decoded).unwrap() 69 | } 70 | 71 | /// Deserializes the information about the chain. 72 | /// 73 | /// This is the invert operation of [`encode_chain_storage`]. 74 | pub fn decode_chain( 75 | encoded: &str, 76 | block_number_bytes: usize, 77 | ) -> Result< 78 | ( 79 | chain_information::ValidChainInformation, 80 | Option, Vec, fnv::FnvBuildHasher>>, 81 | ), 82 | CorruptedError, 83 | > { 84 | let encoded: defs::SerializedChainInformation = 85 | serde_json::from_str(encoded).map_err(|e| CorruptedError(CorruptedErrorInner::Serde(e)))?; 86 | 87 | let (chain_info, storage) = encoded 88 | .decode(block_number_bytes) 89 | .map_err(|err| CorruptedError(CorruptedErrorInner::Deserialize(err)))?; 90 | 91 | let chain_info = chain_information::ValidChainInformation::try_from(chain_info) 92 | .map_err(CorruptedErrorInner::InvalidChain) 93 | .map_err(CorruptedError)?; 94 | 95 | Ok((chain_info, storage)) 96 | } 97 | 98 | /// Opaque error indicating a corruption in the data stored in the local storage. 99 | #[derive(Debug, derive_more::Display)] 100 | #[display(fmt = "{}", _0)] 101 | pub struct CorruptedError(CorruptedErrorInner); 102 | 103 | #[derive(Debug, derive_more::Display)] 104 | enum CorruptedErrorInner { 105 | #[display(fmt = "{}", _0)] 106 | Serde(serde_json::Error), 107 | #[display(fmt = "{}", _0)] 108 | Deserialize(defs::DeserializeError), 109 | #[display(fmt = "Invalid chain information: {}", _0)] 110 | InvalidChain(chain_information::ValidityError), 111 | } 112 | -------------------------------------------------------------------------------- /src/executor.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! WebAssembly runtime code execution. 19 | //! 20 | //! WebAssembly (often abbreviated *Wasm*) plays a big role in Substrate/Polkadot. The storage of 21 | //! each block in the chain has a special key named `:code` which contains the WebAssembly code 22 | //! of what we call *the runtime*. 23 | //! 24 | //! The runtime is a program (in WebAssembly) that decides, amongst other things, whether 25 | //! transactions are valid and how to apply them on the storage, and whether blocks themselves are 26 | //! valid. 27 | //! 28 | //! This module contains everything necessary to execute runtime code. The highest-level 29 | //! sub-module is [`runtime_host`]. 30 | 31 | mod allocator; // TODO: make public after refactoring 32 | pub mod host; 33 | pub mod read_only_runtime_host; 34 | pub mod runtime_host; 35 | pub mod storage_diff; 36 | pub mod vm; 37 | 38 | pub use host::{CoreVersion, CoreVersionError, CoreVersionRef}; 39 | 40 | /// Default number of heap pages if the storage doesn't specify otherwise. 41 | /// 42 | /// # Context 43 | /// 44 | /// In order to initialize a [`host::HostVmPrototype`], one needs to pass a certain number of 45 | /// heap pages that are available to the runtime. 46 | /// 47 | /// This number is normally found in the storage, at the key `:heappages`. But if it is not 48 | /// specified, then the value of this constant must be used. 49 | pub const DEFAULT_HEAP_PAGES: vm::HeapPages = vm::HeapPages::new(2048); 50 | 51 | /// Converts a value of the key `:heappages` found in the storage to an actual number of heap 52 | /// pages. 53 | pub fn storage_heap_pages_to_value( 54 | storage_value: Option<&[u8]>, 55 | ) -> Result { 56 | if let Some(storage_value) = storage_value { 57 | let bytes = 58 | <[u8; 8]>::try_from(storage_value).map_err(|_| InvalidHeapPagesError::WrongLen)?; 59 | let num = u64::from_le_bytes(bytes); 60 | let num = u32::try_from(num).map_err(|_| InvalidHeapPagesError::TooLarge)?; 61 | Ok(vm::HeapPages::from(num)) 62 | } else { 63 | Ok(DEFAULT_HEAP_PAGES) 64 | } 65 | } 66 | 67 | /// Error potentially returned by [`storage_heap_pages_to_value`]. 68 | #[derive(Debug, derive_more::Display, Clone)] 69 | pub enum InvalidHeapPagesError { 70 | /// Storage value has the wrong length. 71 | WrongLen, 72 | /// Number of heap pages is too large. 73 | TooLarge, 74 | } 75 | -------------------------------------------------------------------------------- /src/executor/host/zstd.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use alloc::{borrow::Cow, vec::Vec}; 19 | 20 | mod tests; 21 | 22 | /// A runtime blob beginning with this prefix should first be decompressed with `zstandard` 23 | /// compression. 24 | /// 25 | /// This differs from the Wasm magic bytes, so real Wasm blobs will not have this prefix. 26 | pub(super) const ZSTD_PREFIX: [u8; 8] = [82, 188, 83, 118, 70, 219, 142, 5]; 27 | 28 | /// If the given blob starts with [`ZSTD_PREFIX`], decompresses it. Otherwise, passes it through. 29 | /// 30 | /// The output data shall not be larger than `max_allowed`, to avoid potential zip bombs. 31 | pub(super) fn zstd_decode_if_necessary( 32 | data: &[u8], 33 | max_allowed: usize, 34 | ) -> Result, Error> { 35 | if data.starts_with(&ZSTD_PREFIX) { 36 | Ok(Cow::Owned(zstd_decode( 37 | &data[ZSTD_PREFIX.len()..], 38 | max_allowed, 39 | )?)) 40 | } else if data.len() > max_allowed { 41 | Err(Error::TooLarge) 42 | } else { 43 | Ok(Cow::Borrowed(data)) 44 | } 45 | } 46 | 47 | /// Decompresses the given blob of zstd-compressed data. 48 | /// 49 | /// The output data shall not be larger than `max_allowed`, to avoid potential zip bombs. 50 | fn zstd_decode(mut data: &[u8], max_allowed: usize) -> Result, Error> { 51 | let mut decoder = ruzstd::frame_decoder::FrameDecoder::new(); 52 | decoder.init(&mut data).map_err(|_| Error::InvalidZstd)?; 53 | 54 | match decoder.decode_blocks( 55 | &mut data, 56 | ruzstd::frame_decoder::BlockDecodingStrategy::UptoBytes(max_allowed), 57 | ) { 58 | Ok(true) => {} 59 | Ok(false) => return Err(Error::TooLarge), 60 | Err(_) => return Err(Error::InvalidZstd), 61 | } 62 | debug_assert!(decoder.is_finished()); 63 | 64 | // When the decoding is finished, `Some` is always guaranteed to be returned. 65 | let out_buf = decoder.collect().unwrap(); 66 | debug_assert!(out_buf.len() <= max_allowed); 67 | Ok(out_buf) 68 | } 69 | 70 | /// Error possibly returned when decoding a zstd-compressed Wasm blob. 71 | #[derive(Debug, derive_more::Display, Clone)] 72 | pub enum Error { 73 | /// The data is zstandard-compressed, but the data is in an invalid format. 74 | InvalidZstd, 75 | /// The size of the code exceeds the maximum allowed length. 76 | TooLarge, 77 | } 78 | -------------------------------------------------------------------------------- /src/executor/host/zstd/example-runtime.wasm.zstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/smoldot/938055a638ec201c022f680c8e8cbd0349e70ed1/src/executor/host/zstd/example-runtime.wasm.zstd -------------------------------------------------------------------------------- /src/executor/host/zstd/polkadot-runtime-v9160.wasm.zstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/smoldot/938055a638ec201c022f680c8e8cbd0349e70ed1/src/executor/host/zstd/polkadot-runtime-v9160.wasm.zstd -------------------------------------------------------------------------------- /src/executor/host/zstd/tests.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![cfg(test)] 19 | 20 | #[test] 21 | fn basic_works() { 22 | // Note that this example file doesn't have the zstd header. 23 | super::zstd_decode( 24 | &include_bytes!("./example-runtime.wasm.zstd")[..], 25 | 10 * 1024 * 1024, 26 | ) 27 | .unwrap(); 28 | } 29 | 30 | #[test] 31 | fn limit_reached() { 32 | // Note that this example file doesn't have the zstd header. 33 | assert!(matches!( 34 | super::zstd_decode( 35 | &include_bytes!("./example-runtime.wasm.zstd")[..], 36 | 16 * 1024 37 | ), 38 | Err(super::Error::TooLarge) 39 | )); 40 | } 41 | 42 | #[test] 43 | fn invalid_data() { 44 | assert!(matches!( 45 | super::zstd_decode(&(0..2048).map(|_| 0xff).collect::>(), 1024 * 1024), 46 | Err(super::Error::InvalidZstd) 47 | )); 48 | } 49 | 50 | #[test] 51 | fn basic_works_with_header() { 52 | super::zstd_decode_if_necessary( 53 | &include_bytes!("./polkadot-runtime-v9160.wasm.zstd")[..], 54 | 10 * 1024 * 1024, 55 | ) 56 | .unwrap(); 57 | } 58 | -------------------------------------------------------------------------------- /src/executor/vm/test-polkadot-runtime-v9160.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/smoldot/938055a638ec201c022f680c8e8cbd0349e70ed1/src/executor/vm/test-polkadot-runtime-v9160.wasm -------------------------------------------------------------------------------- /src/executor/vm/tests.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![cfg(test)] 19 | 20 | #[test] 21 | fn is_send() { 22 | // Makes sure that the virtual machine types implement `Send`. 23 | fn test() {} 24 | test::(); 25 | test::(); 26 | } 27 | 28 | #[test] 29 | fn basic_seems_to_work() { 30 | fn test(exec_hint: super::ExecHint) { 31 | let module = super::Module::new( 32 | &include_bytes!("./test-polkadot-runtime-v9160.wasm")[..], 33 | exec_hint, 34 | ) 35 | .unwrap(); 36 | 37 | let prototype = super::VirtualMachinePrototype::new(&module, |_, _, _| Ok(0)).unwrap(); 38 | 39 | // Note that this test doesn't test much, as anything elaborate would require implementing 40 | // the Substrate/Polkadot allocator. 41 | 42 | let mut vm = prototype 43 | .start( 44 | super::HeapPages::new(1024), 45 | "Core_version", 46 | &[super::WasmValue::I32(0), super::WasmValue::I32(0)], 47 | ) 48 | .unwrap(); 49 | 50 | loop { 51 | match vm.run(None) { 52 | Ok(super::ExecOutcome::Finished { 53 | return_value: Ok(_), 54 | }) => break, 55 | Ok(super::ExecOutcome::Finished { 56 | return_value: Err(_), 57 | }) => panic!(), 58 | Ok(super::ExecOutcome::Interrupted { id: 0, .. }) => break, 59 | Ok(super::ExecOutcome::Interrupted { .. }) => panic!(), 60 | Err(_) => panic!(), 61 | } 62 | } 63 | } 64 | 65 | test(super::ExecHint::ForceWasmi); 66 | if let Some(exec_hint) = super::ExecHint::force_wasmtime_if_available() { 67 | test(exec_hint); 68 | } 69 | } 70 | 71 | #[test] 72 | fn out_of_memory_access() { 73 | let input = [ 74 | 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x0b, 0x06, 75 | 0x01, 0x00, 0x41, 0x03, 0x0b, 0x00, 76 | ]; 77 | 78 | if let Some(exec_hint) = super::ExecHint::force_wasmtime_if_available() { 79 | let module1 = super::Module::new(input, exec_hint).unwrap(); 80 | assert!(super::VirtualMachinePrototype::new(&module1, |_, _, _| Ok(0)).is_err()); 81 | } 82 | 83 | let module2 = super::Module::new(input, super::ExecHint::ForceWasmi).unwrap(); 84 | assert!(super::VirtualMachinePrototype::new(&module2, |_, _, _| Ok(0)).is_err()); 85 | } 86 | 87 | #[test] 88 | fn has_start_function() { 89 | let input = [ 90 | 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x02, 91 | 0x09, 0x01, 0x01, 0x71, 0x03, 0x69, 0x6d, 0x70, 0x00, 0x00, 0x08, 0x01, 0x00, 92 | ]; 93 | 94 | if let Some(exec_hint) = super::ExecHint::force_wasmtime_if_available() { 95 | let module1 = super::Module::new(input, exec_hint).unwrap(); 96 | assert!(super::VirtualMachinePrototype::new(&module1, |_, _, _| Ok(0)).is_err()); 97 | } 98 | 99 | let module2 = super::Module::new(input, super::ExecHint::ForceWasmi).unwrap(); 100 | assert!(super::VirtualMachinePrototype::new(&module2, |_, _, _| Ok(0)).is_err()); 101 | } 102 | 103 | #[test] 104 | fn unsupported_type() { 105 | let input = [ 106 | 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7b, 107 | 0x02, 0x0d, 0x01, 0x04, 0x74, 0x65, 0x73, 0x74, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x00, 0x00, 108 | ]; 109 | 110 | if let Some(exec_hint) = super::ExecHint::force_wasmtime_if_available() { 111 | if let Ok(module1) = super::Module::new(input, exec_hint) { 112 | assert!(super::VirtualMachinePrototype::new(&module1, |_, _, _| Ok(0)).is_err()); 113 | } 114 | } 115 | 116 | if let Ok(module2) = super::Module::new(input, super::ExecHint::ForceWasmi) { 117 | assert!(super::VirtualMachinePrototype::new(&module2, |_, _, _| Ok(0)).is_err()); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/finality.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Finality consists is declaring a block as irreversible. It is now forever part of the chain. 19 | 20 | pub mod grandpa; 21 | pub mod justification; 22 | -------------------------------------------------------------------------------- /src/finality/grandpa.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | pub mod commit; 19 | pub mod warp_sync; 20 | -------------------------------------------------------------------------------- /src/finality/grandpa/commit.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! GrandPa commits contain a proof of the finality of a block. 19 | //! 20 | //! In order to finalize a block, GrandPa authorities must emit votes, also called pre-commits. 21 | //! We consider a block finalized when more than two thirds of the authorities have voted for that 22 | //! block (or one of its descendants) to be finalized. 23 | //! 24 | //! A commit contains all the votes that have been used to prove that a certain block has been 25 | //! finalized. 26 | //! 27 | //! Contrary to justifications, commits don't include the block headers of the forks that have 28 | //! been voted on. It is expected for the node to be aware of these headers through a separate 29 | //! mechanism. 30 | //! 31 | //! When a commit is received from a third party, it must first be verified. See the [`verify`] 32 | //! module. 33 | 34 | pub mod decode; 35 | pub mod verify; 36 | -------------------------------------------------------------------------------- /src/finality/justification.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Justifications contain a proof of the finality of a block. 19 | //! 20 | //! In order to finalize a block, GrandPa authorities must emit votes, also called pre-commits. 21 | //! We consider a block finalized when more than two thirds of the authorities have voted for that 22 | //! block (or one of its descendants) to be finalized. 23 | //! 24 | //! A justification contains all the votes that have been used to prove that a certain block has 25 | //! been finalized. Its purpose is to later be sent to a third party in order to prove this 26 | //! finality. 27 | //! 28 | //! When a justification is received from a third party, it must first be verified. See the 29 | //! [`verify`] module. 30 | 31 | pub mod decode; 32 | pub mod verify; 33 | -------------------------------------------------------------------------------- /src/header/tests-header-kusama-7472481: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/smoldot/938055a638ec201c022f680c8e8cbd0349e70ed1/src/header/tests-header-kusama-7472481 -------------------------------------------------------------------------------- /src/header/tests-header-polkadot-512271: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/smoldot/938055a638ec201c022f680c8e8cbd0349e70ed1/src/header/tests-header-polkadot-512271 -------------------------------------------------------------------------------- /src/header/tests.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![cfg(test)] 19 | 20 | #[test] 21 | fn decode_rococo() { 22 | // Rococo block taken 2021-04-08 around 11:00 UTC. 23 | super::decode( 24 | &[ 25 | 5, 35, 55, 218, 117, 209, 29, 117, 103, 130, 55, 39, 55, 132, 95, 54, 138, 185, 89, 79, 26 | 123, 161, 124, 51, 67, 40, 71, 126, 0, 210, 240, 78, 57, 177, 102, 97, 175, 183, 124, 27 | 206, 195, 77, 217, 117, 83, 14, 134, 50, 246, 163, 138, 196, 199, 78, 108, 145, 187, 28 | 240, 123, 5, 18, 219, 158, 44, 174, 132, 41, 70, 121, 181, 160, 189, 104, 253, 173, 29 | 135, 222, 15, 45, 68, 248, 23, 46, 6, 140, 247, 18, 52, 37, 9, 32, 38, 102, 12, 190, 8, 30 | 212, 237, 12, 6, 66, 65, 66, 69, 181, 1, 1, 0, 0, 0, 0, 253, 121, 18, 16, 0, 0, 0, 0, 31 | 182, 14, 80, 77, 46, 39, 209, 60, 81, 14, 141, 206, 160, 50, 106, 233, 35, 123, 4, 185, 32 | 66, 182, 193, 156, 19, 45, 137, 155, 123, 186, 11, 120, 251, 123, 81, 117, 113, 108, 33 | 169, 115, 142, 208, 243, 50, 102, 4, 117, 254, 247, 226, 199, 113, 132, 25, 141, 90, 34 | 247, 19, 211, 5, 152, 96, 121, 6, 40, 217, 92, 0, 33, 38, 199, 73, 36, 129, 161, 159, 35 | 184, 208, 215, 110, 150, 127, 221, 158, 50, 102, 118, 40, 146, 24, 8, 98, 7, 56, 144, 36 | 0, 4, 66, 69, 69, 70, 132, 3, 39, 11, 33, 224, 56, 100, 17, 18, 118, 159, 167, 103, 10, 37 | 86, 125, 222, 20, 189, 120, 236, 48, 202, 89, 180, 71, 31, 56, 185, 23, 33, 23, 87, 5, 38 | 66, 65, 66, 69, 1, 1, 180, 253, 231, 90, 196, 206, 208, 183, 14, 97, 124, 243, 43, 160, 39 | 133, 94, 19, 162, 126, 19, 7, 15, 222, 73, 114, 113, 104, 78, 24, 52, 113, 47, 39, 154, 40 | 108, 148, 28, 146, 180, 232, 199, 20, 52, 170, 93, 214, 0, 109, 168, 175, 162, 91, 234, 41 | 195, 228, 139, 236, 170, 251, 200, 178, 123, 26, 130, 42 | ], 43 | 4, 44 | ) 45 | .unwrap(); 46 | } 47 | 48 | #[test] 49 | fn decode_polkadot() { 50 | // Polkadot block #512271. 51 | // Has a GrandPa scheduled change. 52 | super::decode(include_bytes!("./tests-header-polkadot-512271"), 4).unwrap(); 53 | } 54 | 55 | #[test] 56 | fn decode_reencode_kusama_7472481() { 57 | // Kusama block #7472481. 58 | // Make sure that it encodes back to what is was decoded. 59 | let expected = include_bytes!("./tests-header-kusama-7472481"); 60 | let decoded = super::decode(expected, 4).unwrap(); 61 | assert_eq!(decoded.scale_encoding_vec(4), expected); 62 | } 63 | 64 | #[test] 65 | fn regression_2336() { 66 | // Regression test for https://github.com/paritytech/smoldot/issues/2336 67 | let _ = super::decode( 68 | &[ 69 | 231, 0, 0, 255, 255, 255, 255, 255, 255, 255, 229, 255, 107, 0, 2, 0, 0, 0, 0, 0, 0, 0, 70 | 0, 0, 0, 32, 0, 0, 0, 192, 127, 64, 0, 0, 0, 0, 0, 192, 33, 255, 255, 255, 255, 255, 71 | 255, 0, 52, 148, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 114, 98, 0, 98, 255, 72 | 255, 255, 255, 0, 1, 0, 4, 4, 5, 4, 4, 4, 4, 4, 1, 0, 1, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 73 | 53, 4, 4, 4, 4, 53, 4, 4, 97, 117, 114, 97, 1, 1, 1, 19, 19, 19, 19, 19, 19, 19, 19, 74 | 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 75 | 19, 19, 19, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 249, 254, 1, 1, 1, 3, 255, 254, 254, 1, 76 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 77 | ], 78 | 4, 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /src/identity.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Substrate-based chains make frequent use of asynchronous cryptography. The identity of a user, 19 | //! be it an account address, a validator, or else, consists of a public key (also sometimes known 20 | //! as a verification key). A user can prove their identity by generating a signature using the 21 | //! private key (also sometimes known as a signing key) corresponding to that public key. 22 | //! 23 | //! A private key and a public key consist of 32 bytes of data. This poses a problem when it comes 24 | //! to end user experience. It is common for (human) users to have to manually copy a private or 25 | //! public key (using a pencil or their keyboard), and making a mistake could have catastrophic 26 | //! outcomes. It would be unwise to display keys and ask users to input keys as, for example, 27 | //! hexadecimal, because it is easy to make a copying mistake. 28 | //! 29 | //! For this reason, Substrate-defines two human-readable formats: 30 | //! 31 | //! - A human-readable format for public keys. 32 | //! - A human-readable format for private keys, named SS58. 33 | //! 34 | //! These formats unfortunately do not mention which asynchronous cryptographic algorithm (e.g. 35 | //! Ed25519 or Sr25519) is used for the public and private keys. This must be deduced from the 36 | //! context. 37 | //! 38 | //! ## Public keys (SS58) 39 | //! 40 | //! Examples: 41 | //! 42 | //! - `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY` 43 | //! - `12bzRJfh7arnnfPPUZHeJUaE62QLEwhK48QnH9LXeK2m1iZU` 44 | //! 45 | //! The format for public keys consists in: 46 | //! 47 | //! > `base58(concat(prefix, 32-bytes-public-key, checksum))` 48 | //! 49 | //! The prefix, also known as network identifier, also known as an address type is one or more 50 | //! bytes identifying which blockchain network the address corresponds to. This is used for 51 | //! UX-related purposes in order to prevent end users from using an address on a different 52 | //! blockchain than the one the address was generated for. 53 | //! 54 | //! A registry of existing network identifiers can be found 55 | //! [here](https://wiki.polkadot.network/docs/build-ss58-registry). 56 | //! 57 | //! The checksum is verified when the human-readable format is turned into a public key. Its 58 | //! presence of a checksum guarantees that simple copying mistakes will be caught. 59 | //! 60 | //! ## Private keys 61 | //! 62 | //! Examples: 63 | //! 64 | //! - `cry opinion donkey dolphin tobacco version pilot sponsor canal page vote main` 65 | //! - `canoe gravity deputy pottery glass cousin era cube double rather clutch crazy//Foo//Bar` 66 | //! - `//Alice` 67 | //! 68 | //! The human-readable format for private keys is also called a secret string. 69 | //! 70 | //! The format for private keys consists in: 71 | //! 72 | //! - An optional [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) seed 73 | //! phrase. If it is missing, then the [`seed_phrase::DEFAULT_SEED_PHRASE`] is automatically used 74 | //! instead. 75 | //! 76 | //! - An optional derivation path. When using one `/`, this is called a *soft* derivation. When 77 | //! using two `/`, this is called a *hard* derivation. Soft derivations are reversible and can 78 | //! only be performed on public keys. Hard derivations are not reversible and can only be 79 | //! performed on private keys. 80 | //! 81 | //! - An optional password. The private key format can contain a `///password` suffix, in which 82 | //! case `password` will be used when decoding the BIP39 phrase. See the BIP39 specification. If 83 | //! no password is provided, then the empty string (`""`) is used. 84 | //! 85 | 86 | pub mod keystore; 87 | pub mod seed_phrase; 88 | 89 | // TODO: implement ss58 90 | -------------------------------------------------------------------------------- /src/json_rpc.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! JSON-RPC servers. Trusted access to the blockchain. 19 | //! 20 | //! # Context 21 | //! 22 | //! The traditional model used in the blockchain ecosystem is this one: 23 | //! 24 | //! ```notrust 25 | //! 26 | //! +---------------+ +--------+ +----------------------+ 27 | //! | | JSON-RPC | | libp2p | | 28 | //! | Application | <--------> | Node | <------> | Blockchain network | 29 | //! | (e.g. a UI) | | | | | 30 | //! +---------------+ +--------+ +----------------------+ 31 | //! 32 | //! ``` 33 | //! 34 | //! The node is connected to the blockchain network using the libp2p protocol, and one or more 35 | //! applications are connected to the node using the JSON-RPC protocol. 36 | //! 37 | //! > **Note**: An example application that can be put on top of a node is 38 | //! > [PolkadotJS](https://polkadot.js.org/). 39 | //! 40 | //! Contrary to the traffic with the blockchain network, the communication between the JSON-RPC 41 | //! client (i.e. the application) and the JSON-RPC server (i.e. the node) is not trustless. In 42 | //! other words, the client has no way to check the accuracy of what the server sends, and 43 | //! therefore trusts that the server isn't malicious. The role of the node is precisely to turn 44 | //! untrusted data coming from the blockchain network into trusted information. 45 | //! 46 | //! The trust goes the other way around: some of the JSON-RPC requests that the client can perform 47 | //! can modify the configuration of the node, while some others are very CPU or I/O-intensive. As 48 | //! such, the server also trusts the client to not be malicious. 49 | //! 50 | //! # JSON-RPC protocol 51 | //! 52 | //! The protocol used is the JSON-RPC v2.0 protocol described [here](https://www.jsonrpc.org/). 53 | //! 54 | //! As specified in the JSON-RPC v2.0 protocol, both sides of the TCP/IP connection may send 55 | //! requests and/or notifications to the other side. 56 | //! 57 | //! In practice, the listening side of the connection should be capable of serving the various 58 | //! JSON-RPC functions described in the [`methods`] submodule. As part of the logic of these 59 | //! functions, the listening side might send notifications to the initiator of the connection. 60 | //! 61 | 62 | // TODO: write docs about usage ^ 63 | 64 | pub mod methods; 65 | pub mod parse; 66 | pub mod payment_info; 67 | pub mod requests_subscriptions; 68 | pub mod websocket_server; 69 | -------------------------------------------------------------------------------- /src/json_rpc/websocket_server/tests.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use super::{Config, Event, WsServer}; 19 | 20 | use futures::io::{BufReader, BufWriter}; 21 | 22 | #[test] 23 | fn basic_works() { 24 | async_std::task::block_on(async move { 25 | let mut server: WsServer = WsServer::new(Config { 26 | bind_address: "127.0.0.1:0".parse().unwrap(), 27 | max_frame_size: 1024 * 1024, 28 | send_buffer_len: 32, 29 | capacity: 32, 30 | }) 31 | .await 32 | .unwrap(); 33 | 34 | let server_addr = server.local_addr().unwrap(); 35 | 36 | let client_task = async_std::task::spawn(async move { 37 | let (mut sender, mut receiver) = { 38 | let mut client = { 39 | let socket = async_std::net::TcpStream::connect(server_addr) 40 | .await 41 | .unwrap(); 42 | let io = BufReader::new(BufWriter::new(socket)); 43 | soketto::handshake::Client::new(io, "example.com", "/") 44 | }; 45 | 46 | assert!(matches!( 47 | client.handshake().await.unwrap(), 48 | soketto::handshake::ServerResponse::Accepted { .. } 49 | )); 50 | client.into_builder().finish() 51 | }; 52 | 53 | sender.send_text("hello world!").await.unwrap(); 54 | sender.flush().await.unwrap(); 55 | 56 | let mut message = Vec::new(); 57 | match receiver.receive_data(&mut message).await { 58 | Ok(soketto::Data::Text(n)) => { 59 | assert_eq!(&message[..n], b"hello back"); 60 | } 61 | _ => panic!(), 62 | } 63 | }); 64 | 65 | let id = match server.next_event().await { 66 | Event::ConnectionOpen { .. } => server.accept(12), 67 | _ => panic!(), 68 | }; 69 | 70 | match server.next_event().await { 71 | Event::TextFrame { 72 | connection_id, 73 | user_data, 74 | message, 75 | } => { 76 | assert_eq!(connection_id, id); 77 | assert_eq!(*user_data, 12); 78 | *user_data += 1; 79 | assert_eq!(message, "hello world!"); 80 | } 81 | _ => panic!(), 82 | }; 83 | 84 | server.queue_send(id, "hello back".to_owned()); 85 | 86 | match server.next_event().await { 87 | Event::ConnectionError { 88 | connection_id, 89 | user_data, 90 | } => { 91 | assert_eq!(connection_id, id); 92 | assert_eq!(user_data, 13); 93 | } 94 | _ => panic!(), 95 | }; 96 | 97 | client_task.await; 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /src/libp2p.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Low-level peer-to-peer networking. 19 | //! 20 | //! The peer-to-peer networking protocol used by Substrate-based chains is called *libp2p*. Its 21 | //! specifications can be found in the repository. This module 22 | //! contains code that allows connecting to libp2p-compatible nodes. All the logic specific to 23 | //! Substrate/Polkadot isn't handled here and is instead done in the [`crate::network`] module. 24 | //! 25 | //! # Network identity 26 | //! 27 | //! In order to join the peer-to-peer network, one must first generate a *network identity*. A 28 | //! network identity is a small struct containing a cryptographic public key (typically Ed25519, 29 | //! but other algorithms might be used) or the hash of a cryptographic public key. A network 30 | //! identity is represented with the [`PeerId`] struct. 31 | //! 32 | //! Network identities primarily have a binary encoding. When displayed for UI purposes, the 33 | //! string representation, which consists in the Base58 encoding of the binary encoding, is used. 34 | //! Example string representation: `12D3KooWR3UGwwSP5wdBMk2JXXuzXoscPSudv8hmQkzfZTBzSbeE`. 35 | //! 36 | //! In order to generate a network identity, fill a [`peer_id::PublicKey::Ed25519`] with an 37 | //! Ed25519 public key, then use [`PeerId::from_public_key`]. 38 | //! 39 | //! When establishing a connection to another member the peer-to-peer network, a Diffie-Hellman 40 | //! handshake is performed in order to ensure that the remote indeed possesses the private key 41 | //! corresponding to its network identity. 42 | //! 43 | //! See also the documentation of [`peer_id`] for more information. 44 | //! 45 | //! # The `ReadWrite` object 46 | //! 47 | //! One of the most important objects in this module is the [`read_write::ReadWrite`] struct. 48 | //! 49 | //! In order to allow for better determinism and testability, absolutely no code in this module 50 | //! directly interacts with operating-system-provided TCP sockets. Instead, this modules provides 51 | //! state machines that need to be synchronized manually with a [`read_write::ReadWrite`] through 52 | //! function calls. Once synchronized, the API user must in turn manually synchronize this 53 | //! [`read_write::ReadWrite`] with the actual state of the operating-system-provided TCP socket. 54 | //! 55 | //! The [`read_write::ReadWrite`] struct notably contains data that has been received on the 56 | //! socket but hasn't been processed yet, and data that has been queued for sending out but hasn't 57 | //! been sent yet. 58 | //! 59 | //! See also the documentation of [`read_write`] for more information. 60 | //! 61 | //! # State machine 62 | //! 63 | //! The main object of this module is the [`peers::Peers`]. It is a state machine which, in 64 | //! summary, contains: 65 | //! 66 | //! - A list of handshaking and established connections, that the API user must manually 67 | //! synchronize by calling [`collection::SingleStreamConnectionTask::read_write`], 68 | //! [`collection::SingleStreamConnectionTask::reset`], 69 | //! [`collection::MultiStreamConnectionTask::substream_read_write`], 70 | //! [`collection::MultiStreamConnectionTask::reset`], 71 | //! [`collection::MultiStreamConnectionTask::add_substream`], and/or 72 | //! [`collection::MultiStreamConnectionTask::desired_outbound_substreams`]. When 73 | //! inserting a new outgoing connection, the API user can specify which [`PeerId`] this connection 74 | //! is expected to reach. 75 | //! - A list of [`̀PeerId`]s that have been marked by the API user as desired. The [`peers::Peers`] 76 | //! is then able to provide the list of [`PeerId`]s that have been marked as desired but that no 77 | //! existing connection reaches or tries to reach. 78 | //! - A list of events that happen on the set of peer-to-peer connections, and that can be 79 | //! retrieved one by one by calling [`peers::Peers::next_event`]. 80 | //! 81 | //! It is the responsibility of the API user to grab the list of unfulfilled [`̀PeerId`]s and 82 | //! insert new connections that are expected to reach these unfulfilled [`PeerId`]s. To do so, 83 | //! one must run a certain discovery mechanism in order to find out the addresses that will 84 | //! permit to reach peers. This is out of scope of this module. 85 | //! 86 | //! It is also the responsibility of the API user to call [`peers::Peers::next_event`] in order to 87 | //! react to the activity on the various connections, and user the various other methods of the 88 | //! [`peers::Peers`] state machine, such as for example [`peers::Peers::start_request`], to 89 | //! interact with the remotes. 90 | //! 91 | //! See also the documentation of [`peers`] for more information. 92 | //! 93 | 94 | pub mod async_std_connection; 95 | pub mod collection; 96 | pub mod connection; 97 | pub mod multiaddr; 98 | pub mod multihash; 99 | pub mod peer_id; 100 | pub mod peers; 101 | pub mod read_write; 102 | pub mod websocket; 103 | 104 | pub use multiaddr::Multiaddr; 105 | pub use peer_id::PeerId; 106 | -------------------------------------------------------------------------------- /src/libp2p/async_std_connection.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![cfg(feature = "std")] 19 | #![cfg_attr(docsrs, doc(cfg(feature = "std")))] 20 | 21 | pub mod with_buffers; 22 | -------------------------------------------------------------------------------- /src/libp2p/connection.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Module containing everything related to the processing of a single libp2p connection. 19 | //! 20 | //! # Overview 21 | //! 22 | //! Libp2p connections use TCP sockets provided by the operating system. 23 | //! 24 | //! > **Note**: Explaining the TCP/IP protocol is out of scope of this documentation. See for 25 | //! > example [the Wikipedia page](https://en.wikipedia.org/wiki/Transmission_Control_Protocol) 26 | //! > for more information. 27 | //! 28 | //! Once a TCP connection has been established, the *handshake* phase starts. This handshake 29 | //! is described in details in the documentation [`single_stream_handshake`] module. 30 | //! 31 | //! The handshake consists in negotiating with the remote an encryption layer, thanks to which all 32 | //! communications are encrypted, and a multiplexing layer, thanks to which the stream of data can 33 | //! be split into a multitude of **substreams**. 34 | //! 35 | //! After the handshake is over, the connection is now in the *established* phase. See the 36 | //! documentation of the [`established`] module for more details. 37 | //! 38 | //! Once in the established phase, substreams can be opened either by the local endpoint or by the 39 | //! remote endpoint. When a substream is opened, a protocol is first negotiated in order to 40 | //! determine the purpose of this substream. Two kinds of protocols, and therefore two kinds of 41 | //! substreams, are supported: 42 | //! 43 | //! - So-called "request-response" substreams. After the protocol is negotiated, the opening side 44 | //! sends a single message containing a request, and the other endpoint sends back a single 45 | //! message containing a response. The substream is then closed. 46 | //! - So-called "notifications" substreams. After the protocol is negotiated, the opening side 47 | //! sends a single handshake message. The other endpoint must then either immediately close the 48 | //! substream to reject the handshake, or send back a handshake in order to accept it. After the 49 | //! handshake is accepted, the opening side can send an unbounded number of individual messages. 50 | //! Only the opening side is allowed to send messages on any given notifications substream. 51 | //! 52 | //! These two kinds of substreams serve as a base framework. The exact nature of these 53 | //! request-responses and of these notifications is out of scope of this module. 54 | //! 55 | //! # Usage 56 | //! 57 | //! To connect to a libp2p node, start by establishing a TCP connection with the target. This can 58 | //! be done either through an outgoing connection or connection incoming from a TCP listening 59 | //! socket. 60 | //! 61 | //! > **Note**: This module only contains *no_std*-friendly code, and creating TCP connections 62 | //! > isn't handled by it. 63 | //! 64 | //! After a TCP connection is established, use 65 | //! [`single_stream_handshake::HealthyHandshake::noise_yamux`] to initialize the state machine that 66 | //! needs to be maintained in parallel of the connection. The data sent and received over the 67 | //! socket must respectively be obtained or injected using 68 | //! [`single_stream_handshake::HealthyHandshake::read_write`]. See the [`single_stream_handshake`] 69 | //! module documentation for more details. 70 | //! 71 | //! Keep in mind that the [`single_stream_handshake`] module doesn't provide any timeout. Users 72 | //! are strongly encouraged to add one, in order to detect situations where the remote is, 73 | //! intentionally or not, unresponsive. 74 | //! 75 | //! After the handshake is successful, use [`established::ConnectionPrototype::into_connection`] 76 | //! to obtain an [`established::SingleStream`]. Similar to the handshake, use 77 | //! [`established::SingleStream::read_write`] to update the state machine. 78 | //! 79 | 80 | pub use noise::{NoiseKey, UnsignedNoiseKey}; 81 | 82 | pub mod established; 83 | pub mod multistream_select; 84 | pub mod noise; 85 | pub mod single_stream_handshake; 86 | pub mod yamux; 87 | -------------------------------------------------------------------------------- /src/libp2p/connection/single_stream_handshake/tests.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | #![cfg(test)] 19 | 20 | use super::{super::super::read_write::ReadWrite, Handshake, NoiseKey}; 21 | 22 | #[test] 23 | fn handshake_basic_works() { 24 | fn test_with_buffer_sizes(size1: usize, size2: usize) { 25 | let key1 = NoiseKey::new(&rand::random()); 26 | let key2 = NoiseKey::new(&rand::random()); 27 | 28 | let mut handshake1 = Handshake::noise_yamux(true); 29 | let mut handshake2 = Handshake::noise_yamux(false); 30 | 31 | let mut buf_1_to_2 = Vec::new(); 32 | let mut buf_2_to_1 = Vec::new(); 33 | 34 | while !matches!( 35 | (&handshake1, &handshake2), 36 | (Handshake::Success { .. }, Handshake::Success { .. }) 37 | ) { 38 | match handshake1 { 39 | Handshake::Success { .. } => {} 40 | Handshake::NoiseKeyRequired(req) => handshake1 = req.resume(&key1).into(), 41 | Handshake::Healthy(nego) => { 42 | if buf_1_to_2.is_empty() { 43 | buf_1_to_2.resize(size1, 0); 44 | let mut read_write = ReadWrite { 45 | now: 0, 46 | incoming_buffer: Some(&buf_2_to_1), 47 | outgoing_buffer: Some((&mut buf_1_to_2, &mut [])), 48 | read_bytes: 0, 49 | written_bytes: 0, 50 | wake_up_after: None, 51 | }; 52 | handshake1 = nego.read_write(&mut read_write).unwrap(); 53 | let (read_bytes, written_bytes) = 54 | (read_write.read_bytes, read_write.written_bytes); 55 | for _ in 0..read_bytes { 56 | buf_2_to_1.remove(0); 57 | } 58 | buf_1_to_2.truncate(written_bytes); 59 | } else { 60 | let mut read_write = ReadWrite { 61 | now: 0, 62 | incoming_buffer: Some(&buf_2_to_1), 63 | outgoing_buffer: Some((&mut [], &mut [])), 64 | read_bytes: 0, 65 | written_bytes: 0, 66 | wake_up_after: None, 67 | }; 68 | handshake1 = nego.read_write(&mut read_write).unwrap(); 69 | for _ in 0..read_write.read_bytes { 70 | buf_2_to_1.remove(0); 71 | } 72 | } 73 | } 74 | } 75 | 76 | match handshake2 { 77 | Handshake::Success { .. } => {} 78 | Handshake::NoiseKeyRequired(req) => handshake2 = req.resume(&key2).into(), 79 | Handshake::Healthy(nego) => { 80 | if buf_2_to_1.is_empty() { 81 | buf_2_to_1.resize(size2, 0); 82 | let mut read_write = ReadWrite { 83 | now: 0, 84 | incoming_buffer: Some(&buf_1_to_2), 85 | outgoing_buffer: Some((&mut buf_2_to_1, &mut [])), 86 | read_bytes: 0, 87 | written_bytes: 0, 88 | wake_up_after: None, 89 | }; 90 | handshake2 = nego.read_write(&mut read_write).unwrap(); 91 | let (read_bytes, written_bytes) = 92 | (read_write.read_bytes, read_write.written_bytes); 93 | for _ in 0..read_bytes { 94 | buf_1_to_2.remove(0); 95 | } 96 | buf_2_to_1.truncate(written_bytes); 97 | } else { 98 | let mut read_write = ReadWrite { 99 | now: 0, 100 | incoming_buffer: Some(&buf_1_to_2), 101 | outgoing_buffer: Some((&mut [], &mut [])), 102 | read_bytes: 0, 103 | written_bytes: 0, 104 | wake_up_after: None, 105 | }; 106 | handshake2 = nego.read_write(&mut read_write).unwrap(); 107 | for _ in 0..read_write.read_bytes { 108 | buf_1_to_2.remove(0); 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | test_with_buffer_sizes(256, 256); 117 | // TODO: not passing because Noise wants at least 19 bytes of buffer 118 | //test_with_buffer_sizes(1, 1); 119 | //test_with_buffer_sizes(1, 2048); 120 | //test_with_buffer_sizes(2048, 1); 121 | } 122 | -------------------------------------------------------------------------------- /src/libp2p/multihash.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! A multihash is a small data structure containing a code (an integer) and data. The format of 19 | //! the data depends on the code. 20 | //! 21 | //! See 22 | 23 | use alloc::vec::Vec; 24 | use core::fmt; 25 | 26 | use crate::util; 27 | 28 | /// A multihash made of a code and a slice of data. 29 | /// 30 | /// This type is a *reference* to a multihash stored somewhere else, such as in a `Vec`. You 31 | /// are supposed to store a `MultihashRef` for long term usage. Instead, store a `Vec` for 32 | /// example. The `MultihashRef` can be constructed from that `Vec` if it needs decoding. 33 | pub struct MultihashRef<'a>(u32, &'a [u8]); 34 | 35 | impl<'a> MultihashRef<'a> { 36 | /// Builds a multihash from the "identity" hash algorithm code and the provided data. 37 | /// 38 | /// Calling [`MultihashRef::data`] on the returned value will always yield back the same data 39 | /// as was passed as parameter. 40 | pub fn identity(data: &'a [u8]) -> Self { 41 | MultihashRef(0, data) 42 | } 43 | 44 | /// Returns the code stored in this multihash. 45 | pub fn hash_algorithm_code(&self) -> u32 { 46 | self.0 47 | } 48 | 49 | /// Returns the data stored in this multihash. 50 | pub fn data(&self) -> &'a [u8] { 51 | self.1 52 | } 53 | 54 | /// Checks whether `input` is a valid multihash. 55 | pub fn from_bytes(input: &'a [u8]) -> Result { 56 | match nom::combinator::all_consuming(multihash::>)(input) { 57 | Ok((_rest, multihash)) => { 58 | debug_assert!(_rest.is_empty()); 59 | Ok(multihash) 60 | } 61 | Err(_) => Err(FromBytesError::DecodeError), 62 | } 63 | } 64 | 65 | /// Checks whether `input` is a valid multihash. 66 | /// 67 | /// Contrary to [`MultihashRef::from_bytes`], doesn't return an error if the slice is too long 68 | /// but returns the remainder. 69 | pub fn from_bytes_partial(input: &'a [u8]) -> Result<(MultihashRef, &'a [u8]), FromBytesError> { 70 | match multihash::>(input) { 71 | Ok((rest, multihash)) => Ok((multihash, rest)), 72 | Err(_) => Err(FromBytesError::DecodeError), 73 | } 74 | } 75 | 76 | /// Returns an iterator to a list of buffers that, when concatenated together, form the 77 | /// binary representation of this multihash. 78 | pub fn as_bytes(&'_ self) -> impl Iterator + '_> + '_ { 79 | let code = util::leb128::encode(self.0).collect::>(); // TODO: actual length? 80 | let len = util::leb128::encode_usize(self.1.len()).collect::>(); // TODO: actual length? 81 | [either::Left(code), either::Left(len), either::Right(self.1)].into_iter() 82 | } 83 | 84 | /// Turns this multihash into a `Vec`. 85 | pub fn to_vec(&self) -> Vec { 86 | let mut out = Vec::with_capacity(7 + 7 + self.1.len()); 87 | for slice in self.as_bytes() { 88 | out.extend_from_slice(slice.as_ref()); 89 | } 90 | out 91 | } 92 | } 93 | 94 | /// Error when turning bytes into a [`MultihashRef`]. 95 | #[derive(Debug, derive_more::Display, Clone)] 96 | pub enum FromBytesError { 97 | /// The multihash is invalid. 98 | DecodeError, 99 | } 100 | 101 | impl<'a> fmt::Debug for MultihashRef<'a> { 102 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 103 | fmt::Display::fmt(self, f) 104 | } 105 | } 106 | 107 | impl<'a> fmt::Display for MultihashRef<'a> { 108 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 109 | let base58 = bs58::encode(&self.to_vec()).into_string(); 110 | write!(f, "{}", base58) 111 | } 112 | } 113 | 114 | impl<'a> TryFrom<&'a [u8]> for MultihashRef<'a> { 115 | type Error = FromBytesError; 116 | 117 | fn try_from(input: &'a [u8]) -> Result { 118 | Self::from_bytes(input) 119 | } 120 | } 121 | 122 | fn multihash<'a, E: nom::error::ParseError<&'a [u8]>>( 123 | bytes: &'a [u8], 124 | ) -> nom::IResult<&'a [u8], MultihashRef<'a>, E> { 125 | nom::combinator::map( 126 | nom::sequence::tuple(( 127 | nom::combinator::map_opt(crate::util::leb128::nom_leb128_usize, |c| { 128 | u32::try_from(c).ok() 129 | }), 130 | nom::multi::length_data(crate::util::leb128::nom_leb128_usize), 131 | )), 132 | |(code, data)| MultihashRef(code, data), 133 | )(bytes) 134 | } 135 | -------------------------------------------------------------------------------- /src/network.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | /********************************************************* 19 | ** Prototype for a rewrite of the networking code ** 20 | ** Not ready yet ** 21 | *********************************************************/ 22 | 23 | pub mod kademlia; 24 | pub mod protocol; 25 | pub mod service; 26 | -------------------------------------------------------------------------------- /src/network/kademlia.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // TODO: work in progress 19 | 20 | pub mod kbuckets; 21 | 22 | /// Data structure containing the k-buckets and the state of the current Kademlia queries. 23 | // TODO: unused 24 | pub struct Kademlia {} 25 | 26 | impl Kademlia { 27 | /// Initializes a new empty data structure with empty k-buckets. 28 | pub fn new() -> Self { 29 | Kademlia {} 30 | } 31 | } 32 | 33 | impl Default for Kademlia { 34 | fn default() -> Self { 35 | Self::new() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/network/protocol.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Substrate/Polkadot-specific protocols on top of libp2p. 19 | //! 20 | //! Libp2p provides a framework on top of which higher-level, domain-specific protocols can be 21 | //! used. This module contains the domain-specific protocols specific to Substrate and Polkadot. 22 | //! 23 | //! This module only provides the tools to encode/decode messages. 24 | 25 | // TODO: expand docs 26 | 27 | // Implementation note: each protocol goes into a different sub-module whose content is 28 | // re-exported here. 29 | 30 | mod block_announces; 31 | mod block_request; 32 | mod grandpa; 33 | mod grandpa_warp_sync; 34 | mod identify; 35 | mod kademlia; 36 | mod state_request; 37 | mod storage_call_proof; 38 | 39 | pub use self::block_announces::*; 40 | pub use self::block_request::*; 41 | pub use self::grandpa::*; 42 | pub use self::grandpa_warp_sync::*; 43 | pub use self::identify::*; 44 | pub use self::kademlia::*; 45 | pub use self::state_request::*; 46 | pub use self::storage_call_proof::*; 47 | -------------------------------------------------------------------------------- /src/network/protocol/kademlia.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::{ 19 | libp2p::{multiaddr, peer_id}, 20 | util::protobuf, 21 | }; 22 | 23 | use alloc::vec::Vec; 24 | 25 | // See https://github.com/libp2p/specs/tree/master/kad-dht#rpc-messages for the protobuf format. 26 | 27 | /// Builds a wire message to send on the Kademlia request-response protocol to ask the target to 28 | /// return the nodes closest to the parameter. 29 | // TODO: parameter type? 30 | pub fn build_find_node_request(peer_id: &[u8]) -> Vec { 31 | // The capacity is arbitrary but large enough to avoid Vec reallocations. 32 | let mut out = Vec::with_capacity(64 + peer_id.len()); 33 | for slice in protobuf::enum_tag_encode(1, 4) { 34 | out.extend_from_slice(slice.as_ref()); 35 | } 36 | for slice in protobuf::bytes_tag_encode(2, peer_id) { 37 | out.extend_from_slice(slice.as_ref()); 38 | } 39 | out 40 | } 41 | 42 | /// Decodes a response to a request built using [`build_find_node_request`]. 43 | // TODO: return a borrow of the response bytes ; we're limited by protobuf library 44 | pub fn decode_find_node_response( 45 | response_bytes: &[u8], 46 | ) -> Result)>, DecodeFindNodeResponseError> { 47 | let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>( 48 | nom::combinator::complete(protobuf::message_decode! { 49 | #[optional] response_ty = 1 => protobuf::enum_tag_decode, 50 | #[repeated(max = 1024)] peers = 8 => protobuf::message_tag_decode(protobuf::message_decode!{ 51 | #[required] peer_id = 1 => protobuf::bytes_tag_decode, 52 | #[repeated(max = 1024)] addrs = 2 => protobuf::bytes_tag_decode, 53 | }), 54 | }), 55 | ); 56 | 57 | let closer_peers = match nom::Finish::finish(parser(response_bytes)) { 58 | Ok((_, out)) if out.response_ty.unwrap_or(0) == 4 => out.peers, 59 | Ok((_, _)) => return Err(DecodeFindNodeResponseError::BadResponseTy), 60 | Err(_) => { 61 | return Err(DecodeFindNodeResponseError::ProtobufDecode( 62 | ProtobufDecodeError, 63 | )) 64 | } 65 | }; 66 | 67 | let mut result = Vec::with_capacity(closer_peers.len()); 68 | for peer in closer_peers { 69 | let peer_id = peer_id::PeerId::from_bytes(peer.peer_id.to_vec()) 70 | .map_err(|(err, _)| DecodeFindNodeResponseError::BadPeerId(err))?; 71 | 72 | let mut multiaddrs = Vec::with_capacity(peer.addrs.len()); 73 | for addr in peer.addrs { 74 | let addr = multiaddr::Multiaddr::try_from(addr.to_vec()) 75 | .map_err(DecodeFindNodeResponseError::BadMultiaddr)?; 76 | multiaddrs.push(addr); 77 | } 78 | 79 | result.push((peer_id, multiaddrs)); 80 | } 81 | 82 | Ok(result) 83 | } 84 | 85 | /// Error potentially returned by [`decode_find_node_response`]. 86 | #[derive(Debug, derive_more::Display)] 87 | pub enum DecodeFindNodeResponseError { 88 | /// Error while decoding the Protobuf encoding. 89 | #[display(fmt = "Error decoding the response: {}", _0)] 90 | ProtobufDecode(ProtobufDecodeError), 91 | /// Response isn't a response to a find node request. 92 | BadResponseTy, 93 | /// Error while parsing a [`peer_id::PeerId`] in the response. 94 | #[display(fmt = "Invalid PeerId: {}", _0)] 95 | BadPeerId(peer_id::FromBytesError), 96 | /// Error while parsing a [`multiaddr::Multiaddr`] in the response. 97 | #[display(fmt = "Invalid multiaddress: {}", _0)] 98 | BadMultiaddr(multiaddr::FromVecError), 99 | } 100 | 101 | /// Error while decoding the Protobuf encoding. 102 | #[derive(Debug, derive_more::Display)] 103 | pub struct ProtobufDecodeError; 104 | -------------------------------------------------------------------------------- /src/sync.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Syncing, short for synchronizing, consists in synchronizing the state of a local chain with 19 | //! the state of the chain contained in other machines, called *remotes* or *sources*. 20 | //! 21 | //! > **Note**: While the above summary is a bit abstract, in practice it is almost always 22 | //! > done by exchanging messages through a peer-to-peer network. 23 | //! 24 | //! Multiple strategies exist for syncing, one for each sub-module, and which one to employ 25 | //! depends on the amount of information that is desired (e.g. is it required to know the header 26 | //! and/or body of every single block, or can some blocks be skipped?) and the distance between 27 | //! the highest block of the local chain and the highest block available on the remotes. 28 | //! 29 | //! The [`all`] module represents a good combination of all syncing strategies and should be the 30 | //! default choice for most clients. 31 | //! 32 | //! # Security considerations 33 | //! 34 | //! ## Eclipse attacks 35 | //! 36 | //! While there exists various trade-offs between syncing strategies, safety is never part of 37 | //! these trade-offs. All syncing strategies are safe, in the sense that malicious remotes cannot 38 | //! corrupt the state of the local chain. 39 | //! 40 | //! It is possible, however, for a malicious remote to omit some information, such as the 41 | //! existence of a specific block. If all the remotes that are being synchronized from are 42 | //! malicious and collude to omit the same information, there is no way for the local node to 43 | //! learn this information or even to be aware that it is missing an information. This is called 44 | //! an **eclipse attack**. 45 | //! 46 | //! For this reason, it is important to ensure a large number and a good distribution of the 47 | //! sources. In the context of a peer-to-peer network where machines are picked randomly, a 48 | //! minimum threshold of around 7 peers is generally considered acceptable. Similarly, in the 49 | //! context of a peer-to-peer network, it is important to establish outgoing connections to other 50 | //! nodes and not only rely on incoming connections, as there is otherwise the possibility of a 51 | //! single actor controlling all said incoming connections. 52 | //! 53 | //! ## Malicious runtimes 54 | //! 55 | //! Synchronizing the chain requires executing a piece of WebAssembly code known as the runtime. 56 | //! Each chain has a different runtime, stored on chain, and this runtime can be modified as part 57 | //! of a the transactions in a block. 58 | //! 59 | //! There are two safety considerations concerning executing runtime code: 60 | //! 61 | //! - The execution could take a lot of time, or possibly be stuck in an infinite loop. 62 | //! - The execution could modify a lot of storage items. Smoldot needs to store every single 63 | //! storage item change, and a lot of modification could make the memory usage of smoldot explode. 64 | //! 65 | //! The Substrate model, and thus the smoldot implementation, intentionally do not do anything to 66 | //! address these two safety issues. In other words, the runtime of a chain is considered to never 67 | //! be malicious. After all, a chain whose runtime is intentionally trying to crash a client is 68 | //! borked and there is no reason to connect to it. 69 | //! 70 | 71 | pub mod all; 72 | pub mod all_forks; 73 | pub mod optimistic; 74 | pub mod para; 75 | pub mod warp_sync; 76 | -------------------------------------------------------------------------------- /src/transactions.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Transactions handling. 19 | //! 20 | //! This module contains everything related to the handling of transactions. 21 | //! 22 | //! # Overview 23 | //! 24 | //! From a thousand miles perspective, the process of including a transaction in the chain is as 25 | //! follows: 26 | //! 27 | //! - The transaction gets built, in other words the bytes that encode the transaction are 28 | //! generated. This can be done for example through a UI, through an off-chain worker, or other. A 29 | //! transaction can be either signed (i.e. have a signature attached to it) or unsigned, depending 30 | //! on the action to be performed. A balance transfer, for example, generally always requires a 31 | //! signature. 32 | //! 33 | //! - The transaction is then processed by a node, generally the node that belongs to the author 34 | //! of the transaction, where it is *validated* by passing it as parameter to a runtime entry 35 | //! point. See the [`validate`] module for more info. The [`pool`] module contains a data 36 | //! structure that manages the list of pending transactions. 37 | //! 38 | //! - If the validation process indicates that the transaction can be propagated, it is then sent 39 | //! over the peer-to-peer network to other peers. Each node that receives the transaction 40 | //! similarly validates it and relays it to its own peers. 41 | //! 42 | //! - When a block is authored, the node that authors it picks from its pool of validated 43 | //! transactions the ones to include in the block. The logic under which transactions are picked 44 | //! and their ordering depends on the output of the validation. The *body* of the newly-authored 45 | //! block is made of (but not exclusively) the transactions that have been included in said block. 46 | //! 47 | //! - When a node receives a new block, the transactions in the pool are re-validated against 48 | //! this block. The validation function found in the runtime is expected to return an error if 49 | //! the transaction in question is already present in the chain and should not be included again. 50 | //! 51 | //! ## About duplicate transactions 52 | //! 53 | //! In practice, the vast majority of the time, a given transaction can only ever be inserted 54 | //! once in a chain, as the vast majority of transactions include some sort of nonce in their 55 | //! encoding. In other words, once a transaction has been included in a block, trying to 56 | //! re-validate that same transaction against that same block or any of its descendants will 57 | //! return an error. 58 | //! 59 | //! Once a transaction has been included in a block, it becomes known to everyone. If the runtime 60 | //! *always* considered a certain transaction as valid, then this transaction could be repeatedly 61 | //! included in the chain over and over again (and by anyone, since the transaction is public). 62 | //! On a higher level, the logic of such a transaction couldn't possibly make sense, as the reason 63 | //! why transactions exist in the first place is to modify the state of the chain. In other words, 64 | //! a runtime that always considers a certain transaction to be valid doesn't make sense on a 65 | //! higher level. 66 | //! 67 | //! With that in mind, a Substrate/Polkadot client implementation is allowed to make two 68 | //! assumptions: 69 | //! 70 | //! - After a transaction has been included in a block, trying to re-validate it against that 71 | //! same block or any of its descendants always yields an error. 72 | //! - The same transaction can't possibly be included twice in the same block. 73 | //! 74 | //! > **Note**: The official Substrate client, in particular, tracks transactions by their hash, 75 | //! > and removes from the pool any transaction from the pool after it has been included 76 | //! > in the best chain, without even attempting to re-validate it (but re-inserts 77 | //! > transactions in the pool if a block gets reverted). 78 | //! 79 | //! However, this is in theory not the correct behavior. A legitimate (but in practice very 80 | //! uncommon) use-case for runtimes is to consider a transaction as invalid right after it has 81 | //! been included, but then valid again some time in the future. 82 | //! 83 | //! The specific use case is considered to be uncommon enough to not explicitly be taken into 84 | //! account. The client considers that once a transaction has been considered invalid against a 85 | //! certain block B, it will forever remain considered as invalid on any descendant of B, but a 86 | //! client also attempts to not cache that information for *too long* through heuristics. 87 | 88 | pub mod light_pool; 89 | pub mod pool; 90 | pub mod validate; 91 | -------------------------------------------------------------------------------- /src/verify.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Methods that verify whether a block is correct. 19 | //! 20 | //! # Description 21 | //! 22 | //! When a block is received from the network (or any other untrusted source), this block should 23 | //! first be verified. 24 | //! 25 | //! Two possibilities are offered: 26 | //! 27 | //! - Verifying the authenticity of the block, using only its header and the ancestor blocks' 28 | //! headers. 29 | //! - Verifying both the validity and authenticity of the block, using.the block's header and 30 | //! body, the headers of the ancestors, and storage of its parent. 31 | //! 32 | //! > **Note**: The body of a block consists in the list of its extrinsics. An extrinsic is 33 | //! > either an intrinsic (added by the block's author) or a transaction. 34 | //! 35 | //! > **Note**: The option of verifying only the authenticity of a block, and not its validity, 36 | //! > is normally for situations where a block's body or the parent block's storage is 37 | //! > not known. If the parent storage and the block's body are known, it is encouraged 38 | //! > that the block's validity be checked as well. 39 | //! 40 | //! Verifying the authenticity of a block consists in verifying the consensus digest logs found 41 | //! in its header in order to make sure that the author of the block was authorized to produce it. 42 | //! Whether the block has been correctly crafted isn't and cannot be verified with only the 43 | //! header. 44 | //! 45 | //! Verifying the block's validity consists, in addition to verifying its header, in *executing* 46 | //! the block. This involves calling the `BlockBuilder_check_inherents` and `Core_execute_block` 47 | //! runtime functions, passing as parameter the header and body of the block, and providing access 48 | //! to the storage of the parent block. The runtime verifies that the header and body are correct. 49 | //! 50 | //! # Trust 51 | //! 52 | //! Verifying only the authenticity of a block, and not its validity, means that the author of 53 | //! the block is trusted to have generated a correct block. 54 | //! 55 | //! Situations where the block author had the right to generate a block but created an invalid 56 | //! block would result in a loss of money for this author, and are therefore most likely the 57 | //! consequence of a bug (either on the side of the block author or on the side of the local 58 | //! verification code). 59 | //! 60 | //! However, if the blocks' validity isn't verified, it is possible for a block author to target 61 | //! a weak implementation by directly connecting to it and announcing invalid blocks directly 62 | //! to it. Consequently, if blocks are for example used to verify whether a payment has gone 63 | //! through, it is preferable to wait for blocks to have been finalized. 64 | //! 65 | //! No matter the validity of a block, keep in mind that a reorg (a "reorganization") might 66 | //! happen, and many valid blocks don't get finalized. 67 | //! 68 | 69 | pub mod aura; 70 | pub mod babe; 71 | pub mod header_body; 72 | pub mod header_only; 73 | pub mod inherents; 74 | -------------------------------------------------------------------------------- /src/verify/inherents.rs: -------------------------------------------------------------------------------- 1 | // Smoldot 2 | // Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. 3 | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 4 | 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //! Inherents, together with transactions, form the body of a block. 19 | //! 20 | //! The body of a block consists of a list of what is called extrinsics. An extrinsic can be 21 | //! either a transaction, when it was submitted by a user, or an inherent, which is what this 22 | //! module is about. 23 | //! 24 | //! When a block is authored, one of the first steps is for the block author to generate the list 25 | //! of inherents. This is done by calling a runtime function, passing as parameter an encoded 26 | //! [`InherentData`]. 27 | //! 28 | //! When a block is later verified, the inherents are verified by calling a runtime function and 29 | //! passing as parameter an encoded [`InherentData`] as well. 30 | 31 | /// Values of the inherents to pass to the runtime. 32 | /// 33 | /// Historically, the inherent data included an Aura or Babe slot number, using the identifiers 34 | /// `auraslot` or `babeslot`. The runtime-side verification of the slot number has been removed in 35 | /// May 2021, and all the checks performed by the runtime are now performed by the client instead. 36 | /// Older runtime versions still require the slot number. For this reason, verifying the inherents 37 | /// (calling `BlockBuilder_check_inherents`) of blocks that are using older runtime versions will 38 | /// lead to errors concerning the Aura or Babe modules that should simply be ignored. Authoring 39 | /// blocks using older runtime versions is not supported anymore. 40 | #[derive(Debug)] 41 | pub struct InherentData { 42 | /// Number of milliseconds since the UNIX epoch when the block is generated, ignoring leap 43 | /// seconds. 44 | /// 45 | /// Its identifier passed to the runtime is: `timstap0`. 46 | pub timestamp: u64, 47 | // TODO: figure out uncles 48 | /*/// List of valid block headers that have the same height as the parent of the one being 49 | /// generated. 50 | /// 51 | /// Its identifier passed to the runtime is: `uncles00`. 52 | /// 53 | /// `TUnc` must be an iterator yielding SCALE-encoded headers. 54 | pub uncles: TUnc,*/ 55 | 56 | // TODO: parachain-related inherents are missing 57 | } 58 | 59 | impl InherentData { 60 | /// Turns this list of inherents into a list that can be passed as parameter to the runtime. 61 | pub fn as_raw_list( 62 | &'_ self, 63 | ) -> impl ExactSizeIterator + Clone + '_)> + Clone + '_ { 64 | [(*b"timstap0", self.timestamp.to_le_bytes())].into_iter() 65 | } 66 | } 67 | --------------------------------------------------------------------------------