26 Commits

Author SHA1 Message Date
Luke Parker
0849d60f28 Run Bitcoin, Monero nodes on Alpine
While prior this didn't work well, presumably due to stack size limitations,
a shell script is included to raise the default stack size limit. This should
be tried again.
2025-12-08 02:30:34 -05:00
Luke Parker
3a792f9ce5 Update documentation on the serai-runtime build.rs 2025-12-08 02:22:29 -05:00
Luke Parker
50959fa0e3 Update the polkadot-sdk used
Removes `parity-wasm` as a dependency, closing
https://github.com/serai-dex/issues/227 and tidying our `deny.toml`.

This removes the `import-memory` flag from the linker as part of
`parity-wasm`'s usage was to map imports into exports
(5a1128b94b/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs (L91-L142)).
2025-12-08 02:22:25 -05:00
Luke Parker
2fb90ebe55 Extend crates we patch to be empty from the Ethereum ecosystem
`ruint` pulls in many versions of many crates. This has it pull in less.
2025-12-06 08:27:34 -05:00
Luke Parker
b24adcbd14 Add panic-on-poison to no-std std_shims::sync::Mutex
We already had this behavior on `std`. It was omitted when no-`std` due to
deferring to `spin::Mutex`, which does not track poisoning at all. This
increases the parity of the two.

Part of https://github.com/serai-dex/serai/issues/698.
2025-12-06 08:06:38 -05:00
Luke Parker
b791256648 Remove substrate-wasm-builder
By defining our own build script, we gain complete clarity and control over how
the WASM is built. This also removes the need to patch the upstream due to it
allowing pollution of the environment variables from the host.

Notable appreciation is given to
https://github.com/rust-lang/rust/issues/145491 for identifying an issue
encountered here, with the associated PR clarifying the necessary flags for the
linker to fix this.
2025-12-04 23:23:38 -05:00
Luke Parker
36ac9c56a4 Remove workaround for lack of musl-dev now that musl-dev is provided in Rust Alpine images
Additionally, optimizes the build process a bit via leaving only the runtime
(and `busybox`) in the final image, and additionally building the runtime
without `std` (as we solely need the WASM blob from this process).
2025-12-04 11:58:38 -05:00
Luke Parker
57bf4984f8 panic = "abort"
`panic = "unwind"` was originally a requirement of Substrate, notably due to
its [native runtime](https://github.com/paritytech/substrate/issues/10874).
This does not mean all of Serai should use this setting however.

As the native runtime has been removed, we do no longer need this for the
Substrate node. With a review of our derivative, a panic guard is only used
when fetching the version from the runtime, causing an error on boot if a
panic occurs. Accordingly, we shouldn't have a need for `panic = "unwind"`
within the node, and the runtime itself should be fine.

The rest of Serai's services already registered bespoke hooks to ensure any
panic caused the process to exit. Those are left as-is, even though they're
now unnecessary.
2025-12-04 11:58:38 -05:00
Luke Parker
87750407de cargo-deny 0.18.8, remove bip39 git dependency
The former is necessary due to `cargo-deny` misinterpreting select licenses.
The latter is finally possible with the recent 2.2.1 release 🎉
2025-12-04 11:58:28 -05:00
Luke Parker
3ce90c55d9 Define a 512 KiB block size limit 2025-12-02 21:24:05 -05:00
Luke Parker
ff95c58341 Round out the runtime
Ensures the block's size limit is respected.

Defines a policy for weights. While I'm unsure I want to commit to this
forever, I do want to acknowledge it's valid and well-defined.

Cleans up the `serai-runtime` crate a bit with further modules in the `wasm`
folder.
2025-12-02 21:16:34 -05:00
Luke Parker
98044f93b1 Stub the in-instructions pallet 2025-12-02 16:46:10 -05:00
Luke Parker
eb04f873d5 Stub the genesis-liquidity pallet 2025-12-02 16:46:06 -05:00
Luke Parker
af74c318aa Add event emissions to the DEX pallet 2025-12-02 13:31:33 -05:00
Luke Parker
d711d8915f Update docs Ruby/gem versions 2025-12-02 13:20:17 -05:00
Luke Parker
3d549564a8 Misc tweaks in the style of the last commit
Notably removes the `kvdb-rocksdb` patch via updating the Substrate version
used to one which disables the `jemalloc` feature itself.

Simplifies the path of the built WASM file within the Dockerfile to consumers.
This also ensures if the image is built, the path of the WASM file is as
expected (prior unasserted).
2025-12-02 09:10:44 -05:00
Luke Parker
9a75f92864 Thoroughly update versions and methodology
For hash-pinned dependencies, adds comments documenting the associated
versions.

Adds a pin to `slither-analyzer` which was prior missing.

Updates to Monero 0.18.4.4.

`mimalloc` now has the correct option set when building for `musl`. A C++
compiler is no longer required in its Docker image.

The runtime's `Dockerfile` now symlinks a `libc.so` already present on the
image instead of creating one itself. It also builds the runtime within the
image to ensure it only happens once. The test to ensure the methodology is
reproducible has been updated to not simply create containers from the image,
yet rebuild the image entirely, accordingly. This also is more robust and
arguably should have already been done.

The pin to the exact hash of the `patch-polkadot-sdk` repo in every
`Cargo.toml` has been removed. The lockfile already serves that role,
simplifying updating in the future.

The latest Rust nightly is adopted as well (superseding
https://github.com/serai-dex/serai/pull/697).

The `librocksdb-sys` patch is replaced with a `kvdb-rocksdb` patch, removing a
git dependency, thanks to https://github.com/paritytech/parity-common/pull/950.
2025-12-01 18:17:01 -05:00
Luke Parker
30ea9d9a06 Tidy the DEX pallet 2025-11-30 21:42:27 -05:00
Luke Parker
c45c973ca1 Remove musl-dev from runtime/Dockerfile
It wasn't pinned with a hash yet with a version tag. This ensures we are
deterministic to the image (specified by hash), `Cargo.lock`, and source code
alone.

Unfortunately, this was incredibly annoying to do, the exact process uncovering
a SIGSEGV in stable Rust. The extensive documentation details the solution.
Thankfully, it works now.
2025-11-27 03:37:37 -05:00
Luke Parker
6e37ac030d Add patch for alloy-eip2124 to an empty crate
Removes the `crc` dependency which had a unique author associated.
2025-11-26 17:01:03 -05:00
Luke Parker
e7c759c468 Improve substrate-median tests
The use of a dedicated test module ensures the API doesn't hide anything which
needs to be public. There's also now explicit tests for when the median is the
popped value.
2025-11-25 23:46:12 -05:00
Luke Parker
8ec0582237 Add module to calculate medians 2025-11-25 22:39:52 -05:00
Luke Parker
8d8e8a7a77 Remove unnecessary MSRVs from patches/ 2025-11-25 17:05:30 -05:00
Luke Parker
028ec3cce0 borsh 1.6.0
Bumps th MSRV for some of our crates, which is fine.
2025-11-25 16:58:19 -05:00
Luke Parker
c49215805f Update Substrate 2025-11-25 00:06:54 -05:00
Luke Parker
2ffdd2a01d Update monero-oxide, Substrate 2025-11-22 11:49:25 -05:00
136 changed files with 3817 additions and 6936 deletions

View File

@@ -12,7 +12,7 @@ runs:
steps: steps:
- name: Bitcoin Daemon Cache - name: Bitcoin Daemon Cache
id: cache-bitcoind id: cache-bitcoind
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # 4.2.4
with: with:
path: bitcoin.tar.gz path: bitcoin.tar.gz
key: bitcoind-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }} key: bitcoind-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}

View File

@@ -52,7 +52,7 @@ runs:
- name: Install solc - name: Install solc
shell: bash shell: bash
run: | run: |
cargo +1.91 install svm-rs --version =0.5.19 cargo +1.91.1 install svm-rs --version =0.5.22
svm install 0.8.29 svm install 0.8.29
svm use 0.8.29 svm use 0.8.29
@@ -75,11 +75,8 @@ runs:
if: runner.os == 'Linux' if: runner.os == 'Linux'
- name: Install rootless Docker - name: Install rootless Docker
uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 uses: docker/setup-docker-action@e61617a16c407a86262fb923c35a616ddbe070b3 # 4.6.0
with: with:
rootless: true rootless: true
set-host: true set-host: true
if: runner.os == 'Linux' if: runner.os == 'Linux'
# - name: Cache Rust
# uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43

View File

@@ -5,14 +5,14 @@ inputs:
version: version:
description: "Version to download and run" description: "Version to download and run"
required: false required: false
default: v0.18.4.3 default: v0.18.4.4
runs: runs:
using: "composite" using: "composite"
steps: steps:
- name: Monero Wallet RPC Cache - name: Monero Wallet RPC Cache
id: cache-monero-wallet-rpc id: cache-monero-wallet-rpc
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # 4.2.4
with: with:
path: monero-wallet-rpc path: monero-wallet-rpc
key: monero-wallet-rpc-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }} key: monero-wallet-rpc-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}

View File

@@ -5,14 +5,14 @@ inputs:
version: version:
description: "Version to download and run" description: "Version to download and run"
required: false required: false
default: v0.18.4.3 default: v0.18.4.4
runs: runs:
using: "composite" using: "composite"
steps: steps:
- name: Monero Daemon Cache - name: Monero Daemon Cache
id: cache-monerod id: cache-monerod
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # 4.2.4
with: with:
path: /usr/bin/monerod path: /usr/bin/monerod
key: monerod-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }} key: monerod-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}

View File

@@ -5,7 +5,7 @@ inputs:
monero-version: monero-version:
description: "Monero version to download and run as a regtest node" description: "Monero version to download and run as a regtest node"
required: false required: false
default: v0.18.4.3 default: v0.18.4.4
bitcoin-version: bitcoin-version:
description: "Bitcoin version to download and run as a regtest node" description: "Bitcoin version to download and run as a regtest node"
@@ -19,9 +19,9 @@ runs:
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies
- name: Install Foundry - name: Install Foundry
uses: foundry-rs/foundry-toolchain@8f1998e9878d786675189ef566a2e4bf24869773 uses: foundry-rs/foundry-toolchain@50d5a8956f2e319df19e6b57539d7e2acb9f8c1e # 1.5.0
with: with:
version: nightly-f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9 version: v1.5.0
cache: false cache: false
- name: Run a Monero Regtest Node - name: Run a Monero Regtest Node

View File

@@ -1 +1 @@
nightly-2025-11-11 nightly-2025-12-01

View File

@@ -17,7 +17,7 @@ jobs:
test-common: test-common:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -31,7 +31,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Install Build Dependencies - name: Install Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -19,7 +19,7 @@ jobs:
test-crypto: test-crypto:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -9,16 +9,10 @@ jobs:
name: Run cargo deny name: Run cargo deny
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Advisory Cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
with:
path: ~/.cargo/advisory-db
key: rust-advisory-db
- name: Install cargo deny - name: Install cargo deny
run: cargo +1.91 install cargo-deny --version =0.18.5 run: cargo +1.91.1 install cargo-deny --version =0.18.8
- name: Run cargo deny - name: Run cargo deny
run: cargo deny -L error --all-features check --hide-inclusion-graph run: cargo deny -L error --all-features check --hide-inclusion-graph

View File

