Merge remote-tracking branch 'origin/dev' into artem/viewing-key-binding

This commit is contained in:
agureev 2026-07-02 20:04:30 +04:00
commit 8e084e22d1
365 changed files with 19609 additions and 16279 deletions

View File

@ -61,7 +61,7 @@ allow-git = [
"https://github.com/logos-blockchain/logos-blockchain-circuits.git",
"https://github.com/logos-blockchain/logos-blockchain-rust-rapidsnark.git",
"https://github.com/logos-blockchain/sponges",
"https://github.com/arkworks-rs/spongefish.git"
"https://github.com/arkworks-rs/spongefish.git",
]
unknown-git = "deny"
unknown-registry = "deny"

View File

@ -1,19 +0,0 @@
name: Setup Logos Blockchain Circuits
description: Set up Logos Blockchain Circom Circuits, Rapidsnark prover and Rapidsnark verifier using the setup-logos-blockchain-circuits.sh script.
inputs:
github-token:
description: GitHub token for downloading releases
required: true
runs:
using: "composite"
steps:
- name: Setup logos-blockchain-circuits
shell: bash
working-directory: ${{ github.workspace }}
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
run: |
curl -sSL https://raw.githubusercontent.com/logos-blockchain/logos-blockchain/6ac348bea4160ca708b70a86b3964e9f1ce82fff/scripts/setup-logos-blockchain-circuits.sh | bash

View File

@ -2,6 +2,7 @@ on:
push:
branches:
- main
- dev
paths-ignore:
- "**.md"
- "!.github/workflows/*.yml"
@ -94,7 +95,7 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Lint workspace
env:
@ -104,7 +105,7 @@ jobs:
- name: Lint programs
env:
RISC0_SKIP_BUILD: "1"
run: cargo clippy -p "*programs" -- -D warnings
run: cargo clippy -p "*program" -- -D warnings
unit-tests:
runs-on: ubuntu-latest
@ -125,7 +126,7 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Install nextest
run: cargo install --locked cargo-nextest
@ -156,7 +157,7 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Install nextest
run: cargo install --locked cargo-nextest
@ -245,7 +246,7 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Test valid proof
env:
@ -268,7 +269,7 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Install just
run: cargo install --locked just

118
.github/workflows/pr-review-board.yml vendored Normal file
View File

@ -0,0 +1,118 @@
name: PR review board
# Keeps the org "LEZ PR Review Queue" board in sync for this repo.
# Adds each PR to the board and sets its Status column, enforcing the
# team rule that Approved means 2+ approvals.
on:
pull_request:
types: [opened, reopened, ready_for_review, converted_to_draft, closed, labeled, unlabeled]
pull_request_review:
types: [submitted, dismissed]
permissions:
contents: read
pull-requests: read
concurrency:
group: pr-review-board-${{ github.event.pull_request.number }}
cancel-in-progress: false
jobs:
sync:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.PROJECT_PAT }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
PROJECT_ID: PVT_kwDODrxXXM4BbXZ_
STATUS_FIELD: PVTSSF_lADODrxXXM4BbXZ_zhWIZnU
OPT_DRAFT: f735bdac
OPT_NEEDS: cbc7321d
OPT_CHANGES: 302bbbd6
OPT_APPROVED: c9e0743e
PRIORITY_FIELD: PVTSSF_lADODrxXXM4BbXZ_zhWIyPA
PRIO_URGENT: b72854fd
PRIO_HIGH: 5e87ebf9
PRIO_MEDIUM: 22367611
PRIO_LOW: 41f60140
steps:
- name: Sync PR status to board
run: |
set -euo pipefail
DATA=$(gh pr view "$PR" --repo "$REPO" --json id,isDraft,mergedAt,state,latestReviews,labels)
PR_NODE=$(echo "$DATA" | jq -r '.id')
IS_DRAFT=$(echo "$DATA" | jq -r '.isDraft')
MERGED=$(echo "$DATA" | jq -r '(.mergedAt != null)')
STATE=$(echo "$DATA" | jq -r '.state')
NAP=$(echo "$DATA" | jq '[.latestReviews[] | select(.state=="APPROVED")] | length')
NCH=$(echo "$DATA" | jq '[.latestReviews[] | select(.state=="CHANGES_REQUESTED")] | length')
if [ "$STATE" = "CLOSED" ] && [ "$MERGED" != "true" ]; then
echo "PR #$PR closed unmerged; leaving board entry untouched."
exit 0
fi
# Add to the project (idempotent: returns existing item if already present)
ITEM=$(gh api graphql -f query='
mutation($p:ID!, $c:ID!) {
addProjectV2ItemById(input:{projectId:$p, contentId:$c}) { item { id } }
}' -f p="$PROJECT_ID" -f c="$PR_NODE" --jq '.data.addProjectV2ItemById.item.id')
# Merged PRs are archived off the active board
if [ "$MERGED" = "true" ]; then
gh api graphql -f query='
mutation($p:ID!, $i:ID!) {
archiveProjectV2Item(input:{projectId:$p, itemId:$i}) { item { id } }
}' -f p="$PROJECT_ID" -f i="$ITEM"
echo "$REPO#$PR merged -> archived"
exit 0
fi
# Status
if [ "$IS_DRAFT" = "true" ]; then
OPT=$OPT_DRAFT; LABEL="Draft"
elif [ "$NCH" -gt 0 ]; then
OPT=$OPT_CHANGES; LABEL="Changes requested"
elif [ "$NAP" -ge 2 ]; then
OPT=$OPT_APPROVED; LABEL="Approved"
else
OPT=$OPT_NEEDS; LABEL="Needs review"
fi
gh api graphql -f query='
mutation($p:ID!, $i:ID!, $f:ID!, $o:String!) {
updateProjectV2ItemFieldValue(input:{
projectId:$p, itemId:$i, fieldId:$f,
value:{ singleSelectOptionId:$o }
}) { projectV2Item { id } }
}' -f p="$PROJECT_ID" -f i="$ITEM" -f f="$STATUS_FIELD" -f o="$OPT"
# Set Priority from a priority:* label (clear it when none is present)
LABELS=$(echo "$DATA" | jq -r '.labels[].name')
case "$LABELS" in
*priority:urgent*) POPT=$PRIO_URGENT ;;
*priority:high*) POPT=$PRIO_HIGH ;;
*priority:medium*) POPT=$PRIO_MEDIUM ;;
*priority:low*) POPT=$PRIO_LOW ;;
*) POPT="" ;;
esac
if [ -n "$POPT" ]; then
gh api graphql -f query='
mutation($p:ID!, $i:ID!, $f:ID!, $o:String!) {
updateProjectV2ItemFieldValue(input:{
projectId:$p, itemId:$i, fieldId:$f,
value:{ singleSelectOptionId:$o }
}) { projectV2Item { id } }
}' -f p="$PROJECT_ID" -f i="$ITEM" -f f="$PRIORITY_FIELD" -f o="$POPT"
else
gh api graphql -f query='
mutation($p:ID!, $i:ID!, $f:ID!) {
clearProjectV2ItemFieldValue(input:{
projectId:$p, itemId:$i, fieldId:$f
}) { projectV2Item { id } }
}' -f p="$PROJECT_ID" -f i="$ITEM" -f f="$PRIORITY_FIELD"
fi
echo "$REPO#$PR (approvals=$NAP changes=$NCH) -> $LABEL"

View File

@ -7,7 +7,38 @@ on:
- "v*"
jobs:
# Shared base (toolchain + r0vm), single source of truth in
# lez/docker/risc0-base.Dockerfile. Built and pushed once so the service
# builds below can pull it as the `risc0_base` named context. The
# docker-container builder resolves named contexts from the registry (not the
# host image store), so the base must be pushed, not just loaded.
risc0_base:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push risc0 base image
uses: docker/build-push-action@v5
with:
context: .
file: ./lez/docker/risc0-base.Dockerfile
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ github.repository }}/risc0_base:sha-${{ github.sha }}
cache-from: type=gha,scope=risc0-base
cache-to: type=gha,mode=max,scope=risc0-base
publish:
needs: risc0_base
runs-on: ubuntu-latest
strategy:
matrix:
@ -62,5 +93,6 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: ${{ matrix.build_args }}
build-contexts: risc0_base=docker-image://${{ secrets.DOCKER_REGISTRY }}/${{ github.repository }}/risc0_base:sha-${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max

10
.gitignore vendored
View File

@ -1,17 +1,25 @@
.gitconfig
res/
target/
deps/
data/
.idea/
.vscode/
rocksdb
rocksdb*
sequencer/service/data/
storage.json
result
wallet-ffi/wallet_ffi.h
bedrock_signing_key
integration_tests/configs/debug/
venv/
keycard_wallet/python/__pycache__/
keycard_wallet/python/keycard-py/
.DS_Store

View File

@ -57,12 +57,16 @@ Before merging a PR, consider squashing non-meaningful commits. E.g.:
Could be squashed to an empty commit if they belong to the same PR.
## Default branch
By default all PRs must be directed into the `dev` branch. This helps us to keep releases stable.
## Branch workflow
When bringing your feature branch up to date, prefer rebasing on top of `main`.
When bringing your feature branch up to date, prefer rebasing on top of `dev`.
- Preferred: `git rebase main`
- Avoid: `git merge main` in feature branches
- Preferred: `git rebase dev`
- Avoid: `git merge dev` in feature branches
This keeps commit history cleaner and makes reviews easier.

225
Cargo.lock generated
View File