@@ -13,7 +13,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Install Build Dependencies - name: Install Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Get nightly version to use - name: Get nightly version to use
id: nightly id: nightly
@@ -43,16 +43,10 @@ jobs:
deny: deny:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Advisory Cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
with:
path: ~/.cargo/advisory-db
key: rust-advisory-db
- name: Install cargo deny - name: Install cargo deny
run: cargo +1.91 install cargo-deny --version =0.18.5 run: cargo +1.91.1 install cargo-deny --version =0.18.8
- name: Run cargo deny - name: Run cargo deny
run: cargo deny -L error --all-features check --hide-inclusion-graph run: cargo deny -L error --all-features check --hide-inclusion-graph
@@ -60,7 +54,7 @@ jobs:
fmt: fmt:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Get nightly version to use - name: Get nightly version to use
id: nightly id: nightly
@@ -73,10 +67,10 @@ jobs:
- name: Run rustfmt - name: Run rustfmt
run: cargo +${{ steps.nightly.outputs.version }} fmt -- --check run: cargo +${{ steps.nightly.outputs.version }} fmt -- --check
- name: Install foundry - name: Install Foundry
uses: foundry-rs/foundry-toolchain@8f1998e9878d786675189ef566a2e4bf24869773 uses: foundry-rs/foundry-toolchain@50d5a8956f2e319df19e6b57539d7e2acb9f8c1e # 1.5.0
with: with:
version: nightly-41d4e5437107f6f42c7711123890147bc736a609 version: v1.5.0
cache: false cache: false
- name: Run forge fmt - name: Run forge fmt
@@ -85,20 +79,20 @@ jobs:
machete: machete:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Verify all dependencies are in use - name: Verify all dependencies are in use
run: | run: |
cargo +1.91 install cargo-machete --version =0.9.1 cargo +1.91.1 install cargo-machete --version =0.9.1
cargo +1.91 machete cargo +1.91.1 machete
msrv: msrv:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Verify claimed `rust-version` - name: Verify claimed `rust-version`
shell: bash shell: bash
run: | run: |
cargo +1.91 install cargo-msrv --version =0.18.4 cargo +1.91.1 install cargo-msrv --version =0.18.4
function check_msrv { function check_msrv {
# We `cd` into the directory passed as the first argument, but will return to the # We `cd` into the directory passed as the first argument, but will return to the
@@ -189,16 +183,16 @@ jobs:
slither: slither:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies
- name: Slither - name: Slither
run: | run: |
python3 -m pip install slither-analyzer python3 -m pip install slither-analyzer==0.11.3
slither --include-paths ./networks/ethereum/schnorr/contracts/Schnorr.sol slither ./networks/ethereum/schnorr/contracts/Schnorr.sol
slither --include-paths ./networks/ethereum/schnorr/contracts ./networks/ethereum/schnorr/contracts/tests/Schnorr.sol slither --include-paths ./networks/ethereum/schnorr/contracts ./networks/ethereum/schnorr/contracts/tests/Schnorr.sol
slither processor/ethereum/deployer/contracts/Deployer.sol slither processor/ethereum/deployer/contracts/Deployer.sol
slither processor/ethereum/erc20/contracts/IERC20.sol slither processor/ethereum/erc20/contracts/IERC20.sol

View File

@@ -27,7 +27,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Install Build Dependencies - name: Install Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -17,7 +17,7 @@ jobs:
test-common: test-common:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -9,7 +9,7 @@ jobs:
name: Update nightly name: Update nightly
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
with: with:
submodules: "recursive" submodules: "recursive"

View File

@@ -21,7 +21,7 @@ jobs:
test-networks: test-networks:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Test Dependencies - name: Test Dependencies
uses: ./.github/actions/test-dependencies uses: ./.github/actions/test-dependencies

View File

@@ -23,7 +23,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Install Build Dependencies - name: Install Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -46,16 +46,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71 # 1.268.0
with: with:
bundler-cache: true bundler-cache: true
cache-version: 0 cache-version: 0
working-directory: "${{ github.workspace }}/docs" working-directory: "${{ github.workspace }}/docs"
- name: Setup Pages - name: Setup Pages
id: pages id: pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # 5.0.0
- name: Build with Jekyll - name: Build with Jekyll
run: cd ${{ github.workspace }}/docs && bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" run: cd ${{ github.workspace }}/docs && bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
env: env:
@@ -74,7 +74,7 @@ jobs:
mv target/doc docs/_site/rust mv target/doc docs/_site/rust
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # 4.0.0
with: with:
path: "docs/_site/" path: "docs/_site/"
@@ -88,4 +88,4 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # 4.0.5

View File

@@ -31,7 +31,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Install Build Dependencies - name: Install Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

View File

@@ -27,10 +27,10 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Install Build Dependencies - name: Install Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies
- name: Run Reproducible Runtime tests - name: Run Reproducible Runtime tests
run: GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-reproducible-runtime-tests run: GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-reproducible-runtime-tests -- --nocapture

View File

@@ -29,7 +29,7 @@ jobs:
test-infra: test-infra:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies
@@ -74,7 +74,7 @@ jobs:
test-substrate: test-substrate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies
@@ -84,6 +84,7 @@ jobs:
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features \ GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features \
-p serai-primitives \ -p serai-primitives \
-p serai-abi \ -p serai-abi \
-p substrate-median \
-p serai-core-pallet \ -p serai-core-pallet \
-p serai-coins-pallet \ -p serai-coins-pallet \
-p serai-validator-sets-pallet \ -p serai-validator-sets-pallet \
@@ -100,7 +101,7 @@ jobs:
test-serai-client: test-serai-client:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # 6.0.0
- name: Build Dependencies - name: Build Dependencies
uses: ./.github/actions/build-dependencies uses: ./.github/actions/build-dependencies

1281
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -80,6 +80,8 @@ members = [
"substrate/primitives", "substrate/primitives",
"substrate/abi", "substrate/abi",
"substrate/median",
"substrate/core", "substrate/core",
"substrate/coins", "substrate/coins",
"substrate/validator-sets", "substrate/validator-sets",
@@ -114,6 +116,19 @@ members = [
"tests/reproducible-runtime", "tests/reproducible-runtime",
] ]
[profile.dev]
panic = "abort"
overflow-checks = true
[profile.release]
panic = "abort"
overflow-checks = true
# These do not respect the `panic` configuration value, so we don't provide them
[profile.test]
# panic = "abort" # https://github.com/rust-lang/issues/67650
overflow-checks = true
[profile.bench]
overflow-checks = true
[profile.dev.package] [profile.dev.package]
# Always compile Monero (and a variety of dependencies) with optimizations due # Always compile Monero (and a variety of dependencies) with optimizations due
# to the extensive operations required for Bulletproofs # to the extensive operations required for Bulletproofs
@@ -131,11 +146,14 @@ dalek-ff-group = { opt-level = 3 }
multiexp = { opt-level = 3 } multiexp = { opt-level = 3 }
monero-generators = { opt-level = 3 } monero-io = { opt-level = 3 }
monero-borromean = { opt-level = 3 } monero-primitives = { opt-level = 3 }
monero-bulletproofs = { opt-level = 3 } monero-ed25519 = { opt-level = 3 }
monero-mlsag = { opt-level = 3 } monero-mlsag = { opt-level = 3 }
monero-clsag = { opt-level = 3 } monero-clsag = { opt-level = 3 }
monero-borromean = { opt-level = 3 }
monero-bulletproofs-generators = { opt-level = 3 }
monero-bulletproofs = {opt-level = 3 }
monero-oxide = { opt-level = 3 } monero-oxide = { opt-level = 3 }
# Always compile the eVRF DKG tree with optimizations as well # Always compile the eVRF DKG tree with optimizations as well
@@ -160,16 +178,17 @@ revm-precompile = { opt-level = 3 }
revm-primitives = { opt-level = 3 } revm-primitives = { opt-level = 3 }
revm-state = { opt-level = 3 } revm-state = { opt-level = 3 }
[profile.release]
panic = "unwind"
overflow-checks = true
[patch.crates-io] [patch.crates-io]
# Point to empty crates for crates unused within in our tree # Point to empty crates for crates unused within in our tree
alloy-eip2124 = { path = "patches/ethereum/alloy-eip2124" }
ark-ff-3 = { package = "ark-ff", path = "patches/ethereum/ark-ff-0.3" } ark-ff-3 = { package = "ark-ff", path = "patches/ethereum/ark-ff-0.3" }
ark-ff-4 = { package = "ark-ff", path = "patches/ethereum/ark-ff-0.4" } ark-ff-4 = { package = "ark-ff", path = "patches/ethereum/ark-ff-0.4" }
c-kzg = { path = "patches/ethereum/c-kzg" } c-kzg = { path = "patches/ethereum/c-kzg" }
secp256k1-30 = { package = "secp256k1", path = "patches/ethereum/secp256k1-30" } fastrlp-3 = { package = "fastrlp", path = "patches/ethereum/fastrlp-0.3" }
fastrlp-4 = { package = "fastrlp", path = "patches/ethereum/fastrlp-0.4" }
primitive-types-12 = { package = "primitive-types", path = "patches/ethereum/primitive-types-0.12" }
rlp = { path = "patches/ethereum/rlp" }
secp256k1-30 = { package = "secp256k1", path = "patches/ethereum/secp256k1-0.30" }
# Dependencies from monero-oxide which originate from within our own tree, potentially shimmed to account for deviations since publishing # Dependencies from monero-oxide which originate from within our own tree, potentially shimmed to account for deviations since publishing
std-shims = { path = "patches/std-shims" } std-shims = { path = "patches/std-shims" }
@@ -208,9 +227,6 @@ parity-bip39 = { path = "patches/parity-bip39" }
k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" } k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" } p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
# `jemalloc` conflicts with `mimalloc`, so patch to a `rocksdb` which never uses `jemalloc`
librocksdb-sys = { path = "patches/librocksdb-sys" }
[workspace.lints.clippy] [workspace.lints.clippy]
incompatible_msrv = "allow" # Manually verified with a GitHub workflow incompatible_msrv = "allow" # Manually verified with a GitHub workflow
manual_is_multiple_of = "allow" manual_is_multiple_of = "allow"

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/common/db"
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = [] keywords = []
edition = "2021" edition = "2021"
rust-version = "1.65" rust-version = "1.77"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@@ -6,12 +6,63 @@ pub use std::sync::{Arc, Weak};
mod mutex_shim { mod mutex_shim {
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
pub use spin::{Mutex, MutexGuard}; mod spin_mutex {
use core::ops::{Deref, DerefMut};
// We wrap this in an `Option` so we can consider `None` as poisoned
pub(super) struct Mutex<T>(spin::Mutex<Option<T>>);
/// An acquired view of a `Mutex`.
pub struct MutexGuard<'mutex, T> {
mutex: spin::MutexGuard<'mutex, Option<T>>,
// This is `Some` for the lifetime of this guard, and is only represented as an `Option` due
// to needing to move it on `Drop` (which solely gives us a mutable reference to `self`)
value: Option<T>,
}
impl<T> Mutex<T> {
pub(super) const fn new(value: T) -> Self {
Self(spin::Mutex::new(Some(value)))
}
pub(super) fn lock(&self) -> MutexGuard<'_, T> {
let mut mutex = self.0.lock();
// Take from the `Mutex` so future acquisitions will see `None` unless this is restored
let value = mutex.take();
// Check the prior acquisition did in fact restore the value
if value.is_none() {
panic!("locking a `spin::Mutex` held by a thread which panicked");
}
MutexGuard { mutex, value }
}
}
impl<T> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.value.as_ref().expect("no value yet checked upon lock acquisition")
}
}
impl<T> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
self.value.as_mut().expect("no value yet checked upon lock acquisition")
}
}
impl<'mutex, T> Drop for MutexGuard<'mutex, T> {
fn drop(&mut self) {
// Restore the value
*self.mutex = self.value.take();
}
}
}
#[cfg(not(feature = "std"))]
pub use spin_mutex::*;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use std::sync::{Mutex, MutexGuard}; pub use std::sync::{Mutex, MutexGuard};
/// A shimmed `Mutex` with an API mutual to `spin` and `std`. /// A shimmed `Mutex` with an API mutual to `spin` and `std`.
#[derive(Default, Debug)]
pub struct ShimMutex<T>(Mutex<T>); pub struct ShimMutex<T>(Mutex<T>);
impl<T> ShimMutex<T> { impl<T> ShimMutex<T> {
/// Construct a new `Mutex`. /// Construct a new `Mutex`.
@@ -21,8 +72,9 @@ mod mutex_shim {
/// Acquire a lock on the contents of the `Mutex`. /// Acquire a lock on the contents of the `Mutex`.
/// ///
/// On no-`std` environments, this may spin until the lock is acquired. On `std` environments, /// This will panic if the `Mutex` was poisoned.
/// this may panic if the `Mutex` was poisoned. ///
/// On no-`std` environments, the implementation presumably defers to that of a spin lock.
pub fn lock(&self) -> MutexGuard<'_, T> { pub fn lock(&self) -> MutexGuard<'_, T> {
#[cfg(feature = "std")] #[cfg(feature = "std")]
let res = self.0.lock().unwrap(); let res = self.0.lock().unwrap();

View File

@@ -6,7 +6,7 @@ license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/tendermint" repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/tendermint"
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021" edition = "2021"
rust-version = "1.75" rust-version = "1.77"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@@ -575,14 +575,9 @@ impl<TD: Db, TDT: DbTxn, P: P2p> ScanBlock<'_, TD, TDT, P> {
}; };
let msgs = ( let msgs = (
decode_signed_message::<TendermintNetwork<TD, Transaction, P>>(&data.0).unwrap(), decode_signed_message::<TendermintNetwork<TD, Transaction, P>>(&data.0).unwrap(),
if data.1.is_some() { data.1.as_ref().map(|data| {
Some( decode_signed_message::<TendermintNetwork<TD, Transaction, P>>(data).unwrap()
decode_signed_message::<TendermintNetwork<TD, Transaction, P>>(&data.1.unwrap()) }),
.unwrap(),
)
} else {
None
},
); );
// Since anything with evidence is fundamentally faulty behavior, not just temporal // Since anything with evidence is fundamentally faulty behavior, not just temporal

View File

@@ -7,8 +7,7 @@ db-urls = ["https://github.com/rustsec/advisory-db"]
yanked = "deny" yanked = "deny"
ignore = [ ignore = [
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227 "RUSTSEC-2024-0370", # `proc-macro-error` is unmaintained, in-tree due to Substrate/`litep2p`
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
"RUSTSEC-2024-0436", # paste is unmaintained "RUSTSEC-2024-0436", # paste is unmaintained
] ]
@@ -79,7 +78,7 @@ exceptions = [
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-libp2p-p2p" }, { allow = ["AGPL-3.0-only"], name = "serai-coordinator-libp2p-p2p" },
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator" }, { allow = ["AGPL-3.0-only"], name = "serai-coordinator" },
{ allow = ["AGPL-3.0-only"], name = "pallet-session" }, { allow = ["AGPL-3.0-only"], name = "substrate-median" },
{ allow = ["AGPL-3.0-only"], name = "serai-core-pallet" }, { allow = ["AGPL-3.0-only"], name = "serai-core-pallet" },
{ allow = ["AGPL-3.0-only"], name = "serai-coins-pallet" }, { allow = ["AGPL-3.0-only"], name = "serai-coins-pallet" },
@@ -151,8 +150,5 @@ allow-git = [
"https://github.com/rust-lang-nursery/lazy-static.rs", "https://github.com/rust-lang-nursery/lazy-static.rs",
"https://github.com/kayabaNerve/elliptic-curves", "https://github.com/kayabaNerve/elliptic-curves",
"https://github.com/monero-oxide/monero-oxide", "https://github.com/monero-oxide/monero-oxide",
"https://github.com/kayabaNerve/monero-oxide",
"https://github.com/rust-bitcoin/rust-bip39",
"https://github.com/rust-rocksdb/rust-rocksdb",
"https://github.com/serai-dex/patch-polkadot-sdk", "https://github.com/serai-dex/patch-polkadot-sdk",
] ]

View File

@@ -1 +1 @@
3.3.4 3.3.10

View File

@@ -1,4 +1,4 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem "jekyll", "~> 4.3.3" gem "jekyll", "~> 4.4"
gem "just-the-docs", "0.8.2" gem "just-the-docs", "0.10.1"

View File

@@ -1,34 +1,39 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
addressable (2.8.7) addressable (2.8.8)
public_suffix (>= 2.0.2, < 7.0) public_suffix (>= 2.0.2, < 8.0)
bigdecimal (3.1.8) base64 (0.3.0)
bigdecimal (3.3.1)
colorator (1.1.0) colorator (1.1.0)
concurrent-ruby (1.3.4) concurrent-ruby (1.3.5)
csv (3.3.5)
em-websocket (0.5.3) em-websocket (0.5.3)
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
http_parser.rb (~> 0) http_parser.rb (~> 0)
eventmachine (1.2.7) eventmachine (1.2.7)
ffi (1.17.0-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-gnu)
forwardable-extended (2.6.0) forwardable-extended (2.6.0)
google-protobuf (4.28.2-x86_64-linux) google-protobuf (4.33.1-x86_64-linux-gnu)
bigdecimal bigdecimal
rake (>= 13) rake (>= 13)
http_parser.rb (0.8.0) http_parser.rb (0.8.0)
i18n (1.14.6) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jekyll (4.3.4) jekyll (4.4.1)
addressable (~> 2.4) addressable (~> 2.4)
base64 (~> 0.2)
colorator (~> 1.0) colorator (~> 1.0)
csv (~> 3.0)
em-websocket (~> 0.5) em-websocket (~> 0.5)
i18n (~> 1.0) i18n (~> 1.0)
jekyll-sass-converter (>= 2.0, < 4.0) jekyll-sass-converter (>= 2.0, < 4.0)
jekyll-watch (~> 2.0) jekyll-watch (~> 2.0)
json (~> 2.6)
kramdown (~> 2.3, >= 2.3.1) kramdown (~> 2.3, >= 2.3.1)
kramdown-parser-gfm (~> 1.0) kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0) liquid (~> 4.0)
mercenary (>= 0.3.6, < 0.5) mercenary (~> 0.3, >= 0.3.6)
pathutil (~> 0.9) pathutil (~> 0.9)
rouge (>= 3.0, < 5.0) rouge (>= 3.0, < 5.0)
safe_yaml (~> 1.0) safe_yaml (~> 1.0)
@@ -36,19 +41,20 @@ GEM
webrick (~> 1.7) webrick (~> 1.7)
jekyll-include-cache (0.2.1) jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0) jekyll (>= 3.7, < 5.0)
jekyll-sass-converter (3.0.0) jekyll-sass-converter (3.1.0)
sass-embedded (~> 1.54) sass-embedded (~> 1.75)
jekyll-seo-tag (2.8.0) jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0) jekyll (>= 3.8, < 5.0)
jekyll-watch (2.2.1) jekyll-watch (2.2.1)
listen (~> 3.0) listen (~> 3.0)
just-the-docs (0.8.2) json (2.16.0)
just-the-docs (0.10.1)
jekyll (>= 3.8.5) jekyll (>= 3.8.5)
jekyll-include-cache jekyll-include-cache
jekyll-seo-tag (>= 2.0) jekyll-seo-tag (>= 2.0)
rake (>= 12.3.1) rake (>= 12.3.1)
kramdown (2.4.0) kramdown (2.5.1)
rexml rexml (>= 3.3.9)
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
liquid (4.0.4) liquid (4.0.4)
@@ -58,27 +64,27 @@ GEM
mercenary (0.4.0) mercenary (0.4.0)
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (6.0.1) public_suffix (7.0.0)
rake (13.2.1) rake (13.3.1)
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.11.1) rb-inotify (0.11.1)
ffi (~> 1.0) ffi (~> 1.0)
rexml (3.3.7) rexml (3.4.4)
rouge (4.4.0) rouge (4.6.1)
safe_yaml (1.0.5) safe_yaml (1.0.5)
sass-embedded (1.79.3-x86_64-linux-gnu) sass-embedded (1.94.2-x86_64-linux-gnu)
google-protobuf (~> 4.27) google-protobuf (~> 4.31)
terminal-table (3.0.2) terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.6.0) unicode-display_width (2.6.0)
webrick (1.8.2) webrick (1.9.2)
PLATFORMS PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
jekyll (~> 4.3.3) jekyll (~> 4.4)
just-the-docs (= 0.8.2) just-the-docs (= 0.10.1)
BUNDLED WITH BUNDLED WITH
2.5.11 2.5.22

View File

@@ -1,50 +0,0 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
# This GPG-signed message exists to confirm the SHA256 sums of Monero binaries.
#
# Please verify the signature against the key for binaryFate in the
# source code repository (/utils/gpg_keys).
#
#
## CLI
4e1481835824b9233f204553d4a19645274824f3f6185d8a4b50198470752f54 monero-android-armv7-v0.18.4.3.tar.bz2
1aebd24aaaec3d1e87a64163f2e30ab2cd45f3902a7a859413f6870944775c21 monero-android-armv8-v0.18.4.3.tar.bz2
ff7b9c5cf2cb3d602c3dff1902ac0bc3394768cefc260b6003a9ad4bcfb7c6a4 monero-freebsd-x64-v0.18.4.3.tar.bz2
3ac83049bc565fb5238501f0fa629cdd473bbe94d5fb815088af8e6ff1d761cd monero-linux-armv7-v0.18.4.3.tar.bz2
b1cc5f135de3ba8512d56deb4b536b38c41addde922b2a53bf443aeaf2a5a800 monero-linux-armv8-v0.18.4.3.tar.bz2
95baaa6e8957b92caeaed7fb19b5c2659373df8dd5f4de2601ed3dae7b17ce2f monero-linux-riscv64-v0.18.4.3.tar.bz2
3a7b36ae4da831a4e9913e0a891728f4c43cd320f9b136cdb6686b1d0a33fafa monero-linux-x64-v0.18.4.3.tar.bz2
e0b51ca71934c33cb83cfa8535ffffebf431a2fc9efe3acf2baad96fb6ce21ec monero-linux-x86-v0.18.4.3.tar.bz2
bab9a6d3c2ca519386cff5ff0b5601642a495ed1a209736acaf354468cba1145 monero-mac-armv8-v0.18.4.3.tar.bz2
a8d8273b14f31569f5b7aa3063fbd322e3caec3d63f9f51e287dfc539c7f7d61 monero-mac-x64-v0.18.4.3.tar.bz2
bd9f615657c35d2d7dd9a5168ad54f1547dbf9a335dee7f12fab115f6f394e36 monero-win-x64-v0.18.4.3.zip
e642ed7bbfa34c30b185387fa553aa9c3ea608db1f3fc0e9332afa9b522c9c1a monero-win-x86-v0.18.4.3.zip
6ba5e082c8fa25216aba7aea8198f3e23d4b138df15c512457081e1eb3d03ff6 monero-source-v0.18.4.3.tar.bz2
#
## GUI
7b9255c696a462a00a810d9c8f94e60400a9e7d6438e8d6a8b693e9c13dca9ab monero-gui-install-win-x64-v0.18.4.3.exe
0bd84de0a7c18b2a3ea8e8eff2194ae000cf1060045badfd4ab48674bc1b9325 monero-gui-linux-x64-v0.18.4.3.tar.bz2
68ea30db32efb4a0671ec723297b6629d932fa188edf76edb38a37adaa3528e6 monero-gui-mac-armv8-v0.18.4.3.dmg
27243b01f030fdae68c59cae1daf21f530bbadeaf10579d2908db9a834191cee monero-gui-mac-x64-v0.18.4.3.dmg
dc9531cb4319b37b2c2dea4126e44a0fe6e7b6f34d278ccf5dd9ba693e3031e0 monero-gui-win-x64-v0.18.4.3.zip
0d44687644db9b1824f324416e98f4a46b3bb0a5ed09af54b2835b6facaa0cdd monero-gui-source-v0.18.4.3.tar.bz2
#
#
# ~binaryFate
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEgaxZH+nEtlxYBq/D8K9NRioL35IFAmjntxwACgkQ8K9NRioL
35J57g//dUOY1KAoLNaV7XLJyGbNk1lT6c2+A8h1wkK6iNQhXsmnc6rcigsHXrG0
LQyVUuZJ6ELhNb6BnH5V0zbcB72t8XjkSEqlYhStUfMnaUvj1VdXtL/OnSs3fEvt
Zwz6QBTxIKDDYEYyXrvCK96cYaYlIOgK3IVn/zoHdrHRUqTXqRJkFoTHum5l783y
vr9BwMFFWYePUrilphjIiLyJDl+eB5al8PaJkqK2whxBUHoA2jF1edJOSq2mZajI
+L2fBYClePS8oqwoKGquqCh2RVcmdtXtQTVzRIoNx14qFzP8ymqa+6z1Ygkri7bV
qMCJk7KQ8ND7uU9NShpaCIqrZpr5GZ4Al6SRkcpK/7mipQcy2QpKJR3iOpcfiTX1
YmYGVmLB3zmHu2kiS0kogZv6Ob7+tVFzOQ8NZX4FVnpB0N0phqMfNFOfHzdQZrsZ
qg29HNc9sHlUmsOVmE5w+7Oq+s79yvQB3034XXi/9wQu+f8fKRhqZboe0fe77FLf
QXoAYrZZ7LnGz0Z75Q9O4RB7uxM0Ug5imvyEFus4iuBVyBWjgcfyLnbkKJtbXmfn
BZBbTProhPJfVa/VffBxW9HZB27W7O14oGWVpUkGWnVMZfVY/78XTUHwxaScQsPO
SGawjobQsB3pTMNr/kra1XTjkti70si8Fcs5ueYWGB3yfc6r3hU=
=5HRY
-----END PGP SIGNATURE-----

View File

@@ -0,0 +1,50 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
# This GPG-signed message exists to confirm the SHA256 sums of Monero binaries.
#
# Please verify the signature against the key for binaryFate in the
# source code repository (/utils/gpg_keys).
#
#
## CLI
7c2ad18ca3a1ad5bc603630ca935a753537a38a803e98d645edd6a3b94a5f036 monero-android-armv7-v0.18.4.4.tar.bz2
eb81b71f029884ab5fec76597be583982c95fd7dc3fc5f5083a422669cee311e monero-android-armv8-v0.18.4.4.tar.bz2
bc539178df23d1ae8b69569d9c328b5438ae585c0aacbebe12d8e7d387a745b0 monero-freebsd-x64-v0.18.4.4.tar.bz2
2040dc22748ef39ed8a755324d2515261b65315c67b91f449fa1617c5978910b monero-linux-armv7-v0.18.4.4.tar.bz2
b9daede195a24bdd05bba68cb5cb21e42c2e18b82d4d134850408078a44231c5 monero-linux-armv8-v0.18.4.4.tar.bz2
c939ea6e8002798f24a56ac03cbfc4ff586f70d7d9c3321b7794b3bcd1fa4c45 monero-linux-riscv64-v0.18.4.4.tar.bz2
7fe45ee9aade429ccdcfcad93b905ba45da5d3b46d2dc8c6d5afc48bd9e7f108 monero-linux-x64-v0.18.4.4.tar.bz2
8c174b756e104534f3d3a69fe68af66d6dc4d66afa97dfe31735f8d069d20570 monero-linux-x86-v0.18.4.4.tar.bz2
645e9bbae0275f555b2d72a9aa30d5f382df787ca9528d531521750ce2da9768 monero-mac-armv8-v0.18.4.4.tar.bz2
af3d98f09da94632db3e2f53c62cc612e70bf94aa5942d2a5200b4393cd9c842 monero-mac-x64-v0.18.4.4.tar.bz2
7eb3b87a105b3711361dd2b3e492ad14219d21ed8fd3dd726573a6cbd96e83a6 monero-win-x64-v0.18.4.4.zip
a148a2bd2b14183fb36e2cf917fce6f33fb687564db2ed53193b8432097ab398 monero-win-x86-v0.18.4.4.zip
84570eee26238d8f686605b5e31d59569488a3406f32e7045852de91f35508a2 monero-source-v0.18.4.4.tar.bz2
#
## GUI
4c81c8e97bd542daa453776d888557db1ceb2a718d43f6135ad68b12c8119948 monero-gui-install-win-x64-v0.18.4.4.exe
e45cb3fa9d972d67628cfed6463fb7604ae1414a11ba449f5e2f901c769ac788 monero-gui-linux-x64-v0.18.4.4.tar.bz2
a6f071719c401df339dba2d43ec6fffe103fda3e1df46f354b2496f34bb61cc4 monero-gui-mac-armv8-v0.18.4.4.dmg
811df70811a25f31289f24ebc0edc8f7648670384698d4c768bac5c2acbf2026 monero-gui-mac-x64-v0.18.4.4.dmg
b96faa56aa77cabed1f31f3fc9496e756a8da8c1124da2b9cb0b3730a8b6fbd9 monero-gui-win-x64-v0.18.4.4.zip
a7f6b91bc9efaa83173a397614626bf7612123e0017a48f66137ac397f7d19f8 monero-gui-source-v0.18.4.4.tar.bz2
#
#
# ~binaryFate
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEgaxZH+nEtlxYBq/D8K9NRioL35IFAmkbGLgACgkQ8K9NRioL
35LWYRAAnPeUu7TADV9Nly2gBlwu7bMK6l7pcUzs3hHhCMpg/Zb7wF8lx4D/r/hT
3wf3gNVK6tYl5GMPpF7GSKvK35SSzNN+8khRd7vhRByG75LGLnrNlcBsQU2wOzUv
Rmm2R8L8GP0B/+zXO92uJDMZ7Q7x72O+3fVX05217HBwz2kvzE1NpXe+EJPnUukA
Tr5CRnxKhxPbilvIhoEHdwkScMZqHMfsbdrefrB3KpO3xEaUz+gO9wESp7nzr4vp
Du6gJYBPK25Z2heZHCRsGN4WQP4QQv4MC0IFczc9fkVDBjywsJeNRRUbGtxR/BNt
vNJGI/kS+7KV140j6GkqAh/leZcaVJ5LRyCaHAwEQNA2T5okhrM0WZpoOAsZMi5K
bW4lNOXfWSw6/tokEPeuoi49yw0f9z0C8a4VLNOZGWKqmHcsA8WE6oVfmvVk6xWu
BqTU1Z9LJqL17GWRAReSX1ZuNA0Q0Pb/klUwP4X2afJcCVZ2YeBNr4jr21u3dYXY
QiLj0Gv7gg7a/GiMpVglNn5GzCu6mT0D94sbMNK+U5Tbve7aOtijJZ8JR62eO/mR
h+oNEys/xEcP9PQ5p74cNL71hNSfWSOcNi+GLSgXC75vsOGr7i96uaamilsHnsYB
p8PZMHzOf1pi6i/L5oOEuRgaujd9IjyCbxoYh3bbxxjBOhNEMqU=
=CVLA
-----END PGP SIGNATURE-----

View File

@@ -0,0 +1,166 @@
# Raises `PT_GNU_STACK`'s memory to be at least 8 MB.
#
# This causes `musl` to use a 8 MB default for new threads, resolving the primary
# compatibility issue faced when executing a program on a `musl` system.
#
# See https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size
# for reference. This differs that instead of setting at time of link, it
# patches the binary as an already-linked ELF executable.
#!/bin/bash
set -eo pipefail
ELF="$1"
if [ ! -f "$ELF" ]; then
echo "\`increase_default_stack_size.sh\` [ELF binary]"
echo ""
echo "Sets the \`PT_GNU_STACK\` program header to its existing value or 8 MB,"
echo "whichever is greater."
exit 1
fi
function hex {
hexdump -e '1 1 "%.2x"' -v
}
function read_bytes {
dd status=none bs=1 skip=$1 count=$2 if="$ELF" | hex
}
function write_bytes {
POS=$1
BYTES=$2
while [ ! $BYTES = "" ]; do
printf "\x$(printf $BYTES | head -c2)" | dd status=none conv=notrunc bs=1 seek=$POS of="$ELF"
# Start with the third byte, as in, after the first two bytes
BYTES=$(printf $BYTES | tail -c+3)
POS=$(($POS + 1))
done
}
# Magic
MAGIC=$(read_bytes 0 4)
if [ ! $MAGIC = $(printf "\x7fELF" | hex) ]; then
echo "Not ELF"
exit 2
fi
# 1 if 32-bit, 2 if 64-bit
BITS=$(read_bytes 4 1)
case $BITS in
"01") BITS=32;;
"02") BITS=64;;
*)
echo "Not 32- or 64- bit"
exit 3
;;
esac
# For `value_per_bits a b`, `a` if 32-bit and `b` if 64-bit
function value_per_bits {
RESULT=$(($1))
if [ $BITS = 64 ]; then
RESULT=$(($2))
fi
printf $RESULT
}
# Read an integer by its offset, differing depending on if 32- or 64-bit
function read_integer_by_offset {
OFFSET=$(value_per_bits $1 $2)
printf $(( 0x$(swap_native_endian $(read_bytes $OFFSET $3)) ))
}
# 1 if little-endian, 2 if big-endian
LITTLE_ENDIAN=$(read_bytes 5 1)
case $LITTLE_ENDIAN in
"01") LITTLE_ENDIAN=1;;
"02") LITTLE_ENDIAN=0;;
*)
echo "Not little- or big- endian"
exit 4
;;
esac
# While this script is written in big-endian, we need to work with the file in
# its declared endian. This function swaps from big to native, or vice versa,
# as necessary.
function swap_native_endian {
BYTES="$1"
if [ "$BYTES" = "" ]; then
read BYTES
fi
if [ $LITTLE_ENDIAN -eq 0 ]; then
printf $BYTES
return
fi
while [ ! $BYTES = "" ]; do
printf $(printf $BYTES | tail -c2)
BYTES=$(printf $BYTES | head -c-2)
done
}
ELF_VERSION=$(read_bytes 6 1)
if [ ! $ELF_VERSION = "01" ]; then
echo "Unknown ELF Version ($ELF_VERSION)"
exit 5
fi
ELF_VERSION_2=$(read_bytes $((0x14)) 4)
if [ ! $ELF_VERSION_2 = $(swap_native_endian 00000001) ]; then
echo "Unknown secondary ELF Version ($ELF_VERSION_2)"
exit 6
fi
# Find where the program headers are
PROGRAM_HEADERS_OFFSET=$(read_integer_by_offset 0x1c 0x20 $(value_per_bits 4 8))
PROGRAM_HEADER_SIZE=$(value_per_bits 0x20 0x38)
DECLARED_PROGRAM_HEADER_SIZE=$(read_integer_by_offset 0x2a 0x36 2)
if [ ! $PROGRAM_HEADER_SIZE -eq $DECLARED_PROGRAM_HEADER_SIZE ]; then
echo "Unexpected size of a program header ($DECLARED_PROGRAM_HEADER_SIZE)"
exit 7
fi
function program_header_start {
printf $(($PROGRAM_HEADERS_OFFSET + ($1 * $PROGRAM_HEADER_SIZE)))
}
function read_program_header {
read_bytes $(program_header_start $1) $PROGRAM_HEADER_SIZE
}
# Iterate over each program header
PROGRAM_HEADERS=$(read_integer_by_offset 0x2c 0x38 2)
NEXT_PROGRAM_HEADER=$(( $PROGRAM_HEADERS - 1 ))
FOUND=0
while [ $NEXT_PROGRAM_HEADER -ne -1 ]; do
THIS_PROGRAM_HEADER=$NEXT_PROGRAM_HEADER
NEXT_PROGRAM_HEADER=$(( $NEXT_PROGRAM_HEADER - 1 ))
PROGRAM_HEADER=$(read_program_header $THIS_PROGRAM_HEADER)
HEADER_TYPE=$(printf $PROGRAM_HEADER | head -c8)
# `PT_GNU_STACK`
# https://github.com/torvalds/linux/blob/c2f2b01b74be8b40a2173372bcd770723f87e7b2/include/uapi/linux/elf.h#L41
if [ ! "$(swap_native_endian $HEADER_TYPE)" = "6474e551" ]; then
continue
fi
FOUND=1
MEMSZ_OFFSET=$(( $(program_header_start $THIS_PROGRAM_HEADER) + $(value_per_bits 0x14 0x28) ))
MEMSZ_LEN=$(value_per_bits 4 8)
# `MEMSZ_OFFSET MEMSZ_OFFSET` as we've already derived it depending on the amount of bits
MEMSZ=$(read_integer_by_offset $MEMSZ_OFFSET $MEMSZ_OFFSET $MEMSZ_LEN)
DESIRED_STACK_SIZE=$((8 * 1024 * 1024))
# Only run if the inherent value is _smaller_
if [ $MEMSZ -lt $DESIRED_STACK_SIZE ]; then
# `2 *`, as this is its length in hexadecimal
HEX_MEMSZ=$(printf %."$((2 * $MEMSZ_LEN))"x $DESIRED_STACK_SIZE)
write_bytes $MEMSZ_OFFSET $(swap_native_endian $HEX_MEMSZ)
fi
done
if [ $FOUND -eq 0 ]; then
echo "\`PT_GNU_STACK\` program header not found"
exit 8
fi
echo "All instances of \`PT_GNU_STACK\` patched to be at least 8 MB"
exit 0

View File

@@ -1,13 +1,12 @@
# rust:1.91.1-alpine as of November 11th, 2025 (GMT) #check=skip=FromPlatformFlagConstDisallowed
FROM --platform=linux/amd64 rust@sha256:700c0959b23445f69c82676b72caa97ca4359decd075dca55b13339df27dc4d3 AS deterministic # We want to explicitly set the platform to ensure a constant host environment
RUN apk add musl-dev=1.2.5-r10 # rust:1.91.1-alpine as of December 4th, 2025 (GMT)
FROM --platform=linux/amd64 rust@sha256:84f263251b0ada72c1913d82a824d47be15a607f3faf015d8bdae48db544cdf2 AS builder
# Add the wasm toolchain # Add the WASM toolchain
RUN rustup target add wasm32v1-none RUN rustup target add wasm32v1-none
FROM deterministic
# Add files for build # Add files for build
ADD patches /serai/patches ADD patches /serai/patches
ADD common /serai/common ADD common /serai/common
@@ -27,8 +26,17 @@ ADD AGPL-3.0 /serai
WORKDIR /serai WORKDIR /serai
# Build the runtime, copying it to the volume if it exists # Build the runtime
ENV RUSTFLAGS="-Ctarget-feature=-crt-static" RUN cargo build --release -p serai-runtime --no-default-features
CMD cargo build --release -p serai-runtime && \
mkdir -p /volume && \ # Copy the artifact to its own image which solely exists to further export it
cp /serai/target/release/wbuild/serai-runtime/serai_runtime.wasm /volume/serai.wasm FROM scratch
# Copy `busybox`, including the necessary shared libraries, from the builder for a functioning `cp`
COPY --from=builder /lib/ld-musl-x86_64.so.1 /lib/libc.musl-x86_64.so.1 /lib/
COPY --from=builder /bin/busybox /bin/
ENV LD_LIBRARY_PATH=/lib/
ENV PATH=/bin
# Copy the artifact itself
COPY --from=builder /serai/target/release/serai_runtime.wasm /serai.wasm
# By default, copy the artifact to `/volume`, presumably a provided volume
CMD ["busybox", "cp", "/serai.wasm", "/volume/serai.wasm"]

View File