@ -123,6 +123,7 @@ dependencies = [
"amm_core",
"lee",
"lee_core",
"programs",
"token_core",
]
@ -205,9 +206,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.102"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "arbitrary"
@ -513,6 +514,24 @@ version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156"
[[package]]
name = "associated_token_account_core"
version = "0.1.0"
dependencies = [
"lee_core",
"risc0-zkvm",
"serde",
]
[[package]]
name = "associated_token_account_program"
version = "0.1.0"
dependencies = [
"associated_token_account_core",
"lee_core",
"token_core",
]
[[package]]
name = "astral-tokio-tar"
version = "0.6.2"
@ -665,24 +684,6 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "ata_core"
version = "0.1.0"
dependencies = [
"lee_core",
"risc0-zkvm",
"serde",
]
[[package]]
name = "ata_program"
version = "0.1.0"
dependencies = [
"ata_core",
"lee_core",
"token_core",
]
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
@ -758,6 +759,14 @@ dependencies = [
"serde",
]
[[package]]
name = "authenticated_transfer_program"
version = "0.1.0"
dependencies = [
"authenticated_transfer_core",
"lee_core",
]
[[package]]
name = "autocfg"
version = "1.5.1"
@ -975,9 +984,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitcoin_hashes"
version = "0.14.100"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f"
checksum = "4ed83caece3afc59919481b33b472e1432d1abc4641ed9100be142ef5110b406"
dependencies = [
"hex-conservative",
]
@ -1145,6 +1154,16 @@ dependencies = [
"serde",
]
[[package]]
name = "bridge_program"
version = "0.1.0"
dependencies = [
"authenticated_transfer_core",
"bridge_core",
"lee_core",
"vault_core",
]
[[package]]
name = "bs58"
version = "0.5.1"
@ -1154,6 +1173,14 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "build_utils"
version = "0.1.0"
dependencies = [
"anyhow",
"risc0-binfmt",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
@ -1452,6 +1479,14 @@ dependencies = [
"lee_core",
]
[[package]]
name = "clock_program"
version = "0.1.0"
dependencies = [
"clock_core",
"lee_core",
]
[[package]]
name = "cmov"
version = "0.5.4"
@ -1514,9 +1549,11 @@ dependencies = [
"lee_core",
"log",
"logos-blockchain-common-http-client",
"programs",
"serde",
"serde_with",
"sha2",
"system_accounts",
"thiserror 2.0.18",
]
@ -1932,7 +1969,7 @@ version = "0.1.0"
dependencies = [
"amm_core",
"anyhow",
"ata_core",
"associated_token_account_core",
"authenticated_transfer_core",
"borsh",
"clap",
@ -1940,9 +1977,11 @@ dependencies = [
"criterion",
"lee",
"lee_core",
"programs",
"risc0-zkvm",
"serde",
"serde_json",
"test_programs",
"token_core",
]
@ -2038,7 +2077,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090"
dependencies = [
"data-encoding",
"syn 2.0.117",
"syn 1.0.109",
]
[[package]]
@ -2640,6 +2679,16 @@ dependencies = [
"serde",
]
[[package]]
name = "faucet_program"
version = "0.1.0"
dependencies = [
"authenticated_transfer_core",
"faucet_core",
"lee_core",
"vault_core",
]
[[package]]
name = "fd-lock"
version = "4.0.4"
@ -3799,6 +3848,7 @@ name = "indexer_core"
version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"async-stream",
"authenticated_transfer_core",
"borsh",
@ -3823,16 +3873,15 @@ dependencies = [
name = "indexer_ffi"
version = "0.1.0"
dependencies = [
"anyhow",
"cbindgen",
"indexer_service",
"env_logger",
"futures",
"indexer_core",
"indexer_service_protocol",
"indexer_service_rpc",
"jsonrpsee",
"lee",
"log",
"serde_json",
"tokio",
"url",
]
[[package]]
@ -3959,7 +4008,7 @@ name = "integration_tests"
version = "0.1.0"
dependencies = [
"anyhow",
"ata_core",
"associated_token_account_core",
"authenticated_transfer_core",
"borsh",
"bridge_core",
@ -3980,12 +4029,15 @@ dependencies = [
"logos-blockchain-key-management-system-service",
"logos-blockchain-zone-sdk",
"num-bigint 0.4.6",
"programs",
"reqwest",
"sequencer_core",
"sequencer_service_rpc",
"serde_json",
"system_accounts",
"tempfile",
"test_fixtures",
"test_programs",
"token_core",
"tokio",
"vault_core",
@ -4501,10 +4553,8 @@ dependencies = [
"anyhow",
"authenticated_transfer_core",
"borsh",
"bridge_core",
"clock_core",
"build_utils",
"env_logger",
"faucet_core",
"hex",
"hex-literal 1.1.0",
"k256",
@ -4512,13 +4562,12 @@ dependencies = [
"log",
"rand 0.8.6",
"risc0-binfmt",
"risc0-build",
"risc0-zkvm",
"serde",
"serde_with",
"sha2",
"test-case",
"test_program_methods",
"test_methods",
"thiserror 2.0.18",
"token_core",
]
@ -7112,6 +7161,23 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pinata_program"
version = "0.1.0"
dependencies = [
"lee_core",
"risc0-zkvm",
]
[[package]]
name = "pinata_token_program"
version = "0.1.0"
dependencies = [
"lee_core",
"risc0-zkvm",
"token_core",
]
[[package]]
name = "pkcs1"
version = "0.7.5"
@ -7265,6 +7331,14 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "privacy_preserving_circuit_program"
version = "0.1.0"
dependencies = [
"lee_core",
"risc0-zkvm",
]
[[package]]
name = "proc-macro-crate"
version = "3.5.0"
@ -7366,28 +7440,22 @@ dependencies = [
"wallet",
]
[[package]]
name = "program_methods"
version = "0.1.0"
dependencies = [
"risc0-build",
]
[[package]]
name = "programs"
version = "0.1.0"
dependencies = [
"amm_core",
"amm_program",
"ata_core",
"ata_program",
"associated_token_account_core",
"associated_token_account_program",
"authenticated_transfer_core",
"bridge_core",
"build_utils",
"clock_core",
"faucet_core",
"lee",
"lee_core",
"risc0-zkvm",
"serde",
"token_core",
"token_program",
"vault_core",
@ -7610,9 +7678,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
version = "0.11.14"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e"
dependencies = [
"bytes",
"getrandom 0.3.4",
@ -8842,14 +8910,17 @@ dependencies = [
"logos-blockchain-zone-sdk",
"mempool",
"num-bigint 0.4.6",
"programs",
"rand 0.8.6",
"risc0-zkvm",
"serde",
"serde_json",
"storage",
"system_accounts",
"tempfile",
"test_program_methods",
"test_programs",
"testnet_initial_state",
"token_core",
"tokio",
"url",
"vault_core",
@ -8870,6 +8941,7 @@ dependencies = [
"lee",
"log",
"mempool",
"programs",
"sequencer_core",
"sequencer_service_protocol",
"sequencer_service_rpc",
@ -8882,8 +8954,10 @@ name = "sequencer_service_protocol"
version = "0.1.0"
dependencies = [
"common",
"hex",
"lee",
"lee_core",
"serde_with",
]
[[package]]
@ -9388,7 +9462,9 @@ dependencies = [
"borsh",
"common",
"lee",
"programs",
"rocksdb",
"system_accounts",
"tempfile",
"thiserror 2.0.18",
]
@ -9541,6 +9617,17 @@ dependencies = [
"libc",
]
[[package]]
name = "system_accounts"
version = "0.1.0"
dependencies = [
"bridge_core",
"clock_core",
"faucet_core",
"lee_core",
"programs",
]
[[package]]
name = "tachys"
version = "0.2.15"
@ -9663,6 +9750,7 @@ dependencies = [
"lee",
"lee_core",
"log",
"programs",
"sequencer_core",
"sequencer_service",
"sequencer_service_rpc",
@ -9676,14 +9764,23 @@ dependencies = [
]
[[package]]
name = "test_program_methods"
name = "test_methods"
version = "0.1.0"
dependencies = [
"risc0-build",
]
[[package]]
name = "test_programs"
name = "test_methods_guests"
version = "0.1.0"
dependencies = [
"lee_core",
"risc0-zkvm",
"serde",
]
[[package]]
name = "test_program_guests"
version = "0.1.0"
dependencies = [
"authenticated_transfer_core",
@ -9691,7 +9788,14 @@ dependencies = [
"faucet_core",
"lee_core",
"risc0-zkvm",
"serde",
]
[[package]]
name = "test_programs"
version = "0.1.0"
dependencies = [
"lee",
"risc0-build",
]
[[package]]
@ -9731,11 +9835,12 @@ dependencies = [
name = "testnet_initial_state"
version = "0.1.0"
dependencies = [
"common",
"key_protocol",
"lee",
"lee_core",
"programs",
"serde",
"system_accounts",
]
[[package]]
@ -10693,6 +10798,15 @@ dependencies = [
"serde",
]
[[package]]
name = "vault_program"
version = "0.1.0"
dependencies = [
"authenticated_transfer_core",
"lee_core",
"vault_core",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
@ -10721,8 +10835,8 @@ version = "0.1.0"
dependencies = [
"amm_core",
"anyhow",
"associated_token_account_core",
"async-stream",
"ata_core",
"authenticated_transfer_core",
"base58",
"bincode",
@ -10744,6 +10858,7 @@ dependencies = [
"lee_core",
"log",
"optfield",
"programs",
"pyo3",
"rand 0.8.6",
"rpassword",
@ -10751,6 +10866,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
"system_accounts",
"tempfile",
"testnet_initial_state",
"thiserror 2.0.18",
@ -10765,16 +10881,19 @@ dependencies = [
name = "wallet-ffi"
version = "0.1.0"
dependencies = [
"bip39",
"cbindgen",
"common",
"key_protocol",
"lee",
"lee_core",
"programs",
"risc0-zkvm",
"sequencer_service_rpc",
"serde_json",
"tempfile",
"tokio",
"vault_core",
"wallet",
]

View File

@ -5,25 +5,17 @@ license = "MIT or Apache-2.0"
resolver = "3"
members = [
"integration_tests",
"lez/storage",
"lee/key_protocol",
"lee/state_machine",
"lee/state_machine/core",
"lez/mempool",
"lez/wallet",
"lez/wallet-ffi",
"lez/common",
"programs/amm/core",
"programs/amm",
"programs/clock/core",
"programs/token/core",
"programs/token",
"programs/associated_token_account/core",
"programs/associated_token_account",
"programs/authenticated_transfer/core",
"programs/faucet/core",
"programs/bridge/core",
"programs/vault/core",
"lee/privacy_preserving_circuit",
"lee/state_machine/test_methods",
"lee/state_machine/test_methods/guest",
"lez",
"lez/system_accounts",
"lez/sequencer/core",
"lez/sequencer/service",
"lez/sequencer/service/protocol",
@ -33,18 +25,36 @@ members = [
"lez/indexer/service/protocol",
"lez/indexer/service/rpc",
"lez/explorer_service",
"program_methods",
"program_methods/guest",
"test_program_methods",
"test_program_methods/guest",
"lez/testnet_initial_state",
"lez/indexer/ffi",
"lez/keycard_wallet",
"lez/mempool",
"lez/wallet",
"lez/wallet-ffi",
"lez/common",
"lez/programs",
"lez/programs/amm",
"lez/programs/associated_token_account",
"lez/programs/authenticated_transfer",
"lez/programs/bridge",
"lez/programs/clock",
"lez/programs/faucet",
"lez/programs/pinata",
"lez/programs/pinata_token",
"lez/programs/token",
"lez/programs/vault",
"test_programs",
"test_programs/guest",
"examples/program_deployment",
"examples/program_deployment/methods",
"examples/program_deployment/methods/guest",
"lez/testnet_initial_state",
"lez/indexer/ffi",
"lez",
"lez/keycard_wallet",
"build_utils",
"test_fixtures",
"tools/cycle_bench",
"tools/crypto_primitives_bench",
"tools/integration_bench",
@ -69,18 +79,23 @@ wallet = { path = "lez/wallet" }
wallet-ffi = { path = "lez/wallet-ffi", default-features = false }
indexer_ffi = { path = "lez/indexer/ffi" }
lez = { path = "lez" }
clock_core = { path = "programs/clock/core" }
token_core = { path = "programs/token/core" }
token_program = { path = "programs/token" }
amm_core = { path = "programs/amm/core" }
amm_program = { path = "programs/amm" }
ata_core = { path = "programs/associated_token_account/core" }
ata_program = { path = "programs/associated_token_account" }
authenticated_transfer_core = { path = "programs/authenticated_transfer/core" }
faucet_core = { path = "programs/faucet/core" }
bridge_core = { path = "programs/bridge/core" }
vault_core = { path = "programs/vault/core" }
test_program_methods = { path = "test_program_methods" }
programs = { path = "lez/programs", default-features = false, features = [
"artifacts",
] }
system_accounts = { path = "lez/system_accounts" }
clock_core = { path = "lez/programs/clock/core" }
token_core = { path = "lez/programs/token/core" }
token_program = { path = "lez/programs/token" }
amm_core = { path = "lez/programs/amm/core" }
amm_program = { path = "lez/programs/amm" }
associated_token_account_core = { path = "lez/programs/associated_token_account/core" }
ata_program = { path = "lez/programs/associated_token_account" }
authenticated_transfer_core = { path = "lez/programs/authenticated_transfer/core" }
faucet_core = { path = "lez/programs/faucet/core" }
bridge_core = { path = "lez/programs/bridge/core" }
vault_core = { path = "lez/programs/vault/core" }
build_utils = { path = "build_utils" }
test_programs = { path = "test_programs" }
testnet_initial_state = { path = "lez/testnet_initial_state" }
keycard_wallet = { path = "lez/keycard_wallet" }
test_fixtures = { path = "test_fixtures" }
@ -117,6 +132,7 @@ hex = "0.4.3"
bytemuck = "1.24.0"
bytesize = { version = "2.3.1", features = ["serde"] }
humantime-serde = "1.1"
arc-swap = "1.7"
humantime = "2.1"
aes-gcm = "0.10.3"
toml = "0.9.8"

View File

@ -4,19 +4,25 @@ default:
@just --list
# ---- Configuration ----
METHODS_PATH := "program_methods"
TEST_METHODS_PATH := "test_program_methods"
ARTIFACTS := "artifacts"
# Build risc0 program artifacts.
build-artifacts:
@echo "🔨 Building artifacts"
@for methods_path in {{METHODS_PATH}} {{TEST_METHODS_PATH}}; do \
echo "Building artifacts for $methods_path"; \
CARGO_TARGET_DIR=target/$methods_path cargo risczero build --manifest-path $methods_path/guest/Cargo.toml; \
mkdir -p {{ARTIFACTS}}/$methods_path; \
cp target/$methods_path/riscv32im-risc0-zkvm-elf/docker/*.bin {{ARTIFACTS}}/$methods_path; \
done
@rm -rf {{ARTIFACTS}}
@just build-artifact lee/privacy_preserving_circuit
@just build-artifact lez/programs programs
build-artifact methods_path features="":
@echo "Building artifacts for {{methods_path}}"
@rm -rf target/{{methods_path}}/riscv32im-risc0-zkvm-elf/docker/*.bin
@if [ "{{features}}" = "" ]; then \
CARGO_TARGET_DIR=target/{{methods_path}} cargo risczero build --manifest-path {{methods_path}}/Cargo.toml; \
else \
CARGO_TARGET_DIR=target/{{methods_path}} cargo risczero build --no-default-features --features {{features}} --manifest-path {{methods_path}}/Cargo.toml; \
fi
@mkdir -p {{ARTIFACTS}}/{{methods_path}}
@cp target/{{methods_path}}/riscv32im-risc0-zkvm-elf/docker/*.bin {{ARTIFACTS}}/{{methods_path}}
# Format codebase.
fmt:
@ -59,10 +65,10 @@ run-indexer mock="":
@echo "🔍 Running indexer"
@if [ "{{mock}}" = "mock" ]; then \
echo "🧪 Using mock data"; \
RUST_LOG=info cargo run --release --features mock-responses -p indexer_service configs/indexer_config.json; \
RUST_LOG=info cargo run --release --features mock-responses -p indexer_service configs/debug/indexer_config.json; \
else \
echo "🚀 Using real data"; \
RUST_LOG=info cargo run --release -p indexer_service configs/indexer_config.json; \
RUST_LOG=info cargo run --release -p indexer_service configs/debug/indexer_config.json; \
fi
# Run Explorer.
@ -93,7 +99,7 @@ clean:
@echo "🧹 Cleaning run artifacts"
rm -rf lez/sequencer/service/bedrock_signing_key
rm -rf lez/sequencer/service/rocksdb
rm -rf lez/indexer/service/rocksdb
rm -rf lez/indexer/service/rocksdb*
rm -rf lez/wallet/configs/debug/storage.json
rm -rf rocksdb
rm -rf rocksdb*
cd bedrock && docker compose down -v

View File

@ -156,7 +156,7 @@ The sequencer and logos blockchain node can be run locally:
- `docker compose up`
2. On another terminal go to the `logos-blockchain/logos-execution-zone` repo and run indexer service:
- `RUST_LOG=info cargo run -p indexer_service lez/indexer/service/configs/indexer_config.json`
- `RUST_LOG=info cargo run -p indexer_service lez/indexer/service/configs/debug/indexer_config.json`
3. On another terminal go to the `logos-blockchain/logos-execution-zone` repo and run the sequencer:
- `RUST_LOG=info cargo run -p sequencer_service lez/sequencer/service/configs/debug/sequencer_config.json`

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,7 @@ services:
logos-blockchain-node-0:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:91d6c5bf07e07fcfba5e7cf07d21ee686a6bc4b9f6210f2d28bffbcad9a3729f
ports:
- "${PORT:-8080}:18080/tcp"
- "${PORT:-18080}:18080/tcp"
volumes:
- ./scripts:/etc/logos-blockchain/scripts
- ./kzgrs_test_params:/kzgrs_test_params:z

11
build_utils/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "build_utils"
version = "0.1.0"
edition = "2024"
license = { workspace = true }
[lints]
[dependencies]
anyhow.workspace = true
risc0-binfmt = "3.0.2"

69
build_utils/src/lib.rs Normal file
View File

@ -0,0 +1,69 @@
//! Utilities for build-scripts.
use std::{env, fmt::Write as _, fs, path::PathBuf};
use anyhow::{Context as _, Result, bail};
/// Include artifact binaries as byte arrays and their corresponding image IDs as u32 arrays in a
/// generated Rust module.
///
/// The `artifacts_sub_dir` parameter specifies the subdirectory under `artifacts/`.
///
/// Caller should include resulting module as follows:
///
/// ```
/// mod guests {
/// include!(concat!(env!("OUT_DIR"), "/artifacts_sub_dir/mod.rs"));
/// }
/// ```
pub fn include_artifacts(artifacts_sub_dir: &str) -> Result<()> {
let manifest_dir = PathBuf::from(std::env!("CARGO_MANIFEST_DIR"));
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
let mod_dir = out_dir.join(artifacts_sub_dir);
let mod_file = mod_dir.join("mod.rs");
let artifacts_dir = manifest_dir.join(format!("../artifacts/{artifacts_sub_dir}/"));
println!("cargo:rerun-if-changed={}", artifacts_dir.display());
let bins = fs::read_dir(&artifacts_dir)
.with_context(|| {
format!(
"Failed to read {} artifacts directory",
artifacts_dir.display()
)
})?
.filter_map(Result::ok)
.filter(|e| e.path().extension().is_some_and(|ext| ext == "bin"))
.collect::<Vec<_>>();
if bins.is_empty() {
bail!("No .bin files found in {}", artifacts_dir.display());
}
fs::create_dir_all(&mod_dir)
.with_context(|| format!("Failed to create directory {}", mod_dir.display()))?;
let mut src = String::new();
for entry in bins {
let path = entry.path();
let name = path.file_stem().unwrap().to_string_lossy();
let bytecode =
fs::read(&path).with_context(|| format!("Failed to read {}", path.display()))?;
let image_id: [u32; 8] = risc0_binfmt::compute_image_id(&bytecode)
.with_context(|| format!("Failed to compute image ID for {}", path.display()))?
.into();
write!(
src,
"pub const {}_ELF: &[u8] = include_bytes!(r#\"{}\"#);\n\
#[expect(clippy::unreadable_literal, reason = \"Generated image IDs from risc0 are cryptographic hashes represented as u32 arrays\")]\n\
pub const {}_ID: [u32; 8] = {:?};\n",
name.to_uppercase(),
path.display(),
name.to_uppercase(),
image_id
)?;
}
fs::write(&mod_file, src).with_context(|| format!("Failed to write {}", mod_file.display()))?;
println!("cargo:warning=Generated module at {}", mod_file.display());
Ok(())
}

View File

@ -2,8 +2,6 @@
services:
logos-blockchain-node-0:
ports: !override
- "18080:18080/tcp"
environment:
- RUST_LOG=error

View File

@ -46,7 +46,7 @@ export EXAMPLE_PROGRAMS_BUILD_DIR=$(pwd)/target/riscv32im-risc0-zkvm-elf/docker
> [!IMPORTANT]
> **All remaining commands must be run from the `examples/program_deployment` directory.**
# 3. Hello world example
# 3. Hello world example
The Hello world program reads an arbitrary sequence of bytes from its instruction and appends them to the data field of the input account.
Execution succeeds only if the account is:
@ -211,7 +211,7 @@ This is the account that the program will claim and write data into.
### 3. Loading the program bytecode
```rust
let bytecode: Vec<u8> = std::fs::read(program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
```
The Risc0 ELF is read from disk and wrapped in a Program object, which can be used to compute the program ID. The ID is used by the node to identify which program is invoked by the transaction.
@ -266,7 +266,7 @@ The relevant part for this tutorial is the account id `7EDHyxejuynBpmbLuiEym9HMU
> [!NOTE]
> As with public accounts, you can use the `--label` option to assign a label: `wallet account new private --label "my-private-account"`.
You can check it's uninitialized with
You can check it's uninitialized with
```bash
wallet account get --account-id Private/7EDHyxejuynBpmbLuiEym9HMUyCYxZDuF8X3B89ADeMr
@ -452,7 +452,7 @@ Because these operations may involve multiple accounts, we'll see how public and
> See `methods/guest/src/bin/hello_world_with_move_function.rs`. The program just reads the instruction bytes and updates the accounts state.
> All privacy handling happens on the runner side. When constructing the transaction, the runner decides which accounts are public or private and prepares the appropriate proofs. The program itself can't differentiate between privacy modes.
Let's start by deploying the program
Let's start by deploying the program
```bash
wallet deploy-program $EXAMPLE_PROGRAMS_BUILD_DIR/hello_world_with_move_function.bin
```
@ -486,7 +486,7 @@ Output:
Generated new account with account_id Private/8vzkK7vsdrS2gdPhLk72La8X4FJkgJ5kJLUBRbEVkReU at path /1
```
Let's execute the write function
Let's execute the write function
```bash
cargo run --bin run_hello_world_with_move_function \

View File

@ -43,7 +43,7 @@ async fn main() {
// Load the program
let bytecode: Vec<u8> = std::fs::read(program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
// Define the desired greeting in ASCII
let greeting: Vec<u8> = vec![72, 111, 108, 97, 32, 109, 117, 110, 100, 111, 33];

View File

@ -39,7 +39,7 @@ async fn main() {
// Load the program
let bytecode: Vec<u8> = std::fs::read(program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
// Define the desired greeting in ASCII
let greeting: Vec<u8> = vec![72, 111, 108, 97, 32, 109, 117, 110, 100, 111, 33];

View File

@ -43,7 +43,7 @@ async fn main() {
// Load the program
let bytecode: Vec<u8> = std::fs::read(program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
let instruction_data = ();
let nonces = vec![];

View File

@ -44,9 +44,9 @@ async fn main() {
// Load the program and its dependencies (the hellow world program)
let simple_tail_call_bytecode: Vec<u8> = std::fs::read(simple_tail_call_path).unwrap();
let simple_tail_call = Program::new(simple_tail_call_bytecode).unwrap();
let simple_tail_call = Program::new(simple_tail_call_bytecode.into()).unwrap();
let hello_world_bytecode: Vec<u8> = std::fs::read(hello_world_path).unwrap();
let hello_world = Program::new(hello_world_bytecode).unwrap();
let hello_world = Program::new(hello_world_bytecode.into()).unwrap();
let dependencies: HashMap<ProgramId, Program> =
std::iter::once((hello_world.id(), hello_world)).collect();
let program_with_dependencies = ProgramWithDependencies::new(simple_tail_call, dependencies);

View File

@ -45,7 +45,7 @@ async fn main() {
// Load the program
let bytecode: Vec<u8> = std::fs::read(program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
// Load signing keys to provide authorization
let signing_key = wallet_core

View File

@ -43,7 +43,7 @@ async fn main() {
// Load the program
let bytecode: Vec<u8> = std::fs::read(program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
// Compute the PDA to pass it as input account to the public execution
let pda = AccountId::for_public_pda(&program.id(), &PDA_SEED);

View File

@ -63,7 +63,7 @@ async fn main() {
// Load the program
let bytecode: Vec<u8> = std::fs::read(cli.program_path).unwrap();
let program = Program::new(bytecode).unwrap();
let program = Program::new(bytecode.into()).unwrap();
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();

2222
flake.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -99,6 +99,28 @@
sha256 = recursionZkrHash;
};
# risc0 compiles its Metal (GPU) prover kernels by invoking
# `xcrun metal` / `xcrun metallib`. Under nix, the darwin stdenv sets
# DEVELOPER_DIR/SDKROOT to its own SDK, which makes `xcrun` look for
# the `metal` tool in the wrong place and fail with
# error: cannot execute tool 'metal' due to missing Metal Toolchain
# even when a working Metal Toolchain is installed. This wrapper, put
# first in PATH, clears those two vars for metal/metallib invocations
# only — so they resolve the real system Xcode Metal Toolchain — while
# every other xcrun call passes through with the nix environment
# intact. (On recent macOS the Metal Toolchain is a per-user component;
# `xcodebuild -downloadComponent MetalToolchain` must have been run.)
metalStub = pkgs.writeShellScriptBin "xcrun" ''
tool=
for a in "$@"; do
case "$a" in metal|metallib) tool=1 ;; esac
done
if [ -n "$tool" ]; then
unset DEVELOPER_DIR SDKROOT
fi
exec /usr/bin/xcrun "$@"
'';
commonArgs = {
inherit src;
buildInputs = [ pkgs.openssl ];
@ -118,13 +140,14 @@
RECURSION_SRC_PATH = "${recursionZkr}";
# Provide a writable HOME so risc0-build-kernel can use its cache directory
# (needed on macOS for Metal kernel compilation cache).
# On macOS, append /usr/bin to PATH so xcrun (Metal compiler) can be found,
# while keeping Nix tools (like gnutar) first in PATH.
# On macOS, put the metalStub xcrun wrapper first so `xcrun metal` /
# `metallib` resolve the system Metal Toolchain (see metalStub above),
# and append /usr/bin for the real xcrun it execs.
# This requires running with --option sandbox false for Metal GPU support.
preBuild = ''
export HOME=$(mktemp -d)
'' + pkgs.lib.optionalString pkgs.stdenv.isDarwin ''
export PATH="$PATH:/usr/bin"
export PATH="${metalStub}/bin:$PATH:/usr/bin"
'';
};

View File

@ -9,7 +9,6 @@ workspace = true
[dependencies]
test_fixtures.workspace = true
lee_core = { workspace = true, features = ["host"] }
lee.workspace = true
authenticated_transfer_core.workspace = true
@ -19,7 +18,7 @@ common.workspace = true
key_protocol.workspace = true
serde_json.workspace = true
token_core.workspace = true
ata_core.workspace = true
associated_token_account_core.workspace = true
vault_core.workspace = true
faucet_core.workspace = true
bridge_core.workspace = true
@ -28,6 +27,9 @@ sequencer_service_rpc = { workspace = true, features = ["client"] }
wallet-ffi.workspace = true
indexer_ffi.workspace = true
indexer_service_protocol.workspace = true
system_accounts.workspace = true
programs.workspace = true
test_programs.workspace = true
logos-blockchain-http-api-common.workspace = true
logos-blockchain-core.workspace = true

View File

@ -6,12 +6,169 @@
use std::time::Duration;
use anyhow::{Context as _, Result};
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
use lee::AccountId;
use log::info;
use sequencer_service_rpc::RpcClient as _;
pub use test_fixtures::*;
use wallet::{
cli::{
CliAccountMention, Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
programs::{
native_token_transfer::AuthTransferSubcommand, token::TokenProgramAgnosticSubcommand,
},
},
storage::key_chain::FoundPrivateAccount,
};
/// Maximum time to wait for the indexer to catch up to the sequencer.
pub const L2_TO_L1_TIMEOUT: Duration = Duration::from_mins(6);
/// Create a private or public account at the given chain index and return its ID.
/// Pass `cci: None` to use the wallet's next available chain index.
pub async fn new_account(
ctx: &mut TestContext,
private: bool,
cci: Option<ChainIndex>,
) -> Result<AccountId> {
let subcommand = if private {
NewSubcommand::Private { cci, label: None }
} else {
NewSubcommand::Public { cci, label: None }
};
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(subcommand)),
)
.await?;
let SubcommandReturnValue::RegisterAccount { account_id } = result else {
anyhow::bail!("Expected RegisterAccount return value");
};
Ok(account_id)
}
/// Send `amount` from `from` to `to` via an authenticated transfer (identifier 0).
pub async fn send(
ctx: &mut TestContext,
from: CliAccountMention,
to: CliAccountMention,
amount: u128,
) -> Result<()> {
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from,
to: Some(to),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
Ok(())
}
/// Create a token (New) and wait for the block to be included.
pub async fn create_token(
ctx: &mut TestContext,
definition_account_id: CliAccountMention,
supply_account_id: CliAccountMention,
name: impl Into<String>,
total_supply: u128,
) -> Result<()> {
let subcommand = TokenProgramAgnosticSubcommand::New {
definition_account_id,
supply_account_id,
name: name.into(),
total_supply,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
Ok(())
}
/// Send tokens and wait for the block to be included.
pub async fn token_send(
ctx: &mut TestContext,
from: CliAccountMention,
to: CliAccountMention,
amount: u128,
) -> Result<()> {
let subcommand = TokenProgramAgnosticSubcommand::Send {
from,
to: Some(to),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
Ok(())
}
/// Retrieve the native token balance for `account_id`.
pub async fn account_balance(ctx: &TestContext, account_id: AccountId) -> Result<u128> {
Ok(ctx
.sequencer_client()
.get_account_balance(account_id)
.await?)
}
/// Fetch the full account state for `account_id` from the sequencer.
pub async fn get_account(ctx: &TestContext, account_id: AccountId) -> Result<lee::Account> {
Ok(ctx.sequencer_client().get_account(account_id).await?)
}
/// Fetch the current commitment for `account_id` and assert it is present in the sequencer state.
pub async fn assert_private_commitment_in_state(
ctx: &TestContext,
account_id: AccountId,
label: &str,
) -> Result<()> {
let commitment = ctx
.wallet()
.get_private_account_commitment(account_id)
.with_context(|| format!("Failed to get commitment for {label}"))?;
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
Ok(())
}
/// Sync the wallet's private accounts.
pub async fn sync_private(ctx: &mut TestContext) -> Result<()> {
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::SyncPrivate {}),
)
.await?;
Ok(())
}
/// Look up a restored private account for `account_id`, panicking with `label` if absent.
pub fn restored_private_account<'ctx>(
ctx: &'ctx TestContext,
account_id: AccountId,
label: &str,
) -> FoundPrivateAccount<'ctx> {
ctx.wallet()
.storage()
.key_chain()
.private_account(account_id)
.unwrap_or_else(|| panic!("{label} should be restored"))
}
/// Assert that a restored public account's signing key exists, panicking with `label` if absent.
pub fn assert_public_account_restored(ctx: &TestContext, account_id: AccountId, label: &str) {
ctx.wallet()
.storage()
.key_chain()
.pub_account_signing_key(account_id)
.unwrap_or_else(|| panic!("{label} should be restored"));
}
/// Poll the indexer until its last finalized block id reaches the sequencer's
/// current last block id or until [`L2_TO_L1_TIMEOUT`] elapses.
/// Returns the last indexer block id observed.

View File

@ -4,12 +4,11 @@
)]
use anyhow::{Context as _, Result};
use integration_tests::{TestContext, private_mention};
use integration_tests::{TestContext, get_account, new_account, private_mention};
use key_protocol::key_management::KeyChain;
use lee::{Data, program::Program};
use lee::Data;
use lee_core::account::Nonce;
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::{
account::{AccountIdWithPrivacy, HumanReadableAccount, Label},
@ -24,14 +23,11 @@ use wallet::{
async fn get_existing_account() -> Result<()> {
let ctx = TestContext::new().await?;
let account = ctx
.sequencer_client()
.get_account(ctx.existing_public_accounts()[0])
.await?;
let account = get_account(&ctx, ctx.existing_public_accounts()[0]).await?;
assert_eq!(
account.program_owner,
Program::authenticated_transfer_program().id()
programs::authenticated_transfer().id()
);
assert_eq!(account.balance, 10000);
assert!(account.data.is_empty());
@ -95,18 +91,7 @@ async fn add_label_to_existing_account() -> Result<()> {
async fn new_public_account_without_label() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
}));
let result = execute_subcommand(ctx.wallet_mut(), command).await?;
// Extract the account_id from the result
let wallet::cli::SubcommandReturnValue::RegisterAccount { account_id } = result else {
panic!("Expected RegisterAccount return value")
};
let account_id = new_account(&mut ctx, false, None).await?;
// Verify no label was stored for the account id
assert!(
@ -162,7 +147,7 @@ async fn import_private_account() -> Result<()> {
0,
));
let account = lee::Account {
program_owner: Program::authenticated_transfer_program().id(),
program_owner: programs::authenticated_transfer().id(),
balance: 777,
data: Data::default(),
nonce: Nonce::default(),
@ -226,7 +211,7 @@ async fn import_private_account_second_time_overrides_account_data() -> Result<(
serde_json::to_string(&key_chain).context("Failed to serialize key chain")?;
let initial_account = lee::Account {
program_owner: Program::authenticated_transfer_program().id(),
program_owner: programs::authenticated_transfer().id(),
balance: 100,
data: Data::default(),
nonce: Nonce::default(),
@ -245,7 +230,7 @@ async fn import_private_account_second_time_overrides_account_data() -> Result<(
.await?;
let updated_account = lee::Account {
program_owner: Program::authenticated_transfer_program().id(),
program_owner: programs::authenticated_transfer().id(),
balance: 999,
data: Data::default(),
nonce: Nonce::default(),

View File

@ -7,16 +7,18 @@
use std::time::Duration;
use anyhow::Result;
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, create_token, get_account, new_account,
public_mention, token_send,
};
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::{
account::Label,
cli::{
Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
programs::{amm::AmmProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand},
programs::amm::AmmProgramAgnosticSubcommand,
},
};
@ -25,148 +27,60 @@ async fn amm_public() -> Result<()> {
let mut ctx = TestContext::new().await?;
// Create new account for the token definition
let SubcommandReturnValue::RegisterAccount {
account_id: definition_account_id_1,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let definition_account_id_1 = new_account(&mut ctx, false, None).await?;
// Create new account for the token supply holder
let SubcommandReturnValue::RegisterAccount {
account_id: supply_account_id_1,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let supply_account_id_1 = new_account(&mut ctx, false, None).await?;
// Create new account for receiving a token transaction
let SubcommandReturnValue::RegisterAccount {
account_id: recipient_account_id_1,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let recipient_account_id_1 = new_account(&mut ctx, false, None).await?;
// Create new account for the token definition
let SubcommandReturnValue::RegisterAccount {
account_id: definition_account_id_2,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let definition_account_id_2 = new_account(&mut ctx, false, None).await?;
// Create new account for the token supply holder
let SubcommandReturnValue::RegisterAccount {
account_id: supply_account_id_2,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let supply_account_id_2 = new_account(&mut ctx, false, None).await?;
// Create new account for receiving a token transaction
let SubcommandReturnValue::RegisterAccount {
account_id: recipient_account_id_2,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let recipient_account_id_2 = new_account(&mut ctx, false, None).await?;
// Create new token
let subcommand = TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id_1),
supply_account_id: public_mention(supply_account_id_1),
name: "A NAM1".to_owned(),
total_supply: 37,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
create_token(
&mut ctx,
public_mention(definition_account_id_1),
public_mention(supply_account_id_1),
"A NAM1".to_owned(),
37,
)
.await?;
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_1`
let subcommand = TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id_1),
to: Some(public_mention(recipient_account_id_1)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 7,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
token_send(
&mut ctx,
public_mention(supply_account_id_1),
public_mention(recipient_account_id_1),
7,
)
.await?;
// Create new token
let subcommand = TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id_2),
supply_account_id: public_mention(supply_account_id_2),
name: "A NAM2".to_owned(),
total_supply: 37,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
create_token(
&mut ctx,
public_mention(definition_account_id_2),
public_mention(supply_account_id_2),
"A NAM2".to_owned(),
37,
)
.await?;
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_2`
let subcommand = TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id_2),
to: Some(public_mention(recipient_account_id_2)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 7,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
token_send(
&mut ctx,
public_mention(supply_account_id_2),
public_mention(recipient_account_id_2),
7,
)
.await?;
info!("=================== SETUP FINISHED ===============");
@ -174,19 +88,7 @@ async fn amm_public() -> Result<()> {
// Setup accounts
// Create new account for the user holding lp
let SubcommandReturnValue::RegisterAccount {
account_id: user_holding_lp,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let user_holding_lp = new_account(&mut ctx, false, None).await?;
// Send creation tx
let subcommand = AmmProgramAgnosticSubcommand::New {
@ -201,17 +103,11 @@ async fn amm_public() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let user_holding_a_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_1)
.await?;
let user_holding_a_acc = get_account(&ctx, recipient_account_id_1).await?;
let user_holding_b_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_2)
.await?;
let user_holding_b_acc = get_account(&ctx, recipient_account_id_2).await?;
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
let user_holding_lp_acc = get_account(&ctx, user_holding_lp).await?;
assert_eq!(
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
@ -244,17 +140,11 @@ async fn amm_public() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let user_holding_a_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_1)
.await?;
let user_holding_a_acc = get_account(&ctx, recipient_account_id_1).await?;
let user_holding_b_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_2)
.await?;
let user_holding_b_acc = get_account(&ctx, recipient_account_id_2).await?;
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
let user_holding_lp_acc = get_account(&ctx, user_holding_lp).await?;
assert_eq!(
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
@ -287,17 +177,11 @@ async fn amm_public() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let user_holding_a_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_1)
.await?;
let user_holding_a_acc = get_account(&ctx, recipient_account_id_1).await?;
let user_holding_b_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_2)
.await?;
let user_holding_b_acc = get_account(&ctx, recipient_account_id_2).await?;
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
let user_holding_lp_acc = get_account(&ctx, user_holding_lp).await?;
assert_eq!(
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
@ -331,17 +215,11 @@ async fn amm_public() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let user_holding_a_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_1)
.await?;
let user_holding_a_acc = get_account(&ctx, recipient_account_id_1).await?;
let user_holding_b_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_2)
.await?;
let user_holding_b_acc = get_account(&ctx, recipient_account_id_2).await?;
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
let user_holding_lp_acc = get_account(&ctx, user_holding_lp).await?;
assert_eq!(
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
@ -375,17 +253,11 @@ async fn amm_public() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let user_holding_a_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_1)
.await?;
let user_holding_a_acc = get_account(&ctx, recipient_account_id_1).await?;
let user_holding_b_acc = ctx
.sequencer_client()
.get_account(recipient_account_id_2)
.await?;
let user_holding_b_acc = get_account(&ctx, recipient_account_id_2).await?;
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
let user_holding_lp_acc = get_account(&ctx, user_holding_lp).await?;
assert_eq!(
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
@ -412,33 +284,9 @@ async fn amm_new_pool_using_labels() -> Result<()> {
let mut ctx = TestContext::new().await?;
// Create token 1 accounts
let SubcommandReturnValue::RegisterAccount {
account_id: definition_account_id_1,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let definition_account_id_1 = new_account(&mut ctx, false, None).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: supply_account_id_1,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let supply_account_id_1 = new_account(&mut ctx, false, None).await?;
// Create holding_a with a label
let holding_a_label = Label::new("amm-holding-a-label");
@ -457,33 +305,9 @@ async fn amm_new_pool_using_labels() -> Result<()> {
};
// Create token 2 accounts
let SubcommandReturnValue::RegisterAccount {
account_id: definition_account_id_2,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let definition_account_id_2 = new_account(&mut ctx, false, None).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: supply_account_id_2,
} = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let supply_account_id_2 = new_account(&mut ctx, false, None).await?;
// Create holding_b with a label
let holding_b_label = Label::new("amm-holding-b-label");
@ -518,48 +342,40 @@ async fn amm_new_pool_using_labels() -> Result<()> {
};
// Create token 1 and distribute to holding_a
let subcommand = TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id_1),
supply_account_id: public_mention(supply_account_id_1),
name: "TOKEN1".to_owned(),
total_supply: 10,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
create_token(
&mut ctx,
public_mention(definition_account_id_1),
public_mention(supply_account_id_1),
"TOKEN1".to_owned(),
10,
)
.await?;
let subcommand = TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id_1),
to: Some(public_mention(holding_a_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 5,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
token_send(
&mut ctx,
public_mention(supply_account_id_1),
public_mention(holding_a_id),
5,
)
.await?;
// Create token 2 and distribute to holding_b
let subcommand = TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id_2),
supply_account_id: public_mention(supply_account_id_2),
name: "TOKEN2".to_owned(),
total_supply: 10,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
create_token(
&mut ctx,
public_mention(definition_account_id_2),
public_mention(supply_account_id_2),
"TOKEN2".to_owned(),
10,
)
.await?;
let subcommand = TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id_2),
to: Some(public_mention(holding_b_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 5,
};
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
token_send(
&mut ctx,
public_mention(supply_account_id_2),
public_mention(holding_b_id),
5,
)
.await?;
// Create AMM pool using account labels instead of IDs
let subcommand = AmmProgramAgnosticSubcommand::New {
@ -572,7 +388,7 @@ async fn amm_new_pool_using_labels() -> Result<()> {
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let holding_lp_acc = ctx.sequencer_client().get_account(holding_lp_id).await?;
let holding_lp_acc = get_account(&ctx, holding_lp_id).await?;
// LP balance should be 3 (geometric mean of 3, 3)
assert_eq!(

View File

@ -7,78 +7,36 @@
use std::time::Duration;
use anyhow::{Context as _, Result};
use ata_core::{compute_ata_seed, get_associated_token_account_id};
use associated_token_account_core::{compute_ata_seed, get_associated_token_account_id};
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
verify_commitment_is_in_state,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, create_token, get_account, new_account,
private_mention, public_mention, token_send, verify_commitment_is_in_state,
};
use lee::program::Program;
use log::info;
use sequencer_service_rpc::RpcClient as _;
use token_core::{TokenDefinition, TokenHolding};
use tokio::test;
use wallet::cli::{
Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
programs::{ata::AtaSubcommand, token::TokenProgramAgnosticSubcommand},
};
/// Create a public account and return its ID.
async fn new_public_account(ctx: &mut TestContext) -> Result<lee::AccountId> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?;
let SubcommandReturnValue::RegisterAccount { account_id } = result else {
anyhow::bail!("Expected RegisterAccount return value");
};
Ok(account_id)
}
/// Create a private account and return its ID.
async fn new_private_account(ctx: &mut TestContext) -> Result<lee::AccountId> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
})),
)
.await?;
let SubcommandReturnValue::RegisterAccount { account_id } = result else {
anyhow::bail!("Expected RegisterAccount return value");
};
Ok(account_id)
}
use wallet::cli::{Command, programs::ata::AtaSubcommand};
#[test]
async fn create_ata_initializes_holding_account() -> Result<()> {
let mut ctx = TestContext::new().await?;
let definition_account_id = new_public_account(&mut ctx).await?;
let supply_account_id = new_public_account(&mut ctx).await?;
let owner_account_id = new_public_account(&mut ctx).await?;
let definition_account_id = new_account(&mut ctx, false, None).await?;
let supply_account_id = new_account(&mut ctx, false, None).await?;
let owner_account_id = new_account(&mut ctx, false, None).await?;
// Create a fungible token
let total_supply = 100_u128;
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id),
supply_account_id: public_mention(supply_account_id),
name: "TEST".to_owned(),
total_supply,
}),
create_token(
&mut ctx,
public_mention(definition_account_id),
public_mention(supply_account_id),
"TEST".to_owned(),
total_supply,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Create the ATA for owner + definition
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
@ -93,7 +51,7 @@ async fn create_ata_initializes_holding_account() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Derive expected ATA address and check on-chain state
let ata_program_id = Program::ata().id();
let ata_program_id = programs::ata().id();
let ata_id = get_associated_token_account_id(
&ata_program_id,
&compute_ata_seed(owner_account_id, definition_account_id),
@ -105,7 +63,7 @@ async fn create_ata_initializes_holding_account() -> Result<()> {
.await
.context("ATA account not found")?;
assert_eq!(ata_acc.program_owner, Program::token().id());
assert_eq!(ata_acc.program_owner, programs::token().id());
let holding = TokenHolding::try_from(&ata_acc.data)?;
assert_eq!(
holding,
@ -122,25 +80,20 @@ async fn create_ata_initializes_holding_account() -> Result<()> {
async fn create_ata_is_idempotent() -> Result<()> {
let mut ctx = TestContext::new().await?;
let definition_account_id = new_public_account(&mut ctx).await?;
let supply_account_id = new_public_account(&mut ctx).await?;
let owner_account_id = new_public_account(&mut ctx).await?;
let definition_account_id = new_account(&mut ctx, false, None).await?;
let supply_account_id = new_account(&mut ctx, false, None).await?;
let owner_account_id = new_account(&mut ctx, false, None).await?;
// Create a fungible token
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id),
supply_account_id: public_mention(supply_account_id),
name: "TEST".to_owned(),
total_supply: 100,
}),
create_token(
&mut ctx,
public_mention(definition_account_id),
public_mention(supply_account_id),
"TEST".to_owned(),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Create the ATA once
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
@ -168,7 +121,7 @@ async fn create_ata_is_idempotent() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// State must be unchanged
let ata_program_id = Program::ata().id();
let ata_program_id = programs::ata().id();
let ata_id = get_associated_token_account_id(
&ata_program_id,
&compute_ata_seed(owner_account_id, definition_account_id),
@ -180,7 +133,7 @@ async fn create_ata_is_idempotent() -> Result<()> {
.await
.context("ATA account not found")?;
assert_eq!(ata_acc.program_owner, Program::token().id());
assert_eq!(ata_acc.program_owner, programs::token().id());
let holding = TokenHolding::try_from(&ata_acc.data)?;
assert_eq!(
holding,
@ -197,30 +150,25 @@ async fn create_ata_is_idempotent() -> Result<()> {
async fn transfer_and_burn_via_ata() -> Result<()> {
let mut ctx = TestContext::new().await?;
let definition_account_id = new_public_account(&mut ctx).await?;
let supply_account_id = new_public_account(&mut ctx).await?;
let sender_account_id = new_public_account(&mut ctx).await?;
let recipient_account_id = new_public_account(&mut ctx).await?;
let definition_account_id = new_account(&mut ctx, false, None).await?;
let supply_account_id = new_account(&mut ctx, false, None).await?;
let sender_account_id = new_account(&mut ctx, false, None).await?;
let recipient_account_id = new_account(&mut ctx, false, None).await?;
let total_supply = 1000_u128;
// Create a fungible token, supply goes to supply_account_id
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id),
supply_account_id: public_mention(supply_account_id),
name: "TEST".to_owned(),
total_supply,
}),
create_token(
&mut ctx,
public_mention(definition_account_id),
public_mention(supply_account_id),
"TEST".to_owned(),
total_supply,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Derive ATA addresses
let ata_program_id = Program::ata().id();
let ata_program_id = programs::ata().id();
let sender_ata_id = get_associated_token_account_id(
&ata_program_id,
&compute_ata_seed(sender_account_id, definition_account_id),
@ -253,23 +201,14 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
// Fund sender's ATA from the supply account (direct token transfer)
let fund_amount = 200_u128;
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id),
to: Some(public_mention(sender_ata_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: fund_amount,
}),
token_send(
&mut ctx,
public_mention(supply_account_id),
public_mention(sender_ata_id),
fund_amount,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Transfer from sender's ATA to recipient's ATA via the ATA program
let transfer_amount = 50_u128;
wallet::cli::execute_subcommand(
@ -287,7 +226,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Verify sender ATA balance decreased
let sender_ata_acc = ctx.sequencer_client().get_account(sender_ata_id).await?;
let sender_ata_acc = get_account(&ctx, sender_ata_id).await?;
let sender_holding = TokenHolding::try_from(&sender_ata_acc.data)?;
assert_eq!(
sender_holding,
@ -298,7 +237,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
);
// Verify recipient ATA balance increased
let recipient_ata_acc = ctx.sequencer_client().get_account(recipient_ata_id).await?;
let recipient_ata_acc = get_account(&ctx, recipient_ata_id).await?;
let recipient_holding = TokenHolding::try_from(&recipient_ata_acc.data)?;
assert_eq!(
recipient_holding,
@ -324,7 +263,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Verify sender ATA balance after burn
let sender_ata_acc = ctx.sequencer_client().get_account(sender_ata_id).await?;
let sender_ata_acc = get_account(&ctx, sender_ata_id).await?;
let sender_holding = TokenHolding::try_from(&sender_ata_acc.data)?;
assert_eq!(
sender_holding,
@ -335,10 +274,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
);
// Verify the token definition total_supply decreased by burn_amount
let definition_acc = ctx
.sequencer_client()
.get_account(definition_account_id)
.await?;
let definition_acc = get_account(&ctx, definition_account_id).await?;
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
assert_eq!(
token_definition,
@ -356,25 +292,20 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
async fn create_ata_with_private_owner() -> Result<()> {
let mut ctx = TestContext::new().await?;
let definition_account_id = new_public_account(&mut ctx).await?;
let supply_account_id = new_public_account(&mut ctx).await?;
let owner_account_id = new_private_account(&mut ctx).await?;
let definition_account_id = new_account(&mut ctx, false, None).await?;
let supply_account_id = new_account(&mut ctx, false, None).await?;
let owner_account_id = new_account(&mut ctx, true, None).await?;
// Create a fungible token
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id),
supply_account_id: public_mention(supply_account_id),
name: "TEST".to_owned(),
total_supply: 100,
}),
create_token(
&mut ctx,
public_mention(definition_account_id),
public_mention(supply_account_id),
"TEST".to_owned(),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Create the ATA for the private owner + definition
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
@ -389,7 +320,7 @@ async fn create_ata_with_private_owner() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Derive expected ATA address and check on-chain state
let ata_program_id = Program::ata().id();
let ata_program_id = programs::ata().id();
let ata_id = get_associated_token_account_id(
&ata_program_id,
&compute_ata_seed(owner_account_id, definition_account_id),
@ -401,7 +332,7 @@ async fn create_ata_with_private_owner() -> Result<()> {
.await
.context("ATA account not found")?;
assert_eq!(ata_acc.program_owner, Program::token().id());
assert_eq!(ata_acc.program_owner, programs::token().id());
let holding = TokenHolding::try_from(&ata_acc.data)?;
assert_eq!(
holding,
@ -425,30 +356,25 @@ async fn create_ata_with_private_owner() -> Result<()> {
async fn transfer_via_ata_private_owner() -> Result<()> {
let mut ctx = TestContext::new().await?;
let definition_account_id = new_public_account(&mut ctx).await?;
let supply_account_id = new_public_account(&mut ctx).await?;
let sender_account_id = new_private_account(&mut ctx).await?;
let recipient_account_id = new_public_account(&mut ctx).await?;
let definition_account_id = new_account(&mut ctx, false, None).await?;
let supply_account_id = new_account(&mut ctx, false, None).await?;
let sender_account_id = new_account(&mut ctx, true, None).await?;
let recipient_account_id = new_account(&mut ctx, false, None).await?;
let total_supply = 1000_u128;
// Create a fungible token
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id),
supply_account_id: public_mention(supply_account_id),
name: "TEST".to_owned(),
total_supply,
}),
create_token(
&mut ctx,
public_mention(definition_account_id),
public_mention(supply_account_id),
"TEST".to_owned(),
total_supply,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Derive ATA addresses
let ata_program_id = Program::ata().id();
let ata_program_id = programs::ata().id();
let sender_ata_id = get_associated_token_account_id(
&ata_program_id,
&compute_ata_seed(sender_account_id, definition_account_id),
@ -481,23 +407,14 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
// Fund sender's ATA from the supply account (direct token transfer)
let fund_amount = 200_u128;
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id),
to: Some(public_mention(sender_ata_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: fund_amount,
}),
token_send(
&mut ctx,
public_mention(supply_account_id),
public_mention(sender_ata_id),
fund_amount,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Transfer from sender's ATA (private owner) to recipient's ATA
let transfer_amount = 50_u128;
wallet::cli::execute_subcommand(
@ -515,7 +432,7 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Verify sender ATA balance decreased
let sender_ata_acc = ctx.sequencer_client().get_account(sender_ata_id).await?;
let sender_ata_acc = get_account(&ctx, sender_ata_id).await?;
let sender_holding = TokenHolding::try_from(&sender_ata_acc.data)?;
assert_eq!(
sender_holding,
@ -526,7 +443,7 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
);
// Verify recipient ATA balance increased
let recipient_ata_acc = ctx.sequencer_client().get_account(recipient_ata_id).await?;
let recipient_ata_acc = get_account(&ctx, recipient_ata_id).await?;
let recipient_holding = TokenHolding::try_from(&recipient_ata_acc.data)?;
assert_eq!(
recipient_holding,
@ -550,29 +467,24 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
async fn burn_via_ata_private_owner() -> Result<()> {
let mut ctx = TestContext::new().await?;
let definition_account_id = new_public_account(&mut ctx).await?;
let supply_account_id = new_public_account(&mut ctx).await?;
let holder_account_id = new_private_account(&mut ctx).await?;
let definition_account_id = new_account(&mut ctx, false, None).await?;
let supply_account_id = new_account(&mut ctx, false, None).await?;
let holder_account_id = new_account(&mut ctx, true, None).await?;
let total_supply = 500_u128;
// Create a fungible token
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::New {
definition_account_id: public_mention(definition_account_id),
supply_account_id: public_mention(supply_account_id),
name: "TEST".to_owned(),
total_supply,
}),
create_token(
&mut ctx,
public_mention(definition_account_id),
public_mention(supply_account_id),
"TEST".to_owned(),
total_supply,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Derive holder's ATA address
let ata_program_id = Program::ata().id();
let ata_program_id = programs::ata().id();
let holder_ata_id = get_associated_token_account_id(
&ata_program_id,
&compute_ata_seed(holder_account_id, definition_account_id),
@ -593,23 +505,14 @@ async fn burn_via_ata_private_owner() -> Result<()> {
// Fund holder's ATA from the supply account
let fund_amount = 300_u128;
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Token(TokenProgramAgnosticSubcommand::Send {
from: public_mention(supply_account_id),
to: Some(public_mention(holder_ata_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: fund_amount,
}),
token_send(
&mut ctx,
public_mention(supply_account_id),
public_mention(holder_ata_id),
fund_amount,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Burn from holder's ATA (private owner)
let burn_amount = 100_u128;
wallet::cli::execute_subcommand(
@ -626,7 +529,7 @@ async fn burn_via_ata_private_owner() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Verify holder ATA balance after burn
let holder_ata_acc = ctx.sequencer_client().get_account(holder_ata_id).await?;
let holder_ata_acc = get_account(&ctx, holder_ata_id).await?;
let holder_holding = TokenHolding::try_from(&holder_ata_acc.data)?;
assert_eq!(
holder_holding,
@ -637,10 +540,7 @@ async fn burn_via_ata_private_owner() -> Result<()> {
);
// Verify the token definition total_supply decreased by burn_amount
let definition_acc = ctx
.sequencer_client()
.get_account(definition_account_id)
.await?;
let definition_acc = get_account(&ctx, definition_account_id).await?;
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
assert_eq!(
token_definition,

View File

@ -3,15 +3,18 @@ use std::time::Duration;
use anyhow::{Context as _, Result};
use common::transaction::LeeTransaction;
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx, private_mention,
public_mention, verify_commitment_is_in_state,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance,
assert_private_commitment_in_state, fetch_privacy_preserving_tx, get_account, new_account,
private_mention, public_mention, send, sync_private, verify_commitment_is_in_state,
};
use lee::{
AccountId, execute_and_prove, privacy_preserving_transaction::circuit::ProgramWithDependencies,
program::Program,
};
use lee_core::{
InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata,
DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, InputAccountIdentity, Nullifier, NullifierPublicKey,
account::{Account, AccountWithMetadata},
compute_digest_for_path,
encryption::ViewingPublicKey,
};
use log::info;
@ -33,32 +36,13 @@ async fn private_transfer_to_owned_account() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
let to: AccountId = ctx.existing_private_accounts()[1];
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(&mut ctx, private_mention(from), private_mention(to), 100).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = ctx
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment for sender")?;
assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await);
let new_commitment2 = ctx
.wallet()
.get_private_account_commitment(to)
.context("Failed to get private account commitment for receiver")?;
assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, from, "sender").await?;
assert_private_commitment_in_state(&ctx, to, "receiver").await?;
info!("Successfully transferred privately to owned account");
@ -124,17 +108,7 @@ async fn deshielded_transfer_to_public_account() -> Result<()> {
.context("Failed to get sender's private account")?;
assert_eq!(from_acc.balance, 10000);
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
to: Some(public_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(&mut ctx, private_mention(from), public_mention(to), 100).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
@ -143,13 +117,9 @@ async fn deshielded_transfer_to_public_account() -> Result<()> {
.wallet()
.get_account_private(from)
.context("Failed to get sender's private account")?;
let new_commitment = ctx
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment")?;
assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, from, "sender").await?;
let acc_2_balance = ctx.sequencer_client().get_account_balance(to).await?;
let acc_2_balance = account_balance(&ctx, to).await?;
assert_eq!(from_acc.balance, 9900);
assert_eq!(acc_2_balance, 20100);
@ -166,18 +136,7 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
// Create a new private account
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
}));
let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id,
} = sub_ret
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let to_account_id = new_account(&mut ctx, true, None).await?;
// Get the keys for the newly created account
let to = ctx
@ -206,14 +165,13 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash).await;
// Sync the wallet to claim the new account
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
sync_private(&mut ctx).await?;
let new_commitment1 = ctx
let sender_commitment = ctx
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment for sender")?;
assert_eq!(tx.message.new_commitments[0], new_commitment1);
assert_eq!(tx.message.new_commitments[0], sender_commitment);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments {
@ -238,17 +196,7 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> {
let from: AccountId = ctx.existing_public_accounts()[0];
let to: AccountId = ctx.existing_private_accounts()[1];
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(from),
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(&mut ctx, public_mention(from), private_mention(to), 100).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
@ -257,13 +205,9 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> {
.wallet()
.get_account_private(to)
.context("Failed to get receiver's private account")?;
let new_commitment = ctx
.wallet()
.get_private_account_commitment(to)
.context("Failed to get receiver's commitment")?;
assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, to, "receiver").await?;
let acc_from_balance = ctx.sequencer_client().get_account_balance(from).await?;
let acc_from_balance = account_balance(&ctx, from).await?;
assert_eq!(acc_from_balance, 9900);
assert_eq!(acc_to.balance, 20100);
@ -302,7 +246,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash).await;
let acc_1_balance = ctx.sequencer_client().get_account_balance(from).await?;
let acc_1_balance = account_balance(&ctx, from).await?;
assert!(
verify_commitment_is_in_state(
@ -331,18 +275,7 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
// Create a new private account
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
}));
let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id,
} = sub_ret
else {
anyhow::bail!("Failed to register account");
};
let to_account_id = new_account(&mut ctx, true, None).await?;
// Get the newly created account's keys
let to = ctx
@ -395,14 +328,7 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
async fn initialize_private_account() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
}));
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount { account_id } = result else {
anyhow::bail!("Expected RegisterAccount return value");
};
let account_id = new_account(&mut ctx, true, None).await?;
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
account_id: private_mention(account_id),
@ -412,14 +338,9 @@ async fn initialize_private_account() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Syncing private accounts");
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
sync_private(&mut ctx).await?;
let new_commitment = ctx
.wallet()
.get_private_account_commitment(account_id)
.context("Failed to get private account commitment")?;
assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, account_id, "account").await?;
let account = ctx
.wallet()
@ -428,7 +349,7 @@ async fn initialize_private_account() -> Result<()> {
assert_eq!(
account.program_owner,
Program::authenticated_transfer_program().id()
programs::authenticated_transfer().id()
);
assert_eq!(account.balance, 0);
assert!(account.data.is_empty());
@ -454,32 +375,19 @@ async fn private_transfer_using_from_label() -> Result<()> {
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send using the label instead of account ID
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: CliAccountMention::Label(label),
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(
&mut ctx,
CliAccountMention::Label(label),
private_mention(to),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = ctx
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment for sender")?;
assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await);
let new_commitment2 = ctx
.wallet()
.get_private_account_commitment(to)
.context("Failed to get private account commitment for receiver")?;
assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, from, "sender").await?;
assert_private_commitment_in_state(&ctx, to, "receiver").await?;
info!("Successfully transferred privately using from_label");
@ -509,14 +417,9 @@ async fn initialize_private_account_using_label() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
sync_private(&mut ctx).await?;
let new_commitment = ctx
.wallet()
.get_private_account_commitment(account_id)
.context("Failed to get private account commitment")?;
assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, account_id, "account").await?;
let account = ctx
.wallet()
@ -525,7 +428,7 @@ async fn initialize_private_account_using_label() -> Result<()> {
assert_eq!(
account.program_owner,
Program::authenticated_transfer_program().id()
programs::authenticated_transfer().id()
);
info!("Successfully initialized private account using label");
@ -592,11 +495,7 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::SyncPrivate {}),
)
.await?;
sync_private(&mut ctx).await?;
// Both accounts must be discovered with the correct balances.
let account_id_1 = AccountId::for_regular_private_account(&npk, &vpk, identifier_1);
@ -645,23 +544,20 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
async fn ppt_cant_chain_call_faucet() -> Result<()> {
let ctx = TestContext::new().await?;
let binary = std::fs::read(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../artifacts/test_program_methods/faucet_chain_caller.bin"),
)?;
let faucet_chain_caller = test_programs::faucet_chain_caller();
let deploy_tx = LeeTransaction::ProgramDeployment(lee::ProgramDeploymentTransaction::new(
lee::program_deployment_transaction::Message::new(binary.clone()),
lee::program_deployment_transaction::Message::new(faucet_chain_caller.elf().to_owned()),
));
ctx.sequencer_client().send_transaction(deploy_tx).await?;
info!("Waiting for deploy block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let faucet_account_id = lee::system_faucet_account_id();
let faucet_account_id = system_accounts::faucet_account_id();
let attacker_id = ctx.existing_public_accounts()[0];
let faucet_program_id = Program::faucet().id();
let vault_program_id = Program::vault().id();
let auth_transfer_program_id = Program::authenticated_transfer_program().id();
let faucet_program_id = programs::faucet().id();
let vault_program_id = programs::vault().id();
let auth_transfer_program_id = programs::authenticated_transfer().id();
let nsk: lee_core::NullifierSecretKey = [3; 32];
let npk = NullifierPublicKey::from(&nsk);
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
@ -672,30 +568,22 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
let amount: u128 = 1;
let faucet_pre = AccountWithMetadata::new(
ctx.sequencer_client()
.get_account(faucet_account_id)
.await?,
get_account(&ctx, faucet_account_id).await?,
false,
faucet_account_id,
);
let vault_pda_pre = AccountWithMetadata::new(
ctx.sequencer_client()
.get_account(attacker_vault_id)
.await?,
get_account(&ctx, attacker_vault_id).await?,
false,
attacker_vault_id,
);
let faucet_chain_caller = Program::new(binary)?;
let program_with_deps = ProgramWithDependencies::new(
faucet_chain_caller,
[
(faucet_program_id, Program::faucet()),
(vault_program_id, Program::vault()),
(
auth_transfer_program_id,
Program::authenticated_transfer_program(),
),
(faucet_program_id, programs::faucet()),
(vault_program_id, programs::vault()),
(auth_transfer_program_id, programs::authenticated_transfer()),
]
.into(),
);
@ -713,6 +601,7 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
random_seed: [0; 32],
npk,
identifier: 1337,
commitment_root: DUMMY_COMMITMENT_HASH,
seed: None,
},
],
@ -723,3 +612,99 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
Ok(())
}
async fn prove_init_with_commitment_root(
ctx: &TestContext,
commitment_root: lee_core::CommitmentSetDigest,
) -> Result<lee_core::PrivacyPreservingCircuitOutput> {
let program = programs::authenticated_transfer();
let sender_id = ctx.existing_public_accounts()[0];
let sender_pre = AccountWithMetadata::new(
ctx.sequencer_client().get_account(sender_id).await?,
true,
sender_id,
);
let nsk: lee_core::NullifierSecretKey = [7; 32];
let npk = NullifierPublicKey::from(&nsk);
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
let recipient_account_id = AccountId::for_regular_private_account(&npk, &vpk, 0);
let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id);
let (output, _) = execute_and_prove(
vec![sender_pre, recipient],
Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer {
amount: 1,
})?,
vec![
InputAccountIdentity::Public,
InputAccountIdentity::PrivateUnauthorized {
vpk,
random_seed: [0; 32],
npk,
identifier: 0,
commitment_root,
},
],
&program.into(),
)?;
Ok(output)
}
#[test]
async fn init_with_dummy_commitment_root_produces_valid_root() -> Result<()> {
let ctx = TestContext::new().await?;
let dummy_proof = ctx
.sequencer_client()
.get_proof_for_commitment(DUMMY_COMMITMENT)
.await?
.expect("DUMMY_COMMITMENT must be in genesis commitment set");
let expected_digest = compute_digest_for_path(&DUMMY_COMMITMENT, &dummy_proof);
let nsk: lee_core::NullifierSecretKey = [7; 32];
let npk = NullifierPublicKey::from(&nsk);
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
let recipient_account_id = AccountId::for_regular_private_account(&npk, &vpk, 0);
let output = prove_init_with_commitment_root(&ctx, expected_digest).await?;
assert_eq!(output.new_nullifiers.len(), 1);
let (nullifier, digest) = &output.new_nullifiers[0];
assert_eq!(
*nullifier,
Nullifier::for_account_initialization(&recipient_account_id)
);
assert_eq!(*digest, expected_digest);
assert_ne!(*digest, DUMMY_COMMITMENT_HASH);
Ok(())
}
#[test]
async fn init_nullifier_digest_is_bound_to_commitment_root() -> Result<()> {
let ctx = TestContext::new().await?;
let dummy_proof = ctx
.sequencer_client()
.get_proof_for_commitment(DUMMY_COMMITMENT)
.await?
.expect("DUMMY_COMMITMENT must be in genesis commitment set");
let expected_digest = compute_digest_for_path(&DUMMY_COMMITMENT, &dummy_proof);
let output_with_root = prove_init_with_commitment_root(&ctx, expected_digest).await?;
let output_without_root = prove_init_with_commitment_root(&ctx, DUMMY_COMMITMENT_HASH).await?;
assert_eq!(output_with_root.new_nullifiers[0].1, expected_digest);
assert_eq!(
output_without_root.new_nullifiers[0].1,
DUMMY_COMMITMENT_HASH
);
assert_ne!(
output_with_root.new_nullifiers[0].1,
output_without_root.new_nullifiers[0].1,
);
Ok(())
}

View File

@ -1,17 +1,19 @@
use std::{path::PathBuf, time::Duration};
use std::time::Duration;
use anyhow::Result;
use common::transaction::LeeTransaction;
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
use lee::{program::Program, public_transaction, system_faucet_account_id};
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance, get_account, new_account,
public_mention, send,
};
use lee::public_transaction;
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::{
account::Label,
cli::{
CliAccountMention, Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
CliAccountMention, Command, account::AccountSubcommand,
programs::native_token_transfer::AuthTransferSubcommand,
},
};
@ -20,30 +22,22 @@ use wallet::{
async fn successful_transfer_to_existing_account() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(ctx.existing_public_accounts()[0]),
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let sender = ctx.existing_public_accounts()[0];
let receiver = ctx.existing_public_accounts()[1];
send(
&mut ctx,
public_mention(sender),
public_mention(receiver),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[1])
.await?;
let acc_1_balance = account_balance(&ctx, sender).await?;
let acc_2_balance = account_balance(&ctx, receiver).await?;
info!("Balance of sender: {acc_1_balance:#?}");
info!("Balance of receiver: {acc_2_balance:#?}");
@ -58,51 +52,23 @@ async fn successful_transfer_to_existing_account() -> Result<()> {
pub async fn successful_transfer_to_new_account() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
}));
let new_persistent_account_id = new_account(&mut ctx, false, None).await?;
wallet::cli::execute_subcommand(ctx.wallet_mut(), command)
.await
.unwrap();
let new_persistent_account_id = ctx
.wallet()
.storage()
.key_chain()
.public_account_ids()
.map(|(account_id, _)| account_id)
.find(|acc_id| {
*acc_id != ctx.existing_public_accounts()[0]
&& *acc_id != ctx.existing_public_accounts()[1]
})
.expect("Failed to find newly created account in the wallet storage");
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(ctx.existing_public_accounts()[0]),
to: Some(public_mention(new_persistent_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let sender = ctx.existing_public_accounts()[0];
send(
&mut ctx,
public_mention(sender),
public_mention(new_persistent_account_id),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(new_persistent_account_id)
.await?;
let acc_1_balance = account_balance(&ctx, sender).await?;
let acc_2_balance = account_balance(&ctx, new_persistent_account_id).await?;
info!("Balance of sender: {acc_1_balance:#?}");
info!("Balance of receiver: {acc_2_balance:#?}");
@ -134,14 +100,8 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking balances unchanged");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[1])
.await?;
let acc_1_balance = account_balance(&ctx, ctx.existing_public_accounts()[0]).await?;
let acc_2_balance = account_balance(&ctx, ctx.existing_public_accounts()[1]).await?;
info!("Balance of sender: {acc_1_balance:#?}");
info!("Balance of receiver: {acc_2_balance:#?}");
@ -156,31 +116,24 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> {
async fn two_consecutive_successful_transfers() -> Result<()> {
let mut ctx = TestContext::new().await?;
// First transfer
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(ctx.existing_public_accounts()[0]),
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
let sender = ctx.existing_public_accounts()[0];
let receiver = ctx.existing_public_accounts()[1];
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// First transfer
send(
&mut ctx,
public_mention(sender),
public_mention(receiver),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move after first transfer");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[1])
.await?;
let acc_1_balance = account_balance(&ctx, sender).await?;
let acc_2_balance = account_balance(&ctx, receiver).await?;
info!("Balance of sender: {acc_1_balance:#?}");
info!("Balance of receiver: {acc_2_balance:#?}");
@ -191,30 +144,20 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
info!("First TX Success!");
// Second transfer
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(ctx.existing_public_accounts()[0]),
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(
&mut ctx,
public_mention(sender),
public_mention(receiver),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move after second transfer");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[1])
.await?;
let acc_1_balance = account_balance(&ctx, sender).await?;
let acc_2_balance = account_balance(&ctx, receiver).await?;
info!("Balance of sender: {acc_1_balance:#?}");
info!("Balance of receiver: {acc_2_balance:#?}");
@ -231,14 +174,7 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
async fn initialize_public_account() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
}));
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount { account_id } = result else {
anyhow::bail!("Expected RegisterAccount return value");
};
let account_id = new_account(&mut ctx, false, None).await?;
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
account_id: public_mention(account_id),
@ -246,11 +182,11 @@ async fn initialize_public_account() -> Result<()> {
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
info!("Checking correct execution");
let account = ctx.sequencer_client().get_account(account_id).await?;
let account = get_account(&ctx, account_id).await?;
assert_eq!(
account.program_owner,
Program::authenticated_transfer_program().id()
programs::authenticated_transfer().id()
);
assert_eq!(account.balance, 0);
assert_eq!(account.nonce.0, 1);
@ -274,30 +210,22 @@ async fn successful_transfer_using_from_label() -> Result<()> {
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send using the label instead of account ID
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: CliAccountMention::Label(label),
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let sender = ctx.existing_public_accounts()[0];
let receiver = ctx.existing_public_accounts()[1];
send(
&mut ctx,
CliAccountMention::Label(label),
public_mention(receiver),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[1])
.await?;
let acc_1_balance = account_balance(&ctx, sender).await?;
let acc_2_balance = account_balance(&ctx, receiver).await?;
assert_eq!(acc_1_balance, 9900);
assert_eq!(acc_2_balance, 20100);
@ -320,30 +248,22 @@ async fn successful_transfer_using_to_label() -> Result<()> {
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send using the label for the recipient
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(ctx.existing_public_accounts()[0]),
to: Some(CliAccountMention::Label(label)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let sender = ctx.existing_public_accounts()[0];
let receiver = ctx.existing_public_accounts()[1];
send(
&mut ctx,
public_mention(sender),
CliAccountMention::Label(label),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let acc_2_balance = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[1])
.await?;
let acc_1_balance = account_balance(&ctx, sender).await?;
let acc_2_balance = account_balance(&ctx, receiver).await?;
assert_eq!(acc_1_balance, 9900);
assert_eq!(acc_2_balance, 20100);
@ -356,21 +276,15 @@ async fn successful_transfer_using_to_label() -> Result<()> {
#[test]
async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
let ctx = TestContext::new().await?;
let faucet_account_id = system_faucet_account_id();
let faucet_account_id = system_accounts::faucet_account_id();
let recipient = ctx.existing_public_accounts()[0];
let recipient_balance_before = ctx
.sequencer_client()
.get_account_balance(recipient)
.await?;
let faucet_balance_before = ctx
.sequencer_client()
.get_account_balance(faucet_account_id)
.await?;
let recipient_balance_before = account_balance(&ctx, recipient).await?;
let faucet_balance_before = account_balance(&ctx, faucet_account_id).await?;
let amount = 1_u128;
let message = public_transaction::Message::try_new(
Program::authenticated_transfer_program().id(),
programs::authenticated_transfer().id(),
vec![faucet_account_id, recipient],
vec![],
authenticated_transfer_core::Instruction::Transfer { amount },
@ -387,14 +301,8 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let recipient_balance_after = ctx
.sequencer_client()
.get_account_balance(recipient)
.await?;
let faucet_balance_after = ctx
.sequencer_client()
.get_account_balance(faucet_account_id)
.await?;
let recipient_balance_after = account_balance(&ctx, recipient).await?;
let faucet_balance_after = account_balance(&ctx, faucet_account_id).await?;
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
assert_eq!(recipient_balance_after, recipient_balance_before);
@ -407,24 +315,18 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
#[test]
async fn cannot_execute_faucet_program() -> Result<()> {
let ctx = TestContext::new().await?;
let faucet_account_id = system_faucet_account_id();
let faucet_account_id = system_accounts::faucet_account_id();
let recipient = ctx.existing_public_accounts()[0];
let vault_program_id = Program::vault().id();
let vault_program_id = programs::vault().id();
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient);
let recipient_balance_before = ctx
.sequencer_client()
.get_account_balance(recipient)
.await?;
let faucet_balance_before = ctx
.sequencer_client()
.get_account_balance(faucet_account_id)
.await?;
let recipient_balance_before = account_balance(&ctx, recipient).await?;
let faucet_balance_before = account_balance(&ctx, faucet_account_id).await?;
let amount = 1_u128;
let message = public_transaction::Message::try_new(
Program::faucet().id(),
programs::faucet().id(),
vec![faucet_account_id, recipient_vault_id],
vec![],
faucet_core::Instruction::GenesisTransferVault {
@ -445,14 +347,8 @@ async fn cannot_execute_faucet_program() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let recipient_balance_after = ctx
.sequencer_client()
.get_account_balance(recipient)
.await?;
let faucet_balance_after = ctx
.sequencer_client()
.get_account_balance(faucet_account_id)
.await?;
let recipient_balance_after = account_balance(&ctx, recipient).await?;
let faucet_balance_after = account_balance(&ctx, faucet_account_id).await?;
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
assert_eq!(recipient_balance_after, recipient_balance_before);
@ -466,28 +362,24 @@ async fn cannot_execute_faucet_program() -> Result<()> {
async fn user_tx_that_chain_calls_faucet_is_dropped() -> Result<()> {
let ctx = TestContext::new().await?;
let binary = std::fs::read(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../artifacts/test_program_methods/faucet_chain_caller.bin"),
)?;
let faucet_chain_caller_id = Program::new(binary.clone())?.id();
let faucet_chain_caller = test_programs::faucet_chain_caller();
let deploy_tx = LeeTransaction::ProgramDeployment(lee::ProgramDeploymentTransaction::new(
lee::program_deployment_transaction::Message::new(binary),
lee::program_deployment_transaction::Message::new(faucet_chain_caller.elf().to_owned()),
));
ctx.sequencer_client().send_transaction(deploy_tx).await?;
info!("Waiting for deploy block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let faucet_account_id = system_faucet_account_id();
let faucet_account_id = system_accounts::faucet_account_id();
let attacker = ctx.existing_public_accounts()[0];
let faucet_program_id = Program::faucet().id();
let vault_program_id = Program::vault().id();
let faucet_program_id = programs::faucet().id();
let vault_program_id = programs::vault().id();
let attacker_vault_id = vault_core::compute_vault_account_id(vault_program_id, attacker);
let amount: u128 = 1;
let message = public_transaction::Message::try_new(
faucet_chain_caller_id,
faucet_chain_caller.id(),
vec![faucet_account_id, attacker_vault_id],
vec![],
(faucet_program_id, vault_program_id, attacker, amount),
@ -497,28 +389,16 @@ async fn user_tx_that_chain_calls_faucet_is_dropped() -> Result<()> {
lee::public_transaction::WitnessSet::from_raw_parts(vec![]),
));
let faucet_balance_before = ctx
.sequencer_client()
.get_account_balance(faucet_account_id)
.await?;
let vault_balance_before = ctx
.sequencer_client()
.get_account_balance(attacker_vault_id)
.await?;
let faucet_balance_before = account_balance(&ctx, faucet_account_id).await?;
let vault_balance_before = account_balance(&ctx, attacker_vault_id).await?;
let tx_hash = ctx.sequencer_client().send_transaction(attack_tx).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let faucet_balance_after = ctx
.sequencer_client()
.get_account_balance(faucet_account_id)
.await?;
let vault_balance_after = ctx
.sequencer_client()
.get_account_balance(attacker_vault_id)
.await?;
let faucet_balance_after = account_balance(&ctx, faucet_account_id).await?;
let vault_balance_after = account_balance(&ctx, attacker_vault_id).await?;
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
assert_eq!(faucet_balance_after, faucet_balance_before);

View File

@ -94,16 +94,12 @@ async fn accept_transaction_within_limit() -> Result<()> {
#[test]
async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let artifacts_dir =
std::path::PathBuf::from(manifest_dir).join("../artifacts/test_program_methods");
let burner_bytecode = std::fs::read(artifacts_dir.join("burner.bin"))?;
let chain_caller_bytecode = std::fs::read(artifacts_dir.join("chain_caller.bin"))?;
let claimer = test_programs::claimer();
let chain_caller = test_programs::chain_caller();
// Calculate block size to fit only one of the two transactions, leaving some room for headers
// (e.g., 10 KiB)
let max_program_size = burner_bytecode.len().max(chain_caller_bytecode.len());
let max_program_size = claimer.elf().len().max(chain_caller.elf().len());
let block_size = ByteSize::b((max_program_size + 10 * 1024) as u64);
let ctx = TestContext::builder()
@ -116,16 +112,13 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
.build()
.await?;
let burner_id = Program::new(burner_bytecode.clone())?.id();
let chain_caller_id = Program::new(chain_caller_bytecode.clone())?.id();
let initial_block_height = ctx.sequencer_client().get_last_block_id().await?;
// Submit both program deployments
ctx.sequencer_client()
.send_transaction(LeeTransaction::ProgramDeployment(
lee::ProgramDeploymentTransaction::new(
lee::program_deployment_transaction::Message::new(burner_bytecode),
lee::program_deployment_transaction::Message::new(claimer.elf().to_owned()),
),
))
.await?;
@ -133,7 +126,7 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
ctx.sequencer_client()
.send_transaction(LeeTransaction::ProgramDeployment(
lee::ProgramDeploymentTransaction::new(
lee::program_deployment_transaction::Message::new(chain_caller_bytecode),
lee::program_deployment_transaction::Message::new(chain_caller.elf().to_owned()),
),
))
.await?;
@ -156,7 +149,7 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
.filter_map(|tx| {
if let LeeTransaction::ProgramDeployment(deployment) = tx {
let bytecode = deployment.message.clone().into_bytecode();
Program::new(bytecode).ok().map(|p| p.id())
Program::new(bytecode.into()).ok().map(|p| p.id())
} else {
None
}
@ -173,8 +166,9 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
"Expected exactly one program deployment in block 1"
);
assert_eq!(
block1_program_ids[0], burner_id,
"Expected burner program to be deployed in block 1"
block1_program_ids[0],
claimer.id(),
"Expected claimer program to be deployed in block 1"
);
// Wait for second block
@ -194,7 +188,8 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
"Expected exactly one program deployment in block 2"
);
assert_eq!(
block2_program_ids[0], chain_caller_id,
block2_program_ids[0],
chain_caller.id(),
"Expected chain_caller program to be deployed in block 2"
);

View File

@ -11,7 +11,8 @@ use borsh::BorshSerialize;
use common::transaction::LeeTransaction;
use futures::StreamExt as _;
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, wait_for_indexer_to_catch_up,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance, get_account,
wait_for_indexer_to_catch_up,
};
use lee::{
AccountId, execute_and_prove, privacy_preserving_transaction, program::Program,
@ -43,12 +44,12 @@ async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
let ctx = TestContext::new().await?;
let recipient_id = ctx.existing_public_accounts()[0];
let bridge_account_id = lee::system_bridge_account_id();
let vault_program_id = Program::vault().id();
let bridge_account_id = system_accounts::bridge_account_id();
let vault_program_id = programs::vault().id();
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
let message = public_transaction::Message::try_new(
Program::bridge().id(),
programs::bridge().id(),
vec![bridge_account_id, recipient_vault_id],
vec![],
bridge_core::Instruction::Deposit {
@ -65,6 +66,54 @@ async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
lee::public_transaction::WitnessSet::from_raw_parts(vec![]),
));
let bridge_balance_before = account_balance(&ctx, bridge_account_id).await?;
let vault_balance_before = account_balance(&ctx, recipient_vault_id).await?;
let tx_hash = ctx.sequencer_client().send_transaction(attack_tx).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let bridge_balance_after = account_balance(&ctx, bridge_account_id).await?;
let vault_balance_after = account_balance(&ctx, recipient_vault_id).await?;
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
assert_eq!(bridge_balance_after, bridge_balance_before);
assert_eq!(vault_balance_after, vault_balance_before);
assert!(
tx_on_chain.is_none(),
"Direct public bridge::Deposit invocation should be rejected"
);
Ok(())
}
#[test]
async fn public_bridge_deposit_with_zero_amount_is_rejected() -> anyhow::Result<()> {
let ctx = TestContext::new().await?;
let recipient_id = ctx.existing_public_accounts()[0];
let bridge_account_id = system_accounts::bridge_account_id();
let vault_program_id = programs::vault().id();
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
let message = public_transaction::Message::try_new(
programs::bridge().id(),
vec![bridge_account_id, recipient_vault_id],
vec![],
bridge_core::Instruction::Deposit {
l1_deposit_op_id: [0_u8; 32],
vault_program_id,
recipient_id,
amount: 0,
},
)
.context("Failed to build zero-amount public bridge deposit transaction")?;
let attack_tx = LeeTransaction::Public(lee::PublicTransaction::new(
message,
lee::public_transaction::WitnessSet::from_raw_parts(vec![]),
));
let bridge_balance_before = ctx
.sequencer_client()
.get_account_balance(bridge_account_id)
@ -92,7 +141,7 @@ async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
assert_eq!(vault_balance_after, vault_balance_before);
assert!(
tx_on_chain.is_none(),
"Direct public bridge::Deposit invocation should be rejected"
"Public bridge::Deposit with zero amount should be rejected"
);
Ok(())
@ -103,22 +152,18 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
let ctx = TestContext::new().await?;
let recipient_id = ctx.existing_public_accounts()[0];
let bridge_account_id = lee::system_bridge_account_id();
let vault_program_id = Program::vault().id();
let bridge_account_id = system_accounts::bridge_account_id();
let vault_program_id = programs::vault().id();
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
// Get pre-state of bridge and vault accounts
let bridge_pre = AccountWithMetadata::new(
ctx.sequencer_client()
.get_account(bridge_account_id)
.await?,
get_account(&ctx, bridge_account_id).await?,
false,
bridge_account_id,
);
let vault_pre = AccountWithMetadata::new(
ctx.sequencer_client()
.get_account(recipient_vault_id)
.await?,
get_account(&ctx, recipient_vault_id).await?,
false,
recipient_vault_id,
);
@ -126,12 +171,12 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
// Create program with dependencies
let program_with_deps =
lee::privacy_preserving_transaction::circuit::ProgramWithDependencies::new(
Program::bridge(),
programs::bridge(),
[
(vault_program_id, Program::vault()),
(vault_program_id, programs::vault()),
(
Program::authenticated_transfer_program().id(),
Program::authenticated_transfer_program(),
programs::authenticated_transfer().id(),
programs::authenticated_transfer(),
),
]
.into(),
@ -169,27 +214,15 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
witness_set,
));
let bridge_balance_before = ctx
.sequencer_client()
.get_account_balance(bridge_account_id)
.await?;
let vault_balance_before = ctx
.sequencer_client()
.get_account_balance(recipient_vault_id)
.await?;
let bridge_balance_before = account_balance(&ctx, bridge_account_id).await?;
let vault_balance_before = account_balance(&ctx, recipient_vault_id).await?;
let tx_hash = ctx.sequencer_client().send_transaction(attack_tx).await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let bridge_balance_after = ctx
.sequencer_client()
.get_account_balance(bridge_account_id)
.await?;
let vault_balance_after = ctx
.sequencer_client()
.get_account_balance(recipient_vault_id)
.await?;
let bridge_balance_after = account_balance(&ctx, bridge_account_id).await?;
let vault_balance_after = account_balance(&ctx, recipient_vault_id).await?;
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
assert_eq!(bridge_balance_after, bridge_balance_before);
@ -361,7 +394,7 @@ async fn wait_for_vault_balance(
+ Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS);
tokio::time::timeout(timeout, async {
loop {
let balance = ctx.sequencer_client().get_account_balance(vault_id).await?;
let balance = account_balance(ctx, vault_id).await?;
if balance == expected_balance {
return Ok(());
}
@ -386,17 +419,11 @@ async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Res
let bedrock_account_pk = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
let recipient_id = ctx.existing_public_accounts()[0];
let amount = 1_u64;
let vault_program_id = Program::vault().id();
let vault_program_id = programs::vault().id();
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
let vault_balance_before = ctx
.sequencer_client()
.get_account_balance(recipient_vault_id)
.await?;
let recipient_balance_before = ctx
.sequencer_client()
.get_account_balance(recipient_id)
.await?;
let vault_balance_before = account_balance(&ctx, recipient_vault_id).await?;
let recipient_balance_before = account_balance(&ctx, recipient_id).await?;
// Submit deposit to Bedrock
submit_bedrock_deposit(ctx.bedrock_addr(), bedrock_account_pk, recipient_id, amount)
@ -447,14 +474,8 @@ async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Res
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let claim_on_chain = ctx.sequencer_client().get_transaction(claim_hash).await?;
let vault_balance_after_claim = ctx
.sequencer_client()
.get_account_balance(recipient_vault_id)
.await?;
let recipient_balance_after_claim = ctx
.sequencer_client()
.get_account_balance(recipient_id)
.await?;
let vault_balance_after_claim = account_balance(&ctx, recipient_vault_id).await?;
let recipient_balance_after_claim = account_balance(&ctx, recipient_id).await?;
assert!(
claim_on_chain.is_some(),
@ -474,7 +495,7 @@ async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Res
// state as the sequencer — including the bridge system account the deposit
// modifies, which is the case the hot fix unblocks.
wait_for_indexer_to_catch_up(&ctx).await?;
let bridge_account_id = lee::system_bridge_account_id();
let bridge_account_id = system_accounts::bridge_account_id();
for account_id in [recipient_id, recipient_vault_id, bridge_account_id] {
let indexer_account = indexer_service_rpc::RpcClient::get_account(
// `deref` is needed for correct trait resolution
@ -483,7 +504,7 @@ async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Res
account_id.into(),
)
.await?;
let sequencer_account = ctx.sequencer_client().get_account(account_id).await?;
let sequencer_account = get_account(&ctx, account_id).await?;
assert_eq!(
indexer_account,
sequencer_account.into(),

View File

@ -5,8 +5,7 @@
)]
use anyhow::Result;
use indexer_ffi::{Runtime, api::types::FfiOption};
use integration_tests::L2_TO_L1_TIMEOUT;
use indexer_ffi::api::types::FfiOption;
use log::info;
#[path = "indexer_ffi_helpers/mod.rs"]
@ -14,21 +13,15 @@ mod indexer_ffi_helpers;
#[test]
fn indexer_ffi_block_batching() -> Result<()> {
let (ctx, indexer_ffi, _indexer_dir) = indexer_ffi_helpers::setup()?;
// `_ctx` keeps the bedrock/sequencer harness (and its runtime) alive for the
// duration of the test; the indexer was started on that runtime.
let (_ctx, indexer_ffi, _indexer_dir) = indexer_ffi_helpers::setup()?;
// WAIT
// WAIT: poll until the indexer has finalized at least two blocks (so the
// chain-consistency check below verifies at least one block link), returning
// early instead of sleeping for the full timeout.
info!("Waiting for indexer to parse blocks");
std::thread::sleep(L2_TO_L1_TIMEOUT);
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
let last_block_indexer_ffi_res = unsafe {
indexer_ffi_helpers::query_last_block(&raw const runtime, &raw const indexer_ffi)
};
assert!(last_block_indexer_ffi_res.error.is_ok());
let last_block_indexer = unsafe { *last_block_indexer_ffi_res.value };
let last_block_indexer = indexer_ffi_helpers::wait_for_indexer_ffi_block(&indexer_ffi, 2)?;
info!("Last block on indexer FFI now is {last_block_indexer}");
@ -37,14 +30,8 @@ fn indexer_ffi_block_batching() -> Result<()> {
let before_ffi = FfiOption::<u64>::from_none();
let limit = 100;
let block_batch_ffi_res = unsafe {
indexer_ffi_helpers::query_block_vec(
&raw const runtime,
&raw const indexer_ffi,
before_ffi,
limit,
)
};
let block_batch_ffi_res =
unsafe { indexer_ffi_helpers::query_block_vec(&raw const indexer_ffi, before_ffi, limit) };
assert!(block_batch_ffi_res.error.is_ok());

View File

@ -13,6 +13,7 @@ use indexer_ffi::{
api::{
PointerResult,
lifecycle::InitializedIndexerServiceFFIResult,
query::LastBlockIdResult,
types::{FfiAccountId, FfiOption, FfiVec, account::FfiAccount, block::FfiBlock},
},
};
@ -20,20 +21,15 @@ use integration_tests::{BlockingTestContext, TestContext};
use tempfile::TempDir;
unsafe extern "C" {
pub unsafe fn query_last_block(
runtime: *const Runtime,
indexer: *const IndexerServiceFFI,
) -> PointerResult<u64, OperationStatus>;
pub unsafe fn query_last_block(indexer: *const IndexerServiceFFI) -> LastBlockIdResult;
pub unsafe fn query_block_vec(
runtime: *const Runtime,
indexer: *const IndexerServiceFFI,
before: FfiOption<u64>,
limit: u64,
) -> PointerResult<FfiVec<FfiBlock>, OperationStatus>;
pub unsafe fn query_account(
runtime: *const Runtime,
indexer: *const IndexerServiceFFI,
account_id: FfiAccountId,
) -> PointerResult<FfiAccount, OperationStatus>;
@ -41,14 +37,11 @@ unsafe extern "C" {
pub unsafe fn start_indexer(
runtime: *const Runtime,
config_path: *const c_char,
port: u16,
storage_dir: *const c_char,
) -> InitializedIndexerServiceFFIResult;
}
pub fn setup_indexer_ffi(
runtime: &Runtime,
bedrock_addr: SocketAddr,
) -> Result<(IndexerServiceFFI, TempDir)> {
pub fn setup_indexer_ffi(bedrock_addr: SocketAddr) -> Result<(IndexerServiceFFI, TempDir)> {
let temp_indexer_dir =
tempfile::tempdir().context("Failed to create temp dir for indexer home")?;
@ -57,9 +50,8 @@ pub fn setup_indexer_ffi(
temp_indexer_dir.path().display()
);
let indexer_config =
integration_tests::config::indexer_config(bedrock_addr, temp_indexer_dir.path().to_owned())
.context("Failed to create Indexer config")?;
let indexer_config = integration_tests::config::indexer_config(bedrock_addr)
.context("Failed to create Indexer config")?;
let config_json = serde_json::to_vec(&indexer_config)?;
let config_path = temp_indexer_dir.path().join("indexer_config.json");
@ -67,9 +59,13 @@ pub fn setup_indexer_ffi(
file.write_all(&config_json)?;
file.flush()?;
let config_path_c = CString::new(config_path.to_str().unwrap())?;
let storage_dir_c = CString::new(temp_indexer_dir.path().to_str().unwrap())?;
let res =
// SAFETY: lib function ensures validity of value.
unsafe { start_indexer(std::ptr::from_ref(runtime), CString::new(config_path.to_str().unwrap())?.as_ptr(), 0) };
// SAFETY: null runtime → the FFI creates and owns its own tokio runtime,
// so there is no external runtime whose address we must keep stable. The
// temp dir is the indexer's storage location.
unsafe { start_indexer(std::ptr::null(), config_path_c.as_ptr(), storage_dir_c.as_ptr()) };
if res.error.is_error() {
anyhow::bail!("Indexer FFI error {:?}", res.error);
@ -84,8 +80,35 @@ pub fn setup_indexer_ffi(
pub fn setup() -> Result<(BlockingTestContext, IndexerServiceFFI, TempDir)> {
let ctx = TestContext::builder().disable_indexer().build_blocking()?;
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
let (indexer_ffi, indexer_dir) = setup_indexer_ffi(&runtime, ctx.ctx().bedrock_addr())?;
// Don't borrow `ctx.runtime()`: `ctx` (and its by-value tokio runtime) is
// moved into the returned tuple, which would leave any pointer into it
// dangling. Pass a null runtime so the FFI owns its own — the same path the
// production module uses.
let (indexer_ffi, indexer_dir) = setup_indexer_ffi(ctx.ctx().bedrock_addr())?;
Ok((ctx, indexer_ffi, indexer_dir))
}
/// Poll the indexer FFI until its last finalized block id reaches `min_block_id`
/// or until [`integration_tests::L2_TO_L1_TIMEOUT`] elapses.
///
/// This avoids blindly sleeping for the full timeout: the indexer typically
/// catches up in a fraction of that time, so we return as soon as it does and
/// only use the timeout as a ceiling. Returns the last observed block id.
pub fn wait_for_indexer_ffi_block(indexer: &IndexerServiceFFI, min_block_id: u64) -> Result<u64> {
let start = std::time::Instant::now();
loop {
// SAFETY: `indexer` is a valid reference for the duration of the call.
let res = unsafe { query_last_block(std::ptr::from_ref(indexer)) };
if res.error.is_ok() && res.is_some && res.block_id >= min_block_id {
return Ok(res.block_id);
}
if start.elapsed() >= integration_tests::L2_TO_L1_TIMEOUT {
anyhow::bail!(
"Indexer FFI did not reach block {min_block_id} within {:?}. Last observed block id: {}",
integration_tests::L2_TO_L1_TIMEOUT,
res.block_id
);
}
std::thread::sleep(std::time::Duration::from_secs(2));
}
}

View File

@ -8,7 +8,6 @@
use std::time::Duration;
use anyhow::{Context as _, Result};
use indexer_ffi::Runtime;
use indexer_service_protocol::Account;
use integration_tests::{
L2_TO_L1_TIMEOUT, TIME_TO_WAIT_FOR_BLOCK_SECONDS, private_mention, public_mention,
@ -102,11 +101,8 @@ fn indexer_ffi_state_consistency() -> Result<()> {
info!("Waiting for indexer to parse blocks");
std::thread::sleep(L2_TO_L1_TIMEOUT);
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
let acc1_ind_state_ffi = unsafe {
indexer_ffi_helpers::query_account(
&raw const runtime,
&raw const indexer_ffi,
(&ctx.ctx().existing_public_accounts()[0]).into(),
)
@ -119,7 +115,6 @@ fn indexer_ffi_state_consistency() -> Result<()> {
let acc2_ind_state_ffi = unsafe {
indexer_ffi_helpers::query_account(
&raw const runtime,
&raw const indexer_ffi,
(&ctx.ctx().existing_public_accounts()[1]).into(),
)

View File

@ -8,7 +8,6 @@
use std::time::Duration;
use anyhow::Result;
use indexer_ffi::Runtime;
use indexer_service_protocol::Account;
use integration_tests::{L2_TO_L1_TIMEOUT, TIME_TO_WAIT_FOR_BLOCK_SECONDS, public_mention};
use log::info;
@ -75,11 +74,8 @@ fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
info!("Waiting for indexer to parse blocks");
std::thread::sleep(L2_TO_L1_TIMEOUT);
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
let acc1_ind_state_ffi = unsafe {
indexer_ffi_helpers::query_account(
&raw const runtime,
&raw const indexer_ffi,
(&ctx.ctx().existing_public_accounts()[0]).into(),
)

View File

@ -1,51 +1,36 @@
#![expect(
clippy::shadow_unrelated,
clippy::tests_outside_test_module,
reason = "We don't care about these in tests"
)]
use std::time::Duration;
use anyhow::{Context as _, Result};
use anyhow::Result;
use indexer_service_rpc::RpcClient as _;
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
verify_commitment_is_in_state, wait_for_indexer_to_catch_up,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance,
assert_private_commitment_in_state, get_account, private_mention, public_mention, send,
wait_for_indexer_to_catch_up,
};
use lee::AccountId;
use log::info;
use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcommand};
#[tokio::test]
async fn indexer_state_consistency() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(ctx.existing_public_accounts()[0]),
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let (acc0, acc1) = (
ctx.existing_public_accounts()[0],
ctx.existing_public_accounts()[1],
);
send(&mut ctx, public_mention(acc0), public_mention(acc1), 100).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = sequencer_service_rpc::RpcClient::get_account_balance(
ctx.sequencer_client(),
ctx.existing_public_accounts()[0],
)
.await?;
let acc_2_balance = sequencer_service_rpc::RpcClient::get_account_balance(
ctx.sequencer_client(),
ctx.existing_public_accounts()[1],
)
.await?;
let acc_1_balance = account_balance(&ctx, ctx.existing_public_accounts()[0]).await?;
let acc_2_balance = account_balance(&ctx, ctx.existing_public_accounts()[1]).await?;
info!("Balance of sender: {acc_1_balance:#?}");
info!("Balance of receiver: {acc_2_balance:#?}");
@ -56,32 +41,13 @@ async fn indexer_state_consistency() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
let to: AccountId = ctx.existing_private_accounts()[1];
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(&mut ctx, private_mention(from), private_mention(to), 100).await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = ctx
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment for sender")?;
assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await);
let new_commitment2 = ctx
.wallet()
.get_private_account_commitment(to)
.context("Failed to get private account commitment for receiver")?;
assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await);
assert_private_commitment_in_state(&ctx, from, "sender").await?;
assert_private_commitment_in_state(&ctx, to, "receiver").await?;
info!("Successfully transferred privately to owned account");
@ -100,16 +66,8 @@ async fn indexer_state_consistency() -> Result<()> {
.unwrap();
info!("Checking correct state transition");
let acc1_seq_state = sequencer_service_rpc::RpcClient::get_account(
ctx.sequencer_client(),
ctx.existing_public_accounts()[0],
)
.await?;
let acc2_seq_state = sequencer_service_rpc::RpcClient::get_account(
ctx.sequencer_client(),
ctx.existing_public_accounts()[1],
)
.await?;
let acc1_seq_state = get_account(&ctx, ctx.existing_public_accounts()[0]).await?;
let acc2_seq_state = get_account(&ctx, ctx.existing_public_accounts()[1]).await?;
assert_eq!(acc1_ind_state, acc1_seq_state.into());
assert_eq!(acc2_ind_state, acc2_seq_state.into());

View File

@ -9,12 +9,13 @@ use std::time::Duration;
use anyhow::Result;
use indexer_service_rpc::RpcClient as _;
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention, wait_for_indexer_to_catch_up,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance, get_account, public_mention,
send, wait_for_indexer_to_catch_up,
};
use log::info;
use wallet::{
account::Label,
cli::{CliAccountMention, Command, programs::native_token_transfer::AuthTransferSubcommand},
cli::{CliAccountMention, Command},
};
#[tokio::test]
@ -38,31 +39,19 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd).await?;
// Send using labels instead of account IDs
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: CliAccountMention::Label(from_label),
to: Some(CliAccountMention::Label(to_label)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(
&mut ctx,
CliAccountMention::Label(from_label),
CliAccountMention::Label(to_label),
100,
)
.await?;
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let acc_1_balance = sequencer_service_rpc::RpcClient::get_account_balance(
ctx.sequencer_client(),
ctx.existing_public_accounts()[0],
)
.await?;
let acc_2_balance = sequencer_service_rpc::RpcClient::get_account_balance(
ctx.sequencer_client(),
ctx.existing_public_accounts()[1],
)
.await?;
let acc_1_balance = account_balance(&ctx, ctx.existing_public_accounts()[0]).await?;
let acc_2_balance = account_balance(&ctx, ctx.existing_public_accounts()[1]).await?;
assert_eq!(acc_1_balance, 9900);
assert_eq!(acc_2_balance, 20100);
@ -75,11 +64,7 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
.get_account(ctx.existing_public_accounts()[0].into())
.await
.unwrap();
let acc1_seq_state = sequencer_service_rpc::RpcClient::get_account(
ctx.sequencer_client(),
ctx.existing_public_accounts()[0],
)
.await?;
let acc1_seq_state = get_account(&ctx, ctx.existing_public_accounts()[0]).await?;
assert_eq!(acc1_ind_state, acc1_seq_state.into());

View File

@ -1,12 +1,9 @@
#![expect(
clippy::tests_outside_test_module,
clippy::undocumented_unsafe_blocks,
reason = "We don't care about these in tests"
)]
use anyhow::Result;
use indexer_ffi::Runtime;
use integration_tests::L2_TO_L1_TIMEOUT;
use log::info;
#[path = "indexer_ffi_helpers/mod.rs"]
@ -14,20 +11,13 @@ mod indexer_ffi_helpers;
#[test]
fn indexer_test_run_ffi() -> Result<()> {
let (ctx, indexer_ffi, _indexer_dir) = indexer_ffi_helpers::setup()?;
// `_ctx` keeps the bedrock/sequencer harness (and its runtime) alive for the
// duration of the test; the indexer was started on that runtime.
let (_ctx, indexer_ffi, _indexer_dir) = indexer_ffi_helpers::setup()?;
// RUN OBSERVATION
std::thread::sleep(L2_TO_L1_TIMEOUT);
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
let last_block_indexer_ffi_res = unsafe {
indexer_ffi_helpers::query_last_block(&raw const runtime, &raw const indexer_ffi)
};
assert!(last_block_indexer_ffi_res.error.is_ok());
let last_block_indexer_ffi = unsafe { *last_block_indexer_ffi_res.value };
// RUN OBSERVATION: poll until the indexer has finalized at least one block,
// returning early instead of sleeping for the full timeout.
let last_block_indexer_ffi = indexer_ffi_helpers::wait_for_indexer_ffi_block(&indexer_ffi, 1)?;
info!("Last block on indexer FFI now is {last_block_indexer_ffi}");

View File

@ -8,17 +8,17 @@ use std::{str::FromStr as _, time::Duration};
use anyhow::{Context as _, Result};
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx, private_mention,
public_mention, verify_commitment_is_in_state,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, assert_public_account_restored,
fetch_privacy_preserving_tx, new_account, private_mention, public_mention,
restored_private_account, send, verify_commitment_is_in_state,
};
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
use lee::{AccountId, program::Program};
use lee::AccountId;
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::cli::{
Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
Command, SubcommandReturnValue, account::AccountSubcommand,
programs::native_token_transfer::AuthTransferSubcommand,
};
@ -28,35 +28,12 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
// Create a new private account
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
}));
// Key Tree shift — create 3 accounts to advance the key index
for _ in 0..3 {
// Key Tree shift
// This way we have account with child index > 0.
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
})),
)
.await?;
let SubcommandReturnValue::RegisterAccount { account_id: _ } = result else {
anyhow::bail!("Expected RegisterAccount return value");
};
new_account(&mut ctx, true, None).await?;
}
let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id,
} = sub_ret
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let to_account_id = new_account(&mut ctx, true, None).await?;
// Get the keys for the newly created account
let to_account = ctx
@ -118,107 +95,47 @@ async fn restore_keys_from_seed() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
// Create first private account at root
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: Some(ChainIndex::root()),
label: None,
}));
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id1,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
// Create private accounts at root and /0
let to_account_id1 = new_account(&mut ctx, true, Some(ChainIndex::root())).await?;
let to_account_id2 = new_account(&mut ctx, true, Some(ChainIndex::from_str("/0")?)).await?;
// Create second private account at /0
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: Some(ChainIndex::from_str("/0")?),
label: None,
}));
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id2,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
// Send to first private account
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
to: Some(private_mention(to_account_id1)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send to second private account
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
to: Some(private_mention(to_account_id2)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 101,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send to both private accounts
send(
&mut ctx,
private_mention(from),
private_mention(to_account_id1),
100,
)
.await?;
send(
&mut ctx,
private_mention(from),
private_mention(to_account_id2),
101,
)
.await?;
let from: AccountId = ctx.existing_public_accounts()[0];
// Create first public account at root
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: Some(ChainIndex::root()),
label: None,
}));
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id3,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
// Create public accounts at root and /0
let to_account_id3 = new_account(&mut ctx, false, Some(ChainIndex::root())).await?;
let to_account_id4 = new_account(&mut ctx, false, Some(ChainIndex::from_str("/0")?)).await?;
// Create second public account at /0
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: Some(ChainIndex::from_str("/0")?),
label: None,
}));
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::RegisterAccount {
account_id: to_account_id4,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
// Send to first public account
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(from),
to: Some(public_mention(to_account_id3)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 102,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send to second public account
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(from),
to: Some(public_mention(to_account_id4)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 103,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
// Send to both public accounts
send(
&mut ctx,
public_mention(from),
public_mention(to_account_id3),
102,
)
.await?;
send(
&mut ctx,
public_mention(from),
public_mention(to_account_id4),
103,
)
.await?;
info!("Preparation complete, performing keys restoration");
@ -226,42 +143,20 @@ async fn restore_keys_from_seed() -> Result<()> {
wallet::cli::execute_keys_restoration(ctx.wallet_mut(), 10).await?;
// Verify restored private accounts
let acc1 = ctx
.wallet()
.storage()
.key_chain()
.private_account(to_account_id1)
.expect("Acc 1 should be restored");
let acc2 = ctx
.wallet()
.storage()
.key_chain()
.private_account(to_account_id2)
.expect("Acc 2 should be restored");
let acc1 = restored_private_account(&ctx, to_account_id1, "Acc 1");
let acc2 = restored_private_account(&ctx, to_account_id2, "Acc 2");
// Verify restored public accounts
let _acc3 = ctx
.wallet()
.storage()
.key_chain()
.pub_account_signing_key(to_account_id3)
.expect("Acc 3 should be restored");
let _acc4 = ctx
.wallet()
.storage()
.key_chain()
.pub_account_signing_key(to_account_id4)
.expect("Acc 4 should be restored");
assert_public_account_restored(&ctx, to_account_id3, "Acc 3");
assert_public_account_restored(&ctx, to_account_id4, "Acc 4");
assert_eq!(
acc1.account.program_owner,
Program::authenticated_transfer_program().id()
programs::authenticated_transfer().id()
);
assert_eq!(
acc2.account.program_owner,
Program::authenticated_transfer_program().id()
programs::authenticated_transfer().id()
);
assert_eq!(acc1.account.balance, 100);
@ -270,27 +165,20 @@ async fn restore_keys_from_seed() -> Result<()> {
info!("Tree checks passed, testing restored accounts can transact");
// Test that restored accounts can send transactions
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(to_account_id1),
to: Some(private_mention(to_account_id2)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 10,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: public_mention(to_account_id3),
to: Some(public_mention(to_account_id4)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 11,
});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
send(
&mut ctx,
private_mention(to_account_id1),
private_mention(to_account_id2),
10,
)
.await?;
send(
&mut ctx,
public_mention(to_account_id3),
public_mention(to_account_id4),
11,
)
.await?;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;

View File

@ -7,17 +7,14 @@
use std::time::Duration;
use anyhow::{Context as _, Result};
use common::PINATA_BASE58;
use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
verify_commitment_is_in_state,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance, new_account, private_mention,
public_mention, sync_private, verify_commitment_is_in_state,
};
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::cli::{
Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
programs::{
native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand,
},
@ -27,25 +24,9 @@ use wallet::cli::{
async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()> {
let mut ctx = TestContext::new().await?;
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?;
let SubcommandReturnValue::RegisterAccount {
account_id: winner_account_id,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let winner_account_id = new_account(&mut ctx, false, None).await?;
let pinata_balance_pre = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_pre = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
let claim_result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
@ -65,10 +46,7 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()>
"Expected init guidance, got: {err}",
);
let pinata_balance_post = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_post = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
assert_eq!(pinata_balance_post, pinata_balance_pre);
@ -79,25 +57,9 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()>
async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()> {
let mut ctx = TestContext::new().await?;
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
})),
)
.await?;
let SubcommandReturnValue::RegisterAccount {
account_id: winner_account_id,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let winner_account_id = new_account(&mut ctx, true, None).await?;
let pinata_balance_pre = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_pre = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
let claim_result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
@ -117,10 +79,7 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()
"Expected init guidance, got: {err}",
);
let pinata_balance_post = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_post = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
assert_eq!(pinata_balance_post, pinata_balance_pre);
@ -136,10 +95,7 @@ async fn claim_pinata_to_existing_public_account() -> Result<()> {
to: public_mention(ctx.existing_public_accounts()[0]),
});
let pinata_balance_pre = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_pre = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
@ -147,15 +103,9 @@ async fn claim_pinata_to_existing_public_account() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let pinata_balance_post = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_post = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
let winner_balance_post = ctx
.sequencer_client()
.get_account_balance(ctx.existing_public_accounts()[0])
.await?;
let winner_balance_post = account_balance(&ctx, ctx.existing_public_accounts()[0]).await?;
assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize);
assert_eq!(winner_balance_post, 10000 + pinata_prize);
@ -174,10 +124,7 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> {
to: private_mention(ctx.existing_private_accounts()[0]),
});
let pinata_balance_pre = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_pre = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = result else {
@ -188,8 +135,7 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Syncing private accounts");
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
sync_private(&mut ctx).await?;
let new_commitment = ctx
.wallet()
@ -197,10 +143,7 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> {
.context("Failed to get private account commitment")?;
assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await);
let pinata_balance_post = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_post = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize);
@ -216,20 +159,7 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
let pinata_prize = 150;
// Create new private account
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
cci: None,
label: None,
})),
)
.await?;
let SubcommandReturnValue::RegisterAccount {
account_id: winner_account_id,
} = result
else {
anyhow::bail!("Expected RegisterAccount return value");
};
let winner_account_id = new_account(&mut ctx, true, None).await?;
// Initialize account under auth transfer program
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
@ -251,10 +181,7 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
to: private_mention(winner_account_id),
});
let pinata_balance_pre = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_pre = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
@ -267,10 +194,7 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
.context("Failed to get private account commitment")?;
assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await);
let pinata_balance_post = ctx
.sequencer_client()
.get_account_balance(PINATA_BASE58.parse().unwrap())
.await?;
let pinata_balance_post = account_balance(&ctx, system_accounts::pinata_account_id()).await?;
assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize);

View File

@ -3,14 +3,13 @@
reason = "We don't care about these in tests"
)]
use std::{path::PathBuf, time::Duration};
use std::time::Duration;
use anyhow::{Context as _, Result};
use authenticated_transfer_core::Instruction as AuthTransferInstruction;
use common::transaction::LeeTransaction;
use integration_tests::{
LEE_PROGRAM_FOR_TEST_PDA_SPEND_PROXY, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
verify_commitment_is_in_state,
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, sync_private, verify_commitment_is_in_state,
};
use lee::{
AccountId, PrivacyPreservingTransaction, ProgramId,
@ -22,7 +21,7 @@ use lee::{
program::Program,
};
use lee_core::{
InputAccountIdentity, NullifierPublicKey,
DUMMY_COMMITMENT_HASH, InputAccountIdentity, NullifierPublicKey,
account::{Account, AccountWithMetadata},
encryption::ViewingPublicKey,
program::PdaSeed,
@ -30,10 +29,7 @@ use lee_core::{
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::{
AccountIdentity, WalletCore,
cli::{Command, account::AccountSubcommand},
};
use wallet::{AccountIdentity, WalletCore};
/// Funds a private PDA by calling `auth_transfer` directly.
#[expect(
@ -74,6 +70,7 @@ async fn fund_private_pda(
random_seed: [0; 32],
npk,
identifier,
commitment_root: DUMMY_COMMITMENT_HASH,
seed: Some((seed, authority_program_id)),
},
];
@ -160,14 +157,8 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
(kc.nullifier_public_key, kc.viewing_public_key.clone())
};
let proxy = {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../artifacts/test_program_methods")
.join(LEE_PROGRAM_FOR_TEST_PDA_SPEND_PROXY);
Program::new(std::fs::read(&path).with_context(|| format!("reading {path:?}"))?)
.context("invalid pda_spend_proxy binary")?
};
let auth_transfer = Program::authenticated_transfer_program();
let proxy = test_programs::pda_spend_proxy();
let auth_transfer = programs::authenticated_transfer();
let proxy_id = proxy.id();
let auth_transfer_id = auth_transfer.id();
let seed = PdaSeed::new([42; 32]);
@ -219,11 +210,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// Sync so alice's wallet discovers and stores both PDAs.
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::SyncPrivate {}),
)
.await?;
sync_private(&mut ctx).await?;
// Both PDAs must be discoverable and have the correct balance.
let pda_0_account = ctx
@ -302,11 +289,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
info!("Waiting for block");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::SyncPrivate {}),
)
.await?;
sync_private(&mut ctx).await?;
// After spending, PDAs should have the remaining balance.
let pda_0_spent = ctx

View File

@ -3,30 +3,25 @@
reason = "We don't care about these in tests"
)]
use std::{path::PathBuf, time::Duration};
use std::{io::Write as _, time::Duration};
use anyhow::Result;
use common::transaction::LeeTransaction;
use integration_tests::{
LEE_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
};
use lee::program::Program;
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, get_account, new_account};
use log::info;
use sequencer_service_rpc::RpcClient as _;
use tokio::test;
use wallet::cli::{
Command, SubcommandReturnValue,
account::{AccountSubcommand, NewSubcommand},
};
use wallet::cli::Command;
#[test]
async fn deploy_and_execute_program() -> Result<()> {
let mut ctx = TestContext::new().await?;
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let binary_filepath: PathBuf = PathBuf::from(manifest_dir)
.join("../artifacts/test_program_methods")
.join(LEE_PROGRAM_FOR_TEST_DATA_CHANGER);
let claimer = test_programs::claimer();
let mut tempfile = tempfile::NamedTempFile::new()?;
tempfile.write_all(claimer.elf())?;
let binary_filepath = tempfile.path().to_owned();
let command = Command::DeployProgram {
binary_filepath: binary_filepath.clone(),
@ -37,36 +32,15 @@ async fn deploy_and_execute_program() -> Result<()> {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
// The program is the data changer and takes one account as input.
// We pass an uninitialized account and we expect after execution to be owned by the data
// changer program (LEE account claiming mechanism) with data equal to [0] (due to program
// logic)
let bytecode = std::fs::read(binary_filepath)?;
let data_changer = Program::new(bytecode)?;
let SubcommandReturnValue::RegisterAccount { account_id } = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
cci: None,
label: None,
})),
)
.await?
else {
panic!("Expected RegisterAccount return value");
};
let account_id = new_account(&mut ctx, false, None).await?;
let nonces = ctx.wallet().get_accounts_nonces(vec![account_id]).await?;
let private_key = ctx
.wallet()
.get_account_public_signing_key(account_id)
.unwrap();
let message = lee::public_transaction::Message::try_new(
data_changer.id(),
vec![account_id],
nonces,
vec![0],
)?;
let message =
lee::public_transaction::Message::try_new(claimer.id(), vec![account_id], nonces, ())?;
let witness_set = lee::public_transaction::WitnessSet::for_message(&message, &[private_key]);
let transaction = lee::PublicTransaction::new(message, witness_set);
let _response = ctx
@ -79,11 +53,12 @@ async fn deploy_and_execute_program() -> Result<()> {
// block
tokio::time::sleep(Duration::from_secs(2 * TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let post_state_account = ctx.sequencer_client().get_account(account_id).await?;
let post_state_account = get_account(&ctx, account_id).await?;
assert_eq!(post_state_account.program_owner, data_changer.id());
let expected_data: &[u8] = &[];
assert_eq!(post_state_account.program_owner, claimer.id());
assert_eq!(post_state_account.balance, 0);
assert_eq!(post_state_account.data.as_ref(), &[0]);
assert_eq!(post_state_account.data.as_ref(), expected_data);
assert_eq!(post_state_account.nonce.0, 1);
info!("Successfully deployed and executed program");

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