@@ -16,7 +16,7 @@ pub fn coordinator(
) { ) {
let db = network.db(); let db = network.db();
let longer_reattempts = if network == Network::Dev { "longer-reattempts" } else { "" }; let longer_reattempts = if network == Network::Dev { "longer-reattempts" } else { "" };
let setup = mimalloc(Os::Debian).to_string() + let setup = mimalloc(Os::Debian) +
&build_serai_service( &build_serai_service(
"", "",
Os::Debian, Os::Debian,

View File

@@ -3,7 +3,7 @@ use std::path::Path;
use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile}; use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
pub fn ethereum_relayer(orchestration_path: &Path, network: Network) { pub fn ethereum_relayer(orchestration_path: &Path, network: Network) {
let setup = mimalloc(Os::Debian).to_string() + let setup = mimalloc(Os::Debian) +
&build_serai_service( &build_serai_service(
"", "",
Os::Debian, Os::Debian,

View File

@@ -13,7 +13,7 @@ pub fn message_queue(
ethereum_key: <Ristretto as WrappedGroup>::G, ethereum_key: <Ristretto as WrappedGroup>::G,
monero_key: <Ristretto as WrappedGroup>::G, monero_key: <Ristretto as WrappedGroup>::G,
) { ) {
let setup = mimalloc(Os::Alpine).to_string() + let setup = mimalloc(Os::Alpine) +
&build_serai_service("", Os::Alpine, network.release(), network.db(), "serai-message-queue"); &build_serai_service("", Os::Alpine, network.release(), network.db(), "serai-message-queue");
let env_vars = [ let env_vars = [

View File

@@ -1,36 +1,85 @@
use crate::Os; use crate::Os;
pub fn mimalloc(os: Os) -> &'static str { // 2.2.4
const ALPINE_MIMALLOC: &str = r#" const MIMALLOC_VERSION: &str = "fbd8b99c2b828428947d70fdc046bb55609be93e";
const FLAGS: &str =
"-DMI_SECURE=ON -DMI_GUARDED=ON -DMI_BUILD_STATIC=OFF -DMI_BUILD_OBJECT=OFF -DMI_BUILD_TESTS=OFF";
pub fn mimalloc(os: Os) -> String {
let build_script = |env, flags| {
format!(
r#"
#!/bin/sh
set -e
git clone https://github.com/microsoft/mimalloc
cd mimalloc
git checkout {MIMALLOC_VERSION}
# For some reason, `mimalloc` contains binary blobs in the repository, so we remove those now
rm -rf .git ./bin
mkdir -p out/secure
cd out/secure
# `CMakeLists.txt` requires a C++ compiler but `mimalloc` does not use one by default. We claim
# there is a working C++ compiler so CMake doesn't complain, allowing us to not unnecessarily
# install one. If it was ever invoked, our choice of `false` would immediately let us know.
# https://github.com/microsoft/mimalloc/issues/1179
{env} CXX=false cmake -DCMAKE_CXX_COMPILER_WORKS=1 {FLAGS} ../..
make
cd ../..
# Copy the built library to the original directory
cd ..
cp mimalloc/out/secure/libmimalloc-secure.so ./libmimalloc.so
# Clean up the source directory
rm -rf ./mimalloc
"#
)
};
let build_commands = |env, flags| {
let mut result = String::new();
for line in build_script(env, flags)
.lines()
.map(|line| {
assert!(!line.contains('"'));
format!(r#"RUN echo "{line}" >> ./mimalloc.sh"#)
})
.chain(["RUN /bin/sh ./mimalloc.sh", "RUN rm ./mimalloc.sh"].into_iter().map(str::to_string))
{
result.push_str(&line);
result.push('\n');
}
result
};
let alpine_build = build_commands("CC=$(uname -m)-alpine-linux-musl-gcc", "-DMI_LIBC_MUSL=ON");
let debian_build = build_commands("", "");
let alpine_mimalloc = format!(
r#"
FROM alpine:latest AS mimalloc-alpine FROM alpine:latest AS mimalloc-alpine
RUN apk update && apk upgrade && apk --no-cache add gcc g++ libc-dev make cmake git RUN apk update && apk upgrade && apk --no-cache add musl-dev gcc make cmake git
RUN git clone https://github.com/microsoft/mimalloc && \
cd mimalloc && \
git checkout fbd8b99c2b828428947d70fdc046bb55609be93e && \
mkdir -p out/secure && \
cd out/secure && \
cmake -DMI_SECURE=ON -DMI_GUARDED=on ../.. && \
make && \
cp ./libmimalloc-secure.so ../../../libmimalloc.so
"#;
const DEBIAN_MIMALLOC: &str = r#" {alpine_build}
"#
);
let debian_mimalloc = format!(
r#"
FROM debian:trixie-slim AS mimalloc-debian FROM debian:trixie-slim AS mimalloc-debian
RUN apt update && apt upgrade -y && apt install -y gcc g++ make cmake git RUN apt update && apt upgrade -y && apt install -y gcc make cmake git
RUN git clone https://github.com/microsoft/mimalloc && \
cd mimalloc && \ {debian_build}
git checkout fbd8b99c2b828428947d70fdc046bb55609be93e && \ "#
mkdir -p out/secure && \ );
cd out/secure && \
cmake -DMI_SECURE=ON -DMI_GUARDED=on ../.. && \
make && \
cp ./libmimalloc-secure.so ../../../libmimalloc.so
"#;
match os { match os {
Os::Alpine => ALPINE_MIMALLOC, Os::Alpine => alpine_mimalloc,
Os::Debian => DEBIAN_MIMALLOC, Os::Debian => debian_mimalloc,
} }
} }

View File

@@ -29,7 +29,7 @@ RUN tar xzvf bitcoin-${BITCOIN_VERSION}-$(uname -m)-linux-gnu.tar.gz
RUN mv bitcoin-${BITCOIN_VERSION}/bin/bitcoind . RUN mv bitcoin-${BITCOIN_VERSION}/bin/bitcoind .
"#; "#;
let setup = mimalloc(Os::Debian).to_string() + DOWNLOAD_BITCOIN; let setup = mimalloc(Os::Alpine) + DOWNLOAD_BITCOIN;
let run_bitcoin = format!( let run_bitcoin = format!(
r#" r#"
@@ -43,7 +43,7 @@ CMD ["/run.sh"]
network.label() network.label()
); );
let run = os(Os::Debian, "", "bitcoin") + &run_bitcoin; let run = os(Os::Alpine, "", "bitcoin") + &run_bitcoin;
let res = setup + &run; let res = setup + &run;
let mut bitcoin_path = orchestration_path.to_path_buf(); let mut bitcoin_path = orchestration_path.to_path_buf();

View File

@@ -17,7 +17,7 @@ pub fn ethereum(orchestration_path: &Path, network: Network) {
(reth(network), nimbus(network)) (reth(network), nimbus(network))
}; };
let download = mimalloc(Os::Alpine).to_string() + &el_download + &cl_download; let download = mimalloc(Os::Alpine) + &el_download + &cl_download;
let run = format!( let run = format!(
r#" r#"
@@ -26,7 +26,7 @@ CMD ["/run.sh"]
"#, "#,
network.label() network.label()
); );
let run = mimalloc(Os::Debian).to_string() + let run = mimalloc(Os::Debian) +
&os(Os::Debian, &(el_run_as_root + "\r\n" + &cl_run_as_root), "ethereum") + &os(Os::Debian, &(el_run_as_root + "\r\n" + &cl_run_as_root), "ethereum") +
&el_run + &el_run +
&cl_run + &cl_run +

View File

@@ -10,7 +10,7 @@ fn monero_internal(
monero_binary: &str, monero_binary: &str,
ports: &str, ports: &str,
) { ) {
const MONERO_VERSION: &str = "0.18.4.3"; const MONERO_VERSION: &str = "0.18.4.4";
let arch = match std::env::consts::ARCH { let arch = match std::env::consts::ARCH {
// We probably would run this without issues yet it's not worth needing to provide support for // We probably would run this without issues yet it's not worth needing to provide support for
@@ -21,7 +21,7 @@ fn monero_internal(
}; };
#[rustfmt::skip] #[rustfmt::skip]
let download_monero = format!(r#" let mut download_monero = format!(r#"
FROM alpine:latest AS monero FROM alpine:latest AS monero
RUN apk --no-cache add wget gnupg RUN apk --no-cache add wget gnupg
@@ -41,7 +41,17 @@ RUN tar -xvjf monero-linux-{arch}-v{MONERO_VERSION}.tar.bz2 --strip-components=1
network.label(), network.label(),
); );
let setup = mimalloc(os).to_string() + &download_monero; if os == Os::Alpine {
// Increase the default stack size, as Monero does heavily use its stack
download_monero += &format!(
r#"
ADD orchestration/increase_default_stack_size.sh .
RUN ./increase_default_stack_size.sh {monero_binary}
"#
);
}
let setup = mimalloc(os) + &download_monero;
let run_monero = format!( let run_monero = format!(
r#" r#"
@@ -69,13 +79,13 @@ CMD ["/run.sh"]
} }
pub fn monero(orchestration_path: &Path, network: Network) { pub fn monero(orchestration_path: &Path, network: Network) {
monero_internal(network, Os::Debian, orchestration_path, "monero", "monerod", "18080 18081") monero_internal(network, Os::Alpine, orchestration_path, "monero", "monerod", "18080 18081")
} }
pub fn monero_wallet_rpc(orchestration_path: &Path) { pub fn monero_wallet_rpc(orchestration_path: &Path) {
monero_internal( monero_internal(
Network::Dev, Network::Dev,
Os::Debian, Os::Alpine,
orchestration_path, orchestration_path,
"monero-wallet-rpc", "monero-wallet-rpc",
"monero-wallet-rpc", "monero-wallet-rpc",

View File

@@ -17,7 +17,7 @@ pub fn processor(
substrate_evrf_key: Zeroizing<Vec<u8>>, substrate_evrf_key: Zeroizing<Vec<u8>>,
network_evrf_key: Zeroizing<Vec<u8>>, network_evrf_key: Zeroizing<Vec<u8>>,
) { ) {
let setup = mimalloc(Os::Debian).to_string() + let setup = mimalloc(Os::Debian) +
&build_serai_service( &build_serai_service(
if coin == "ethereum" { if coin == "ethereum" {
r#" r#"

View File

@@ -12,8 +12,7 @@ pub fn serai(
serai_key: &Zeroizing<<Ristretto as WrappedGroup>::F>, serai_key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
) { ) {
// Always builds in release for performance reasons // Always builds in release for performance reasons
let setup = let setup = mimalloc(Os::Debian) + &build_serai_service("", Os::Debian, true, "", "serai-node");
mimalloc(Os::Debian).to_string() + &build_serai_service("", Os::Debian, true, "", "serai-node");
let env_vars = [("KEY", hex::encode(serai_key.to_repr()))]; let env_vars = [("KEY", hex::encode(serai_key.to_repr()))];
let mut env_vars_str = String::new(); let mut env_vars_str = String::new();

View File

@@ -7,7 +7,6 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-gr
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["curve25519", "ed25519", "ristretto", "dalek", "group"] keywords = ["curve25519", "ed25519", "ristretto", "dalek", "group"]
edition = "2021" edition = "2021"
rust-version = "1.85"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@@ -0,0 +1,19 @@
[package]
name = "alloy-eip2124"
version = "0.2.99"
description = "Patch to an empty crate"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/alloy-eip2124"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
[features]
std = []
serde = []

View File

@@ -0,0 +1,19 @@
[package]
name = "fastrlp"
version = "0.3.99"
description = "Patch to an empty crate"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/fastrlp-0.3"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
[features]
alloc = []
std = []

View File

@@ -0,0 +1,19 @@
[package]
name = "fastrlp"
version = "0.4.99"
description = "Patch to an empty crate"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/fastrlp-0.4"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
[features]
alloc = []
std = []

View File

@@ -0,0 +1 @@
const _NEVER_COMPILED: [(); 0 - 1] = [(); 0 - 1];

View File

@@ -0,0 +1,18 @@
[package]
name = "primitive-types"
version = "0.12.99"
description = "Patch to an empty crate"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/primitive-types"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
[features]
std = []

View File

@@ -0,0 +1 @@
const _NEVER_COMPILED: [(); 0 - 1] = [(); 0 - 1];

View File

@@ -0,0 +1,18 @@
[package]
name = "rlp"
version = "0.5.99"
description = "Patch to an empty crate"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/rlp"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
[features]
std = []

View File

@@ -0,0 +1 @@
const _NEVER_COMPILED: [(); 0 - 1] = [(); 0 - 1];

View File

@@ -3,7 +3,7 @@ name = "secp256k1"
version = "0.30.99" version = "0.30.99"
description = "Patch to an empty crate" description = "Patch to an empty crate"
license = "MIT" license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/secp256k1-30" repository = "https://github.com/serai-dex/serai/tree/develop/patches/ethereum/secp256k1-0.30"
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = [] keywords = []
edition = "2021" edition = "2021"

View File

@@ -0,0 +1 @@
const _NEVER_COMPILED: [(); 0 - 1] = [(); 0 - 1];

View File

@@ -1,35 +0,0 @@
[package]
name = "librocksdb-sys"
version = "0.17.99"
description = "Replacement for `librocksdb-sys` which removes the `jemalloc` feature"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/librocksdb-sys"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2018"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
[dependencies]
# librocksdb-sys 0.17.3+10.4.2, with the commit provided by crates.io in `cargo_vcs_info.json`
librocksdb-sys = { git = "https://github.com/rust-rocksdb/rust-rocksdb", commit = "bb7d2168eab1bc7849f23adbcb825e3aba1bd2f4", default-features = false }
[features]
default = ["librocksdb-sys/default"]
jemalloc = []
static = ["librocksdb-sys/static"]
bindgen-runtime = ["librocksdb-sys/bindgen-runtime"]
bindgen-static = ["librocksdb-sys/bindgen-static"]
mt_static = ["librocksdb-sys/mt_static"]
io-uring = ["librocksdb-sys/io-uring"]
snappy = ["librocksdb-sys/snappy"]
lz4 = ["librocksdb-sys/lz4"]
zstd = ["librocksdb-sys/zstd"]
zlib = ["librocksdb-sys/zlib"]
bzip2 = ["librocksdb-sys/bzip2"]
rtti = ["librocksdb-sys/rtti"]
lto = ["librocksdb-sys/lto"]

View File

@@ -1 +0,0 @@
pub use librocksdb_sys::*;

View File

@@ -7,6 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/patches/option-ext
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = [] keywords = []
edition = "2021" edition = "2021"
rust-version = "1.56"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@@ -1,8 +1,8 @@
pub trait OptionExt<T: PartialEq> { pub trait OptionExt<T> {
fn contains(&self, x: &T) -> bool; fn contains(&self, x: &T) -> bool where T: PartialEq;
} }
impl<T: PartialEq> OptionExt<T> for Option<T> { impl<T> OptionExt<T> for Option<T> {
fn contains(&self, x: &T) -> bool { fn contains(&self, x: &T) -> bool where T: PartialEq {
self.as_ref() == Some(x) self.as_ref() == Some(x)
} }
} }

View File

@@ -15,7 +15,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[workspace] [workspace]
[dependencies] [dependencies]
bip39 = { git = "https://github.com/rust-bitcoin/rust-bip39", commit = "f735e2559f30049f6738d1bf68c69a0b7bd7b858", default-features = false } bip39 = { version = "2.2.1", default-features = false }
[features] [features]
default = ["bip39/default"] default = ["bip39/default"]

View File

@@ -1,3 +1,3 @@
#![cfg_attr(not(feature = "std"), no_std)] #![no_std]
pub use bip39::*; pub use bip39::*;

View File

@@ -7,7 +7,6 @@ repository = "https://github.com/serai-dex/serai/tree/develop/patches/simple-req
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["nostd", "no_std", "alloc", "io"] keywords = ["nostd", "no_std", "alloc", "io"]
edition = "2021" edition = "2021"
rust-version = "1.65"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@@ -7,7 +7,6 @@ repository = "https://github.com/serai-dex/serai/tree/develop/patches/std-shims"
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["nostd", "no_std", "alloc", "io"] keywords = ["nostd", "no_std", "alloc", "io"]
edition = "2021" edition = "2021"
rust-version = "1.65"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@@ -28,8 +28,8 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = fals
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ed25519"] } dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ed25519"] }
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false } frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "030c60974f0f0306849c1795bca854a3bbb757b4", default-features = false, features = ["std", "multisig"] } monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "af0368e16395dbdda47cd53e54ccdb78a80b87bf", default-features = false, features = ["std", "multisig"] }
monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "030c60974f0f0306849c1795bca854a3bbb757b4", default-features = false } monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "af0368e16395dbdda47cd53e54ccdb78a80b87bf", default-features = false }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] } serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
serai-client-monero = { path = "../../substrate/client/monero", default-features = false } serai-client-monero = { path = "../../substrate/client/monero", default-features = false }

View File

@@ -1,9 +1,13 @@
use zeroize::Zeroizing; use zeroize::Zeroizing;
use ciphersuite::*; use ciphersuite::{group::ff::PrimeField, WrappedGroup, GroupIo};
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::{address::SubaddressIndex, ViewPairError, GuaranteedViewPair}; use monero_wallet::{
ed25519::{Scalar, CompressedPoint},
address::SubaddressIndex,
ViewPairError, GuaranteedViewPair,
};
use view_keys::view_key; use view_keys::view_key;
@@ -29,7 +33,10 @@ pub(crate) const FORWARDED_SUBADDRESS: SubaddressIndex = match SubaddressIndex::
}; };
pub(crate) fn view_pair(key: <Ed25519 as WrappedGroup>::G) -> GuaranteedViewPair { pub(crate) fn view_pair(key: <Ed25519 as WrappedGroup>::G) -> GuaranteedViewPair {
match GuaranteedViewPair::new(key.0, Zeroizing::new(view_key::<Ed25519>(0))) { match GuaranteedViewPair::new(
CompressedPoint::from(key.0.compress().to_bytes()).decompress().unwrap(),
Zeroizing::new(Scalar::read(&mut view_key::<Ed25519>(0).to_repr().as_slice()).unwrap()),
) {
Ok(view_pair) => view_pair, Ok(view_pair) => view_pair,
Err(ViewPairError::TorsionedSpendKey) => { Err(ViewPairError::TorsionedSpendKey) => {
unreachable!("dalek_ff_group::EdwardsPoint had torsion") unreachable!("dalek_ff_group::EdwardsPoint had torsion")

View File

@@ -1,6 +1,9 @@
use std::io; use std::io;
use ciphersuite::WrappedGroup; use ciphersuite::{
group::{ff::PrimeField, GroupEncoding},
WrappedGroup,
};
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::WalletOutput; use monero_wallet::WalletOutput;
@@ -65,9 +68,11 @@ impl ReceivedOutput<<Ed25519 as WrappedGroup>::G, Address> for Output {
// The spend key will be a key we generated, so it'll be in the prime-order subgroup // The spend key will be a key we generated, so it'll be in the prime-order subgroup
// The output's key is the spend key + (key_offset * G), so it's in the prime-order subgroup if // The output's key is the spend key + (key_offset * G), so it's in the prime-order subgroup if
// the spend key is // the spend key is
dalek_ff_group::EdwardsPoint( dalek_ff_group::EdwardsPoint::from_bytes(&self.0.key().compress().to_bytes()).unwrap() -
self.0.key() - (*<Ed25519 as WrappedGroup>::generator() * self.0.key_offset()), dalek_ff_group::EdwardsPoint(
) *<Ed25519 as WrappedGroup>::generator() *
dalek_ff_group::Scalar::from_repr(<[u8; 32]>::from(self.0.key_offset())).unwrap(),
)
} }
fn presumed_origin(&self) -> Option<Address> { fn presumed_origin(&self) -> Option<Address> {

View File

@@ -7,7 +7,7 @@ use rand_chacha::ChaCha20Rng;
use ciphersuite::*; use ciphersuite::*;
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::interface::prelude::*; use monero_wallet::{ed25519::CompressedPoint, interface::prelude::*};
use serai_primitives::{coin::ExternalCoin, balance::Amount}; use serai_primitives::{coin::ExternalCoin, balance::Amount};
use serai_client_monero::Address; use serai_client_monero::Address;
@@ -116,8 +116,8 @@ async fn signable_transaction(
MoneroAddress::new( MoneroAddress::new(
Network::Mainnet, Network::Mainnet,
AddressType::Legacy, AddressType::Legacy,
<Ed25519 as WrappedGroup>::generator().0, CompressedPoint::G.decompress().unwrap(),
<Ed25519 as WrappedGroup>::generator().0, CompressedPoint::G.decompress().unwrap(),
), ),
0, 0,
)); ));

View File

@@ -22,12 +22,12 @@ workspace = true
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] } borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
bitvec = { version = "1", default-features = false, features = ["alloc"] } bitvec = { version = "1", default-features = false, features = ["alloc"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
serde = { version = "1", default-features = false, features = ["derive"], optional = true } serde = { version = "1", default-features = false, features = ["derive"], optional = true }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, features = ["serde"], optional = true } sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false, features = ["serde"], optional = true }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, optional = true } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false, optional = true }
serai-primitives = { path = "../primitives", version = "0.1", default-features = false } serai-primitives = { path = "../primitives", version = "0.1", default-features = false }

View File

@@ -63,6 +63,11 @@ pub struct HeaderV1 {
pub consensus_commitment: [u8; 32], pub consensus_commitment: [u8; 32],
} }
impl HeaderV1 {
/// The size of a serialized V1 header.
pub const SIZE: usize = 8 + 32 + 8 + 32 + 32 + 32;
}
/// A header for a block. /// A header for a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] #[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Header { pub enum Header {
@@ -71,6 +76,9 @@ pub enum Header {
} }
impl Header { impl Header {
/// The size of a serialized header.
pub const SIZE: usize = 1 + HeaderV1::SIZE;
/// Get the hash of the header. /// Get the hash of the header.
pub fn number(&self) -> u64 { pub fn number(&self) -> u64 {
match self { match self {
@@ -109,8 +117,8 @@ impl Header {
/// A block. /// A block.
/// ///
/// This does not guarantee consistency. The header's `transactions_root` may not match the /// This does not guarantee consistency nor validity. The header's `transactions_root` may not
/// contained transactions. /// match the contained transactions, among other ill effects.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct Block { pub struct Block {
/// The block's header. /// The block's header.
@@ -119,6 +127,13 @@ pub struct Block {
pub transactions: Vec<Transaction>, pub transactions: Vec<Transaction>,
} }
impl Block {
/// The size limit for a block.
///
/// This is not enforced upon deserialization. Be careful accordingly.
pub const SIZE_LIMIT: usize = 512 * 1024;
}
#[cfg(feature = "substrate")] #[cfg(feature = "substrate")]
mod substrate { mod substrate {
use core::fmt::Debug; use core::fmt::Debug;
@@ -133,7 +148,7 @@ mod substrate {
use super::*; use super::*;
// Add `serde` implementations which treat self as a `Vec<u8>` // Add `serde` implementations which treat `self` as a `Vec<u8>`
impl sp_core::serde::Serialize for Transaction { impl sp_core::serde::Serialize for Transaction {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where

View File

@@ -8,21 +8,26 @@ use serai_primitives::{
balance::{Amount, ExternalBalance, Balance}, balance::{Amount, ExternalBalance, Balance},
}; };
/// The address used for a liquidity pool by the DEX.
pub fn address(coin: ExternalCoin) -> SeraiAddress {
SeraiAddress::system(borsh::to_vec(&(b"DEX", coin)).unwrap())
}
/// A call to the DEX. /// A call to the DEX.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Call { pub enum Call {
/// Add liquidity. /// Add liquidity.
add_liquidity { add_liquidity {
/// The coin to add liquidity for. /// The pool to add liquidity to, specified by its external coin.
coin: ExternalCoin, external_coin: ExternalCoin,
/// The intended amount of SRI to add as liquidity. /// The intended amount of SRI to add as liquidity.
sri_intended: Amount, sri_intended: Amount,
/// The intended amount of the coin to add as liquidity. /// The intended amount of the coin to add as liquidity.
coin_intended: Amount, external_coin_intended: Amount,
/// The minimum amount of SRI to add as liquidity. /// The minimum amount of SRI to add as liquidity.
sri_minimum: Amount, sri_minimum: Amount,
/// The minimum amount of the coin to add as liquidity. /// The minimum amount of the coin to add as liquidity.
coin_minimum: Amount, external_coin_minimum: Amount,
}, },
/// Transfer these liquidity tokens to the specified address. /// Transfer these liquidity tokens to the specified address.
transfer_liquidity { transfer_liquidity {
@@ -40,17 +45,17 @@ pub enum Call {
/// The minimum amount of SRI to receive. /// The minimum amount of SRI to receive.
sri_minimum: Amount, sri_minimum: Amount,
/// The minimum amount of the coin to receive. /// The minimum amount of the coin to receive.
coin_minimum: Amount, external_coin_minimum: Amount,
}, },
/// Swap an exact amount of coins. /// Swap an exact amount of coins.
swap_exact { swap {
/// The coins to swap. /// The coins to swap.
coins_to_swap: Balance, coins_to_swap: Balance,
/// The minimum balance to receive. /// The minimum balance to receive.
minimum_to_receive: Balance, minimum_to_receive: Balance,
}, },
/// Swap for an exact amount of coins. /// Swap for an exact amount of coins.
swap_for_exact { swap_for {
/// The coins to receive. /// The coins to receive.
coins_to_receive: Balance, coins_to_receive: Balance,
/// The maximum amount to swap. /// The maximum amount to swap.
@@ -64,8 +69,8 @@ impl Call {
Call::add_liquidity { .. } | Call::add_liquidity { .. } |
Call::transfer_liquidity { .. } | Call::transfer_liquidity { .. } |
Call::remove_liquidity { .. } | Call::remove_liquidity { .. } |
Call::swap_exact { .. } | Call::swap { .. } |
Call::swap_for_exact { .. } => true, Call::swap_for { .. } => true,
} }
} }
} }
@@ -74,41 +79,43 @@ impl Call {
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Event { pub enum Event {
/// Liquidity was added to a pool. /// Liquidity was added to a pool.
LiquidityAdded { LiquidityAddition {
/// The account which added the liquidity. /// The account which received the minted liquidity tokens.
origin: SeraiAddress,
/// The account which received the liquidity tokens.
recipient: SeraiAddress, recipient: SeraiAddress,
/// The pool liquidity was added to. /// The liquidity tokens which were minted.
pool: ExternalCoin, liquidity_tokens: ExternalBalance,
/// The amount of liquidity tokens which were minted.
liquidity_tokens_minted: Amount,
/// The amount of the coin which was added to the pool's liquidity.
coin_amount: Amount,
/// The amount of SRI which was added to the pool's liquidity. /// The amount of SRI which was added to the pool's liquidity.
sri_amount: Amount, sri_amount: Amount,
/// The amount of the coin which was added to the pool's liquidity.
external_coin_amount: Amount,
},
/// The specified liquidity tokens were transferred.
LiquidityTransfer {
/// The address transferred from.
from: SeraiAddress,
/// The address transferred to.
to: SeraiAddress,
/// The liquidity tokens transferred.
liquidity_tokens: ExternalBalance,
}, },
/// Liquidity was removed from a pool. /// Liquidity was removed from a pool.
LiquidityRemoved { LiquidityRemoval {
/// The account which removed the liquidity. /// The account which removed the liquidity.
origin: SeraiAddress, from: SeraiAddress,
/// The pool liquidity was removed from. /// The liquidity tokens which were burnt.
pool: ExternalCoin, liquidity_tokens: ExternalBalance,
/// The mount of liquidity tokens which were burnt.
liquidity_tokens_burnt: Amount,
/// The amount of the coin which was removed from the pool's liquidity.
coin_amount: Amount,
/// The amount of SRI which was removed from the pool's liquidity. /// The amount of SRI which was removed from the pool's liquidity.
sri_amount: Amount, sri_amount: Amount,
/// The amount of the coin which was removed from the pool's liquidity.
external_coin_amount: Amount,
}, },
/// A swap through the liquidity pools occurred. /// A swap through the liquidity pools occurred.
Swap { Swap {
/// The account which made the swap. /// The account which made the swap.
origin: SeraiAddress, from: SeraiAddress,
/// The recipient for the output of the swap.
recipient: SeraiAddress,
/// The deltas incurred by the pools. /// The deltas incurred by the pools.
/// ///
/// For a swap of sriABC to sriDEF, this would be /// For a swap of sriABC to sriDEF, this would be

View File

@@ -1,23 +1,38 @@
use borsh::{BorshSerialize, BorshDeserialize}; use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::{ use serai_primitives::{
crypto::Signature, address::SeraiAddress, balance::ExternalBalance, genesis::GenesisValues, crypto::Signature, address::SeraiAddress, coin::ExternalCoin, balance::ExternalBalance,
genesis_liquidity::GenesisValues,
}; };
/// The address used for to hold genesis liquidity for a pool.
pub fn address(coin: ExternalCoin) -> SeraiAddress {
SeraiAddress::system(borsh::to_vec(&(b"GenesisLiquidity", coin)).unwrap())
}
/// A call to the genesis liquidity. /// A call to the genesis liquidity.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Call { pub enum Call {
/// Oraclize the value of non-Bitcoin external coins relative to Bitcoin. /// Oraclize the values of the coins available on genesis, relative to BTC.
///
/// This will trigger the addition of the liquidity into the pools and their initialization.
oraclize_values { oraclize_values {
/// The values of the non-Bitcoin external coins. /// The values of the non-Bitcoin external coins.
values: GenesisValues, values: GenesisValues,
/// The signature by the genesis validators for these values. /// The signature by the genesis validators for these values.
signature: Signature, signature: Signature,
}, },
/// Remove liquidity. /// Transfer genesis liquidity.
remove_liquidity { transfer_genesis_liquidity {
/// The account to transfer the liquidity to.
to: SeraiAddress,
/// The genesis liquidity to transfer.
genesis_liquidity: ExternalBalance,
},
/// Remove genesis liquidity.
remove_genesis_liquidity {
/// The genesis liquidity to remove. /// The genesis liquidity to remove.
balance: ExternalBalance, genesis_liquidity: ExternalBalance,
}, },
} }
@@ -25,7 +40,7 @@ impl Call {
pub(crate) fn is_signed(&self) -> bool { pub(crate) fn is_signed(&self) -> bool {
match self { match self {
Call::oraclize_values { .. } => false, Call::oraclize_values { .. } => false,
Call::remove_liquidity { .. } => true, Call::transfer_genesis_liquidity { .. } | Call::remove_genesis_liquidity { .. } => true,
} }
} }
} }
@@ -38,13 +53,22 @@ pub enum Event {
/// The recipient of the genesis liquidity. /// The recipient of the genesis liquidity.
recipient: SeraiAddress, recipient: SeraiAddress,
/// The coins added as genesis liquidity. /// The coins added as genesis liquidity.
balance: ExternalBalance, genesis_liquidity: ExternalBalance,
},
/// Genesis liquidity added.
GenesisLiquidityTransferred {
/// The address transferred from.
from: SeraiAddress,
/// The address transferred to.
to: SeraiAddress,
/// The genesis liquidity transferred.
genesis_liquidity: ExternalBalance,
}, },
/// Genesis liquidity removed. /// Genesis liquidity removed.
GenesisLiquidityRemoved { GenesisLiquidityRemoved {
/// The account which removed the genesis liquidity. /// The account which removed the genesis liquidity.
origin: SeraiAddress, from: SeraiAddress,
/// The amount of genesis liquidity removed. /// The amount of genesis liquidity removed.
balance: ExternalBalance, genesis_liquidity: ExternalBalance,
}, },
} }

View File

@@ -279,12 +279,13 @@ mod substrate {
/// The implicit context to verify transactions with. /// The implicit context to verify transactions with.
fn implicit_context() -> ImplicitContext; fn implicit_context() -> ImplicitContext;
/// The size of the current block.
fn current_block_size(&self) -> usize;
/// If a block is present in the blockchain. /// If a block is present in the blockchain.
fn block_is_present_in_blockchain(&self, hash: &BlockHash) -> bool; fn block_is_present_in_blockchain(&self, hash: &BlockHash) -> bool;
/// The time embedded into the current block. /// The time embedded into the current block.
/// fn current_time(&self) -> u64;
/// Returns `None` if the time has yet to be set.
fn current_time(&self) -> Option<u64>;
/// Get the next nonce for an account. /// Get the next nonce for an account.
fn next_nonce(&self, signer: &SeraiAddress) -> u32; fn next_nonce(&self, signer: &SeraiAddress) -> u32;
/// If the signer can pay the SRI fee. /// If the signer can pay the SRI fee.
@@ -295,7 +296,7 @@ mod substrate {
) -> Result<(), TransactionValidityError>; ) -> Result<(), TransactionValidityError>;
/// Begin execution of a transaction. /// Begin execution of a transaction.
fn start_transaction(&self); fn start_transaction(&self, len: usize);
/// Consume the next nonce for an account. /// Consume the next nonce for an account.
/// ///
/// This MUST NOT be called if the next nonce is `u32::MAX`. The caller MAY panic in that case. /// This MUST NOT be called if the next nonce is `u32::MAX`. The caller MAY panic in that case.
@@ -390,9 +391,14 @@ mod substrate {
impl<Context: TransactionContext> TransactionWithContext<Context> { impl<Context: TransactionContext> TransactionWithContext<Context> {
fn validate_except_fee<V: ValidateUnsigned<Call = Context::RuntimeCall>>( fn validate_except_fee<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
&self, &self,
len: usize,
source: TransactionSource, source: TransactionSource,
mempool_priority_if_signed: u64, mempool_priority_if_signed: u64,
) -> TransactionValidity { ) -> TransactionValidity {
if self.1.current_block_size().saturating_add(len) > crate::Block::SIZE_LIMIT {
Err(TransactionValidityError::Invalid(InvalidTransaction::ExhaustsResources))?;
}
match &self.0 { match &self.0 {
Transaction::Unsigned { call } => { Transaction::Unsigned { call } => {
let ValidTransaction { priority: _, requires, provides, longevity: _, propagate: _ } = let ValidTransaction { priority: _, requires, provides, longevity: _, propagate: _ } =
@@ -417,13 +423,8 @@ mod substrate {
Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup))?; Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup))?;
} }
if let Some(include_by) = *include_by { if let Some(include_by) = *include_by {
if let Some(current_time) = self.1.current_time() { if self.1.current_time() >= u64::from(include_by) {
if current_time >= u64::from(include_by) { // Since this transaction has a time bound which has passed, error
// Since this transaction has a time bound which has passed, error
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
}
} else {
// Since this transaction has a time bound, yet we don't know the time, error
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?; Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
} }
} }
@@ -471,7 +472,7 @@ mod substrate {
&self, &self,
source: TransactionSource, source: TransactionSource,
info: &DispatchInfo, info: &DispatchInfo,
_len: usize, len: usize,
) -> TransactionValidity { ) -> TransactionValidity {
let mempool_priority_if_signed = match &self.0 { let mempool_priority_if_signed = match &self.0 {
Transaction::Unsigned { .. } => { Transaction::Unsigned { .. } => {
@@ -493,19 +494,19 @@ mod substrate {
} }
} }
}; };
self.validate_except_fee::<V>(source, mempool_priority_if_signed) self.validate_except_fee::<V>(len, source, mempool_priority_if_signed)
} }
fn apply<V: ValidateUnsigned<Call = Context::RuntimeCall>>( fn apply<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
self, self,
_info: &DispatchInfo, _info: &DispatchInfo,
_len: usize, len: usize,
) -> sp_runtime::ApplyExtrinsicResultWithInfo<PostDispatchInfo> { ) -> sp_runtime::ApplyExtrinsicResultWithInfo<PostDispatchInfo> {
// We use 0 for the mempool priority, as this is no longer in the mempool so it's irrelevant // We use 0 for the mempool priority, as this is no longer in the mempool so it's irrelevant
self.validate_except_fee::<V>(TransactionSource::InBlock, 0)?; self.validate_except_fee::<V>(len, TransactionSource::InBlock, 0)?;
// Start the transaction // Start the transaction
self.1.start_transaction(); self.1.start_transaction(len);
let transaction_hash = self.0.hash(); let transaction_hash = self.0.hash();

View File

@@ -23,4 +23,6 @@ serai-primitives = { path = "../../primitives", version = "0.1", default-feature
ciphersuite = { path = "../../../crypto/ciphersuite", default-features = false, features = ["std"] } ciphersuite = { path = "../../../crypto/ciphersuite", default-features = false, features = ["std"] }
dalek-ff-group = { path = "../../../crypto/dalek-ff-group", default-features = false, features = ["std"] } dalek-ff-group = { path = "../../../crypto/dalek-ff-group", default-features = false, features = ["std"] }
monero-address = { git = "https://github.com/monero-oxide/monero-oxide", rev = "030c60974f0f0306849c1795bca854a3bbb757b4", version = "0.1.0", default-features = false, features = ["std"] }
monero-ed25519 = { git = "https://github.com/monero-oxide/monero-oxide", rev = "af0368e16395dbdda47cd53e54ccdb78a80b87bf", version = "0.1.0", default-features = false, features = ["std"] }
monero-address = { git = "https://github.com/monero-oxide/monero-oxide", rev = "af0368e16395dbdda47cd53e54ccdb78a80b87bf", version = "0.1.0", default-features = false, features = ["std"] }

View File

@@ -4,39 +4,43 @@
use core::{str::FromStr, fmt}; use core::{str::FromStr, fmt};
use dalek_ff_group::{EdwardsPoint, Ed25519}; use ciphersuite::{group::GroupEncoding, GroupIo};
use ciphersuite::GroupIo; use dalek_ff_group::Ed25519;
use monero_ed25519::{CompressedPoint, Point};
use monero_address::{Network, AddressType as MoneroAddressType, MoneroAddress}; use monero_address::{Network, AddressType as MoneroAddressType, MoneroAddress};
use serai_primitives::address::ExternalAddress; use serai_primitives::address::ExternalAddress;
#[allow(non_snake_case)]
fn read_G(reader: &mut impl borsh::io::Read) -> borsh::io::Result<Point> {
// We use `Ed25519::read_G` for the strong canonicalization requirements before using
//` monero-ed25519` for the actual values
CompressedPoint::from(Ed25519::read_G(reader)?.to_bytes()).decompress().ok_or_else(|| {
borsh::io::Error::other(
"canonically-encoded torsion-free point was rejected by `monero-ed25519`",
)
})
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum AddressType { enum AddressType {
Legacy, Legacy,
Subaddress, Subaddress,
Featured(u8),
} }
/// A representation of a Monero address. /// A representation of a Monero address.
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Address { pub struct Address {
kind: AddressType, kind: AddressType,
spend: EdwardsPoint, spend: Point,
view: EdwardsPoint, view: Point,
} }
fn byte_for_kind(kind: AddressType) -> u8 { fn byte_for_kind(kind: AddressType) -> u8 {
// We use the second and third highest bits for the type
// This leaves the top bit open for interpretation as a VarInt later
match kind { match kind {
AddressType::Legacy => 0, AddressType::Legacy => 0,
AddressType::Subaddress => 1 << 5, AddressType::Subaddress => 1,
AddressType::Featured(flags) => {
// The flags only take up the low three bits
debug_assert!(flags <= 0b111);
(2 << 5) | flags
}
} }
} }
@@ -52,18 +56,13 @@ impl borsh::BorshDeserialize for Address {
let mut kind_byte = [0xff]; let mut kind_byte = [0xff];
reader.read_exact(&mut kind_byte)?; reader.read_exact(&mut kind_byte)?;
let kind_byte = kind_byte[0]; let kind_byte = kind_byte[0];
let kind = match kind_byte >> 5 { let kind = match kind_byte {
0 => AddressType::Legacy, 0 => AddressType::Legacy,
1 => AddressType::Subaddress, 1 => AddressType::Subaddress,
2 => AddressType::Featured(kind_byte & 0b111),
_ => Err(borsh::io::Error::other("unrecognized type"))?, _ => Err(borsh::io::Error::other("unrecognized type"))?,
}; };
// Check this wasn't malleated let spend = read_G(reader)?;
if byte_for_kind(kind) != kind_byte { let view = read_G(reader)?;
Err(borsh::io::Error::other("malleated type byte"))?;
}
let spend = Ed25519::read_G(reader)?;
let view = Ed25519::read_G(reader)?;
Ok(Self { kind, spend, view }) Ok(Self { kind, spend, view })
} }
} }
@@ -75,20 +74,13 @@ impl TryFrom<MoneroAddress> for Address {
let view = address.view().compress().to_bytes(); let view = address.view().compress().to_bytes();
let kind = match address.kind() { let kind = match address.kind() {
MoneroAddressType::Legacy => AddressType::Legacy, MoneroAddressType::Legacy => AddressType::Legacy,
MoneroAddressType::LegacyIntegrated(_) => Err(())?,
MoneroAddressType::Subaddress => AddressType::Subaddress, MoneroAddressType::Subaddress => AddressType::Subaddress,
MoneroAddressType::Featured { subaddress, payment_id, guaranteed } => { MoneroAddressType::LegacyIntegrated(_) | MoneroAddressType::Featured { .. } => Err(())?,
if payment_id.is_some() {
Err(())?
}
// This maintains the same bit layout as featured addresses use
AddressType::Featured(u8::from(*subaddress) + (u8::from(*guaranteed) << 2))
}
}; };
Ok(Address { Ok(Address {
kind, kind,
spend: Ed25519::read_G(&mut spend.as_slice()).map_err(|_| ())?, spend: read_G(&mut spend.as_slice()).map_err(|_| ())?,
view: Ed25519::read_G(&mut view.as_slice()).map_err(|_| ())?, view: read_G(&mut view.as_slice()).map_err(|_| ())?,
}) })
} }
} }
@@ -98,16 +90,8 @@ impl From<Address> for MoneroAddress {
let kind = match address.kind { let kind = match address.kind {
AddressType::Legacy => MoneroAddressType::Legacy, AddressType::Legacy => MoneroAddressType::Legacy,
AddressType::Subaddress => MoneroAddressType::Subaddress, AddressType::Subaddress => MoneroAddressType::Subaddress,
AddressType::Featured(features) => {
debug_assert!(features <= 0b111);
let subaddress = (features & 1) != 0;
let integrated = (features & (1 << 1)) != 0;
debug_assert!(!integrated);
let guaranteed = (features & (1 << 2)) != 0;
MoneroAddressType::Featured { subaddress, payment_id: None, guaranteed }
}
}; };
MoneroAddress::new(Network::Mainnet, kind, address.spend.0, address.view.0) MoneroAddress::new(Network::Mainnet, kind, address.spend, address.view)
} }
} }

View File

@@ -30,7 +30,7 @@ serai-abi = { path = "../../abi", version = "0.1", default-features = false, fea
[dev-dependencies] [dev-dependencies]
blake2 = { version = "0.11.0-rc.3", default-features = false } blake2 = { version = "0.11.0-rc.3", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk" }
tokio = { version = "1", default-features = false, features = ["rt", "macros"] } tokio = { version = "1", default-features = false, features = ["rt", "macros"] }
dockertest = "0.5" dockertest = "0.5"

View File

@@ -4,7 +4,7 @@ version = "0.1.0"
description = "Coins pallet for Serai" description = "Coins pallet for Serai"
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/coins" repository = "https://github.com/serai-dex/serai/tree/develop/substrate/coins"
authors = ["Akil Demir <akildemir72@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021" edition = "2021"
rust-version = "1.85" rust-version = "1.85"
@@ -18,10 +18,10 @@ workspace = true
[dependencies] [dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] } serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-core-pallet = { path = "../core", default-features = false } serai-core-pallet = { path = "../core", default-features = false }
@@ -29,9 +29,9 @@ serai-core-pallet = { path = "../core", default-features = false }
[dev-dependencies] [dev-dependencies]
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, features = ["std"] } sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false, features = ["std"] }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, features = ["std"] } pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false, features = ["std"] }
[features] [features]
std = [ std = [

View File

@@ -227,6 +227,13 @@ mod pallet {
Self::emit_event(Event::Transfer { from: from.into(), to: to.into(), coins }); Self::emit_event(Event::Transfer { from: from.into(), to: to.into(), coins });
Ok(()) Ok(())
} }
/// Burn `coins` from `from`.
pub fn burn_fn(from: Public, coins: Balance) -> Result<(), Error<T, I>> {
Self::burn_internal(from, coins)?;
Self::emit_event(Event::Burn { from: from.into(), coins });
Ok(())
}
} }
#[pallet::call] #[pallet::call]
@@ -245,8 +252,7 @@ mod pallet {
#[pallet::weight((0, DispatchClass::Normal))] // TODO #[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn burn(origin: OriginFor<T>, coins: Balance) -> DispatchResult { pub fn burn(origin: OriginFor<T>, coins: Balance) -> DispatchResult {
let from = ensure_signed(origin)?; let from = ensure_signed(origin)?;
Self::burn_internal(from, coins)?; Self::burn_fn(from, coins)?;
Self::emit_event(Event::Burn { from: from.into(), coins });
Ok(()) Ok(())
} }

View File

@@ -1,4 +1,4 @@
//! Test environment for Coins pallet. //! Test environment for the Coins pallet.
use borsh::BorshDeserialize; use borsh::BorshDeserialize;
@@ -21,6 +21,8 @@ impl frame_system::Config for Test {
type AccountId = sp_core::sr25519::Public; type AccountId = sp_core::sr25519::Public;
type Lookup = frame_support::sp_runtime::traits::IdentityLookup<Self::AccountId>; type Lookup = frame_support::sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Block = frame_system::mocking::MockBlock<Test>; type Block = frame_system::mocking::MockBlock<Test>;
type BlockLength = serai_core_pallet::Limits;
type BlockWeights = serai_core_pallet::Limits;
} }
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]

View File

@@ -10,7 +10,7 @@ pub type CoinsEvent = serai_abi::coins::Event;
#[test] #[test]
fn mint() { fn mint() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
Core::start_transaction(); Core::start_transaction(0);
// minting u64::MAX should work // minting u64::MAX should work
let coin = Coin::Serai; let coin = Coin::Serai;
@@ -51,7 +51,7 @@ fn mint() {
#[test] #[test]
fn burn_with_instruction() { fn burn_with_instruction() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
Core::start_transaction(); Core::start_transaction(0);
// mint some coin // mint some coin
let coin = Coin::External(ExternalCoin::Bitcoin); let coin = Coin::External(ExternalCoin::Bitcoin);
@@ -106,7 +106,7 @@ fn burn_with_instruction() {
#[test] #[test]
fn transfer() { fn transfer() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
Core::start_transaction(); Core::start_transaction(0);
// mint some coin // mint some coin
let coin = Coin::External(ExternalCoin::Bitcoin); let coin = Coin::External(ExternalCoin::Bitcoin);

View File

@@ -20,12 +20,12 @@ borsh = { version = "1", default-features = false, features = ["derive", "de_str
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] } serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }

View File

@@ -8,6 +8,9 @@ extern crate alloc;
use frame_support::traits::{PreInherents, PostTransactions}; use frame_support::traits::{PreInherents, PostTransactions};
mod limits;
pub use limits::Limits;
mod iumt; mod iumt;
pub use iumt::*; pub use iumt::*;
@@ -83,7 +86,8 @@ pub mod pallet {
#[pallet::config] #[pallet::config]
pub trait Config: pub trait Config:
frame_system::Config<Hash: Into<[u8; 32]>> + pallet_timestamp::Config<Moment = u64> frame_system::Config<Hash: Into<[u8; 32]>, BlockLength = Limits, BlockWeights = Limits>
+ pallet_timestamp::Config<Moment = u64>
{ {
} }
@@ -120,7 +124,7 @@ pub mod pallet {
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none(); BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none(); BlockEventsCommitmentMerkle::<T>::new_expecting_none();
Self::start_transaction(); Self::start_transaction(0);
<_>::build(config); <_>::build(config);
Self::end_transaction([0; 32]); Self::end_transaction([0; 32]);
@@ -130,7 +134,15 @@ pub mod pallet {
/// The code to run when beginning execution of a transaction. /// The code to run when beginning execution of a transaction.
/// ///
/// The caller MUST ensure two transactions aren't simultaneously started. /// The caller MUST ensure two transactions aren't simultaneously started.
pub fn start_transaction() { pub fn start_transaction(len: usize) {
{
let existing_len = frame_system::AllExtrinsicsLen::<T>::get().unwrap_or(0);
let new_len = existing_len.saturating_add(u32::try_from(len).unwrap_or(u32::MAX));
// We panic here as this should've been caught earlier during validation
assert!(new_len <= u32::try_from(serai_abi::Block::SIZE_LIMIT).unwrap());
frame_system::AllExtrinsicsLen::<T>::set(Some(new_len));
}
TransactionEventsMerkle::<T>::new_expecting_none(); TransactionEventsMerkle::<T>::new_expecting_none();
Self::deposit_event(Event::BeginTransaction); Self::deposit_event(Event::BeginTransaction);
} }
@@ -192,7 +204,21 @@ impl<T: Config> PreInherents for StartOfBlock<T> {
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none(); BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none(); BlockEventsCommitmentMerkle::<T>::new_expecting_none();
Pallet::<T>::start_transaction(); /*
We assign the implicit transaction with the block the length of the block itself: its
header's length and the length of the length-prefix for the list of transactions.
The length-prefix will be a little-endian `u32`, as `Block` will be borsh-serialized
(https://borsh.io).
The length of each actual transaction is expected to be accurate as the SCALE implementation
defers to the `borsh` serialization.
*/
assert!(
frame_system::AllExtrinsicsLen::<T>::get().is_none(),
"AllExtrinsicsLen wasn't killed at the end of the last block"
);
Pallet::<T>::start_transaction(serai_abi::Header::SIZE + 4);
// Handle the `SeraiPreExecutionDigest` // Handle the `SeraiPreExecutionDigest`
/* /*
@@ -220,7 +246,7 @@ impl<T: Config> PreInherents for StartOfBlock<T> {
pub struct EndOfBlock<T: Config>(PhantomData<T>); pub struct EndOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> PostTransactions for EndOfBlock<T> { impl<T: Config> PostTransactions for EndOfBlock<T> {
fn post_transactions() { fn post_transactions() {
Pallet::<T>::start_transaction(); Pallet::<T>::start_transaction(0);
// Other modules' `PostTransactions` // Other modules' `PostTransactions`
@@ -229,6 +255,8 @@ impl<T: Config> PostTransactions for EndOfBlock<T> {
end_of_block_transaction_hash[.. 16].copy_from_slice(&[0xff; 16]); end_of_block_transaction_hash[.. 16].copy_from_slice(&[0xff; 16]);
Pallet::<T>::end_transaction(end_of_block_transaction_hash); Pallet::<T>::end_transaction(end_of_block_transaction_hash);
frame_system::AllExtrinsicsLen::<T>::kill();
use serai_abi::SeraiExecutionDigest; use serai_abi::SeraiExecutionDigest;
frame_system::Pallet::<T>::deposit_log( frame_system::Pallet::<T>::deposit_log(
frame_support::sp_runtime::generic::DigestItem::Consensus( frame_support::sp_runtime::generic::DigestItem::Consensus(

View File

@@ -0,0 +1,34 @@
use sp_core::Get;
use frame_support::weights::Weight;
use frame_system::limits::{BlockLength, BlockWeights};
/// The limits for the Serai protocol.
pub struct Limits;
impl Get<BlockLength> for Limits {
fn get() -> BlockLength {
/*
We do not reserve an allocation for mandatory/operational transactions, assuming they'll be
prioritized in the mempool. This does technically give block producers an inventive to
misbehave by on-purposely favoring paying non-operational transactions over operational
transactions, but ensures the entire block is available to the transactions actually present
in the mempool.
*/
BlockLength::max(u32::try_from(serai_abi::Block::SIZE_LIMIT).unwrap())
}
}
impl Get<BlockWeights> for Limits {
fn get() -> BlockWeights {
/*
While Serai does limit the size of a block, every transaction is expected to operate in
complexity constant to the current state size, regardless of what the state is. Accordingly,
the most efficient set of transactions (basic transfers?) is expected to be within an order
of magnitude of the most expensive transactions (multi-pool swaps?).
Instead of engaging with the complexity within the consensus protocol of metering both
bandwidth and computation, we do not define limits for weights. We do, however, still use the
weight system in order to determine fee rates and ensure prioritization to
computationally-cheaper transactions. That solely serves as mempool policy however.
*/
BlockWeights::simple_max(Weight::MAX)
}
}

View File

@@ -4,7 +4,7 @@ version = "0.1.0"
description = "DEX pallet for Serai" description = "DEX pallet for Serai"
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/dex" repository = "https://github.com/serai-dex/serai/tree/develop/substrate/dex"
authors = ["Parity Technologies <admin@parity.io>, Akil Demir <akildemir72@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021" edition = "2021"
rust-version = "1.85" rust-version = "1.85"
@@ -19,54 +19,53 @@ ignored = ["scale"]
workspace = true workspace = true
[dependencies] [dependencies]
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, optional = true }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false } serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-core-pallet = { path = "../core", default-features = false }
serai-primitives = { path = "../primitives", default-features = false } serai-coins-pallet = { path = "../coins", default-features = false }
[dev-dependencies] [dev-dependencies]
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false, features = ["std"] }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false, features = ["std"] }
[features] [features]
default = ["std"]
std = [ std = [
"scale/std", "scale/std",
"sp-std/std",
"sp-io/std",
"sp-api/std",
"sp-runtime/std",
"sp-core/std", "sp-core/std",
"serai-primitives/std",
"frame-system/std", "frame-system/std",
"frame-support/std", "frame-support/std",
"frame-benchmarking?/std",
"coins-pallet/std", "serai-abi/std",
"serai-core-pallet/std",
"serai-coins-pallet/std",
] ]
runtime-benchmarks = [
"sp-runtime/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
]
try-runtime = [ try-runtime = [
"sp-runtime/try-runtime",
"frame-system/try-runtime", "frame-system/try-runtime",
"frame-support/try-runtime", "frame-support/try-runtime",
"serai-abi/try-runtime",
"serai-core-pallet/try-runtime",
"serai-coins-pallet/try-runtime",
] ]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
"serai-coins-pallet/runtime-benchmarks",
]
default = ["std"]

View File

@@ -1,211 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
NOTE
Individual files contain the following tag instead of the full license
text.
SPDX-License-Identifier: Apache-2.0
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/

3
substrate/dex/README.md Normal file
View File

@@ -0,0 +1,3 @@
# DEX Pallet
Pallet implementing the necessary DEX logic for the Serai protocol.

View File

@@ -1,230 +0,0 @@
// This file was originally:
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// It has been forked into a crate distributed under the AGPL 3.0.
// Please check the current distribution for up-to-date copyright and licensing information.
//! Dex pallet benchmarking.
use super::*;
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_support::{assert_ok, storage::bounded::BoundedVec};
use frame_system::RawOrigin as SystemOrigin;
use sp_runtime::traits::StaticLookup;
use sp_std::{ops::Div, prelude::*};
use serai_primitives::{Amount, Balance};
use crate::Pallet as Dex;
use coins_pallet::Pallet as Coins;
const INITIAL_COIN_BALANCE: u64 = 1_000_000_000;
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
type LiquidityTokens<T> = coins_pallet::Pallet<T, coins_pallet::Instance1>;
fn create_coin<T: Config>(coin: &ExternalCoin) -> (T::AccountId, AccountIdLookupOf<T>) {
let caller: T::AccountId = whitelisted_caller();
let caller_lookup = T::Lookup::unlookup(caller);
assert_ok!(Coins::<T>::mint(
caller,
Balance { coin: Coin::native(), amount: Amount(SubstrateAmount::MAX.div(1000u64)) }
));
assert_ok!(Coins::<T>::mint(
caller,
Balance { coin: (*coin).into(), amount: Amount(INITIAL_COIN_BALANCE) }
));
(caller, caller_lookup)
}
fn create_coin_and_pool<T: Config>(
coin: &ExternalCoin,
) -> (ExternalCoin, T::AccountId, AccountIdLookupOf<T>) {
let (caller, caller_lookup) = create_coin::<T>(coin);
assert_ok!(Dex::<T>::create_pool(*coin));
(*coin, caller, caller_lookup)
}
benchmarks! {
add_liquidity {
let coin1 = Coin::native();
let coin2 = ExternalCoin::Bitcoin;
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin2);
let add_amount: u64 = 1000;
}: _(
SystemOrigin::Signed(caller),
coin2,
1000u64,
add_amount,
0u64,
0u64,
caller
)
verify {
let pool_id = Dex::<T>::get_pool_id(coin1, coin2.into()).unwrap();
let lp_minted = Dex::<T>::calc_lp_amount_for_zero_supply(
add_amount,
1000u64,
).unwrap();
assert_eq!(
LiquidityTokens::<T>::balance(caller, lp_token.into()).0,
lp_minted
);
assert_eq!(
Coins::<T>::balance(Dex::<T>::get_pool_account(pool_id), Coin::native()).0,
add_amount
);
assert_eq!(
Coins::<T>::balance(
Dex::<T>::get_pool_account(pool_id),
ExternalCoin::Bitcoin.into(),
).0,
1000
);
}
remove_liquidity {
let coin1 = Coin::native();
let coin2 = ExternalCoin::Monero;
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin2);
let add_amount: u64 = 100;
let lp_minted = Dex::<T>::calc_lp_amount_for_zero_supply(
add_amount,
1000u64
).unwrap();
let remove_lp_amount: u64 = lp_minted.checked_div(10).unwrap();
Dex::<T>::add_liquidity(
SystemOrigin::Signed(caller).into(),
coin2,
1000u64,
add_amount,
0u64,
0u64,
caller,
)?;
let total_supply = LiquidityTokens::<T>::supply(Coin::from(lp_token));
}: _(
SystemOrigin::Signed(caller),
coin2,
remove_lp_amount,
0u64,
0u64,
caller
)
verify {
let new_total_supply = LiquidityTokens::<T>::supply(Coin::from(lp_token));
assert_eq!(
new_total_supply,
total_supply - remove_lp_amount
);
}
swap_exact_tokens_for_tokens {
let native = Coin::native();
let coin1 = ExternalCoin::Bitcoin;
let coin2 = ExternalCoin::Ether;
let (_, caller, _) = create_coin_and_pool::<T>(&coin1);
let (_, _) = create_coin::<T>(&coin2);
Dex::<T>::add_liquidity(
SystemOrigin::Signed(caller).into(),
coin1,
200u64,
// TODO: this call otherwise fails with `InsufficientLiquidityMinted` if we don't multiply
// with 3. Might be again related to their expectance on ed being > 1.
100 * 3,
0u64,
0u64,
caller,
)?;
let swap_amount = 100u64;
// since we only allow the native-coin pools, then the worst case scenario would be to swap
// coin1-native-coin2
Dex::<T>::create_pool(coin2)?;
Dex::<T>::add_liquidity(
SystemOrigin::Signed(caller).into(),
coin2,
1000u64,
500,
0u64,
0u64,
caller,
)?;
let path = vec![Coin::from(coin1), native, Coin::from(coin2)];
let path = BoundedVec::<_, T::MaxSwapPathLength>::try_from(path).unwrap();
let native_balance = Coins::<T>::balance(caller, native).0;
let coin1_balance = Coins::<T>::balance(caller, ExternalCoin::Bitcoin.into()).0;
}: _(SystemOrigin::Signed(caller), path, swap_amount, 1u64, caller)
verify {
let ed_bump = 2u64;
let new_coin1_balance = Coins::<T>::balance(caller, ExternalCoin::Bitcoin.into()).0;
assert_eq!(new_coin1_balance, coin1_balance - 100u64);
}
swap_tokens_for_exact_tokens {
let native = Coin::native();
let coin1 = ExternalCoin::Bitcoin;
let coin2 = ExternalCoin::Ether;
let (_, caller, _) = create_coin_and_pool::<T>(&coin1);
let (_, _) = create_coin::<T>(&coin2);
Dex::<T>::add_liquidity(
SystemOrigin::Signed(caller).into(),
coin1,
500u64,
1000,
0u64,
0u64,
caller,
)?;
// since we only allow the native-coin pools, then the worst case scenario would be to swap
// coin1-native-coin2
Dex::<T>::create_pool(coin2)?;
Dex::<T>::add_liquidity(
SystemOrigin::Signed(caller).into(),
coin2,
1000u64,
500,
0u64,
0u64,
caller,
)?;
let path = vec![Coin::from(coin1), native, Coin::from(coin2)];
let path: BoundedVec<_, T::MaxSwapPathLength> = BoundedVec::try_from(path).unwrap();
let coin2_balance = Coins::<T>::balance(caller, ExternalCoin::Ether.into()).0;
}: _(
SystemOrigin::Signed(caller),
path.clone(),
100u64,
1000,
caller
)
verify {
let new_coin2_balance = Coins::<T>::balance(caller, ExternalCoin::Ether.into()).0;
assert_eq!(new_coin2_balance, coin2_balance + 100u64);
}
impl_benchmark_test_suite!(Dex, crate::mock::new_test_ext(), crate::mock::Test);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,126 +1,64 @@
// This file was originally: //! Test environment for the DEX pallet.
// Copyright (C) Parity Technologies (UK) Ltd. use borsh::BorshDeserialize;
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License"); use frame_support::{sp_runtime::BuildStorage, derive_impl, construct_runtime};
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// It has been forked into a crate distributed under the AGPL 3.0. use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance};
// Please check the current distribution for up-to-date copyright and licensing information.
//! Test environment for Dex pallet.
use super::*;
use crate as dex; use crate as dex;
use frame_support::{
construct_runtime,
traits::{ConstU16, ConstU32, ConstU64},
};
use sp_core::{H256, sr25519::Public};
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
use serai_primitives::{Coin, Balance, Amount, system_address};
pub use coins_pallet as coins;
type Block = frame_system::mocking::MockBlock<Test>;
pub const MEDIAN_PRICE_WINDOW_LENGTH: u16 = 10;
construct_runtime!( construct_runtime!(
pub enum Test pub enum Test
{ {
System: frame_system, System: frame_system,
CoinsPallet: coins, Timestamp: pallet_timestamp,
LiquidityTokens: coins::<Instance1>::{Pallet, Call, Storage, Event<T>}, Core: serai_core_pallet,
Coins: serai_coins_pallet::<CoinsInstance>,
LiquidityTokens: serai_coins_pallet::<LiquidityTokensInstance>,
Dex: dex, Dex: dex,
} }
); );
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test { impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything; type AccountId = sp_core::sr25519::Public;
type BlockWeights = (); type Lookup = frame_support::sp_runtime::traits::IdentityLookup<Self::AccountId>;
type BlockLength = (); type Block = frame_system::mocking::MockBlock<Test>;
type RuntimeOrigin = RuntimeOrigin; type BlockLength = serai_core_pallet::Limits;
type RuntimeCall = RuntimeCall; type BlockWeights = serai_core_pallet::Limits;
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = Public;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
} }
impl coins::Config for Test { #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]
type RuntimeEvent = RuntimeEvent; impl pallet_timestamp::Config for Test {}
type AllowMint = ();
impl serai_core_pallet::Config for Test {}
impl serai_coins_pallet::Config<CoinsInstance> for Test {
type AllowMint = serai_coins_pallet::AlwaysAllowMint;
} }
impl coins::Config<coins::Instance1> for Test { impl serai_coins_pallet::Config<LiquidityTokensInstance> for Test {
type RuntimeEvent = RuntimeEvent; type AllowMint = serai_coins_pallet::AlwaysAllowMint;
type AllowMint = ();
} }
impl Config for Test { impl crate::Config for Test {}
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type LPFee = ConstU32<3>; // means 0.3%
type MaxSwapPathLength = ConstU32<4>;
type MedianPriceWindowLength = ConstU16<{ MEDIAN_PRICE_WINDOW_LENGTH }>;
// 100 is good enough when the main currency has 12 decimals.
type MintMinLiquidity = ConstU64<100>;
}
pub(crate) fn new_test_ext() -> sp_io::TestExternalities { pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap(); let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let accounts: Vec<Public> = vec![ serai_coins_pallet::GenesisConfig::<Test, CoinsInstance> {
system_address(b"account1").into(), accounts: vec![],
system_address(b"account2").into(), _instance: Default::default(),
system_address(b"account3").into(),
system_address(b"account4").into(),
];
coins::GenesisConfig::<Test> {
accounts: accounts
.into_iter()
.map(|a| (a, Balance { coin: Coin::Serai, amount: Amount(1 << 60) }))
.collect(),
_ignore: Default::default(),
} }
.assimilate_storage(&mut t) .assimilate_storage(&mut storage)
.unwrap();
serai_coins_pallet::GenesisConfig::<Test, LiquidityTokensInstance> {
accounts: vec![],
_instance: Default::default(),
}
.assimilate_storage(&mut storage)
.unwrap(); .unwrap();
let mut ext = sp_io::TestExternalities::new(t); storage.into()
ext.execute_with(|| System::set_block_number(1));
ext
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,54 +0,0 @@
// This file was originally:
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// It has been forked into a crate distributed under the AGPL 3.0.
// Please check the current distribution for up-to-date copyright and licensing information.
use super::*;
/// Trait for providing methods to swap between the various coin classes.
pub trait Swap<AccountId, Balance, MultiCoinId> {
/// Swap exactly `amount_in` of coin `path[0]` for coin `path[1]`.
/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
/// the amount desired.
///
/// Withdraws the `path[0]` coin from `sender`, deposits the `path[1]` coin to `send_to`,
///
/// If successful, returns the amount of `path[1]` acquired for the `amount_in`.
fn swap_exact_tokens_for_tokens(
sender: AccountId,
path: Vec<MultiCoinId>,
amount_in: Balance,
amount_out_min: Option<Balance>,
send_to: AccountId,
) -> Result<Balance, DispatchError>;
/// Take the `path[0]` coin and swap some amount for `amount_out` of the `path[1]`. If an
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
/// too costly.
///
/// Withdraws `path[0]` coin from `sender`, deposits `path[1]` coin to `send_to`,
///
/// If successful returns the amount of the `path[0]` taken to provide `path[1]`.
fn swap_tokens_for_exact_tokens(
sender: AccountId,
path: Vec<MultiCoinId>,
amount_out: Balance,
amount_in_max: Option<Balance>,
send_to: AccountId,
) -> Result<Balance, DispatchError>;
}

View File

@@ -1,259 +0,0 @@
// This file was originally:
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// It has been forked into a crate distributed under the AGPL 3.0.
// Please check the current distribution for up-to-date copyright and licensing information.
//! Autogenerated weights for Dex Pallet.
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-07-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-gghbxkbs-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
// Executed Command:
// target/production/substrate
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json
// --pallet=serai_dex_pallet
// --chain=dev
// --header=./HEADER-APACHE2
// --output=./substrate/dex/pallet/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;
/// Weight functions needed for Dex Pallet.
pub trait WeightInfo {
fn create_pool() -> Weight;
fn add_liquidity() -> Weight;
fn remove_liquidity() -> Weight;
fn swap_exact_tokens_for_tokens() -> Weight;
fn swap_tokens_for_exact_tokens() -> Weight;
}
/// Weights for Dex Pallet using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Storage: `DexPallet::Pools` (r:1 w:1)
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:1 w:1)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:1 w:1)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `DexPallet::NextPoolCoinId` (r:1 w:1)
/// Proof: `DexPallet::NextPoolCoinId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Coin` (r:1 w:1)
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Account` (r:1 w:1)
/// Proof: `PoolCoins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn create_pool() -> Weight {
// Proof Size summary in bytes:
// Measured: `729`
// Estimated: `6196`
// Minimum execution time: 131_688_000 picoseconds.
Weight::from_parts(134_092_000, 6196)
.saturating_add(T::DbWeight::get().reads(8_u64))
.saturating_add(T::DbWeight::get().writes(8_u64))
}
/// Storage: `DexPallet::Pools` (r:1 w:0)
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:1 w:1)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:2 w:2)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Coin` (r:1 w:1)
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Account` (r:2 w:2)
/// Proof: `PoolCoins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn add_liquidity() -> Weight {
// Proof Size summary in bytes:
// Measured: `1382`
// Estimated: `6208`
// Minimum execution time: 157_310_000 picoseconds.
Weight::from_parts(161_547_000, 6208)
.saturating_add(T::DbWeight::get().reads(8_u64))
.saturating_add(T::DbWeight::get().writes(7_u64))
}
/// Storage: `DexPallet::Pools` (r:1 w:0)
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:1 w:1)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:2 w:2)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Coin` (r:1 w:1)
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Account` (r:1 w:1)
/// Proof: `PoolCoins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn remove_liquidity() -> Weight {
// Proof Size summary in bytes:
// Measured: `1371`
// Estimated: `6208`
// Minimum execution time: 142_769_000 picoseconds.
Weight::from_parts(145_139_000, 6208)
.saturating_add(T::DbWeight::get().reads(7_u64))
.saturating_add(T::DbWeight::get().writes(6_u64))
}
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:3 w:3)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:6 w:6)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn swap_exact_tokens_for_tokens() -> Weight {
// Proof Size summary in bytes:
// Measured: `1738`
// Estimated: `16644`
// Minimum execution time: 213_186_000 picoseconds.
Weight::from_parts(217_471_000, 16644)
.saturating_add(T::DbWeight::get().reads(10_u64))
.saturating_add(T::DbWeight::get().writes(10_u64))
}
/// Storage: `Coins::Coin` (r:3 w:3)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:6 w:6)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn swap_tokens_for_exact_tokens() -> Weight {
// Proof Size summary in bytes:
// Measured: `1738`
// Estimated: `16644`
// Minimum execution time: 213_793_000 picoseconds.
Weight::from_parts(218_584_000, 16644)
.saturating_add(T::DbWeight::get().reads(10_u64))
.saturating_add(T::DbWeight::get().writes(10_u64))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// Storage: `DexPallet::Pools` (r:1 w:1)
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:1 w:1)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:1 w:1)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `DexPallet::NextPoolCoinId` (r:1 w:1)
/// Proof: `DexPallet::NextPoolCoinId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Coin` (r:1 w:1)
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Account` (r:1 w:1)
/// Proof: `PoolCoins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn create_pool() -> Weight {
// Proof Size summary in bytes:
// Measured: `729`
// Estimated: `6196`
// Minimum execution time: 131_688_000 picoseconds.
Weight::from_parts(134_092_000, 6196)
.saturating_add(RocksDbWeight::get().reads(8_u64))
.saturating_add(RocksDbWeight::get().writes(8_u64))
}
/// Storage: `DexPallet::Pools` (r:1 w:0)
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:1 w:1)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:2 w:2)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Coin` (r:1 w:1)
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Account` (r:2 w:2)
/// Proof: `PoolCoins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn add_liquidity() -> Weight {
// Proof Size summary in bytes:
// Measured: `1382`
// Estimated: `6208`
// Minimum execution time: 157_310_000 picoseconds.
Weight::from_parts(161_547_000, 6208)
.saturating_add(RocksDbWeight::get().reads(8_u64))
.saturating_add(RocksDbWeight::get().writes(7_u64))
}
/// Storage: `DexPallet::Pools` (r:1 w:0)
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:1 w:1)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:2 w:2)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Coin` (r:1 w:1)
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolCoins::Account` (r:1 w:1)
/// Proof: `PoolCoins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn remove_liquidity() -> Weight {
// Proof Size summary in bytes:
// Measured: `1371`
// Estimated: `6208`
// Minimum execution time: 142_769_000 picoseconds.
Weight::from_parts(145_139_000, 6208)
.saturating_add(RocksDbWeight::get().reads(7_u64))
.saturating_add(RocksDbWeight::get().writes(6_u64))
}
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `Coins::Coin` (r:3 w:3)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:6 w:6)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn swap_exact_tokens_for_tokens() -> Weight {
// Proof Size summary in bytes:
// Measured: `1738`
// Estimated: `16644`
// Minimum execution time: 213_186_000 picoseconds.
Weight::from_parts(217_471_000, 16644)
.saturating_add(RocksDbWeight::get().reads(10_u64))
.saturating_add(RocksDbWeight::get().writes(10_u64))
}
/// Storage: `Coins::Coin` (r:3 w:3)
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Coins::Account` (r:6 w:6)
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn swap_tokens_for_exact_tokens() -> Weight {
// Proof Size summary in bytes:
// Measured: `1738`
// Estimated: `16644`
// Minimum execution time: 213_793_000 picoseconds.
Weight::from_parts(218_584_000, 16644)
.saturating_add(RocksDbWeight::get().reads(10_u64))
.saturating_add(RocksDbWeight::get().writes(10_u64))
}
}

View File

@@ -21,8 +21,8 @@ workspace = true
[dependencies] [dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false } dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false } coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
@@ -30,16 +30,16 @@ coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-feat
serai-primitives = { path = "../primitives", default-features = false } serai-primitives = { path = "../primitives", default-features = false }
[dev-dependencies] [dev-dependencies]
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false } validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
[features] [features]
std = [ std = [

View File

@@ -21,11 +21,11 @@ workspace = true
[dependencies] [dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false } coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false } validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }

View File

@@ -1,10 +1,10 @@
[package] [package]
name = "serai-genesis-liquidity-pallet" name = "serai-genesis-liquidity-pallet"
version = "0.1.0" version = "0.1.0"
description = "Genesis liquidity pallet for Serai" description = "Genesis Liquidity pallet for Serai"
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/genesis-liquidity" repository = "https://github.com/serai-dex/serai/tree/develop/substrate/genesis-liquidity"
authors = ["Akil Demir <akildemir72@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021" edition = "2021"
rust-version = "1.85" rust-version = "1.85"
@@ -21,40 +21,48 @@ workspace = true
[dependencies] [dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false } serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false } serai-core-pallet = { path = "../core", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false } serai-coins-pallet = { path = "../coins", default-features = false }
serai-dex-pallet = { path = "../dex", default-features = false }
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../economic-security", default-features = false }
serai-primitives = { path = "../primitives", default-features = false }
[features] [features]
std = [ std = [
"scale/std", "scale/std",
"sp-core/std",
"frame-system/std", "frame-system/std",
"frame-support/std", "frame-support/std",
"sp-std/std", "serai-abi/std",
"sp-core/std", "serai-core-pallet/std",
"sp-application-crypto/std", "serai-coins-pallet/std",
"serai-dex-pallet/std",
"coins-pallet/std", ]
"dex-pallet/std",
"validator-sets-pallet/std", try-runtime = [
"frame-system/try-runtime",
"economic-security-pallet/std", "frame-support/try-runtime",
"serai-primitives/std", "serai-abi/try-runtime",
"serai-core-pallet/try-runtime",
"serai-coins-pallet/try-runtime",
"serai-dex-pallet/try-runtime",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"serai-core-pallet/runtime-benchmarks",
"serai-coins-pallet/runtime-benchmarks",
"serai-dex-pallet/runtime-benchmarks",
] ]
try-runtime = [] # TODO
default = ["std"] default = ["std"]

View File

@@ -1,6 +1,6 @@
AGPL-3.0-only license AGPL-3.0-only license
Copyright (c) 2024-2025 Luke Parker Copyright (c) 2023-2025 Luke Parker
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License Version 3 as it under the terms of the GNU Affero General Public License Version 3 as

View File

@@ -0,0 +1,3 @@
# Genesis Liquidity Pallet
Pallet implementing the Serai protocol's genesis liquidity.

View File

@@ -1,464 +1,102 @@
#![cfg_attr(not(feature = "std"), no_std)] #![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#[allow( extern crate alloc;
unreachable_patterns,
clippy::cast_possible_truncation, #[expect(clippy::cast_possible_truncation)]
clippy::no_effect_underscore_binding,
clippy::empty_docs
)]
#[frame_support::pallet] #[frame_support::pallet]
pub mod pallet { mod pallet {
use frame_system::pallet_prelude::*;
use frame_support::pallet_prelude::*;
use serai_abi::{
primitives::{prelude::*, crypto::Signature, genesis_liquidity::GenesisValues},
genesis_liquidity::Event,
};
use serai_core_pallet::Pallet as Core;
type Coins<T> = serai_coins_pallet::Pallet<T, serai_coins_pallet::CoinsInstance>;
type LiquidityTokens<T> =
serai_coins_pallet::Pallet<T, serai_coins_pallet::LiquidityTokensInstance>;
use super::*; use super::*;
use frame_system::{pallet_prelude::*, RawOrigin};
use frame_support::{pallet_prelude::*, sp_runtime::SaturatedConversion};
use sp_std::{vec, vec::Vec};
use sp_core::sr25519::Signature;
use sp_application_crypto::RuntimePublic;
use dex_pallet::{Pallet as Dex, Config as DexConfig};
use coins_pallet::{Config as CoinsConfig, Pallet as Coins};
use validator_sets_pallet::{Config as VsConfig, Pallet as ValidatorSets};
use economic_security_pallet::{Config as EconomicSecurityConfig, Pallet as EconomicSecurity};
use serai_primitives::*;
use validator_sets_primitives::{ValidatorSet, musig_key};
pub use genesis_liquidity_primitives as primitives;
use primitives::*;
// TODO: Have a more robust way of accessing LiquidityTokens pallet.
/// LiquidityTokens Pallet as an instance of coins pallet.
pub type LiquidityTokens<T> = coins_pallet::Pallet<T, coins_pallet::Instance1>;
/// The configuration of this pallet.
#[pallet::config] #[pallet::config]
pub trait Config: pub trait Config:
frame_system::Config frame_system::Config
+ VsConfig + serai_core_pallet::Config
+ DexConfig + serai_coins_pallet::Config<serai_coins_pallet::CoinsInstance>
+ EconomicSecurityConfig + serai_coins_pallet::Config<serai_coins_pallet::LiquidityTokensInstance>
+ CoinsConfig + serai_dex_pallet::Config
+ coins_pallet::Config<coins_pallet::Instance1>
{ {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
} }
/// An error incurred.
#[pallet::error] #[pallet::error]
pub enum Error<T> { pub enum Error<T> {}
GenesisPeriodEnded,
AmountOverflowed,
NotEnoughLiquidity,
CanOnlyRemoveFullAmount,
}
#[pallet::event]
#[pallet::generate_deposit(fn deposit_event)]
pub enum Event<T: Config> {
GenesisLiquidityAdded { by: SeraiAddress, balance: ExternalBalance },
GenesisLiquidityRemoved { by: SeraiAddress, balance: ExternalBalance },
GenesisLiquidityAddedToPool { coin: ExternalBalance, sri: Amount },
}
/// The Pallet struct.
#[pallet::pallet] #[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>); pub struct Pallet<T>(_);
/// Keeps shares and the amount of coins per account.
#[pallet::storage]
#[pallet::getter(fn liquidity)]
pub(crate) type Liquidity<T: Config> = StorageDoubleMap<
_,
Identity,
ExternalCoin,
Blake2_128Concat,
PublicKey,
LiquidityAmount,
OptionQuery,
>;
/// Keeps the total shares and the total amount of coins per coin.
#[pallet::storage]
#[pallet::getter(fn supply)]
pub(crate) type Supply<T: Config> =
StorageMap<_, Identity, ExternalCoin, LiquidityAmount, OptionQuery>;
#[pallet::storage]
pub(crate) type Oracle<T: Config> = StorageMap<_, Identity, ExternalCoin, u64, OptionQuery>;
#[pallet::storage]
#[pallet::getter(fn genesis_complete_block)]
pub(crate) type GenesisCompleteBlock<T: Config> = StorageValue<_, u64, OptionQuery>;
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(n: BlockNumberFor<T>) -> Weight {
#[cfg(feature = "fast-epoch")]
let final_block = 10u64;
#[cfg(not(feature = "fast-epoch"))]
let final_block = MONTHS;
// Distribute the genesis sri to pools after a month
if (n.saturated_into::<u64>() >= final_block) &&
Self::oraclization_is_done() &&
GenesisCompleteBlock::<T>::get().is_none()
{
// mint the SRI
Coins::<T>::mint(
GENESIS_LIQUIDITY_ACCOUNT.into(),
Balance { coin: Coin::Serai, amount: Amount(GENESIS_SRI) },
)
.unwrap();
// get pool & total values
let mut pool_values = vec![];
let mut total_value: u128 = 0;
for coin in EXTERNAL_COINS {
// initial coin value in terms of btc
let Some(value) = Oracle::<T>::get(coin) else {
continue;
};
let pool_amount =
u128::from(Supply::<T>::get(coin).unwrap_or(LiquidityAmount::zero()).coins);
let pool_value = pool_amount
.checked_mul(value.into())
.unwrap()
.checked_div(10u128.pow(coin.decimals()))
.unwrap();
total_value = total_value.checked_add(pool_value).unwrap();
pool_values.push((coin, pool_amount, pool_value));
}
// add the liquidity per pool
let mut total_sri_distributed = 0;
let pool_values_len = pool_values.len();
for (i, (coin, pool_amount, pool_value)) in pool_values.into_iter().enumerate() {
// whatever sri left for the last coin should be ~= it's ratio
let sri_amount = if i == (pool_values_len - 1) {
GENESIS_SRI.checked_sub(total_sri_distributed).unwrap()
} else {
u64::try_from(
u128::from(GENESIS_SRI)
.checked_mul(pool_value)
.unwrap()
.checked_div(total_value)
.unwrap(),
)
.unwrap()
};
total_sri_distributed = total_sri_distributed.checked_add(sri_amount).unwrap();
// actually add the liquidity to dex
let origin = RawOrigin::Signed(GENESIS_LIQUIDITY_ACCOUNT.into());
let Ok(()) = Dex::<T>::add_liquidity(
origin.into(),
coin,
u64::try_from(pool_amount).unwrap(),
sri_amount,
u64::try_from(pool_amount).unwrap(),
sri_amount,
GENESIS_LIQUIDITY_ACCOUNT.into(),
) else {
continue;
};
// let everyone know about the event
Self::deposit_event(Event::GenesisLiquidityAddedToPool {
coin: ExternalBalance { coin, amount: Amount(u64::try_from(pool_amount).unwrap()) },
sri: Amount(sri_amount),
});
}
assert_eq!(total_sri_distributed, GENESIS_SRI);
// we shouldn't have left any coin in genesis account at this moment, including SRI.
// All transferred to the pools.
for coin in COINS {
assert_eq!(Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), coin), Amount(0));
}
GenesisCompleteBlock::<T>::set(Some(n.saturated_into::<u64>()));
}
Weight::zero() // TODO
}
}
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
/// Add genesis liquidity for the given account. All accounts that provide liquidity fn emit_event(event: Event) {
/// will receive the genesis SRI according to their liquidity ratio. Core::<T>::emit_event(event)
pub fn add_coin_liquidity(account: PublicKey, balance: ExternalBalance) -> DispatchResult {
// check we are still in genesis period
if Self::genesis_ended() {
Err(Error::<T>::GenesisPeriodEnded)?;
}
// calculate new shares & supply
let (new_liquidity, new_supply) = if let Some(supply) = Supply::<T>::get(balance.coin) {
// calculate amount of shares for this amount
let shares = Self::mul_div(supply.shares, balance.amount.0, supply.coins)?;
// get new shares for this account
let existing =
Liquidity::<T>::get(balance.coin, account).unwrap_or(LiquidityAmount::zero());
(
LiquidityAmount {
shares: existing.shares.checked_add(shares).ok_or(Error::<T>::AmountOverflowed)?,
coins: existing
.coins
.checked_add(balance.amount.0)
.ok_or(Error::<T>::AmountOverflowed)?,
},
LiquidityAmount {
shares: supply.shares.checked_add(shares).ok_or(Error::<T>::AmountOverflowed)?,
coins: supply
.coins
.checked_add(balance.amount.0)
.ok_or(Error::<T>::AmountOverflowed)?,
},
)
} else {
let first_amount =
LiquidityAmount { shares: INITIAL_GENESIS_LP_SHARES, coins: balance.amount.0 };
(first_amount, first_amount)
};
// save
Liquidity::<T>::set(balance.coin, account, Some(new_liquidity));
Supply::<T>::set(balance.coin, Some(new_supply));
Self::deposit_event(Event::GenesisLiquidityAdded { by: account.into(), balance });
Ok(())
} }
}
/// Returns the number of blocks since the all networks reached economic security first time. /// The minimum amount of liquidity allowed to be initially added.
/// If networks is yet to be reached that threshold, None is returned. ///
fn blocks_since_ec_security() -> Option<u64> { /// This should be sufficiently low it isn't inaccessible, yet sufficiently high that future
let mut min = u64::MAX; /// additions can be reasonably grained when their share of the new supply is calculated.
for n in EXTERNAL_NETWORKS { ///
let ec_security_block = /// This constant is duplicated with `serai-dex-pallet` intentionally as while they have the same
EconomicSecurity::<T>::economic_security_block(n)?.saturated_into::<u64>(); /// value, they are distinct constants and don't require being equivalent.
let current = <frame_system::Pallet<T>>::block_number().saturated_into::<u64>(); const MINIMUM_LIQUIDITY: u64 = 1 << 16;
let diff = current.saturating_sub(ec_security_block);
min = diff.min(min);
}
Some(min)
}
fn genesis_ended() -> bool { impl<T: Config> Pallet<T> {
Self::oraclization_is_done() && /// Add liquidity on behalf of the specified address.
<frame_system::Pallet<T>>::block_number().saturated_into::<u64>() >= MONTHS pub fn add_liquidity(to: SeraiAddress, balance: ExternalBalance) -> Result<(), Error<T>> {
} todo!("TODO")
fn oraclization_is_done() -> bool {
for c in EXTERNAL_COINS {
if Oracle::<T>::get(c).is_none() {
return false;
}
}
true
}
fn mul_div(a: u64, b: u64, c: u64) -> Result<u64, Error<T>> {
let a = u128::from(a);
let b = u128::from(b);
let c = u128::from(c);
let result = a
.checked_mul(b)
.ok_or(Error::<T>::AmountOverflowed)?
.checked_div(c)
.ok_or(Error::<T>::AmountOverflowed)?;
result.try_into().map_err(|_| Error::<T>::AmountOverflowed)
} }
} }
#[pallet::call] #[pallet::call]
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
/// Remove the provided genesis liquidity for an account. /// Oraclize the values of the coins available on genesis, relative to BTC.
///
/// This will trigger the addition of the liquidity into the pools and their initialization.
#[pallet::call_index(0)] #[pallet::call_index(0)]
#[pallet::weight((0, DispatchClass::Operational))] // TODO #[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn remove_coin_liquidity(origin: OriginFor<T>, balance: ExternalBalance) -> DispatchResult {
let account = ensure_signed(origin)?;
let origin = RawOrigin::Signed(GENESIS_LIQUIDITY_ACCOUNT.into());
let supply = Supply::<T>::get(balance.coin).ok_or(Error::<T>::NotEnoughLiquidity)?;
// check we are still in genesis period
let (new_liquidity, new_supply) = if Self::genesis_ended() {
// see how much liq tokens we have
let total_liq_tokens =
LiquidityTokens::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), Coin::Serai).0;
// get how much user wants to remove
let LiquidityAmount { shares, coins } =
Liquidity::<T>::get(balance.coin, account).unwrap_or(LiquidityAmount::zero());
let total_shares = Supply::<T>::get(balance.coin).unwrap_or(LiquidityAmount::zero()).shares;
let user_liq_tokens = Self::mul_div(total_liq_tokens, shares, total_shares)?;
let amount_to_remove =
Self::mul_div(user_liq_tokens, balance.amount.0, INITIAL_GENESIS_LP_SHARES)?;
// remove liquidity from pool
let prev_sri = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), Coin::Serai);
let prev_coin = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), balance.coin.into());
Dex::<T>::remove_liquidity(
origin.clone().into(),
balance.coin,
amount_to_remove,
1,
1,
GENESIS_LIQUIDITY_ACCOUNT.into(),
)?;
let current_sri = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), Coin::Serai);
let current_coin =
Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), balance.coin.into());
// burn the SRI if necessary
// TODO: take into consideration movement between pools.
let mut sri: u64 = current_sri.0.saturating_sub(prev_sri.0);
let distance_to_full_pay =
GENESIS_SRI_TRICKLE_FEED.saturating_sub(Self::blocks_since_ec_security().unwrap_or(0));
let burn_sri_amount = u64::try_from(
u128::from(sri)
.checked_mul(u128::from(distance_to_full_pay))
.ok_or(Error::<T>::AmountOverflowed)?
.checked_div(u128::from(GENESIS_SRI_TRICKLE_FEED))
.ok_or(Error::<T>::AmountOverflowed)?,
)
.map_err(|_| Error::<T>::AmountOverflowed)?;
Coins::<T>::burn(
origin.clone().into(),
Balance { coin: Coin::Serai, amount: Amount(burn_sri_amount) },
)?;
sri = sri.checked_sub(burn_sri_amount).ok_or(Error::<T>::AmountOverflowed)?;
// transfer to owner
let coin_out = current_coin.0.saturating_sub(prev_coin.0);
Coins::<T>::transfer(
origin.clone().into(),
account,
Balance { coin: balance.coin.into(), amount: Amount(coin_out) },
)?;
Coins::<T>::transfer(
origin.into(),
account,
Balance { coin: Coin::Serai, amount: Amount(sri) },
)?;
// return new amounts
(
LiquidityAmount {
shares: shares.checked_sub(amount_to_remove).ok_or(Error::<T>::AmountOverflowed)?,
coins: coins.checked_sub(coin_out).ok_or(Error::<T>::AmountOverflowed)?,
},
LiquidityAmount {
shares: supply
.shares
.checked_sub(amount_to_remove)
.ok_or(Error::<T>::AmountOverflowed)?,
coins: supply.coins.checked_sub(coin_out).ok_or(Error::<T>::AmountOverflowed)?,
},
)
} else {
if balance.amount.0 != INITIAL_GENESIS_LP_SHARES {
Err(Error::<T>::CanOnlyRemoveFullAmount)?;
}
let existing =
Liquidity::<T>::get(balance.coin, account).ok_or(Error::<T>::NotEnoughLiquidity)?;
// transfer to the user
Coins::<T>::transfer(
origin.into(),
account,
Balance { coin: balance.coin.into(), amount: Amount(existing.coins) },
)?;
(
LiquidityAmount::zero(),
LiquidityAmount {
shares: supply
.shares
.checked_sub(existing.shares)
.ok_or(Error::<T>::AmountOverflowed)?,
coins: supply.coins.checked_sub(existing.coins).ok_or(Error::<T>::AmountOverflowed)?,
},
)
};
// save
if new_liquidity == LiquidityAmount::zero() {
Liquidity::<T>::set(balance.coin, account, None);
} else {
Liquidity::<T>::set(balance.coin, account, Some(new_liquidity));
}
Supply::<T>::set(balance.coin, Some(new_supply));
Self::deposit_event(Event::GenesisLiquidityRemoved { by: account.into(), balance });
Ok(())
}
/// A call to submit the initial coin values in terms of BTC.
#[pallet::call_index(1)]
#[pallet::weight((0, DispatchClass::Operational))] // TODO
pub fn oraclize_values( pub fn oraclize_values(
origin: OriginFor<T>, origin: OriginFor<T>,
values: Values, values: GenesisValues,
_signature: Signature, signature: Signature,
) -> DispatchResult { ) -> DispatchResult {
ensure_none(origin)?; todo!("TODO")
// set their relative values
Oracle::<T>::set(ExternalCoin::Bitcoin, Some(10u64.pow(ExternalCoin::Bitcoin.decimals())));
Oracle::<T>::set(ExternalCoin::Monero, Some(values.monero));
Oracle::<T>::set(ExternalCoin::Ether, Some(values.ether));
Oracle::<T>::set(ExternalCoin::Dai, Some(values.dai));
Ok(())
} }
}
#[pallet::validate_unsigned] /// Transfer genesis liquidity.
impl<T: Config> ValidateUnsigned for Pallet<T> { #[pallet::call_index(1)]
type Call = Call<T>; #[pallet::weight((0, DispatchClass::Normal))] // TODO
pub fn transfer_genesis_liquidity(
origin: OriginFor<T>,
to: SeraiAddress,
genesis_liquidity: ExternalBalance,
) -> DispatchResult {
todo!("TODO")
}
fn validate_unsigned(_: TransactionSource, call: &Self::Call) -> TransactionValidity { /// Remove genesis liquidity.
match call { #[pallet::call_index(2)]
Call::oraclize_values { ref values, ref signature } => { #[pallet::weight((0, DispatchClass::Normal))] // TODO
let network = NetworkId::Serai; pub fn remove_genesis_liquidity(
let Some(session) = ValidatorSets::<T>::session(network) else { origin: OriginFor<T>,
return Err(TransactionValidityError::from(InvalidTransaction::Custom(0))); genesis_liquidity: ExternalBalance,
}; ) -> DispatchResult {
todo!("TODO")
let set = ValidatorSet { network, session };
let signers = ValidatorSets::<T>::participants_for_latest_decided_set(network)
.expect("no participant in the current set")
.into_iter()
.map(|(p, _)| p)
.collect::<Vec<_>>();
// check this didn't get called before
if Self::oraclization_is_done() {
Err(InvalidTransaction::Custom(1))?;
}
// make sure signers settings the value at the end of the genesis period.
// we don't need this check for tests.
#[cfg(not(feature = "fast-epoch"))]
if <frame_system::Pallet<T>>::block_number().saturated_into::<u64>() < MONTHS {
Err(InvalidTransaction::Custom(2))?;
}
if !musig_key(set, &signers).verify(&oraclize_values_message(&set, values), signature) {
Err(InvalidTransaction::BadProof)?;
}
ValidTransaction::with_tag_prefix("GenesisLiquidity")
.and_provides((0, set))
.longevity(u64::MAX)
.propagate(true)
.build()
}
Call::remove_coin_liquidity { .. } => Err(InvalidTransaction::Call)?,
Call::__Ignore(_, _) => unreachable!(),
}
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More