mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-14 03:59:30 +00:00
feat: move initial accounts data into genesis
This commit is contained in:
parent
b8a4d94d96
commit
89f8ddf6ce
@ -14,8 +14,8 @@ ignore = [
|
|||||||
{ id = "RUSTSEC-2025-0141", reason = "`bincode` is unmaintained but continuing to use it." },
|
{ id = "RUSTSEC-2025-0141", reason = "`bincode` is unmaintained but continuing to use it." },
|
||||||
{ id = "RUSTSEC-2023-0089", reason = "atomic-polyfill is pulled transitively via risc0-zkvm; waiting on upstream fix (see https://github.com/risc0/risc0/issues/3453)" },
|
{ id = "RUSTSEC-2023-0089", reason = "atomic-polyfill is pulled transitively via risc0-zkvm; waiting on upstream fix (see https://github.com/risc0/risc0/issues/3453)" },
|
||||||
{ id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" },
|
{ id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" },
|
||||||
{ id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration"},
|
{ id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
|
||||||
{ id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration"},
|
{ id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
|
||||||
]
|
]
|
||||||
yanked = "deny"
|
yanked = "deny"
|
||||||
unused-ignored-advisory = "deny"
|
unused-ignored-advisory = "deny"
|
||||||
|
|||||||
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@ -158,35 +158,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
RISC0_DEV_MODE: "1"
|
RISC0_DEV_MODE: "1"
|
||||||
RUST_LOG: "info"
|
RUST_LOG: "info"
|
||||||
run: cargo nextest run -p integration_tests -- --skip tps_test --skip indexer
|
run: cargo nextest run -p integration_tests -- --skip tps_test
|
||||||
|
|
||||||
integration-tests-indexer:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 60
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
|
|
||||||
|
|
||||||
- uses: ./.github/actions/install-system-deps
|
|
||||||
|
|
||||||
- uses: ./.github/actions/install-risc0
|
|
||||||
|
|
||||||
- uses: ./.github/actions/install-logos-blockchain-circuits
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Install active toolchain
|
|
||||||
run: rustup install
|
|
||||||
|
|
||||||
- name: Install nextest
|
|
||||||
run: cargo install --locked cargo-nextest
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
env:
|
|
||||||
RISC0_DEV_MODE: "1"
|
|
||||||
RUST_LOG: "info"
|
|
||||||
run: cargo nextest run -p integration_tests indexer -- --skip tps_test
|
|
||||||
|
|
||||||
valid-proof-test:
|
valid-proof-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
31
Cargo.lock
generated
31
Cargo.lock
generated
@ -912,6 +912,13 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "authenticated_transfer_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -1622,6 +1629,7 @@ name = "common"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"borsh",
|
"borsh",
|
||||||
"clock_core",
|
"clock_core",
|
||||||
@ -1765,6 +1773,15 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -2181,6 +2198,7 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"convert_case 0.10.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
@ -3775,6 +3793,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"borsh",
|
"borsh",
|
||||||
"common",
|
"common",
|
||||||
"futures",
|
"futures",
|
||||||
@ -3915,6 +3934,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ata_core",
|
"ata_core",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"common",
|
"common",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@ -6298,6 +6318,7 @@ name = "nssa"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"borsh",
|
"borsh",
|
||||||
"clock_core",
|
"clock_core",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@ -7034,6 +7055,7 @@ dependencies = [
|
|||||||
"amm_program",
|
"amm_program",
|
||||||
"ata_core",
|
"ata_core",
|
||||||
"ata_program",
|
"ata_program",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"clock_core",
|
"clock_core",
|
||||||
"nssa_core",
|
"nssa_core",
|
||||||
"risc0-zkvm",
|
"risc0-zkvm",
|
||||||
@ -8394,6 +8416,7 @@ name = "sequencer_core"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"borsh",
|
"borsh",
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -8408,6 +8431,7 @@ dependencies = [
|
|||||||
"nssa",
|
"nssa",
|
||||||
"nssa_core",
|
"nssa_core",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"rocksdb",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"storage",
|
"storage",
|
||||||
@ -9143,6 +9167,7 @@ dependencies = [
|
|||||||
name = "test_programs"
|
name = "test_programs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"authenticated_transfer_core",
|
||||||
"clock_core",
|
"clock_core",
|
||||||
"nssa_core",
|
"nssa_core",
|
||||||
"risc0-zkvm",
|
"risc0-zkvm",
|
||||||
@ -10082,10 +10107,13 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"ata_core",
|
"ata_core",
|
||||||
|
"authenticated_transfer_core",
|
||||||
"base58",
|
"base58",
|
||||||
|
"bincode",
|
||||||
"bip39",
|
"bip39",
|
||||||
"clap",
|
"clap",
|
||||||
"common",
|
"common",
|
||||||
|
"derive_more",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
@ -10103,6 +10131,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"tempfile",
|
||||||
"testnet_initial_state",
|
"testnet_initial_state",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"token_core",
|
"token_core",
|
||||||
@ -10115,9 +10144,11 @@ name = "wallet-ffi"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
|
"key_protocol",
|
||||||
"nssa",
|
"nssa",
|
||||||
"nssa_core",
|
"nssa_core",
|
||||||
"sequencer_service_rpc",
|
"sequencer_service_rpc",
|
||||||
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"wallet",
|
"wallet",
|
||||||
|
|||||||
@ -20,6 +20,7 @@ members = [
|
|||||||
"programs/token",
|
"programs/token",
|
||||||
"programs/associated_token_account/core",
|
"programs/associated_token_account/core",
|
||||||
"programs/associated_token_account",
|
"programs/associated_token_account",
|
||||||
|
"programs/authenticated_transfer/core",
|
||||||
"sequencer/core",
|
"sequencer/core",
|
||||||
"sequencer/service",
|
"sequencer/service",
|
||||||
"sequencer/service/protocol",
|
"sequencer/service/protocol",
|
||||||
@ -65,6 +66,7 @@ amm_core = { path = "programs/amm/core" }
|
|||||||
amm_program = { path = "programs/amm" }
|
amm_program = { path = "programs/amm" }
|
||||||
ata_core = { path = "programs/associated_token_account/core" }
|
ata_core = { path = "programs/associated_token_account/core" }
|
||||||
ata_program = { path = "programs/associated_token_account" }
|
ata_program = { path = "programs/associated_token_account" }
|
||||||
|
authenticated_transfer_core = { path = "programs/authenticated_transfer/core" }
|
||||||
test_program_methods = { path = "test_program_methods" }
|
test_program_methods = { path = "test_program_methods" }
|
||||||
testnet_initial_state = { path = "testnet_initial_state" }
|
testnet_initial_state = { path = "testnet_initial_state" }
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ tokio-util = "0.7.18"
|
|||||||
risc0-zkvm = { version = "3.0.5", features = ['std'] }
|
risc0-zkvm = { version = "3.0.5", features = ['std'] }
|
||||||
risc0-build = "3.0.5"
|
risc0-build = "3.0.5"
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
|
derive_more = "2.1.1"
|
||||||
num_cpus = "1.13.1"
|
num_cpus = "1.13.1"
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
openssl-probe = { version = "0.1.2" }
|
openssl-probe = { version = "0.1.2" }
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
artifacts/program_methods/genesis_supply_account.bin
Normal file
BIN
artifacts/program_methods/genesis_supply_account.bin
Normal file
Binary file not shown.
BIN
artifacts/program_methods/genesis_supply_private_account.bin
Normal file
BIN
artifacts/program_methods/genesis_supply_private_account.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -10,6 +10,7 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nssa.workspace = true
|
nssa.workspace = true
|
||||||
nssa_core.workspace = true
|
nssa_core.workspace = true
|
||||||
|
authenticated_transfer_core.workspace = true
|
||||||
clock_core.workspace = true
|
clock_core.workspace = true
|
||||||
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|||||||
@ -47,12 +47,11 @@ pub fn produce_dummy_empty_transaction() -> NSSATransaction {
|
|||||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
||||||
let account_ids = vec![];
|
let account_ids = vec![];
|
||||||
let nonces = vec![];
|
let nonces = vec![];
|
||||||
let instruction_data: u128 = 0;
|
|
||||||
let message = nssa::public_transaction::Message::try_new(
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
program_id,
|
program_id,
|
||||||
account_ids,
|
account_ids,
|
||||||
nonces,
|
nonces,
|
||||||
instruction_data,
|
authenticated_transfer_core::Instruction::Initialize,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap();
|
let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap();
|
||||||
@ -78,7 +77,9 @@ pub fn create_transaction_native_token_transfer(
|
|||||||
program_id,
|
program_id,
|
||||||
account_ids,
|
account_ids,
|
||||||
nonces,
|
nonces,
|
||||||
balance_to_move,
|
authenticated_transfer_core::Instruction::Transfer {
|
||||||
|
amount: balance_to_move,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||||
|
|||||||
@ -4,153 +4,5 @@
|
|||||||
"bedrock_config": {
|
"bedrock_config": {
|
||||||
"addr": "http://logos-blockchain-node-0:18080"
|
"addr": "http://logos-blockchain-node-0:18080"
|
||||||
},
|
},
|
||||||
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
|
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101"
|
||||||
"initial_accounts": [
|
|
||||||
{
|
|
||||||
"account_id": "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV",
|
|
||||||
"balance": 10000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"account_id": "7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo",
|
|
||||||
"balance": 20000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"initial_commitments": [
|
|
||||||
{
|
|
||||||
"npk":[
|
|
||||||
177,
|
|
||||||
64,
|
|
||||||
1,
|
|
||||||
11,
|
|
||||||
87,
|
|
||||||
38,
|
|
||||||
254,
|
|
||||||
159,
|
|
||||||
231,
|
|
||||||
165,
|
|
||||||
1,
|
|
||||||
94,
|
|
||||||
64,
|
|
||||||
137,
|
|
||||||
243,
|
|
||||||
76,
|
|
||||||
249,
|
|
||||||
101,
|
|
||||||
251,
|
|
||||||
129,
|
|
||||||
33,
|
|
||||||
101,
|
|
||||||
189,
|
|
||||||
30,
|
|
||||||
42,
|
|
||||||
11,
|
|
||||||
191,
|
|
||||||
34,
|
|
||||||
103,
|
|
||||||
186,
|
|
||||||
227,
|
|
||||||
230
|
|
||||||
] ,
|
|
||||||
"account": {
|
|
||||||
"program_owner": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"balance": 10000,
|
|
||||||
"data": [],
|
|
||||||
"nonce": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"npk": [
|
|
||||||
32,
|
|
||||||
67,
|
|
||||||
72,
|
|
||||||
164,
|
|
||||||
106,
|
|
||||||
53,
|
|
||||||
66,
|
|
||||||
239,
|
|
||||||
141,
|
|
||||||
15,
|
|
||||||
52,
|
|
||||||
230,
|
|
||||||
136,
|
|
||||||
177,
|
|
||||||
2,
|
|
||||||
236,
|
|
||||||
207,
|
|
||||||
243,
|
|
||||||
134,
|
|
||||||
135,
|
|
||||||
210,
|
|
||||||
143,
|
|
||||||
87,
|
|
||||||
232,
|
|
||||||
215,
|
|
||||||
128,
|
|
||||||
194,
|
|
||||||
120,
|
|
||||||
113,
|
|
||||||
224,
|
|
||||||
4,
|
|
||||||
165
|
|
||||||
],
|
|
||||||
"account": {
|
|
||||||
"program_owner": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"balance": 20000,
|
|
||||||
"data": [],
|
|
||||||
"nonce": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"signing_key": [
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,117 +16,29 @@
|
|||||||
"node_url": "http://logos-blockchain-node-0:18080"
|
"node_url": "http://logos-blockchain-node-0:18080"
|
||||||
},
|
},
|
||||||
"indexer_rpc_url": "ws://indexer_service:8779",
|
"indexer_rpc_url": "ws://indexer_service:8779",
|
||||||
"initial_accounts": [
|
"genesis": [
|
||||||
{
|
{
|
||||||
"account_id": "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV",
|
"supply_public_account": {
|
||||||
"balance": 10000
|
"account_id": "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV",
|
||||||
},
|
"balance": 10000
|
||||||
{
|
|
||||||
"account_id": "7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo",
|
|
||||||
"balance": 20000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"initial_commitments": [
|
|
||||||
{
|
|
||||||
"npk":[
|
|
||||||
177,
|
|
||||||
64,
|
|
||||||
1,
|
|
||||||
11,
|
|
||||||
87,
|
|
||||||
38,
|
|
||||||
254,
|
|
||||||
159,
|
|
||||||
231,
|
|
||||||
165,
|
|
||||||
1,
|
|
||||||
94,
|
|
||||||
64,
|
|
||||||
137,
|
|
||||||
243,
|
|
||||||
76,
|
|
||||||
249,
|
|
||||||
101,
|
|
||||||
251,
|
|
||||||
129,
|
|
||||||
33,
|
|
||||||
101,
|
|
||||||
189,
|
|
||||||
30,
|
|
||||||
42,
|
|
||||||
11,
|
|
||||||
191,
|
|
||||||
34,
|
|
||||||
103,
|
|
||||||
186,
|
|
||||||
227,
|
|
||||||
230
|
|
||||||
] ,
|
|
||||||
"account": {
|
|
||||||
"program_owner": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"balance": 10000,
|
|
||||||
"data": [],
|
|
||||||
"nonce": 0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"npk": [
|
"supply_public_account": {
|
||||||
32,
|
"account_id": "7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo",
|
||||||
67,
|
"balance": 20000
|
||||||
72,
|
}
|
||||||
164,
|
},
|
||||||
106,
|
{
|
||||||
53,
|
"supply_private_account": {
|
||||||
66,
|
"npk": [177,64,1,11,87,38,254,159,231,165,1,94,64,137,243,76,249,101,251,129,33,101,189,30,42,11,191,34,103,186,227,230],
|
||||||
239,
|
"balance": 10000
|
||||||
141,
|
}
|
||||||
15,
|
},
|
||||||
52,
|
{
|
||||||
230,
|
"supply_private_account": {
|
||||||
136,
|
"npk": [32,67,72,164,106,53,66,239,141,15,52,230,136,177,2,236,207,243,134,135,210,143,87,232,215,128,194,120,113,224,4,165],
|
||||||
177,
|
"balance": 20000
|
||||||
2,
|
|
||||||
236,
|
|
||||||
207,
|
|
||||||
243,
|
|
||||||
134,
|
|
||||||
135,
|
|
||||||
210,
|
|
||||||
143,
|
|
||||||
87,
|
|
||||||
232,
|
|
||||||
215,
|
|
||||||
128,
|
|
||||||
194,
|
|
||||||
120,
|
|
||||||
113,
|
|
||||||
224,
|
|
||||||
4,
|
|
||||||
165
|
|
||||||
],
|
|
||||||
"account": {
|
|
||||||
"program_owner": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"balance": 20000,
|
|
||||||
"data": [],
|
|
||||||
"nonce": 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -50,8 +50,8 @@ async fn main() {
|
|||||||
// Load signing keys to provide authorization
|
// Load signing keys to provide authorization
|
||||||
let signing_key = wallet_core
|
let signing_key = wallet_core
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.get_pub_account_signing_key(account_id)
|
.pub_account_signing_key(account_id)
|
||||||
.expect("Input account should be a self owned public account");
|
.expect("Input account should be a self owned public account");
|
||||||
|
|
||||||
// Define the desired greeting in ASCII
|
// Define the desired greeting in ASCII
|
||||||
|
|||||||
@ -86,7 +86,7 @@ pub async fn get_block_by_id(block_id: BlockId) -> Result<Block, ServerFnError>
|
|||||||
|
|
||||||
/// Get latest block ID
|
/// Get latest block ID
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_latest_block_id() -> Result<BlockId, ServerFnError> {
|
pub async fn get_latest_block_id() -> Result<Option<BlockId>, ServerFnError> {
|
||||||
use indexer_service_rpc::RpcClient as _;
|
use indexer_service_rpc::RpcClient as _;
|
||||||
let client = expect_context::<IndexerRpcClient>();
|
let client = expect_context::<IndexerRpcClient>();
|
||||||
client
|
client
|
||||||
|
|||||||
@ -29,3 +29,4 @@ tokio.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
authenticated_transfer_core.workspace = true
|
||||||
|
|||||||
@ -5,9 +5,10 @@ use common::{
|
|||||||
block::{BedrockStatus, Block},
|
block::{BedrockStatus, Block},
|
||||||
transaction::{NSSATransaction, clock_invocation},
|
transaction::{NSSATransaction, clock_invocation},
|
||||||
};
|
};
|
||||||
|
use log::info;
|
||||||
use logos_blockchain_core::{header::HeaderId, mantle::ops::channel::MsgId};
|
use logos_blockchain_core::{header::HeaderId, mantle::ops::channel::MsgId};
|
||||||
use logos_blockchain_zone_sdk::Slot;
|
use logos_blockchain_zone_sdk::Slot;
|
||||||
use nssa::{Account, AccountId, V03State};
|
use nssa::{Account, AccountId, V03State, ValidatedStateDiff};
|
||||||
use nssa_core::BlockId;
|
use nssa_core::BlockId;
|
||||||
use storage::indexer::RocksDBIO;
|
use storage::indexer::RocksDBIO;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@ -21,14 +22,10 @@ pub struct IndexerStore {
|
|||||||
impl IndexerStore {
|
impl IndexerStore {
|
||||||
/// Starting database at the start of new chain.
|
/// Starting database at the start of new chain.
|
||||||
/// Creates files if necessary.
|
/// Creates files if necessary.
|
||||||
///
|
pub fn open_db(location: &Path) -> Result<Self> {
|
||||||
/// ATTENTION: Will overwrite genesis block.
|
let initial_state = testnet_initial_state::initial_state();
|
||||||
pub fn open_db_with_genesis(
|
let dbio = RocksDBIO::open_or_create(location, &initial_state)?;
|
||||||
location: &Path,
|
|
||||||
genesis_block: &Block,
|
|
||||||
initial_state: &V03State,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let dbio = RocksDBIO::open_or_create(location, genesis_block, initial_state)?;
|
|
||||||
let current_state = dbio.final_state()?;
|
let current_state = dbio.final_state()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -44,8 +41,8 @@ impl IndexerStore {
|
|||||||
.map(HeaderId::from))
|
.map(HeaderId::from))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_last_block_id(&self) -> Result<u64> {
|
pub fn get_last_block_id(&self) -> Result<Option<u64>> {
|
||||||
Ok(self.dbio.get_meta_last_block_in_db()?)
|
self.dbio.get_meta_last_block_id_in_db().map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_block_at_id(&self, id: u64) -> Result<Option<Block>> {
|
pub fn get_block_at_id(&self, id: u64) -> Result<Option<Block>> {
|
||||||
@ -86,18 +83,14 @@ impl IndexerStore {
|
|||||||
Ok(self.dbio.get_acc_transactions(acc_id, offset, limit)?)
|
Ok(self.dbio.get_acc_transactions(acc_id, offset, limit)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
pub fn genesis_id(&self) -> Result<Option<u64>> {
|
||||||
pub fn genesis_id(&self) -> u64 {
|
|
||||||
self.dbio
|
self.dbio
|
||||||
.get_meta_first_block_in_db()
|
.get_meta_first_block_id_in_db()
|
||||||
.expect("Must be set at the DB startup")
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
pub fn last_block(&self) -> Result<Option<u64>> {
|
||||||
pub fn last_block(&self) -> u64 {
|
self.dbio.get_meta_last_block_id_in_db().map_err(Into::into)
|
||||||
self.dbio
|
|
||||||
.get_meta_last_block_in_db()
|
|
||||||
.expect("Must be set at the DB startup")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_state_at_block(&self, block_id: u64) -> Result<V03State> {
|
pub fn get_state_at_block(&self, block_id: u64) -> Result<V03State> {
|
||||||
@ -142,6 +135,7 @@ impl IndexerStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn put_block(&self, mut block: Block, l1_header: HeaderId) -> Result<()> {
|
pub async fn put_block(&self, mut block: Block, l1_header: HeaderId) -> Result<()> {
|
||||||
|
info!("Applying block {}", block.header.block_id);
|
||||||
{
|
{
|
||||||
let mut state_guard = self.current_state.write().await;
|
let mut state_guard = self.current_state.write().await;
|
||||||
|
|
||||||
@ -156,15 +150,32 @@ impl IndexerStore {
|
|||||||
"Last transaction in block must be the clock invocation for the block timestamp"
|
"Last transaction in block must be the clock invocation for the block timestamp"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let is_genesis = block.header.block_id == 1;
|
||||||
for transaction in user_txs {
|
for transaction in user_txs {
|
||||||
transaction
|
if is_genesis {
|
||||||
.clone()
|
let genesis_tx = match transaction {
|
||||||
.transaction_stateless_check()?
|
NSSATransaction::Public(public_tx) => public_tx,
|
||||||
.execute_check_on_state(
|
NSSATransaction::PrivacyPreserving(_)
|
||||||
&mut state_guard,
|
| NSSATransaction::ProgramDeployment(_) => {
|
||||||
block.header.block_id,
|
anyhow::bail!("Genesis block should contain only public transactions")
|
||||||
block.header.timestamp,
|
}
|
||||||
)?;
|
};
|
||||||
|
let state_diff = ValidatedStateDiff::from_public_genesis_transaction(
|
||||||
|
genesis_tx,
|
||||||
|
&state_guard,
|
||||||
|
)
|
||||||
|
.context("Failed to create state diff from genesis transaction")?;
|
||||||
|
state_guard.apply_state_diff(state_diff);
|
||||||
|
} else {
|
||||||
|
transaction
|
||||||
|
.clone()
|
||||||
|
.transaction_stateless_check()?
|
||||||
|
.execute_check_on_state(
|
||||||
|
&mut state_guard,
|
||||||
|
block.header.block_id,
|
||||||
|
block.header.timestamp,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the clock invocation directly (it is expected to modify clock accounts).
|
// Apply the clock invocation directly (it is expected to modify clock accounts).
|
||||||
@ -183,21 +194,19 @@ impl IndexerStore {
|
|||||||
// to represent correct block finality
|
// to represent correct block finality
|
||||||
block.bedrock_status = BedrockStatus::Finalized;
|
block.bedrock_status = BedrockStatus::Finalized;
|
||||||
|
|
||||||
|
info!("Putting block {} into DB", block.header.block_id);
|
||||||
Ok(self.dbio.put_block(&block, l1_header.into())?)
|
Ok(self.dbio.put_block(&block, l1_header.into())?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nssa::{AccountId, PublicKey};
|
use common::{HashType, block::HashableBlockData};
|
||||||
|
use nssa::{AccountId, CLOCK_01_PROGRAM_ACCOUNT_ID, PublicKey, PublicTransaction};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn genesis_block() -> Block {
|
|
||||||
common::test_utils::produce_dummy_block(1, None, vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn acc1_sign_key() -> nssa::PrivateKey {
|
fn acc1_sign_key() -> nssa::PrivateKey {
|
||||||
nssa::PrivateKey::try_new([1; 32]).unwrap()
|
nssa::PrivateKey::try_new([1; 32]).unwrap()
|
||||||
}
|
}
|
||||||
@ -214,49 +223,59 @@ mod tests {
|
|||||||
AccountId::from(&PublicKey::new_from_private_key(&acc2_sign_key()))
|
AccountId::from(&PublicKey::new_from_private_key(&acc2_sign_key()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn genesis_mint_tx(account: AccountId, balance: u128) -> NSSATransaction {
|
||||||
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
|
nssa::program::Program::authenticated_transfer_program().id(),
|
||||||
|
vec![account, CLOCK_01_PROGRAM_ACCOUNT_ID],
|
||||||
|
vec![],
|
||||||
|
authenticated_transfer_core::Instruction::Mint { amount: balance },
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
|
||||||
|
PublicTransaction::new(message, witness_set).into()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn correct_startup() {
|
fn correct_startup() {
|
||||||
let home = tempdir().unwrap();
|
let home = tempdir().unwrap();
|
||||||
|
|
||||||
let storage = IndexerStore::open_db_with_genesis(
|
let storage = IndexerStore::open_db(home.as_ref()).unwrap();
|
||||||
home.as_ref(),
|
|
||||||
&genesis_block(),
|
|
||||||
&nssa::V03State::new_with_genesis_accounts(
|
|
||||||
&[(acc1(), 10000), (acc2(), 20000)],
|
|
||||||
vec![],
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let block = storage.get_block_at_id(1).unwrap().unwrap();
|
|
||||||
let final_id = storage.get_last_block_id().unwrap();
|
let final_id = storage.get_last_block_id().unwrap();
|
||||||
|
|
||||||
assert_eq!(block.header.hash, genesis_block().header.hash);
|
assert_eq!(final_id, None);
|
||||||
assert_eq!(final_id, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn state_transition() {
|
async fn state_transition() {
|
||||||
let home = tempdir().unwrap();
|
let home = tempdir().unwrap();
|
||||||
|
|
||||||
let storage = IndexerStore::open_db_with_genesis(
|
let storage = IndexerStore::open_db(home.as_ref()).unwrap();
|
||||||
home.as_ref(),
|
|
||||||
&genesis_block(),
|
|
||||||
&nssa::V03State::new_with_genesis_accounts(
|
|
||||||
&[(acc1(), 10000), (acc2(), 20000)],
|
|
||||||
vec![],
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut prev_hash = genesis_block().header.hash;
|
|
||||||
|
|
||||||
let from = acc1();
|
let from = acc1();
|
||||||
let to = acc2();
|
let to = acc2();
|
||||||
let sign_key = acc1_sign_key();
|
let sign_key = acc1_sign_key();
|
||||||
|
|
||||||
|
// Submit genesis block
|
||||||
|
let clock_tx = NSSATransaction::Public(clock_invocation(0));
|
||||||
|
let supply_from_tx = genesis_mint_tx(from, 10000);
|
||||||
|
let supply_to_tx = genesis_mint_tx(to, 20000);
|
||||||
|
let genesis_block_data = HashableBlockData {
|
||||||
|
block_id: 1,
|
||||||
|
prev_block_hash: HashType::default(),
|
||||||
|
timestamp: 0,
|
||||||
|
transactions: vec![supply_from_tx, supply_to_tx, clock_tx],
|
||||||
|
};
|
||||||
|
let genesis_block = genesis_block_data.into_pending_block(
|
||||||
|
&common::test_utils::sequencer_sign_key_for_testing(),
|
||||||
|
[0; 32],
|
||||||
|
);
|
||||||
|
let mut prev_hash = Some(genesis_block.header.hash);
|
||||||
|
storage
|
||||||
|
.put_block(genesis_block, HeaderId::from([0_u8; 32]))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for i in 2..10 {
|
for i in 2..10 {
|
||||||
let tx = common::test_utils::create_transaction_native_token_transfer(
|
let tx = common::test_utils::create_transaction_native_token_transfer(
|
||||||
from,
|
from,
|
||||||
@ -267,9 +286,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let block_id = u64::try_from(i).unwrap();
|
let block_id = u64::try_from(i).unwrap();
|
||||||
|
|
||||||
let next_block =
|
let next_block = common::test_utils::produce_dummy_block(block_id, prev_hash, vec![tx]);
|
||||||
common::test_utils::produce_dummy_block(block_id, Some(prev_hash), vec![tx]);
|
prev_hash = Some(next_block.header.hash);
|
||||||
prev_hash = next_block.header.hash;
|
|
||||||
|
|
||||||
storage
|
storage
|
||||||
.put_block(next_block, HeaderId::from([u8::try_from(i).unwrap(); 32]))
|
.put_block(next_block, HeaderId::from([u8::try_from(i).unwrap(); 32]))
|
||||||
@ -288,18 +306,9 @@ mod tests {
|
|||||||
async fn account_state_at_block() {
|
async fn account_state_at_block() {
|
||||||
let home = tempdir().unwrap();
|
let home = tempdir().unwrap();
|
||||||
|
|
||||||
let storage = IndexerStore::open_db_with_genesis(
|
let storage = IndexerStore::open_db(home.as_ref()).unwrap();
|
||||||
home.as_ref(),
|
|
||||||
&genesis_block(),
|
|
||||||
&nssa::V03State::new_with_genesis_accounts(
|
|
||||||
&[(acc1(), 10000), (acc2(), 20000)],
|
|
||||||
vec![],
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut prev_hash = genesis_block().header.hash;
|
let mut prev_hash = None;
|
||||||
|
|
||||||
let from = acc1();
|
let from = acc1();
|
||||||
let to = acc2();
|
let to = acc2();
|
||||||
@ -315,9 +324,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let block_id = u64::try_from(i).unwrap();
|
let block_id = u64::try_from(i).unwrap();
|
||||||
|
|
||||||
let next_block =
|
let next_block = common::test_utils::produce_dummy_block(block_id, prev_hash, vec![tx]);
|
||||||
common::test_utils::produce_dummy_block(block_id, Some(prev_hash), vec![tx]);
|
prev_hash = Some(next_block.header.hash);
|
||||||
prev_hash = next_block.header.hash;
|
|
||||||
|
|
||||||
storage
|
storage
|
||||||
.put_block(next_block, HeaderId::from([u8::try_from(i).unwrap(); 32]))
|
.put_block(next_block, HeaderId::from([u8::try_from(i).unwrap(); 32]))
|
||||||
|
|||||||
@ -10,7 +10,6 @@ use common::config::BasicAuth;
|
|||||||
use humantime_serde;
|
use humantime_serde;
|
||||||
pub use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
pub use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use testnet_initial_state::{PrivateAccountPublicInitialData, PublicAccountPublicInitialData};
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -22,18 +21,12 @@ pub struct ClientConfig {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct IndexerConfig {
|
pub struct IndexerConfig {
|
||||||
/// Home dir of sequencer storage.
|
/// Home dir of indexer storage.
|
||||||
pub home: PathBuf,
|
pub home: PathBuf,
|
||||||
/// Sequencers signing key.
|
|
||||||
pub signing_key: [u8; 32],
|
|
||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
pub consensus_info_polling_interval: Duration,
|
pub consensus_info_polling_interval: Duration,
|
||||||
pub bedrock_config: ClientConfig,
|
pub bedrock_config: ClientConfig,
|
||||||
pub channel_id: ChannelId,
|
pub channel_id: ChannelId,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub initial_public_accounts: Option<Vec<PublicAccountPublicInitialData>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub initial_private_accounts: Option<Vec<PrivateAccountPublicInitialData>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexerConfig {
|
impl IndexerConfig {
|
||||||
|
|||||||
@ -1,17 +1,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use common::block::{Block, HashableBlockData};
|
use common::block::Block;
|
||||||
// ToDo: Remove after testnet
|
// ToDo: Remove after testnet
|
||||||
use common::{HashType, PINATA_BASE58};
|
|
||||||
use futures::StreamExt as _;
|
use futures::StreamExt as _;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use logos_blockchain_core::header::HeaderId;
|
use logos_blockchain_core::header::HeaderId;
|
||||||
use logos_blockchain_zone_sdk::{
|
use logos_blockchain_zone_sdk::{
|
||||||
CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer,
|
CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer,
|
||||||
};
|
};
|
||||||
use nssa::V03State;
|
|
||||||
use testnet_initial_state::initial_state_testnet;
|
|
||||||
|
|
||||||
use crate::{block_store::IndexerStore, config::IndexerConfig};
|
use crate::{block_store::IndexerStore, config::IndexerConfig};
|
||||||
|
|
||||||
@ -27,69 +24,6 @@ pub struct IndexerCore {
|
|||||||
|
|
||||||
impl IndexerCore {
|
impl IndexerCore {
|
||||||
pub fn new(config: IndexerConfig) -> Result<Self> {
|
pub fn new(config: IndexerConfig) -> Result<Self> {
|
||||||
let hashable_data = HashableBlockData {
|
|
||||||
block_id: 1,
|
|
||||||
transactions: vec![],
|
|
||||||
prev_block_hash: HashType([0; 32]),
|
|
||||||
timestamp: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Genesis creation is fine as it is,
|
|
||||||
// because it will be overwritten by sequencer.
|
|
||||||
// Therefore:
|
|
||||||
// ToDo: remove key from indexer config, use some default.
|
|
||||||
let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap();
|
|
||||||
let channel_genesis_msg_id = [0; 32];
|
|
||||||
let genesis_block = hashable_data.into_pending_block(&signing_key, channel_genesis_msg_id);
|
|
||||||
|
|
||||||
let initial_private_accounts: Option<Vec<(nssa_core::Commitment, nssa_core::Nullifier)>> =
|
|
||||||
config.initial_private_accounts.as_ref().map(|accounts| {
|
|
||||||
accounts
|
|
||||||
.iter()
|
|
||||||
.map(|init_comm_data| {
|
|
||||||
let npk = &init_comm_data.npk;
|
|
||||||
let account_id = nssa::AccountId::from((npk, 0));
|
|
||||||
|
|
||||||
let mut acc = init_comm_data.account.clone();
|
|
||||||
|
|
||||||
acc.program_owner =
|
|
||||||
nssa::program::Program::authenticated_transfer_program().id();
|
|
||||||
|
|
||||||
(
|
|
||||||
nssa_core::Commitment::new(&account_id, &acc),
|
|
||||||
nssa_core::Nullifier::for_account_initialization(&account_id),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
let init_accs: Option<Vec<(nssa::AccountId, u128)>> = config
|
|
||||||
.initial_public_accounts
|
|
||||||
.as_ref()
|
|
||||||
.map(|initial_accounts| {
|
|
||||||
initial_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|acc_data| (acc_data.account_id, acc_data.balance))
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
// If initial commitments or accounts are present in config, need to construct state from
|
|
||||||
// them
|
|
||||||
let state = if initial_private_accounts.is_some() || init_accs.is_some() {
|
|
||||||
let mut state = V03State::new_with_genesis_accounts(
|
|
||||||
&init_accs.unwrap_or_default(),
|
|
||||||
initial_private_accounts.unwrap_or_default(),
|
|
||||||
genesis_block.header.timestamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
// ToDo: Remove after testnet
|
|
||||||
state.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
|
||||||
|
|
||||||
state
|
|
||||||
} else {
|
|
||||||
initial_state_testnet()
|
|
||||||
};
|
|
||||||
|
|
||||||
let home = config.home.join("rocksdb");
|
let home = config.home.join("rocksdb");
|
||||||
|
|
||||||
let basic_auth = config.bedrock_config.auth.clone().map(Into::into);
|
let basic_auth = config.bedrock_config.auth.clone().map(Into::into);
|
||||||
@ -102,7 +36,7 @@ impl IndexerCore {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
zone_indexer: Arc::new(zone_indexer),
|
zone_indexer: Arc::new(zone_indexer),
|
||||||
config,
|
config,
|
||||||
store: IndexerStore::open_db_with_genesis(&home, &genesis_block, &state)?,
|
store: IndexerStore::open_db(&home)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,9 +22,10 @@ typedef enum FfiBedrockStatus {
|
|||||||
Finalized,
|
Finalized,
|
||||||
} FfiBedrockStatus;
|
} FfiBedrockStatus;
|
||||||
|
|
||||||
|
typedef struct Option_u64 Option_u64;
|
||||||
|
|
||||||
typedef struct IndexerServiceFFI {
|
typedef struct IndexerServiceFFI {
|
||||||
void *indexer_handle;
|
void *indexer_handle;
|
||||||
void *runtime;
|
|
||||||
void *indexer_client;
|
void *indexer_client;
|
||||||
} IndexerServiceFFI;
|
} IndexerServiceFFI;
|
||||||
|
|
||||||
@ -41,16 +42,56 @@ typedef struct PointerResult_IndexerServiceFFI__OperationStatus {
|
|||||||
|
|
||||||
typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult;
|
typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult;
|
||||||
|
|
||||||
|
typedef enum PointerKind_Tag {
|
||||||
|
Owned,
|
||||||
|
Borrowed,
|
||||||
|
Null,
|
||||||
|
} PointerKind_Tag;
|
||||||
|
|
||||||
|
typedef struct PointerKind {
|
||||||
|
PointerKind_Tag tag;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
void *owned;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
const void *borrowed;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} PointerKind;
|
||||||
|
|
||||||
|
typedef struct Pointer_Runtime {
|
||||||
|
struct PointerKind kind;
|
||||||
|
} Pointer_Runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around [`tokio::runtime::Runtime`] that can be safely passed across the FFI boundary.
|
||||||
|
*/
|
||||||
|
typedef struct Runtime {
|
||||||
|
struct Pointer_Runtime inner;
|
||||||
|
} Runtime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple wrapper around a pointer to a value or an error.
|
* Simple wrapper around a pointer to a value or an error.
|
||||||
*
|
*
|
||||||
* Pointer is not guaranteed. You should check the error field before
|
* Pointer is not guaranteed. You should check the error field before
|
||||||
* dereferencing the pointer.
|
* dereferencing the pointer.
|
||||||
*/
|
*/
|
||||||
typedef struct PointerResult_u64__OperationStatus {
|
typedef struct PointerResult_Runtime__OperationStatus {
|
||||||
uint64_t *value;
|
struct Runtime *value;
|
||||||
enum OperationStatus error;
|
enum OperationStatus error;
|
||||||
} PointerResult_u64__OperationStatus;
|
} PointerResult_Runtime__OperationStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple wrapper around a pointer to a value or an error.
|
||||||
|
*
|
||||||
|
* Pointer is not guaranteed. You should check the error field before
|
||||||
|
* dereferencing the pointer.
|
||||||
|
*/
|
||||||
|
typedef struct PointerResult_Option_u64_____OperationStatus {
|
||||||
|
struct Option_u64 *value;
|
||||||
|
enum OperationStatus error;
|
||||||
|
} PointerResult_Option_u64_____OperationStatus;
|
||||||
|
|
||||||
typedef uint64_t FfiBlockId;
|
typedef uint64_t FfiBlockId;
|
||||||
|
|
||||||
@ -379,8 +420,20 @@ typedef struct PointerResult_FfiVec_FfiTransaction_____OperationStatus {
|
|||||||
*
|
*
|
||||||
* An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
* An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
||||||
* initialized `IndexerServiceFFI` or an error code.
|
* initialized `IndexerServiceFFI` or an error code.
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
* The caller must ensure that:
|
||||||
|
* - `runtime` is a valid pointer to a `tokio::runtime::Runtime` instance.
|
||||||
|
* - `config_path` is a valid pointer to a null-terminated C string.
|
||||||
*/
|
*/
|
||||||
InitializedIndexerServiceFFIResult start_indexer(const char *config_path, uint16_t port);
|
InitializedIndexerServiceFFIResult start_indexer(const struct Runtime *runtime,
|
||||||
|
const char *config_path,
|
||||||
|
uint16_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new [`tokio::runtime::Runtime`].
|
||||||
|
*/
|
||||||
|
struct PointerResult_Runtime__OperationStatus new_runtime(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops and frees the resources associated with the given indexer service.
|
* Stops and frees the resources associated with the given indexer service.
|
||||||
@ -415,25 +468,27 @@ void free_cstring(char *block);
|
|||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
*
|
*
|
||||||
* # Returns
|
* # Returns
|
||||||
*
|
*
|
||||||
* A `PointerResult<u64, OperationStatus>` indicating success or failure.
|
* A `PointerResult<Option<u64>, OperationStatus>` indicating success or failure.
|
||||||
*
|
*
|
||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_u64__OperationStatus query_last_block(const struct IndexerServiceFFI *indexer);
|
struct PointerResult_Option_u64_____OperationStatus query_last_block(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the block by id from indexer.
|
* Query the block by id from indexer.
|
||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
* - `block_id`: `u64` number of block id
|
* - `block_id`: `u64` number of block id
|
||||||
*
|
*
|
||||||
* # Returns
|
* # Returns
|
||||||
@ -443,9 +498,11 @@ struct PointerResult_u64__OperationStatus query_last_block(const struct IndexerS
|
|||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_FfiBlockOpt__OperationStatus query_block(const struct IndexerServiceFFI *indexer,
|
struct PointerResult_FfiBlockOpt__OperationStatus query_block(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer,
|
||||||
FfiBlockId block_id);
|
FfiBlockId block_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -453,7 +510,7 @@ struct PointerResult_FfiBlockOpt__OperationStatus query_block(const struct Index
|
|||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
* - `hash`: `FfiHashType` - hash of block
|
* - `hash`: `FfiHashType` - hash of block
|
||||||
*
|
*
|
||||||
* # Returns
|
* # Returns
|
||||||
@ -463,9 +520,11 @@ struct PointerResult_FfiBlockOpt__OperationStatus query_block(const struct Index
|
|||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_FfiBlockOpt__OperationStatus query_block_by_hash(const struct IndexerServiceFFI *indexer,
|
struct PointerResult_FfiBlockOpt__OperationStatus query_block_by_hash(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer,
|
||||||
FfiHashType hash);
|
FfiHashType hash);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -473,7 +532,7 @@ struct PointerResult_FfiBlockOpt__OperationStatus query_block_by_hash(const stru
|
|||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
* - `account_id`: `FfiAccountId` - id of queried account
|
* - `account_id`: `FfiAccountId` - id of queried account
|
||||||
*
|
*
|
||||||
* # Returns
|
* # Returns
|
||||||
@ -483,9 +542,11 @@ struct PointerResult_FfiBlockOpt__OperationStatus query_block_by_hash(const stru
|
|||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_FfiAccount__OperationStatus query_account(const struct IndexerServiceFFI *indexer,
|
struct PointerResult_FfiAccount__OperationStatus query_account(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer,
|
||||||
FfiAccountId account_id);
|
FfiAccountId account_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -493,7 +554,7 @@ struct PointerResult_FfiAccount__OperationStatus query_account(const struct Inde
|
|||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
* - `hash`: `FfiHashType` - hash of transaction
|
* - `hash`: `FfiHashType` - hash of transaction
|
||||||
*
|
*
|
||||||
* # Returns
|
* # Returns
|
||||||
@ -503,9 +564,11 @@ struct PointerResult_FfiAccount__OperationStatus query_account(const struct Inde
|
|||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_FfiOption_FfiTransaction_____OperationStatus query_transaction(const struct IndexerServiceFFI *indexer,
|
struct PointerResult_FfiOption_FfiTransaction_____OperationStatus query_transaction(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer,
|
||||||
FfiHashType hash);
|
FfiHashType hash);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -513,7 +576,7 @@ struct PointerResult_FfiOption_FfiTransaction_____OperationStatus query_transact
|
|||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
* - `before`: `FfiOption<u64>` - end block of query
|
* - `before`: `FfiOption<u64>` - end block of query
|
||||||
* - `limit`: `u64` - number of blocks to query before `before`
|
* - `limit`: `u64` - number of blocks to query before `before`
|
||||||
*
|
*
|
||||||
@ -524,9 +587,11 @@ struct PointerResult_FfiOption_FfiTransaction_____OperationStatus query_transact
|
|||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_FfiVec_FfiBlock_____OperationStatus query_block_vec(const struct IndexerServiceFFI *indexer,
|
struct PointerResult_FfiVec_FfiBlock_____OperationStatus query_block_vec(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer,
|
||||||
struct FfiOption_u64 before,
|
struct FfiOption_u64 before,
|
||||||
uint64_t limit);
|
uint64_t limit);
|
||||||
|
|
||||||
@ -535,7 +600,7 @@ struct PointerResult_FfiVec_FfiBlock_____OperationStatus query_block_vec(const s
|
|||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
* - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
* - `account_id`: `FfiAccountId` - id of queried account
|
* - `account_id`: `FfiAccountId` - id of queried account
|
||||||
* - `offset`: `u64` - first tx id of query
|
* - `offset`: `u64` - first tx id of query
|
||||||
* - `limit`: `u64` - number of tx ids to query after `offset`
|
* - `limit`: `u64` - number of tx ids to query after `offset`
|
||||||
@ -547,9 +612,11 @@ struct PointerResult_FfiVec_FfiBlock_____OperationStatus query_block_vec(const s
|
|||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
* The caller must ensure that:
|
* The caller must ensure that:
|
||||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
* - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
|
* - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
*/
|
*/
|
||||||
struct PointerResult_FfiVec_FfiTransaction_____OperationStatus query_transactions_by_account(const struct IndexerServiceFFI *indexer,
|
struct PointerResult_FfiVec_FfiTransaction_____OperationStatus query_transactions_by_account(const struct Runtime *runtime,
|
||||||
|
const struct IndexerServiceFFI *indexer,
|
||||||
FfiAccountId account_id,
|
FfiAccountId account_id,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t limit);
|
uint64_t limit);
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
use std::{ffi::c_char, path::PathBuf};
|
use std::{ffi::c_char, path::PathBuf};
|
||||||
|
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
IndexerServiceFFI,
|
IndexerServiceFFI, Runtime,
|
||||||
api::{
|
api::{
|
||||||
PointerResult,
|
PointerResult,
|
||||||
client::{UrlProtocol, addr_to_url},
|
client::{UrlProtocol, addr_to_url},
|
||||||
@ -26,17 +24,33 @@ pub type InitializedIndexerServiceFFIResult = PointerResult<IndexerServiceFFI, O
|
|||||||
///
|
///
|
||||||
/// An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
/// An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
||||||
/// initialized `IndexerServiceFFI` or an error code.
|
/// initialized `IndexerServiceFFI` or an error code.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The caller must ensure that:
|
||||||
|
/// - `runtime` is a valid pointer to a `tokio::runtime::Runtime` instance.
|
||||||
|
/// - `config_path` is a valid pointer to a null-terminated C string.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn start_indexer(
|
pub unsafe extern "C" fn start_indexer(
|
||||||
|
runtime: *const Runtime,
|
||||||
config_path: *const c_char,
|
config_path: *const c_char,
|
||||||
port: u16,
|
port: u16,
|
||||||
) -> InitializedIndexerServiceFFIResult {
|
) -> InitializedIndexerServiceFFIResult {
|
||||||
setup_indexer(config_path, port).map_or_else(
|
// SAFETY: The caller must ensure the validness of the `runtime` and `config_path` pointers.
|
||||||
|
unsafe { setup_indexer(runtime, config_path, port) }.map_or_else(
|
||||||
InitializedIndexerServiceFFIResult::from_error,
|
InitializedIndexerServiceFFIResult::from_error,
|
||||||
InitializedIndexerServiceFFIResult::from_value,
|
InitializedIndexerServiceFFIResult::from_value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`tokio::runtime::Runtime`].
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn new_runtime() -> PointerResult<Runtime, OperationStatus> {
|
||||||
|
Runtime::new().map_or_else(
|
||||||
|
|_e| PointerResult::from_error(OperationStatus::InitializationError),
|
||||||
|
PointerResult::from_value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Initializes and starts an indexer based on the provided
|
/// Initializes and starts an indexer based on the provided
|
||||||
/// configuration file path.
|
/// configuration file path.
|
||||||
///
|
///
|
||||||
@ -49,7 +63,13 @@ pub extern "C" fn start_indexer(
|
|||||||
///
|
///
|
||||||
/// A `Result` containing either the initialized `IndexerServiceFFI` or an
|
/// A `Result` containing either the initialized `IndexerServiceFFI` or an
|
||||||
/// error code.
|
/// error code.
|
||||||
fn setup_indexer(
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The caller must ensure that:
|
||||||
|
/// - `runtime` is a valid pointer to a `tokio::runtime::Runtime` instance.
|
||||||
|
/// - `config_path` is a valid pointer to a null-terminated C string.
|
||||||
|
unsafe fn setup_indexer(
|
||||||
|
runtime: *const Runtime,
|
||||||
config_path: *const c_char,
|
config_path: *const c_char,
|
||||||
port: u16,
|
port: u16,
|
||||||
) -> Result<IndexerServiceFFI, OperationStatus> {
|
) -> Result<IndexerServiceFFI, OperationStatus> {
|
||||||
@ -66,9 +86,11 @@ fn setup_indexer(
|
|||||||
OperationStatus::InitializationError
|
OperationStatus::InitializationError
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let rt = Runtime::new().unwrap();
|
// SAFETY: The caller must ensure that `runtime` is a valid pointer to a
|
||||||
|
// `tokio::runtime::Runtime` instance.
|
||||||
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
let indexer_handle = rt
|
let indexer_handle = runtime
|
||||||
.block_on(indexer_service::run_server(config, port))
|
.block_on(indexer_service::run_server(config, port))
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("Could not start indexer service: {e}");
|
log::error!("Could not start indexer service: {e}");
|
||||||
@ -76,12 +98,14 @@ fn setup_indexer(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let indexer_url = addr_to_url(UrlProtocol::Ws, indexer_handle.addr())?;
|
let indexer_url = addr_to_url(UrlProtocol::Ws, indexer_handle.addr())?;
|
||||||
let indexer_client = rt.block_on(IndexerClient::new(&indexer_url)).map_err(|e| {
|
let indexer_client = runtime
|
||||||
log::error!("Could not start indexer client: {e}");
|
.block_on(IndexerClient::new(&indexer_url))
|
||||||
OperationStatus::InitializationError
|
.map_err(|e| {
|
||||||
})?;
|
log::error!("Could not start indexer client: {e}");
|
||||||
|
OperationStatus::InitializationError
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(IndexerServiceFFI::new(indexer_handle, rt, indexer_client))
|
Ok(IndexerServiceFFI::new(indexer_handle, indexer_client))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops and frees the resources associated with the given indexer service.
|
/// Stops and frees the resources associated with the given indexer service.
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use indexer_service_protocol::{AccountId, HashType};
|
|||||||
use indexer_service_rpc::RpcClient as _;
|
use indexer_service_rpc::RpcClient as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
IndexerServiceFFI,
|
IndexerServiceFFI, Runtime,
|
||||||
api::{
|
api::{
|
||||||
PointerResult,
|
PointerResult,
|
||||||
types::{
|
types::{
|
||||||
@ -19,20 +19,22 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `PointerResult<u64, OperationStatus>` indicating success or failure.
|
/// A `PointerResult<Option<u64>, OperationStatus>` indicating success or failure.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_last_block(
|
pub unsafe extern "C" fn query_last_block(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
) -> PointerResult<u64, OperationStatus> {
|
) -> PointerResult<Option<u64>, OperationStatus> {
|
||||||
if indexer.is_null() {
|
if indexer.is_null() {
|
||||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||||
@ -40,8 +42,8 @@ pub unsafe extern "C" fn query_last_block(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.get_last_finalized_block_id())
|
.block_on(client.get_last_finalized_block_id())
|
||||||
@ -55,7 +57,7 @@ pub unsafe extern "C" fn query_last_block(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
/// - `block_id`: `u64` number of block id
|
/// - `block_id`: `u64` number of block id
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -65,9 +67,11 @@ pub unsafe extern "C" fn query_last_block(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_block(
|
pub unsafe extern "C" fn query_block(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
block_id: FfiBlockId,
|
block_id: FfiBlockId,
|
||||||
) -> PointerResult<FfiBlockOpt, OperationStatus> {
|
) -> PointerResult<FfiBlockOpt, OperationStatus> {
|
||||||
@ -78,8 +82,8 @@ pub unsafe extern "C" fn query_block(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.get_block_by_id(block_id))
|
.block_on(client.get_block_by_id(block_id))
|
||||||
@ -99,7 +103,7 @@ pub unsafe extern "C" fn query_block(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
/// - `hash`: `FfiHashType` - hash of block
|
/// - `hash`: `FfiHashType` - hash of block
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -109,9 +113,11 @@ pub unsafe extern "C" fn query_block(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_block_by_hash(
|
pub unsafe extern "C" fn query_block_by_hash(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
hash: FfiHashType,
|
hash: FfiHashType,
|
||||||
) -> PointerResult<FfiBlockOpt, OperationStatus> {
|
) -> PointerResult<FfiBlockOpt, OperationStatus> {
|
||||||
@ -122,8 +128,8 @@ pub unsafe extern "C" fn query_block_by_hash(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.get_block_by_hash(HashType(hash.data)))
|
.block_on(client.get_block_by_hash(HashType(hash.data)))
|
||||||
@ -143,7 +149,7 @@ pub unsafe extern "C" fn query_block_by_hash(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
/// - `account_id`: `FfiAccountId` - id of queried account
|
/// - `account_id`: `FfiAccountId` - id of queried account
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -153,9 +159,11 @@ pub unsafe extern "C" fn query_block_by_hash(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_account(
|
pub unsafe extern "C" fn query_account(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
account_id: FfiAccountId,
|
account_id: FfiAccountId,
|
||||||
) -> PointerResult<FfiAccount, OperationStatus> {
|
) -> PointerResult<FfiAccount, OperationStatus> {
|
||||||
@ -166,8 +174,8 @@ pub unsafe extern "C" fn query_account(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.get_account(AccountId {
|
.block_on(client.get_account(AccountId {
|
||||||
@ -187,7 +195,7 @@ pub unsafe extern "C" fn query_account(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
/// - `hash`: `FfiHashType` - hash of transaction
|
/// - `hash`: `FfiHashType` - hash of transaction
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -197,9 +205,11 @@ pub unsafe extern "C" fn query_account(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_transaction(
|
pub unsafe extern "C" fn query_transaction(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
hash: FfiHashType,
|
hash: FfiHashType,
|
||||||
) -> PointerResult<FfiOption<FfiTransaction>, OperationStatus> {
|
) -> PointerResult<FfiOption<FfiTransaction>, OperationStatus> {
|
||||||
@ -210,8 +220,8 @@ pub unsafe extern "C" fn query_transaction(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.get_transaction(HashType(hash.data)))
|
.block_on(client.get_transaction(HashType(hash.data)))
|
||||||
@ -231,7 +241,7 @@ pub unsafe extern "C" fn query_transaction(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
/// - `before`: `FfiOption<u64>` - end block of query
|
/// - `before`: `FfiOption<u64>` - end block of query
|
||||||
/// - `limit`: `u64` - number of blocks to query before `before`
|
/// - `limit`: `u64` - number of blocks to query before `before`
|
||||||
///
|
///
|
||||||
@ -242,9 +252,11 @@ pub unsafe extern "C" fn query_transaction(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_block_vec(
|
pub unsafe extern "C" fn query_block_vec(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
before: FfiOption<u64>,
|
before: FfiOption<u64>,
|
||||||
limit: u64,
|
limit: u64,
|
||||||
@ -256,8 +268,8 @@ pub unsafe extern "C" fn query_block_vec(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
let before_std = before.is_some.then(|| unsafe { *before.value });
|
let before_std = before.is_some.then(|| unsafe { *before.value });
|
||||||
|
|
||||||
@ -281,7 +293,7 @@ pub unsafe extern "C" fn query_block_vec(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
/// - `indexer`: A pointer to the [`IndexerServiceFFI`] instance to be queried.
|
||||||
/// - `account_id`: `FfiAccountId` - id of queried account
|
/// - `account_id`: `FfiAccountId` - id of queried account
|
||||||
/// - `offset`: `u64` - first tx id of query
|
/// - `offset`: `u64` - first tx id of query
|
||||||
/// - `limit`: `u64` - number of tx ids to query after `offset`
|
/// - `limit`: `u64` - number of tx ids to query after `offset`
|
||||||
@ -293,9 +305,11 @@ pub unsafe extern "C" fn query_block_vec(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that:
|
/// The caller must ensure that:
|
||||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
/// - `indexer` is a valid pointer to a [`IndexerServiceFFI`] instance.
|
||||||
|
/// - `runtime` is a valid pointer to a [`Runtime`] instance.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn query_transactions_by_account(
|
pub unsafe extern "C" fn query_transactions_by_account(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
account_id: FfiAccountId,
|
account_id: FfiAccountId,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
@ -308,8 +322,8 @@ pub unsafe extern "C" fn query_transactions_by_account(
|
|||||||
|
|
||||||
let indexer = unsafe { &*indexer };
|
let indexer = unsafe { &*indexer };
|
||||||
|
|
||||||
let client = unsafe { indexer.client() };
|
let client = indexer.client();
|
||||||
let runtime = unsafe { indexer.runtime() };
|
let runtime = unsafe { &*runtime };
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.get_transactions_by_account(
|
.block_on(client.get_transactions_by_account(
|
||||||
|
|||||||
@ -1,53 +1,49 @@
|
|||||||
use std::{ffi::c_void, net::SocketAddr};
|
use std::{ffi::c_void, net::SocketAddr};
|
||||||
|
|
||||||
use indexer_service::IndexerHandle;
|
use indexer_service::IndexerHandle;
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
|
|
||||||
use crate::client::IndexerClient;
|
use crate::client::IndexerClient;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct IndexerServiceFFI {
|
pub struct IndexerServiceFFI {
|
||||||
indexer_handle: *mut c_void,
|
indexer_handle: *mut c_void,
|
||||||
runtime: *mut c_void,
|
|
||||||
indexer_client: *mut c_void,
|
indexer_client: *mut c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexerServiceFFI {
|
impl IndexerServiceFFI {
|
||||||
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
indexer_handle: indexer_service::IndexerHandle,
|
indexer_handle: indexer_service::IndexerHandle,
|
||||||
runtime: Runtime,
|
|
||||||
indexer_client: IndexerClient,
|
indexer_client: IndexerClient,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
// Box the complex types and convert to opaque pointers
|
// Box the complex types and convert to opaque pointers
|
||||||
indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::<c_void>(),
|
indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::<c_void>(),
|
||||||
runtime: Box::into_raw(Box::new(runtime)).cast::<c_void>(),
|
|
||||||
indexer_client: Box::into_raw(Box::new(indexer_client)).cast::<c_void>(),
|
indexer_client: Box::into_raw(Box::new(indexer_client)).cast::<c_void>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to take ownership back.
|
/// Helper to take ownership back.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that:
|
|
||||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub unsafe fn into_parts(self) -> (Box<IndexerHandle>, Box<Runtime>, Box<IndexerClient>) {
|
pub fn into_parts(mut self) -> (Box<IndexerHandle>, Box<IndexerClient>) {
|
||||||
let indexer_handle = unsafe { Box::from_raw(self.indexer_handle.cast::<IndexerHandle>()) };
|
let Self {
|
||||||
let runtime = unsafe { Box::from_raw(self.runtime.cast::<Runtime>()) };
|
indexer_handle,
|
||||||
let indexer_client = unsafe { Box::from_raw(self.indexer_client.cast::<IndexerClient>()) };
|
indexer_client,
|
||||||
(indexer_handle, runtime, indexer_client)
|
} = &mut self;
|
||||||
|
|
||||||
|
let indexer_handle_boxed = unsafe { Box::from_raw(indexer_handle.cast::<IndexerHandle>()) };
|
||||||
|
let indexer_client_boxed = unsafe { Box::from_raw(indexer_client.cast::<IndexerClient>()) };
|
||||||
|
|
||||||
|
// Assigning nulls to prevent double free on drop, since ownership is transferred to caller
|
||||||
|
*indexer_handle = std::ptr::null_mut();
|
||||||
|
*indexer_client = std::ptr::null_mut();
|
||||||
|
|
||||||
|
(indexer_handle_boxed, indexer_client_boxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to get indexer handle addr.
|
/// Helper to get indexer handle addr.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that:
|
|
||||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const unsafe fn addr(&self) -> SocketAddr {
|
pub const fn addr(&self) -> SocketAddr {
|
||||||
let indexer_handle = unsafe {
|
let indexer_handle = unsafe {
|
||||||
self.indexer_handle
|
self.indexer_handle
|
||||||
.cast::<IndexerHandle>()
|
.cast::<IndexerHandle>()
|
||||||
@ -59,13 +55,8 @@ impl IndexerServiceFFI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to get indexer handle ref.
|
/// Helper to get indexer handle ref.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that:
|
|
||||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const unsafe fn handle(&self) -> &IndexerHandle {
|
pub const fn handle(&self) -> &IndexerHandle {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.indexer_handle
|
self.indexer_handle
|
||||||
.cast::<IndexerHandle>()
|
.cast::<IndexerHandle>()
|
||||||
@ -75,13 +66,8 @@ impl IndexerServiceFFI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to get indexer client ref.
|
/// Helper to get indexer client ref.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that:
|
|
||||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const unsafe fn client(&self) -> &IndexerClient {
|
pub const fn client(&self) -> &IndexerClient {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.indexer_client
|
self.indexer_client
|
||||||
.cast::<IndexerClient>()
|
.cast::<IndexerClient>()
|
||||||
@ -89,22 +75,6 @@ impl IndexerServiceFFI {
|
|||||||
.expect("Indexer Client must be non-null pointer")
|
.expect("Indexer Client must be non-null pointer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to get indexer runtime ref.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that:
|
|
||||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
|
||||||
#[must_use]
|
|
||||||
pub const unsafe fn runtime(&self) -> &Runtime {
|
|
||||||
unsafe {
|
|
||||||
self.runtime
|
|
||||||
.cast::<Runtime>()
|
|
||||||
.as_ref()
|
|
||||||
.expect("Indexer Runtime must be non-null pointer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Drop to prevent memory leaks
|
// Implement Drop to prevent memory leaks
|
||||||
@ -112,21 +82,14 @@ impl Drop for IndexerServiceFFI {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let Self {
|
let Self {
|
||||||
indexer_handle,
|
indexer_handle,
|
||||||
runtime,
|
|
||||||
indexer_client,
|
indexer_client,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if indexer_handle.is_null() {
|
if !indexer_handle.is_null() {
|
||||||
log::error!("Attempted to drop a null indexer pointer. This is a bug");
|
drop(unsafe { Box::from_raw(indexer_handle.cast::<IndexerHandle>()) });
|
||||||
}
|
}
|
||||||
if runtime.is_null() {
|
if !indexer_client.is_null() {
|
||||||
log::error!("Attempted to drop a null tokio runtime pointer. This is a bug");
|
drop(unsafe { Box::from_raw(indexer_client.cast::<IndexerClient>()) });
|
||||||
}
|
}
|
||||||
if indexer_client.is_null() {
|
|
||||||
log::error!("Attempted to drop a null client pointer. This is a bug");
|
|
||||||
}
|
|
||||||
drop(unsafe { Box::from_raw(indexer_handle.cast::<IndexerHandle>()) });
|
|
||||||
drop(unsafe { Box::from_raw(runtime.cast::<Runtime>()) });
|
|
||||||
drop(unsafe { Box::from_raw(indexer_client.cast::<IndexerClient>()) });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
pub use errors::OperationStatus;
|
pub use errors::OperationStatus;
|
||||||
pub use indexer::IndexerServiceFFI;
|
pub use indexer::IndexerServiceFFI;
|
||||||
|
pub use runtime::Runtime;
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
mod client;
|
mod client;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod indexer;
|
mod indexer;
|
||||||
|
mod runtime;
|
||||||
|
|||||||
129
indexer/ffi/src/runtime.rs
Normal file
129
indexer/ffi/src/runtime.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
/// Wrapper around [`tokio::runtime::Runtime`] that can be safely passed across the FFI boundary.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Runtime {
|
||||||
|
inner: Pointer<tokio::runtime::Runtime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
/// Creates a new owned [`Runtime`] instance.
|
||||||
|
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let inner = tokio::runtime::Runtime::new()?;
|
||||||
|
Ok(Self {
|
||||||
|
inner: Pointer::owned(inner),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new owned [`Runtime`] instance from an existing [`tokio::runtime::Runtime`].
|
||||||
|
pub fn from_owned(inner: tokio::runtime::Runtime) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Pointer::owned(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new borrowed [`Runtime`] instance from a reference to an existing
|
||||||
|
/// `tokio::runtime::Runtime`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The caller must ensure that the provided reference remains valid for the lifetime of the
|
||||||
|
/// returned [`Runtime`].
|
||||||
|
pub const unsafe fn from_borrowed(inner: &tokio::runtime::Runtime) -> Self {
|
||||||
|
Self {
|
||||||
|
// SAFETY: The caller must ensure the validness of the `inner` reference.
|
||||||
|
inner: unsafe { Pointer::borrowed(inner) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<tokio::runtime::Runtime> for Runtime {
|
||||||
|
fn as_ref(&self) -> &tokio::runtime::Runtime {
|
||||||
|
self.inner
|
||||||
|
.as_ref()
|
||||||
|
.expect("Runtime pointer should not be null")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for Runtime {
|
||||||
|
type Target = tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Pointer<T> {
|
||||||
|
kind: PointerKind,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
enum PointerKind {
|
||||||
|
Owned(*mut c_void),
|
||||||
|
Borrowed(*const c_void),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pointer<T> {
|
||||||
|
/// Creates a new owned pointer from a value.
|
||||||
|
pub fn owned(value: T) -> Self {
|
||||||
|
let boxed = Box::new(value);
|
||||||
|
let kind = PointerKind::Owned(Box::into_raw(boxed).cast::<c_void>());
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new borrowed pointer from a reference to an existing value.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The caller must ensure that the provided reference remains valid for the lifetime of the
|
||||||
|
/// returned pointer.
|
||||||
|
pub const unsafe fn borrowed(value: &T) -> Self {
|
||||||
|
let kind = PointerKind::Borrowed(std::ptr::from_ref(value).cast::<c_void>());
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the value if the pointer is owned or borrowed, or [`None`] if it is
|
||||||
|
/// null.
|
||||||
|
pub const fn as_ref(&self) -> Option<&T> {
|
||||||
|
match self.kind {
|
||||||
|
PointerKind::Owned(ptr) => unsafe { (ptr.cast::<T>()).as_ref() },
|
||||||
|
PointerKind::Borrowed(ptr) => unsafe { (ptr.cast::<T>()).as_ref() },
|
||||||
|
PointerKind::Null => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes ownership of the pointer if it is owned, returning the raw pointer and leaving a null
|
||||||
|
/// pointer in its place.
|
||||||
|
/// If the pointer is borrowed or null, returns [`None`].
|
||||||
|
#[expect(dead_code, reason = "May be useful in future")]
|
||||||
|
pub fn take(&mut self) -> Option<T> {
|
||||||
|
match std::mem::replace(&mut self.kind, PointerKind::Null) {
|
||||||
|
PointerKind::Owned(ptr) => {
|
||||||
|
// SAFETY: We ensure that the pointer is valid and was allocated by us.
|
||||||
|
let boxed = unsafe { Box::from_raw(ptr.cast::<T>()) };
|
||||||
|
Some(*boxed)
|
||||||
|
}
|
||||||
|
PointerKind::Borrowed(_) | PointerKind::Null => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Pointer<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let Self { kind, _marker } = self;
|
||||||
|
|
||||||
|
if let PointerKind::Owned(ptr) = *kind {
|
||||||
|
// SAFETY: We ensure that the pointer is valid and was allocated by us.
|
||||||
|
unsafe {
|
||||||
|
drop(Box::from_raw(ptr.cast::<T>()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,153 +4,5 @@
|
|||||||
"bedrock_config": {
|
"bedrock_config": {
|
||||||
"addr": "http://localhost:8080"
|
"addr": "http://localhost:8080"
|
||||||
},
|
},
|
||||||
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
|
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101"
|
||||||
"initial_accounts": [
|
|
||||||
{
|
|
||||||
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
|
|
||||||
"balance": 10000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
|
|
||||||
"balance": 20000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"initial_commitments": [
|
|
||||||
{
|
|
||||||
"npk": [
|
|
||||||
139,
|
|
||||||
19,
|
|
||||||
158,
|
|
||||||
11,
|
|
||||||
155,
|
|
||||||
231,
|
|
||||||
85,
|
|
||||||
206,
|
|
||||||
132,
|
|
||||||
228,
|
|
||||||
220,
|
|
||||||
114,
|
|
||||||
145,
|
|
||||||
89,
|
|
||||||
113,
|
|
||||||
156,
|
|
||||||
238,
|
|
||||||
142,
|
|
||||||
242,
|
|
||||||
74,
|
|
||||||
182,
|
|
||||||
91,
|
|
||||||
43,
|
|
||||||
100,
|
|
||||||
6,
|
|
||||||
190,
|
|
||||||
31,
|
|
||||||
15,
|
|
||||||
31,
|
|
||||||
88,
|
|
||||||
96,
|
|
||||||
204
|
|
||||||
],
|
|
||||||
"account": {
|
|
||||||
"program_owner": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"balance": 10000,
|
|
||||||
"data": [],
|
|
||||||
"nonce": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"npk": [
|
|
||||||
173,
|
|
||||||
134,
|
|
||||||
33,
|
|
||||||
223,
|
|
||||||
54,
|
|
||||||
226,
|
|
||||||
10,
|
|
||||||
71,
|
|
||||||
215,
|
|
||||||
254,
|
|
||||||
143,
|
|
||||||
172,
|
|
||||||
24,
|
|
||||||
244,
|
|
||||||
243,
|
|
||||||
208,
|
|
||||||
65,
|
|
||||||
112,
|
|
||||||
118,
|
|
||||||
70,
|
|
||||||
217,
|
|
||||||
240,
|
|
||||||
69,
|
|
||||||
100,
|
|
||||||
129,
|
|
||||||
3,
|
|
||||||
121,
|
|
||||||
25,
|
|
||||||
213,
|
|
||||||
132,
|
|
||||||
42,
|
|
||||||
45
|
|
||||||
],
|
|
||||||
"account": {
|
|
||||||
"program_owner": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"balance": 20000,
|
|
||||||
"data": [],
|
|
||||||
"nonce": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"signing_key": [
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37,
|
|
||||||
37
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
@ -27,7 +27,7 @@ pub trait Rpc {
|
|||||||
async fn subscribe_to_finalized_blocks(&self) -> SubscriptionResult;
|
async fn subscribe_to_finalized_blocks(&self) -> SubscriptionResult;
|
||||||
|
|
||||||
#[method(name = "getLastFinalizedBlockId")]
|
#[method(name = "getLastFinalizedBlockId")]
|
||||||
async fn get_last_finalized_block_id(&self) -> Result<BlockId, ErrorObjectOwned>;
|
async fn get_last_finalized_block_id(&self) -> Result<Option<BlockId>, ErrorObjectOwned>;
|
||||||
|
|
||||||
#[method(name = "getBlockById")]
|
#[method(name = "getBlockById")]
|
||||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned>;
|
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned>;
|
||||||
|
|||||||
@ -16,6 +16,7 @@ pub struct IndexerHandle {
|
|||||||
/// Option because of `Drop` which forbids to simply move out of `self` in `stopped()`.
|
/// Option because of `Drop` which forbids to simply move out of `self` in `stopped()`.
|
||||||
server_handle: Option<ServerHandle>,
|
server_handle: Option<ServerHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexerHandle {
|
impl IndexerHandle {
|
||||||
const fn new(addr: SocketAddr, server_handle: ServerHandle) -> Self {
|
const fn new(addr: SocketAddr, server_handle: ServerHandle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@ -190,18 +190,16 @@ impl indexer_service_rpc::RpcServer for MockIndexerService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_last_finalized_block_id(&self) -> Result<BlockId, ErrorObjectOwned> {
|
async fn get_last_finalized_block_id(&self) -> Result<Option<BlockId>, ErrorObjectOwned> {
|
||||||
self.state
|
Ok(self
|
||||||
|
.state
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.blocks
|
.blocks
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|block| block.bedrock_status == BedrockStatus::Finalized)
|
.find(|block| block.bedrock_status == BedrockStatus::Finalized)
|
||||||
.map(|block| block.header.block_id)
|
.map(|block| block.header.block_id))
|
||||||
.ok_or_else(|| {
|
|
||||||
ErrorObjectOwned::owned(-32001, "Last block not found".to_owned(), None::<()>)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned> {
|
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned> {
|
||||||
|
|||||||
@ -48,7 +48,7 @@ impl indexer_service_rpc::RpcServer for IndexerService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_last_finalized_block_id(&self) -> Result<BlockId, ErrorObjectOwned> {
|
async fn get_last_finalized_block_id(&self) -> Result<Option<BlockId>, ErrorObjectOwned> {
|
||||||
self.indexer.store.get_last_block_id().map_err(db_error)
|
self.indexer.store.get_last_block_id().map_err(db_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,43 +214,49 @@ impl SubscriptionService {
|
|||||||
tokio::sync::mpsc::unbounded_channel::<Subscription<BlockId>>();
|
tokio::sync::mpsc::unbounded_channel::<Subscription<BlockId>>();
|
||||||
|
|
||||||
let handle = tokio::spawn(async move {
|
let handle = tokio::spawn(async move {
|
||||||
let mut subscribers = Vec::new();
|
let run_loop = async {
|
||||||
|
let mut subscribers = Vec::new();
|
||||||
|
|
||||||
let mut block_stream = pin!(indexer.subscribe_parse_block_stream());
|
let mut block_stream = pin!(indexer.subscribe_parse_block_stream());
|
||||||
|
|
||||||
#[expect(
|
#[expect(
|
||||||
clippy::integer_division_remainder_used,
|
clippy::integer_division_remainder_used,
|
||||||
reason = "Generated by select! macro, can't be easily rewritten to avoid this lint"
|
reason = "Generated by select! macro, can't be easily rewritten to avoid this lint"
|
||||||
)]
|
)]
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
sub = sub_receiver.recv() => {
|
sub = sub_receiver.recv() => {
|
||||||
let Some(subscription) = sub else {
|
let Some(subscription) = sub else {
|
||||||
bail!("Subscription receiver closed unexpectedly");
|
bail!("Subscription receiver closed unexpectedly");
|
||||||
};
|
};
|
||||||
info!("Added new subscription with ID {:?}", subscription.sink.subscription_id());
|
info!("Added new subscription with ID {:?}", subscription.sink.subscription_id());
|
||||||
subscribers.push(subscription);
|
subscribers.push(subscription);
|
||||||
}
|
}
|
||||||
block_opt = block_stream.next() => {
|
block_opt = block_stream.next() => {
|
||||||
debug!("Got new block from block stream");
|
debug!("Got new block from block stream");
|
||||||
let Some(block) = block_opt else {
|
let Some(block) = block_opt else {
|
||||||
bail!("Block stream ended unexpectedly");
|
bail!("Block stream ended unexpectedly");
|
||||||
};
|
};
|
||||||
let block = block.context("Failed to get L2 block data")?;
|
let block = block.context("Failed to get L2 block data")?;
|
||||||
let block: indexer_service_protocol::Block = block.into();
|
let block: indexer_service_protocol::Block = block.into();
|
||||||
|
|
||||||
for sub in &mut subscribers {
|
for sub in &mut subscribers {
|
||||||
if let Err(err) = sub.try_send(&block.header.block_id) {
|
if let Err(err) = sub.try_send(&block.header.block_id) {
|
||||||
warn!(
|
warn!(
|
||||||
"Failed to send block ID {:?} to subscription ID {:?} with error: {err:#?}",
|
"Failed to send block ID {:?} to subscription ID {:?} with error: {err:#?}",
|
||||||
block.header.block_id,
|
block.header.block_id,
|
||||||
sub.sink.subscription_id(),
|
sub.sink.subscription_id(),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
let res: anyhow::Result<futures::never::Never> = run_loop.await;
|
||||||
|
let Err(err) = res;
|
||||||
|
error!("Subscription service loop has unexpectedly finished with error: {err:#?}");
|
||||||
|
Err(err)
|
||||||
});
|
});
|
||||||
SubscriptionLoopParts {
|
SubscriptionLoopParts {
|
||||||
handle,
|
handle,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nssa_core = { workspace = true, features = ["host"] }
|
nssa_core = { workspace = true, features = ["host"] }
|
||||||
nssa.workspace = true
|
nssa.workspace = true
|
||||||
|
authenticated_transfer_core.workspace = true
|
||||||
sequencer_core = { workspace = true, features = ["default", "testnet"] }
|
sequencer_core = { workspace = true, features = ["default", "testnet"] }
|
||||||
sequencer_service.workspace = true
|
sequencer_service.workspace = true
|
||||||
wallet.workspace = true
|
wallet.workspace = true
|
||||||
@ -28,7 +29,6 @@ testnet_initial_state.workspace = true
|
|||||||
indexer_service_protocol.workspace = true
|
indexer_service_protocol.workspace = true
|
||||||
|
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
|||||||
@ -3,16 +3,13 @@ use std::{net::SocketAddr, path::PathBuf, time::Duration};
|
|||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
use indexer_service::{ChannelId, ClientConfig, IndexerConfig};
|
use indexer_service::{ChannelId, ClientConfig, IndexerConfig};
|
||||||
use key_protocol::key_management::KeyChain;
|
use nssa::{AccountId, PrivateKey, PublicKey};
|
||||||
use nssa::{Account, AccountId, PrivateKey, PublicKey};
|
use sequencer_core::config::{BedrockConfig, GenesisTransaction, SequencerConfig};
|
||||||
use nssa_core::{account::Data, program::DEFAULT_PROGRAM_ID};
|
|
||||||
use sequencer_core::config::{BedrockConfig, SequencerConfig};
|
|
||||||
use testnet_initial_state::{
|
|
||||||
PrivateAccountPrivateInitialData, PrivateAccountPublicInitialData,
|
|
||||||
PublicAccountPrivateInitialData, PublicAccountPublicInitialData,
|
|
||||||
};
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use wallet::config::{InitialAccountData, WalletConfig};
|
use wallet::config::WalletConfig;
|
||||||
|
|
||||||
|
pub const INITIAL_PUBLIC_BALANCES_FOR_WALLET: [u128; 2] = [20_000, 40_000];
|
||||||
|
pub const INITIAL_PRIVATE_BALANCES_FOR_WALLET: [u128; 2] = [10_000, 20_000];
|
||||||
|
|
||||||
/// Sequencer config options available for custom changes in integration tests.
|
/// Sequencer config options available for custom changes in integration tests.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@ -34,121 +31,6 @@ impl Default for SequencerPartialConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InitialData {
|
|
||||||
pub public_accounts: Vec<(PrivateKey, u128)>,
|
|
||||||
pub private_accounts: Vec<(KeyChain, Account)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InitialData {
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_two_public_and_two_private_initialized_accounts() -> Self {
|
|
||||||
let mut public_alice_private_key = PrivateKey::new_os_random();
|
|
||||||
let mut public_alice_public_key =
|
|
||||||
PublicKey::new_from_private_key(&public_alice_private_key);
|
|
||||||
let mut public_alice_account_id = AccountId::from(&public_alice_public_key);
|
|
||||||
|
|
||||||
let mut public_bob_private_key = PrivateKey::new_os_random();
|
|
||||||
let mut public_bob_public_key = PublicKey::new_from_private_key(&public_bob_private_key);
|
|
||||||
let mut public_bob_account_id = AccountId::from(&public_bob_public_key);
|
|
||||||
|
|
||||||
// Ensure consistent ordering
|
|
||||||
if public_alice_account_id > public_bob_account_id {
|
|
||||||
std::mem::swap(&mut public_alice_private_key, &mut public_bob_private_key);
|
|
||||||
std::mem::swap(&mut public_alice_public_key, &mut public_bob_public_key);
|
|
||||||
std::mem::swap(&mut public_alice_account_id, &mut public_bob_account_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut private_charlie_key_chain = KeyChain::new_os_random();
|
|
||||||
let mut private_charlie_account_id =
|
|
||||||
AccountId::from((&private_charlie_key_chain.nullifier_public_key, 0));
|
|
||||||
|
|
||||||
let mut private_david_key_chain = KeyChain::new_os_random();
|
|
||||||
let mut private_david_account_id =
|
|
||||||
AccountId::from((&private_david_key_chain.nullifier_public_key, 0));
|
|
||||||
|
|
||||||
// Ensure consistent ordering
|
|
||||||
if private_charlie_account_id > private_david_account_id {
|
|
||||||
std::mem::swap(&mut private_charlie_key_chain, &mut private_david_key_chain);
|
|
||||||
std::mem::swap(
|
|
||||||
&mut private_charlie_account_id,
|
|
||||||
&mut private_david_account_id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
public_accounts: vec![
|
|
||||||
(public_alice_private_key, 10_000),
|
|
||||||
(public_bob_private_key, 20_000),
|
|
||||||
],
|
|
||||||
private_accounts: vec![
|
|
||||||
(
|
|
||||||
private_charlie_key_chain,
|
|
||||||
Account {
|
|
||||||
balance: 10_000,
|
|
||||||
data: Data::default(),
|
|
||||||
program_owner: DEFAULT_PROGRAM_ID,
|
|
||||||
nonce: 0_u128.into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
private_david_key_chain,
|
|
||||||
Account {
|
|
||||||
balance: 20_000,
|
|
||||||
data: Data::default(),
|
|
||||||
program_owner: DEFAULT_PROGRAM_ID,
|
|
||||||
nonce: 0_u128.into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sequencer_initial_public_accounts(&self) -> Vec<PublicAccountPublicInitialData> {
|
|
||||||
self.public_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|(priv_key, balance)| {
|
|
||||||
let pub_key = PublicKey::new_from_private_key(priv_key);
|
|
||||||
let account_id = AccountId::from(&pub_key);
|
|
||||||
PublicAccountPublicInitialData {
|
|
||||||
account_id,
|
|
||||||
balance: *balance,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sequencer_initial_private_accounts(&self) -> Vec<PrivateAccountPublicInitialData> {
|
|
||||||
self.private_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|(key_chain, account)| PrivateAccountPublicInitialData {
|
|
||||||
npk: key_chain.nullifier_public_key,
|
|
||||||
account: account.clone(),
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wallet_initial_accounts(&self) -> Vec<InitialAccountData> {
|
|
||||||
self.public_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|(priv_key, _)| {
|
|
||||||
let pub_key = PublicKey::new_from_private_key(priv_key);
|
|
||||||
let account_id = AccountId::from(&pub_key);
|
|
||||||
InitialAccountData::Public(PublicAccountPrivateInitialData {
|
|
||||||
account_id,
|
|
||||||
pub_sign_key: priv_key.clone(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.chain(self.private_accounts.iter().map(|(key_chain, account)| {
|
|
||||||
InitialAccountData::Private(Box::new(PrivateAccountPrivateInitialData {
|
|
||||||
account: account.clone(),
|
|
||||||
key_chain: key_chain.clone(),
|
|
||||||
identifier: 0,
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum UrlProtocol {
|
pub enum UrlProtocol {
|
||||||
Http,
|
Http,
|
||||||
@ -168,7 +50,7 @@ pub fn sequencer_config(
|
|||||||
partial: SequencerPartialConfig,
|
partial: SequencerPartialConfig,
|
||||||
home: PathBuf,
|
home: PathBuf,
|
||||||
bedrock_addr: SocketAddr,
|
bedrock_addr: SocketAddr,
|
||||||
initial_data: &InitialData,
|
genesis_transactions: Vec<GenesisTransaction>,
|
||||||
) -> Result<SequencerConfig> {
|
) -> Result<SequencerConfig> {
|
||||||
let SequencerPartialConfig {
|
let SequencerPartialConfig {
|
||||||
max_num_tx_in_block,
|
max_num_tx_in_block,
|
||||||
@ -179,15 +61,13 @@ pub fn sequencer_config(
|
|||||||
|
|
||||||
Ok(SequencerConfig {
|
Ok(SequencerConfig {
|
||||||
home,
|
home,
|
||||||
genesis_id: 1,
|
|
||||||
is_genesis_random: true,
|
is_genesis_random: true,
|
||||||
max_num_tx_in_block,
|
max_num_tx_in_block,
|
||||||
max_block_size,
|
max_block_size,
|
||||||
mempool_max_size,
|
mempool_max_size,
|
||||||
block_create_timeout,
|
block_create_timeout,
|
||||||
retry_pending_blocks_timeout: Duration::from_secs(5),
|
retry_pending_blocks_timeout: Duration::from_secs(5),
|
||||||
initial_public_accounts: Some(initial_data.sequencer_initial_public_accounts()),
|
genesis: genesis_transactions,
|
||||||
initial_private_accounts: Some(initial_data.sequencer_initial_private_accounts()),
|
|
||||||
signing_key: [37; 32],
|
signing_key: [37; 32],
|
||||||
bedrock_config: BedrockConfig {
|
bedrock_config: BedrockConfig {
|
||||||
channel_id: bedrock_channel_id(),
|
channel_id: bedrock_channel_id(),
|
||||||
@ -198,10 +78,46 @@ pub fn sequencer_config(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wallet_config(
|
#[must_use]
|
||||||
sequencer_addr: SocketAddr,
|
pub fn default_public_accounts_for_wallet() -> Vec<(PrivateKey, u128)> {
|
||||||
initial_data: &InitialData,
|
let mut first_private_key = PrivateKey::new_os_random();
|
||||||
) -> Result<WalletConfig> {
|
let first_public_key = PublicKey::new_from_private_key(&first_private_key);
|
||||||
|
let mut first_account_id = AccountId::from(&first_public_key);
|
||||||
|
|
||||||
|
let mut second_private_key = PrivateKey::new_os_random();
|
||||||
|
let second_public_key = PublicKey::new_from_private_key(&second_private_key);
|
||||||
|
let mut second_account_id = AccountId::from(&second_public_key);
|
||||||
|
|
||||||
|
// Keep account ordering deterministic for tests that index into account lists.
|
||||||
|
if first_account_id > second_account_id {
|
||||||
|
std::mem::swap(&mut first_private_key, &mut second_private_key);
|
||||||
|
std::mem::swap(&mut first_account_id, &mut second_account_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![
|
||||||
|
(first_private_key, INITIAL_PUBLIC_BALANCES_FOR_WALLET[0]),
|
||||||
|
(second_private_key, INITIAL_PUBLIC_BALANCES_FOR_WALLET[1]),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn genesis_from_public_accounts(
|
||||||
|
public_accounts: &[(PrivateKey, u128)],
|
||||||
|
) -> Vec<GenesisTransaction> {
|
||||||
|
public_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|(private_key, balance)| {
|
||||||
|
let public_key = PublicKey::new_from_private_key(private_key);
|
||||||
|
let account_id = AccountId::from(&public_key);
|
||||||
|
GenesisTransaction::SupplyPublicAccount {
|
||||||
|
account_id,
|
||||||
|
balance: *balance,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wallet_config(sequencer_addr: SocketAddr) -> Result<WalletConfig> {
|
||||||
Ok(WalletConfig {
|
Ok(WalletConfig {
|
||||||
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
|
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
|
||||||
.context("Failed to convert sequencer addr to URL")?,
|
.context("Failed to convert sequencer addr to URL")?,
|
||||||
@ -209,16 +125,11 @@ pub fn wallet_config(
|
|||||||
seq_tx_poll_max_blocks: 15,
|
seq_tx_poll_max_blocks: 15,
|
||||||
seq_poll_max_retries: 10,
|
seq_poll_max_retries: 10,
|
||||||
seq_block_poll_max_amount: 100,
|
seq_block_poll_max_amount: 100,
|
||||||
initial_accounts: Some(initial_data.wallet_initial_accounts()),
|
|
||||||
basic_auth: None,
|
basic_auth: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indexer_config(
|
pub fn indexer_config(bedrock_addr: SocketAddr, home: PathBuf) -> Result<IndexerConfig> {
|
||||||
bedrock_addr: SocketAddr,
|
|
||||||
home: PathBuf,
|
|
||||||
initial_data: &InitialData,
|
|
||||||
) -> Result<IndexerConfig> {
|
|
||||||
Ok(IndexerConfig {
|
Ok(IndexerConfig {
|
||||||
home,
|
home,
|
||||||
consensus_info_polling_interval: Duration::from_secs(1),
|
consensus_info_polling_interval: Duration::from_secs(1),
|
||||||
@ -227,9 +138,6 @@ pub fn indexer_config(
|
|||||||
.context("Failed to convert bedrock addr to URL")?,
|
.context("Failed to convert bedrock addr to URL")?,
|
||||||
auth: None,
|
auth: None,
|
||||||
},
|
},
|
||||||
initial_public_accounts: Some(initial_data.sequencer_initial_public_accounts()),
|
|
||||||
initial_private_accounts: Some(initial_data.sequencer_initial_private_accounts()),
|
|
||||||
signing_key: [37; 32],
|
|
||||||
channel_id: bedrock_channel_id(),
|
channel_id: bedrock_channel_id(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
//! This library contains common code for integration tests.
|
//! This library contains common code for integration tests.
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
use std::{net::SocketAddr, sync::LazyLock};
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use common::{HashType, transaction::NSSATransaction};
|
use common::{HashType, transaction::NSSATransaction};
|
||||||
@ -9,21 +9,24 @@ use indexer_service::IndexerHandle;
|
|||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use nssa::{AccountId, PrivacyPreservingTransaction};
|
use nssa::{AccountId, PrivacyPreservingTransaction};
|
||||||
use nssa_core::Commitment;
|
use nssa_core::Commitment;
|
||||||
|
use sequencer_core::config::GenesisTransaction;
|
||||||
use sequencer_service::SequencerHandle;
|
use sequencer_service::SequencerHandle;
|
||||||
use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder};
|
use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder};
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use testcontainers::compose::DockerCompose;
|
use testcontainers::compose::DockerCompose;
|
||||||
use wallet::WalletCore;
|
use wallet::{WalletCore, account::AccountIdWithPrivacy, cli::CliAccountMention};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
indexer_client::IndexerClient,
|
indexer_client::IndexerClient,
|
||||||
setup::{setup_bedrock_node, setup_indexer, setup_sequencer, setup_wallet},
|
setup::{
|
||||||
|
setup_bedrock_node, setup_indexer, setup_private_accounts_with_initial_supply,
|
||||||
|
setup_sequencer, setup_wallet,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod indexer_client;
|
pub mod indexer_client;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod test_context_ffi;
|
|
||||||
|
|
||||||
// TODO: Remove this and control time from tests
|
// TODO: Remove this and control time from tests
|
||||||
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
||||||
@ -35,6 +38,26 @@ const BEDROCK_SERVICE_PORT: u16 = 18080;
|
|||||||
|
|
||||||
static LOGGER: LazyLock<()> = LazyLock::new(env_logger::init);
|
static LOGGER: LazyLock<()> = LazyLock::new(env_logger::init);
|
||||||
|
|
||||||
|
struct IndexerComponents {
|
||||||
|
indexer_handle: IndexerHandle,
|
||||||
|
indexer_client: IndexerClient,
|
||||||
|
_temp_dir: TempDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IndexerComponents {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let Self {
|
||||||
|
indexer_handle,
|
||||||
|
indexer_client: _,
|
||||||
|
_temp_dir: _,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
if !indexer_handle.is_healthy() {
|
||||||
|
error!("Indexer handle has unexpectedly stopped before IndexerComponents drop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Test context which sets up a sequencer and a wallet for integration tests.
|
/// Test context which sets up a sequencer and a wallet for integration tests.
|
||||||
///
|
///
|
||||||
/// It's memory and logically safe to create multiple instances of this struct in parallel tests,
|
/// It's memory and logically safe to create multiple instances of this struct in parallel tests,
|
||||||
@ -42,14 +65,13 @@ static LOGGER: LazyLock<()> = LazyLock::new(env_logger::init);
|
|||||||
// NOTE: Order of fields is important for proper drop order.
|
// NOTE: Order of fields is important for proper drop order.
|
||||||
pub struct TestContext {
|
pub struct TestContext {
|
||||||
sequencer_client: SequencerClient,
|
sequencer_client: SequencerClient,
|
||||||
indexer_client: IndexerClient,
|
|
||||||
wallet: WalletCore,
|
wallet: WalletCore,
|
||||||
wallet_password: String,
|
wallet_password: String,
|
||||||
/// Optional to move out value in Drop.
|
/// Optional to move out value in Drop.
|
||||||
sequencer_handle: Option<SequencerHandle>,
|
sequencer_handle: Option<SequencerHandle>,
|
||||||
indexer_handle: IndexerHandle,
|
indexer_components: Option<IndexerComponents>,
|
||||||
bedrock_compose: DockerCompose,
|
bedrock_compose: DockerCompose,
|
||||||
_temp_indexer_dir: TempDir,
|
bedrock_addr: SocketAddr,
|
||||||
_temp_sequencer_dir: TempDir,
|
_temp_sequencer_dir: TempDir,
|
||||||
_temp_wallet_dir: TempDir,
|
_temp_wallet_dir: TempDir,
|
||||||
}
|
}
|
||||||
@ -60,61 +82,12 @@ impl TestContext {
|
|||||||
Self::builder().build().await
|
Self::builder().build().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a builder for the test context to customize its configuration.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn builder() -> TestContextBuilder {
|
pub const fn builder() -> TestContextBuilder {
|
||||||
TestContextBuilder::new()
|
TestContextBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn new_configured(
|
|
||||||
sequencer_partial_config: config::SequencerPartialConfig,
|
|
||||||
initial_data: config::InitialData,
|
|
||||||
) -> Result<Self> {
|
|
||||||
// Ensure logger is initialized only once
|
|
||||||
*LOGGER;
|
|
||||||
|
|
||||||
debug!("Test context setup");
|
|
||||||
|
|
||||||
let (bedrock_compose, bedrock_addr) = setup_bedrock_node().await?;
|
|
||||||
|
|
||||||
let (indexer_handle, temp_indexer_dir) = setup_indexer(bedrock_addr, &initial_data)
|
|
||||||
.await
|
|
||||||
.context("Failed to setup Indexer")?;
|
|
||||||
|
|
||||||
let (sequencer_handle, temp_sequencer_dir) =
|
|
||||||
setup_sequencer(sequencer_partial_config, bedrock_addr, &initial_data)
|
|
||||||
.await
|
|
||||||
.context("Failed to setup Sequencer")?;
|
|
||||||
|
|
||||||
let (wallet, temp_wallet_dir, wallet_password) =
|
|
||||||
setup_wallet(sequencer_handle.addr(), &initial_data)
|
|
||||||
.await
|
|
||||||
.context("Failed to setup wallet")?;
|
|
||||||
|
|
||||||
let sequencer_url = config::addr_to_url(config::UrlProtocol::Http, sequencer_handle.addr())
|
|
||||||
.context("Failed to convert sequencer addr to URL")?;
|
|
||||||
let indexer_url = config::addr_to_url(config::UrlProtocol::Ws, indexer_handle.addr())
|
|
||||||
.context("Failed to convert indexer addr to URL")?;
|
|
||||||
let sequencer_client = SequencerClientBuilder::default()
|
|
||||||
.build(sequencer_url)
|
|
||||||
.context("Failed to create sequencer client")?;
|
|
||||||
let indexer_client = IndexerClient::new(&indexer_url)
|
|
||||||
.await
|
|
||||||
.context("Failed to create indexer client")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
sequencer_client,
|
|
||||||
indexer_client,
|
|
||||||
wallet,
|
|
||||||
wallet_password,
|
|
||||||
bedrock_compose,
|
|
||||||
sequencer_handle: Some(sequencer_handle),
|
|
||||||
indexer_handle,
|
|
||||||
_temp_indexer_dir: temp_indexer_dir,
|
|
||||||
_temp_sequencer_dir: temp_sequencer_dir,
|
|
||||||
_temp_wallet_dir: temp_wallet_dir,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to the wallet.
|
/// Get reference to the wallet.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn wallet(&self) -> &WalletCore {
|
pub const fn wallet(&self) -> &WalletCore {
|
||||||
@ -137,10 +110,38 @@ impl TestContext {
|
|||||||
&self.sequencer_client
|
&self.sequencer_client
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get reference to the indexer client.
|
/// Get the Bedrock Node address.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn indexer_client(&self) -> &IndexerClient {
|
pub const fn bedrock_addr(&self) -> SocketAddr {
|
||||||
&self.indexer_client
|
self.bedrock_addr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get reference to the indexer.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the indexer is not enabled in the test context. See
|
||||||
|
/// [`TestContextBuilder::disable_indexer()`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn indexer(&self) -> &IndexerHandle {
|
||||||
|
self.indexer_components
|
||||||
|
.as_ref()
|
||||||
|
.map(|components| &components.indexer_handle)
|
||||||
|
.expect("Called `TestContext::indexer()` on context with disabled indexer")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get reference to the indexer client.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the indexer is not enabled in the test context. See
|
||||||
|
/// [`TestContextBuilder::disable_indexer()`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn indexer_client(&self) -> &IndexerClient {
|
||||||
|
self.indexer_components
|
||||||
|
.as_ref()
|
||||||
|
.map(|components| &components.indexer_client)
|
||||||
|
.expect("Called `TestContext::indexer_client()` on context with disabled indexer")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get existing public account IDs in the wallet.
|
/// Get existing public account IDs in the wallet.
|
||||||
@ -148,8 +149,9 @@ impl TestContext {
|
|||||||
pub fn existing_public_accounts(&self) -> Vec<AccountId> {
|
pub fn existing_public_accounts(&self) -> Vec<AccountId> {
|
||||||
self.wallet
|
self.wallet
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.public_account_ids()
|
.public_account_ids()
|
||||||
|
.map(|(account_id, _idx)| account_id)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +160,9 @@ impl TestContext {
|
|||||||
pub fn existing_private_accounts(&self) -> Vec<AccountId> {
|
pub fn existing_private_accounts(&self) -> Vec<AccountId> {
|
||||||
self.wallet
|
self.wallet
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.private_account_ids()
|
.private_account_ids()
|
||||||
|
.map(|(account_id, _idx)| account_id)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,15 +171,14 @@ impl Drop for TestContext {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let Self {
|
let Self {
|
||||||
sequencer_handle,
|
sequencer_handle,
|
||||||
indexer_handle,
|
|
||||||
bedrock_compose,
|
bedrock_compose,
|
||||||
_temp_indexer_dir: _,
|
bedrock_addr: _,
|
||||||
_temp_sequencer_dir: _,
|
indexer_components: _,
|
||||||
_temp_wallet_dir: _,
|
|
||||||
sequencer_client: _,
|
sequencer_client: _,
|
||||||
indexer_client: _,
|
|
||||||
wallet: _,
|
wallet: _,
|
||||||
wallet_password: _,
|
wallet_password: _,
|
||||||
|
_temp_sequencer_dir: _,
|
||||||
|
_temp_wallet_dir: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let sequencer_handle = sequencer_handle
|
let sequencer_handle = sequencer_handle
|
||||||
@ -192,10 +194,6 @@ impl Drop for TestContext {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !indexer_handle.is_healthy() {
|
|
||||||
error!("Indexer handle has unexpectedly stopped before TestContext drop");
|
|
||||||
}
|
|
||||||
|
|
||||||
let container = bedrock_compose
|
let container = bedrock_compose
|
||||||
.service(BEDROCK_SERVICE_WITH_OPEN_PORT)
|
.service(BEDROCK_SERVICE_WITH_OPEN_PORT)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
@ -216,43 +214,24 @@ impl Drop for TestContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A test context to be used in normal #[test] tests.
|
|
||||||
pub struct BlockingTestContext {
|
|
||||||
ctx: Option<TestContext>,
|
|
||||||
runtime: tokio::runtime::Runtime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockingTestContext {
|
|
||||||
pub fn new() -> Result<Self> {
|
|
||||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
let ctx = runtime.block_on(TestContext::new())?;
|
|
||||||
Ok(Self {
|
|
||||||
ctx: Some(ctx),
|
|
||||||
runtime,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn ctx(&self) -> &TestContext {
|
|
||||||
self.ctx.as_ref().expect("TestContext is set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestContextBuilder {
|
pub struct TestContextBuilder {
|
||||||
initial_data: Option<config::InitialData>,
|
genesis_transactions: Option<Vec<GenesisTransaction>>,
|
||||||
sequencer_partial_config: Option<config::SequencerPartialConfig>,
|
sequencer_partial_config: Option<config::SequencerPartialConfig>,
|
||||||
|
enable_indexer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestContextBuilder {
|
impl TestContextBuilder {
|
||||||
const fn new() -> Self {
|
const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
initial_data: None,
|
genesis_transactions: None,
|
||||||
sequencer_partial_config: None,
|
sequencer_partial_config: None,
|
||||||
|
enable_indexer: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_initial_data(mut self, initial_data: config::InitialData) -> Self {
|
pub fn with_genesis(mut self, genesis_transactions: Vec<GenesisTransaction>) -> Self {
|
||||||
self.initial_data = Some(initial_data);
|
self.genesis_transactions = Some(genesis_transactions);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,14 +244,135 @@ impl TestContextBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exclude Indexer from test context.
|
||||||
|
/// Indexer is enabled by default.
|
||||||
|
///
|
||||||
|
/// Methods like [`TestContext::indexer()`] and [`TestContext::indexer_client()`] will panic if
|
||||||
|
/// called when indexer is disabled.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn disable_indexer(mut self) -> Self {
|
||||||
|
self.enable_indexer = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn build(self) -> Result<TestContext> {
|
pub async fn build(self) -> Result<TestContext> {
|
||||||
TestContext::new_configured(
|
let Self {
|
||||||
self.sequencer_partial_config.unwrap_or_default(),
|
genesis_transactions,
|
||||||
self.initial_data.unwrap_or_else(|| {
|
sequencer_partial_config,
|
||||||
config::InitialData::with_two_public_and_two_private_initialized_accounts()
|
enable_indexer,
|
||||||
}),
|
} = self;
|
||||||
|
|
||||||
|
// Ensure logger is initialized only once
|
||||||
|
*LOGGER;
|
||||||
|
|
||||||
|
debug!("Test context setup");
|
||||||
|
|
||||||
|
let (bedrock_compose, bedrock_addr) = setup_bedrock_node()
|
||||||
|
.await
|
||||||
|
.context("Failed to setup Bedrock node")?;
|
||||||
|
|
||||||
|
let indexer_components = if enable_indexer {
|
||||||
|
let (indexer_handle, temp_indexer_dir) = setup_indexer(bedrock_addr)
|
||||||
|
.await
|
||||||
|
.context("Failed to setup Indexer")?;
|
||||||
|
let indexer_url = config::addr_to_url(config::UrlProtocol::Ws, indexer_handle.addr())
|
||||||
|
.context("Failed to convert indexer addr to URL")?;
|
||||||
|
let indexer_client = IndexerClient::new(&indexer_url)
|
||||||
|
.await
|
||||||
|
.context("Failed to create indexer client")?;
|
||||||
|
Some(IndexerComponents {
|
||||||
|
indexer_handle,
|
||||||
|
indexer_client,
|
||||||
|
_temp_dir: temp_indexer_dir,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let initial_public_accounts = config::default_public_accounts_for_wallet();
|
||||||
|
let (sequencer_handle, temp_sequencer_dir) = setup_sequencer(
|
||||||
|
sequencer_partial_config.unwrap_or_default(),
|
||||||
|
bedrock_addr,
|
||||||
|
genesis_transactions
|
||||||
|
.unwrap_or_else(|| config::genesis_from_public_accounts(&initial_public_accounts)),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.context("Failed to setup Sequencer")?;
|
||||||
|
|
||||||
|
let (mut wallet, temp_wallet_dir, wallet_password) =
|
||||||
|
setup_wallet(sequencer_handle.addr(), &initial_public_accounts)
|
||||||
|
.context("Failed to setup wallet")?;
|
||||||
|
setup_private_accounts_with_initial_supply(&mut wallet)
|
||||||
|
.await
|
||||||
|
.context("Failed to initialize private accounts in wallet")?;
|
||||||
|
|
||||||
|
let sequencer_url = config::addr_to_url(config::UrlProtocol::Http, sequencer_handle.addr())
|
||||||
|
.context("Failed to convert sequencer addr to URL")?;
|
||||||
|
let sequencer_client = SequencerClientBuilder::default()
|
||||||
|
.build(sequencer_url)
|
||||||
|
.context("Failed to create sequencer client")?;
|
||||||
|
|
||||||
|
Ok(TestContext {
|
||||||
|
sequencer_client,
|
||||||
|
wallet,
|
||||||
|
wallet_password,
|
||||||
|
bedrock_compose,
|
||||||
|
bedrock_addr,
|
||||||
|
sequencer_handle: Some(sequencer_handle),
|
||||||
|
indexer_components,
|
||||||
|
_temp_sequencer_dir: temp_sequencer_dir,
|
||||||
|
_temp_wallet_dir: temp_wallet_dir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_blocking(self) -> Result<BlockingTestContext> {
|
||||||
|
let runtime = tokio::runtime::Runtime::new().context("Failed to create Tokio runtime")?;
|
||||||
|
|
||||||
|
let ctx = runtime.block_on(self.build())?;
|
||||||
|
|
||||||
|
Ok(BlockingTestContext {
|
||||||
|
ctx: Some(ctx),
|
||||||
|
runtime,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A test context to be used in normal #[test] tests.
|
||||||
|
pub struct BlockingTestContext {
|
||||||
|
ctx: Option<TestContext>,
|
||||||
|
runtime: tokio::runtime::Runtime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockingTestContext {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
TestContext::builder().build_blocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn ctx(&self) -> &TestContext {
|
||||||
|
self.ctx.as_ref().expect("TestContext is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn runtime(&self) -> &tokio::runtime::Runtime {
|
||||||
|
&self.runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_on<'ctx, F>(&'ctx self, f: impl FnOnce(&'ctx TestContext) -> F) -> F::Output
|
||||||
|
where
|
||||||
|
F: std::future::Future + 'ctx,
|
||||||
|
{
|
||||||
|
let future = f(self.ctx());
|
||||||
|
self.runtime.block_on(future)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_on_mut<'ctx, F>(
|
||||||
|
&'ctx mut self,
|
||||||
|
f: impl FnOnce(&'ctx mut TestContext) -> F,
|
||||||
|
) -> F::Output
|
||||||
|
where
|
||||||
|
F: std::future::Future + 'ctx,
|
||||||
|
{
|
||||||
|
let ctx_mut = self.ctx.as_mut().expect("TestContext is set");
|
||||||
|
let future = f(ctx_mut);
|
||||||
|
self.runtime.block_on(future)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,13 +390,13 @@ impl Drop for BlockingTestContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn format_public_account_id(account_id: AccountId) -> String {
|
pub const fn public_mention(account_id: AccountId) -> CliAccountMention {
|
||||||
format!("Public/{account_id}")
|
CliAccountMention::Id(AccountIdWithPrivacy::Public(account_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn format_private_account_id(account_id: AccountId) -> String {
|
pub const fn private_mention(account_id: AccountId) -> CliAccountMention {
|
||||||
format!("Private/{account_id}")
|
CliAccountMention::Id(AccountIdWithPrivacy::Private(account_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(
|
#[expect(
|
||||||
|
|||||||
@ -1,27 +1,30 @@
|
|||||||
use std::{
|
use std::{net::SocketAddr, path::PathBuf};
|
||||||
ffi::{CString, c_char},
|
|
||||||
fs::File,
|
|
||||||
io::Write as _,
|
|
||||||
net::SocketAddr,
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, bail};
|
use anyhow::{Context as _, Result, bail};
|
||||||
use indexer_ffi::{IndexerServiceFFI, api::lifecycle::InitializedIndexerServiceFFIResult};
|
|
||||||
use indexer_service::IndexerHandle;
|
use indexer_service::IndexerHandle;
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use sequencer_service::SequencerHandle;
|
use nssa::PrivateKey;
|
||||||
|
use sequencer_service::{GenesisTransaction, SequencerHandle};
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use testcontainers::compose::DockerCompose;
|
use testcontainers::compose::DockerCompose;
|
||||||
use wallet::{WalletCore, config::WalletConfigOverrides};
|
use wallet::{
|
||||||
|
WalletCore,
|
||||||
|
cli::{
|
||||||
|
Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
execute_subcommand,
|
||||||
|
programs::native_token_transfer::AuthTransferSubcommand,
|
||||||
|
},
|
||||||
|
config::WalletConfigOverrides,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{BEDROCK_SERVICE_PORT, BEDROCK_SERVICE_WITH_OPEN_PORT, config};
|
use crate::{
|
||||||
|
BEDROCK_SERVICE_PORT, BEDROCK_SERVICE_WITH_OPEN_PORT,
|
||||||
|
config::{self, INITIAL_PRIVATE_BALANCES_FOR_WALLET},
|
||||||
|
private_mention, public_mention,
|
||||||
|
};
|
||||||
|
|
||||||
unsafe extern "C" {
|
pub async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)> {
|
||||||
fn start_indexer(config_path: *const c_char, port: u16) -> InitializedIndexerServiceFFIResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)> {
|
|
||||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||||
let bedrock_compose_path = PathBuf::from(manifest_dir).join("../bedrock/docker-compose.yml");
|
let bedrock_compose_path = PathBuf::from(manifest_dir).join("../bedrock/docker-compose.yml");
|
||||||
|
|
||||||
@ -91,10 +94,7 @@ pub(crate) async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)>
|
|||||||
Ok((compose, addr))
|
Ok((compose, addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn setup_indexer(
|
pub async fn setup_indexer(bedrock_addr: SocketAddr) -> Result<(IndexerHandle, TempDir)> {
|
||||||
bedrock_addr: SocketAddr,
|
|
||||||
initial_data: &config::InitialData,
|
|
||||||
) -> Result<(IndexerHandle, TempDir)> {
|
|
||||||
let temp_indexer_dir =
|
let temp_indexer_dir =
|
||||||
tempfile::tempdir().context("Failed to create temp dir for indexer home")?;
|
tempfile::tempdir().context("Failed to create temp dir for indexer home")?;
|
||||||
|
|
||||||
@ -103,12 +103,8 @@ pub(crate) async fn setup_indexer(
|
|||||||
temp_indexer_dir.path().display()
|
temp_indexer_dir.path().display()
|
||||||
);
|
);
|
||||||
|
|
||||||
let indexer_config = config::indexer_config(
|
let indexer_config = config::indexer_config(bedrock_addr, temp_indexer_dir.path().to_owned())
|
||||||
bedrock_addr,
|
.context("Failed to create Indexer config")?;
|
||||||
temp_indexer_dir.path().to_owned(),
|
|
||||||
initial_data,
|
|
||||||
)
|
|
||||||
.context("Failed to create Indexer config")?;
|
|
||||||
|
|
||||||
indexer_service::run_server(indexer_config, 0)
|
indexer_service::run_server(indexer_config, 0)
|
||||||
.await
|
.await
|
||||||
@ -116,10 +112,10 @@ pub(crate) async fn setup_indexer(
|
|||||||
.map(|handle| (handle, temp_indexer_dir))
|
.map(|handle| (handle, temp_indexer_dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn setup_sequencer(
|
pub async fn setup_sequencer(
|
||||||
partial: config::SequencerPartialConfig,
|
partial: config::SequencerPartialConfig,
|
||||||
bedrock_addr: SocketAddr,
|
bedrock_addr: SocketAddr,
|
||||||
initial_data: &config::InitialData,
|
genesis_transactions: Vec<GenesisTransaction>,
|
||||||
) -> Result<(SequencerHandle, TempDir)> {
|
) -> Result<(SequencerHandle, TempDir)> {
|
||||||
let temp_sequencer_dir =
|
let temp_sequencer_dir =
|
||||||
tempfile::tempdir().context("Failed to create temp dir for sequencer home")?;
|
tempfile::tempdir().context("Failed to create temp dir for sequencer home")?;
|
||||||
@ -133,7 +129,7 @@ pub(crate) async fn setup_sequencer(
|
|||||||
partial,
|
partial,
|
||||||
temp_sequencer_dir.path().to_owned(),
|
temp_sequencer_dir.path().to_owned(),
|
||||||
bedrock_addr,
|
bedrock_addr,
|
||||||
initial_data,
|
genesis_transactions,
|
||||||
)
|
)
|
||||||
.context("Failed to create Sequencer config")?;
|
.context("Failed to create Sequencer config")?;
|
||||||
|
|
||||||
@ -142,12 +138,11 @@ pub(crate) async fn setup_sequencer(
|
|||||||
Ok((sequencer_handle, temp_sequencer_dir))
|
Ok((sequencer_handle, temp_sequencer_dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn setup_wallet(
|
pub fn setup_wallet(
|
||||||
sequencer_addr: SocketAddr,
|
sequencer_addr: SocketAddr,
|
||||||
initial_data: &config::InitialData,
|
initial_public_accounts: &[(PrivateKey, u128)],
|
||||||
) -> Result<(WalletCore, TempDir, String)> {
|
) -> Result<(WalletCore, TempDir, String)> {
|
||||||
let config = config::wallet_config(sequencer_addr, initial_data)
|
let config = config::wallet_config(sequencer_addr).context("Failed to create Wallet config")?;
|
||||||
.context("Failed to create Wallet config")?;
|
|
||||||
let config_serialized =
|
let config_serialized =
|
||||||
serde_json::to_string_pretty(&config).context("Failed to serialize Wallet config")?;
|
serde_json::to_string_pretty(&config).context("Failed to serialize Wallet config")?;
|
||||||
|
|
||||||
@ -162,57 +157,94 @@ pub(crate) async fn setup_wallet(
|
|||||||
let config_overrides = WalletConfigOverrides::default();
|
let config_overrides = WalletConfigOverrides::default();
|
||||||
|
|
||||||
let wallet_password = "test_pass".to_owned();
|
let wallet_password = "test_pass".to_owned();
|
||||||
let (wallet, _mnemonic) = WalletCore::new_init_storage(
|
let (mut wallet, _mnemonic) = WalletCore::new_init_storage(
|
||||||
config_path,
|
config_path,
|
||||||
storage_path,
|
storage_path,
|
||||||
Some(config_overrides),
|
Some(config_overrides),
|
||||||
&wallet_password,
|
&wallet_password,
|
||||||
)
|
)
|
||||||
.context("Failed to init wallet")?;
|
.context("Failed to init wallet")?;
|
||||||
|
|
||||||
|
for (private_key, _balance) in initial_public_accounts {
|
||||||
|
wallet
|
||||||
|
.storage_mut()
|
||||||
|
.key_chain_mut()
|
||||||
|
.add_imported_public_account(private_key.clone());
|
||||||
|
}
|
||||||
|
|
||||||
wallet
|
wallet
|
||||||
.store_persistent_data()
|
.store_persistent_data()
|
||||||
.await
|
|
||||||
.context("Failed to store wallet persistent data")?;
|
.context("Failed to store wallet persistent data")?;
|
||||||
|
|
||||||
Ok((wallet, temp_wallet_dir, wallet_password))
|
Ok((wallet, temp_wallet_dir, wallet_password))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn setup_indexer_ffi(
|
pub async fn setup_private_accounts_with_initial_supply(wallet: &mut WalletCore) -> Result<()> {
|
||||||
bedrock_addr: SocketAddr,
|
for _ in INITIAL_PRIVATE_BALANCES_FOR_WALLET {
|
||||||
initial_data: &config::InitialData,
|
let result = execute_subcommand(
|
||||||
) -> Result<(IndexerServiceFFI, TempDir)> {
|
wallet,
|
||||||
let temp_indexer_dir =
|
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
||||||
tempfile::tempdir().context("Failed to create temp dir for indexer home")?;
|
cci: None,
|
||||||
|
label: None,
|
||||||
debug!(
|
})),
|
||||||
"Using temp indexer home at {}",
|
)
|
||||||
temp_indexer_dir.path().display()
|
.await
|
||||||
);
|
.context("Failed to create a private account")?;
|
||||||
|
let SubcommandReturnValue::RegisterAccount { account_id: _ } = result else {
|
||||||
let indexer_config = config::indexer_config(
|
bail!("Expected RegisterAccount return value when creating private account");
|
||||||
bedrock_addr,
|
};
|
||||||
temp_indexer_dir.path().to_owned(),
|
|
||||||
initial_data,
|
|
||||||
)
|
|
||||||
.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");
|
|
||||||
let mut file = File::create(config_path.as_path())?;
|
|
||||||
file.write_all(&config_json)?;
|
|
||||||
file.flush()?;
|
|
||||||
|
|
||||||
let res =
|
|
||||||
// SAFETY: lib function ensures validity of value.
|
|
||||||
unsafe { start_indexer(CString::new(config_path.to_str().unwrap())?.as_ptr(), 0) };
|
|
||||||
|
|
||||||
if res.error.is_error() {
|
|
||||||
anyhow::bail!("Indexer FFI error {:?}", res.error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
let public_account_ids: Vec<_> = wallet
|
||||||
// SAFETY: lib function ensures validity of value.
|
.storage()
|
||||||
unsafe { std::ptr::read(res.value) },
|
.key_chain()
|
||||||
temp_indexer_dir,
|
.public_account_ids()
|
||||||
))
|
.map(|(account_id, _idx)| account_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if public_account_ids.len() < INITIAL_PRIVATE_BALANCES_FOR_WALLET.len() {
|
||||||
|
bail!(
|
||||||
|
"Expected at least {} public accounts in wallet storage, found {}",
|
||||||
|
INITIAL_PRIVATE_BALANCES_FOR_WALLET.len(),
|
||||||
|
public_account_ids.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let private_account_ids: Vec<_> = wallet
|
||||||
|
.storage()
|
||||||
|
.key_chain()
|
||||||
|
.private_account_ids()
|
||||||
|
.map(|(account_id, _idx)| account_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for ((from, to), amount) in public_account_ids
|
||||||
|
.into_iter()
|
||||||
|
.zip(private_account_ids.into_iter())
|
||||||
|
.zip(INITIAL_PRIVATE_BALANCES_FOR_WALLET)
|
||||||
|
{
|
||||||
|
let result = execute_subcommand(
|
||||||
|
wallet,
|
||||||
|
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
|
from: public_mention(from),
|
||||||
|
to: Some(private_mention(to)),
|
||||||
|
to_npk: None,
|
||||||
|
to_vpk: None,
|
||||||
|
to_identifier: None,
|
||||||
|
amount,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("Failed to perform initial shielded transfer to private account")?;
|
||||||
|
|
||||||
|
if !matches!(
|
||||||
|
result,
|
||||||
|
SubcommandReturnValue::PrivacyPreservingTransfer { .. }
|
||||||
|
) {
|
||||||
|
bail!(
|
||||||
|
"Expected PrivacyPreservingTransfer return value when shielding initial private funds"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,299 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
|
||||||
use futures::FutureExt as _;
|
|
||||||
use indexer_ffi::IndexerServiceFFI;
|
|
||||||
use indexer_service_rpc::RpcClient as _;
|
|
||||||
use log::{debug, error};
|
|
||||||
use nssa::AccountId;
|
|
||||||
use sequencer_service::SequencerHandle;
|
|
||||||
use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder};
|
|
||||||
use tempfile::TempDir;
|
|
||||||
use testcontainers::compose::DockerCompose;
|
|
||||||
use wallet::WalletCore;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
BEDROCK_SERVICE_WITH_OPEN_PORT, LOGGER, TestContextBuilder, config,
|
|
||||||
indexer_client::IndexerClient,
|
|
||||||
setup::{setup_bedrock_node, setup_indexer_ffi, setup_sequencer, setup_wallet},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Test context which sets up a sequencer, indexer through ffi and a wallet for integration tests.
|
|
||||||
///
|
|
||||||
/// It's memory and logically safe to create multiple instances of this struct in parallel tests,
|
|
||||||
/// as each instance uses its own temporary directories for sequencer and wallet data.
|
|
||||||
// NOTE: Order of fields is important for proper drop order.
|
|
||||||
pub struct TestContextFFI {
|
|
||||||
sequencer_client: SequencerClient,
|
|
||||||
indexer_client: IndexerClient,
|
|
||||||
wallet: WalletCore,
|
|
||||||
wallet_password: String,
|
|
||||||
/// Optional to move out value in Drop.
|
|
||||||
sequencer_handle: Option<SequencerHandle>,
|
|
||||||
bedrock_compose: DockerCompose,
|
|
||||||
_temp_indexer_dir: TempDir,
|
|
||||||
_temp_sequencer_dir: TempDir,
|
|
||||||
_temp_wallet_dir: TempDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(
|
|
||||||
clippy::multiple_inherent_impl,
|
|
||||||
reason = "It is more natural to have this implementation here"
|
|
||||||
)]
|
|
||||||
impl TestContextBuilder {
|
|
||||||
pub fn build_ffi(
|
|
||||||
self,
|
|
||||||
runtime: &Arc<tokio::runtime::Runtime>,
|
|
||||||
) -> Result<(TestContextFFI, IndexerServiceFFI)> {
|
|
||||||
TestContextFFI::new_configured(
|
|
||||||
self.sequencer_partial_config.unwrap_or_default(),
|
|
||||||
&self.initial_data.unwrap_or_else(|| {
|
|
||||||
config::InitialData::with_two_public_and_two_private_initialized_accounts()
|
|
||||||
}),
|
|
||||||
runtime,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestContextFFI {
|
|
||||||
/// Create new test context.
|
|
||||||
pub fn new(runtime: &Arc<tokio::runtime::Runtime>) -> Result<(Self, IndexerServiceFFI)> {
|
|
||||||
Self::builder().build_ffi(runtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn builder() -> TestContextBuilder {
|
|
||||||
TestContextBuilder::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_configured(
|
|
||||||
sequencer_partial_config: config::SequencerPartialConfig,
|
|
||||||
initial_data: &config::InitialData,
|
|
||||||
runtime: &Arc<tokio::runtime::Runtime>,
|
|
||||||
) -> Result<(Self, IndexerServiceFFI)> {
|
|
||||||
// Ensure logger is initialized only once
|
|
||||||
*LOGGER;
|
|
||||||
|
|
||||||
debug!("Test context setup");
|
|
||||||
|
|
||||||
let (bedrock_compose, bedrock_addr) = runtime.block_on(setup_bedrock_node())?;
|
|
||||||
|
|
||||||
let (indexer_ffi, temp_indexer_dir) =
|
|
||||||
setup_indexer_ffi(bedrock_addr, initial_data).context("Failed to setup Indexer")?;
|
|
||||||
|
|
||||||
let (sequencer_handle, temp_sequencer_dir) = runtime
|
|
||||||
.block_on(setup_sequencer(
|
|
||||||
sequencer_partial_config,
|
|
||||||
bedrock_addr,
|
|
||||||
initial_data,
|
|
||||||
))
|
|
||||||
.context("Failed to setup Sequencer")?;
|
|
||||||
|
|
||||||
let (wallet, temp_wallet_dir, wallet_password) = runtime
|
|
||||||
.block_on(setup_wallet(sequencer_handle.addr(), initial_data))
|
|
||||||
.context("Failed to setup wallet")?;
|
|
||||||
|
|
||||||
let sequencer_url = config::addr_to_url(config::UrlProtocol::Http, sequencer_handle.addr())
|
|
||||||
.context("Failed to convert sequencer addr to URL")?;
|
|
||||||
let indexer_url = config::addr_to_url(
|
|
||||||
config::UrlProtocol::Ws,
|
|
||||||
// SAFETY: addr is valid if indexer_ffi is valid.
|
|
||||||
unsafe { indexer_ffi.addr() },
|
|
||||||
)
|
|
||||||
.context("Failed to convert indexer addr to URL")?;
|
|
||||||
let sequencer_client = SequencerClientBuilder::default()
|
|
||||||
.build(sequencer_url)
|
|
||||||
.context("Failed to create sequencer client")?;
|
|
||||||
let indexer_client = runtime
|
|
||||||
.block_on(IndexerClient::new(&indexer_url))
|
|
||||||
.context("Failed to create indexer client")?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
Self {
|
|
||||||
sequencer_client,
|
|
||||||
indexer_client,
|
|
||||||
wallet,
|
|
||||||
wallet_password,
|
|
||||||
bedrock_compose,
|
|
||||||
sequencer_handle: Some(sequencer_handle),
|
|
||||||
_temp_indexer_dir: temp_indexer_dir,
|
|
||||||
_temp_sequencer_dir: temp_sequencer_dir,
|
|
||||||
_temp_wallet_dir: temp_wallet_dir,
|
|
||||||
},
|
|
||||||
indexer_ffi,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to the wallet.
|
|
||||||
#[must_use]
|
|
||||||
pub const fn wallet(&self) -> &WalletCore {
|
|
||||||
&self.wallet
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn wallet_password(&self) -> &str {
|
|
||||||
&self.wallet_password
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get mutable reference to the wallet.
|
|
||||||
pub const fn wallet_mut(&mut self) -> &mut WalletCore {
|
|
||||||
&mut self.wallet
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to the sequencer client.
|
|
||||||
#[must_use]
|
|
||||||
pub const fn sequencer_client(&self) -> &SequencerClient {
|
|
||||||
&self.sequencer_client
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to the indexer client.
|
|
||||||
#[must_use]
|
|
||||||
pub const fn indexer_client(&self) -> &IndexerClient {
|
|
||||||
&self.indexer_client
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get existing public account IDs in the wallet.
|
|
||||||
#[must_use]
|
|
||||||
pub fn existing_public_accounts(&self) -> Vec<AccountId> {
|
|
||||||
self.wallet
|
|
||||||
.storage()
|
|
||||||
.user_data
|
|
||||||
.public_account_ids()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get existing private account IDs in the wallet.
|
|
||||||
#[must_use]
|
|
||||||
pub fn existing_private_accounts(&self) -> Vec<AccountId> {
|
|
||||||
self.wallet
|
|
||||||
.storage()
|
|
||||||
.user_data
|
|
||||||
.private_account_ids()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_last_block_sequencer(&self, runtime: &Arc<tokio::runtime::Runtime>) -> Result<u64> {
|
|
||||||
Ok(runtime.block_on(self.sequencer_client.get_last_block_id())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_last_block_indexer(&self, runtime: &Arc<tokio::runtime::Runtime>) -> Result<u64> {
|
|
||||||
Ok(runtime.block_on(self.indexer_client.get_last_finalized_block_id())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TestContextFFI {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let Self {
|
|
||||||
sequencer_handle,
|
|
||||||
bedrock_compose,
|
|
||||||
_temp_indexer_dir: _,
|
|
||||||
_temp_sequencer_dir: _,
|
|
||||||
_temp_wallet_dir: _,
|
|
||||||
sequencer_client: _,
|
|
||||||
indexer_client: _,
|
|
||||||
wallet: _,
|
|
||||||
wallet_password: _,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let sequencer_handle = sequencer_handle
|
|
||||||
.take()
|
|
||||||
.expect("Sequencer handle should be present in TestContext drop");
|
|
||||||
if !sequencer_handle.is_healthy() {
|
|
||||||
let Err(err) = sequencer_handle
|
|
||||||
.failed()
|
|
||||||
.now_or_never()
|
|
||||||
.expect("Sequencer handle should not be running");
|
|
||||||
error!(
|
|
||||||
"Sequencer handle has unexpectedly stopped before TestContext drop with error: {err:#}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let container = bedrock_compose
|
|
||||||
.service(BEDROCK_SERVICE_WITH_OPEN_PORT)
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
panic!("Failed to get Bedrock service container `{BEDROCK_SERVICE_WITH_OPEN_PORT}`")
|
|
||||||
});
|
|
||||||
let output = std::process::Command::new("docker")
|
|
||||||
.args(["inspect", "-f", "{{.State.Running}}", container.id()])
|
|
||||||
.output()
|
|
||||||
.expect("Failed to execute docker inspect command to check if Bedrock container is still running");
|
|
||||||
let stdout = String::from_utf8(output.stdout)
|
|
||||||
.expect("Failed to parse docker inspect output as String");
|
|
||||||
if stdout.trim() != "true" {
|
|
||||||
error!(
|
|
||||||
"Bedrock container `{}` is not running during TestContext drop, docker inspect output: {stdout}",
|
|
||||||
container.id()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A test context with ffi to be used in normal #[test] tests.
|
|
||||||
pub struct BlockingTestContextFFI {
|
|
||||||
ctx: Option<TestContextFFI>,
|
|
||||||
runtime: Arc<tokio::runtime::Runtime>,
|
|
||||||
indexer_ffi: IndexerServiceFFI,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockingTestContextFFI {
|
|
||||||
pub fn new() -> Result<Self> {
|
|
||||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
let runtime_wrapped = Arc::new(runtime);
|
|
||||||
let (ctx, indexer_ffi) = TestContextFFI::new(&runtime_wrapped)?;
|
|
||||||
Ok(Self {
|
|
||||||
ctx: Some(ctx),
|
|
||||||
runtime: runtime_wrapped,
|
|
||||||
indexer_ffi,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn ctx(&self) -> &TestContextFFI {
|
|
||||||
self.ctx.as_ref().expect("TestContext is set")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn ctx_mut(&mut self) -> &mut TestContextFFI {
|
|
||||||
self.ctx.as_mut().expect("TestContext is set")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn runtime(&self) -> &Arc<tokio::runtime::Runtime> {
|
|
||||||
&self.runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn runtime_clone(&self) -> Arc<tokio::runtime::Runtime> {
|
|
||||||
Arc::<tokio::runtime::Runtime>::clone(&self.runtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn indexer_ffi(&self) -> *const IndexerServiceFFI {
|
|
||||||
&raw const (self.indexer_ffi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for BlockingTestContextFFI {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let Self {
|
|
||||||
ctx,
|
|
||||||
runtime,
|
|
||||||
indexer_ffi,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
// Ensure async cleanup of TestContext by blocking on its drop in the runtime.
|
|
||||||
runtime.block_on(async {
|
|
||||||
if let Some(ctx) = ctx.take() {
|
|
||||||
drop(ctx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let indexer_handle =
|
|
||||||
// SAFETY: lib function ensures validity of value.
|
|
||||||
unsafe { indexer_ffi.handle() };
|
|
||||||
|
|
||||||
if !indexer_handle.is_healthy() {
|
|
||||||
error!("Indexer handle has unexpectedly stopped before TestContext drop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,16 +3,21 @@
|
|||||||
reason = "We don't care about these in tests"
|
reason = "We don't care about these in tests"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context as _, Result};
|
||||||
use integration_tests::{TestContext, format_private_account_id};
|
use integration_tests::{TestContext, private_mention};
|
||||||
|
use key_protocol::key_management::KeyChain;
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::program::Program;
|
use nssa::{Data, program::Program};
|
||||||
|
use nssa_core::account::Nonce;
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::{
|
use wallet::{
|
||||||
Command,
|
account::{AccountIdWithPrivacy, HumanReadableAccount, Label},
|
||||||
account::{AccountSubcommand, NewSubcommand},
|
cli::{
|
||||||
execute_subcommand,
|
Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, ImportSubcommand, NewSubcommand},
|
||||||
|
execute_subcommand,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -30,7 +35,7 @@ async fn get_existing_account() -> Result<()> {
|
|||||||
);
|
);
|
||||||
assert_eq!(account.balance, 10000);
|
assert_eq!(account.balance, 10000);
|
||||||
assert!(account.data.is_empty());
|
assert!(account.data.is_empty());
|
||||||
assert_eq!(account.nonce.0, 0);
|
assert_eq!(account.nonce.0, 1);
|
||||||
|
|
||||||
info!("Successfully retrieved account with correct details");
|
info!("Successfully retrieved account with correct details");
|
||||||
|
|
||||||
@ -41,7 +46,7 @@ async fn get_existing_account() -> Result<()> {
|
|||||||
async fn new_public_account_with_label() -> Result<()> {
|
async fn new_public_account_with_label() -> Result<()> {
|
||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
let label = "my-test-public-account".to_owned();
|
let label = Label::new("my-test-public-account");
|
||||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
cci: None,
|
cci: None,
|
||||||
label: Some(label.clone()),
|
label: Some(label.clone()),
|
||||||
@ -55,14 +60,9 @@ async fn new_public_account_with_label() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Verify the label was stored
|
// Verify the label was stored
|
||||||
let stored_label = ctx
|
let resolved = ctx.wallet().storage().resolve_label(&label);
|
||||||
.wallet()
|
|
||||||
.storage()
|
|
||||||
.labels
|
|
||||||
.get(&account_id.to_string())
|
|
||||||
.expect("Label should be stored for the new account");
|
|
||||||
|
|
||||||
assert_eq!(stored_label.to_string(), label);
|
assert_eq!(resolved, Some(AccountIdWithPrivacy::Public(account_id)));
|
||||||
|
|
||||||
info!("Successfully created public account with label");
|
info!("Successfully created public account with label");
|
||||||
|
|
||||||
@ -74,23 +74,17 @@ async fn add_label_to_existing_account() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
let account_id = ctx.existing_private_accounts()[0];
|
let account_id = ctx.existing_private_accounts()[0];
|
||||||
let label = "my-test-private-account".to_owned();
|
let label = Label::new("my-test-private-account");
|
||||||
let command = Command::Account(AccountSubcommand::Label {
|
let command = Command::Account(AccountSubcommand::Label {
|
||||||
account_id: Some(format_private_account_id(account_id)),
|
account_id: private_mention(account_id),
|
||||||
account_label: None,
|
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
execute_subcommand(ctx.wallet_mut(), command).await?;
|
execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
let stored_label = ctx
|
let resolved = ctx.wallet().storage().resolve_label(&label);
|
||||||
.wallet()
|
|
||||||
.storage()
|
|
||||||
.labels
|
|
||||||
.get(&account_id.to_string())
|
|
||||||
.expect("Label should be stored for the account");
|
|
||||||
|
|
||||||
assert_eq!(stored_label.to_string(), label);
|
assert_eq!(resolved, Some(AccountIdWithPrivacy::Private(account_id)));
|
||||||
|
|
||||||
info!("Successfully set label on existing private account");
|
info!("Successfully set label on existing private account");
|
||||||
|
|
||||||
@ -114,12 +108,13 @@ async fn new_public_account_without_label() -> Result<()> {
|
|||||||
panic!("Expected RegisterAccount return value")
|
panic!("Expected RegisterAccount return value")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify no label was stored
|
// Verify no label was stored for the account id
|
||||||
assert!(
|
assert!(
|
||||||
!ctx.wallet()
|
ctx.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.labels
|
.labels_for_account(AccountIdWithPrivacy::Public(account_id))
|
||||||
.contains_key(&account_id.to_string()),
|
.next()
|
||||||
|
.is_none(),
|
||||||
"No label should be stored when not provided"
|
"No label should be stored when not provided"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -127,3 +122,150 @@ async fn new_public_account_without_label() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn import_public_account() -> Result<()> {
|
||||||
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
|
let private_key = nssa::PrivateKey::new_os_random();
|
||||||
|
let account_id = nssa::AccountId::from(&nssa::PublicKey::new_from_private_key(&private_key));
|
||||||
|
|
||||||
|
let command = Command::Account(AccountSubcommand::Import(ImportSubcommand::Public {
|
||||||
|
private_key,
|
||||||
|
}));
|
||||||
|
let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
let SubcommandReturnValue::Empty = sub_ret else {
|
||||||
|
anyhow::bail!("Expected Empty return value");
|
||||||
|
};
|
||||||
|
|
||||||
|
let imported_key = ctx
|
||||||
|
.wallet()
|
||||||
|
.storage()
|
||||||
|
.key_chain()
|
||||||
|
.pub_account_signing_key(account_id);
|
||||||
|
assert!(
|
||||||
|
imported_key.is_some(),
|
||||||
|
"Imported public account should be present"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn import_private_account() -> Result<()> {
|
||||||
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
|
let key_chain = KeyChain::new_os_random();
|
||||||
|
let account_id = nssa::AccountId::from((&key_chain.nullifier_public_key, 0));
|
||||||
|
let account = nssa::Account {
|
||||||
|
program_owner: Program::authenticated_transfer_program().id(),
|
||||||
|
balance: 777,
|
||||||
|
data: Data::default(),
|
||||||
|
nonce: Nonce::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_chain_json = serde_json::to_string(&key_chain)
|
||||||
|
.context("Failed to serialize key chain for private import")?;
|
||||||
|
let account_state = HumanReadableAccount::from(account.clone());
|
||||||
|
|
||||||
|
let command = Command::Account(AccountSubcommand::Import(ImportSubcommand::Private {
|
||||||
|
key_chain_json,
|
||||||
|
account_state,
|
||||||
|
chain_index: None,
|
||||||
|
identifier: 0,
|
||||||
|
}));
|
||||||
|
let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
let SubcommandReturnValue::Empty = sub_ret else {
|
||||||
|
anyhow::bail!("Expected Empty return value");
|
||||||
|
};
|
||||||
|
|
||||||
|
let imported_acc = ctx
|
||||||
|
.wallet()
|
||||||
|
.storage()
|
||||||
|
.key_chain()
|
||||||
|
.private_account(account_id)
|
||||||
|
.context("Imported private account should be present")?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
imported_acc.key_chain.secret_spending_key,
|
||||||
|
key_chain.secret_spending_key
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
imported_acc.key_chain.nullifier_public_key,
|
||||||
|
key_chain.nullifier_public_key
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
imported_acc.key_chain.viewing_public_key,
|
||||||
|
key_chain.viewing_public_key
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(imported_acc.chain_index, None);
|
||||||
|
|
||||||
|
assert_eq!(imported_acc.identifier, 0);
|
||||||
|
|
||||||
|
assert_eq!(imported_acc.account, &account);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn import_private_account_second_time_overrides_account_data() -> Result<()> {
|
||||||
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
|
let key_chain = KeyChain::new_os_random();
|
||||||
|
let account_id = nssa::AccountId::from((&key_chain.nullifier_public_key, 0));
|
||||||
|
let key_chain_json =
|
||||||
|
serde_json::to_string(&key_chain).context("Failed to serialize key chain")?;
|
||||||
|
|
||||||
|
let initial_account = nssa::Account {
|
||||||
|
program_owner: Program::authenticated_transfer_program().id(),
|
||||||
|
balance: 100,
|
||||||
|
data: Data::default(),
|
||||||
|
nonce: Nonce::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// First import
|
||||||
|
wallet::cli::execute_subcommand(
|
||||||
|
ctx.wallet_mut(),
|
||||||
|
Command::Account(AccountSubcommand::Import(ImportSubcommand::Private {
|
||||||
|
key_chain_json: key_chain_json.clone(),
|
||||||
|
account_state: HumanReadableAccount::from(initial_account),
|
||||||
|
chain_index: None,
|
||||||
|
identifier: 0,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let updated_account = nssa::Account {
|
||||||
|
program_owner: Program::authenticated_transfer_program().id(),
|
||||||
|
balance: 999,
|
||||||
|
data: Data::default(),
|
||||||
|
nonce: Nonce::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Second import with different account data (same key chain)
|
||||||
|
wallet::cli::execute_subcommand(
|
||||||
|
ctx.wallet_mut(),
|
||||||
|
Command::Account(AccountSubcommand::Import(ImportSubcommand::Private {
|
||||||
|
key_chain_json,
|
||||||
|
account_state: HumanReadableAccount::from(updated_account.clone()),
|
||||||
|
chain_index: None,
|
||||||
|
identifier: 0,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let imported = ctx
|
||||||
|
.wallet()
|
||||||
|
.storage()
|
||||||
|
.key_chain()
|
||||||
|
.private_account(account_id)
|
||||||
|
.context("Imported private account should be present")?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
imported.account, &updated_account,
|
||||||
|
"Second import should override account data"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@ -7,14 +7,17 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id};
|
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
|
||||||
use log::info;
|
use log::info;
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::{
|
use wallet::{
|
||||||
Command, SubcommandReturnValue,
|
account::Label,
|
||||||
account::{AccountSubcommand, NewSubcommand},
|
cli::{
|
||||||
programs::{amm::AmmProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand},
|
Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
programs::{amm::AmmProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -113,10 +116,8 @@ async fn amm_public() -> Result<()> {
|
|||||||
|
|
||||||
// Create new token
|
// Create new token
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id_1)),
|
definition_account_id: public_mention(definition_account_id_1),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id_1),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id_1)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "A NAM1".to_owned(),
|
name: "A NAM1".to_owned(),
|
||||||
|
|
||||||
total_supply: 37,
|
total_supply: 37,
|
||||||
@ -127,10 +128,8 @@ async fn amm_public() -> Result<()> {
|
|||||||
|
|
||||||
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_1`
|
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_1`
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id_1)),
|
from: public_mention(supply_account_id_1),
|
||||||
from_label: None,
|
to: Some(public_mention(recipient_account_id_1)),
|
||||||
to: Some(format_public_account_id(recipient_account_id_1)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -143,10 +142,8 @@ async fn amm_public() -> Result<()> {
|
|||||||
|
|
||||||
// Create new token
|
// Create new token
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id_2)),
|
definition_account_id: public_mention(definition_account_id_2),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id_2),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id_2)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "A NAM2".to_owned(),
|
name: "A NAM2".to_owned(),
|
||||||
|
|
||||||
total_supply: 37,
|
total_supply: 37,
|
||||||
@ -157,10 +154,8 @@ async fn amm_public() -> Result<()> {
|
|||||||
|
|
||||||
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_2`
|
// Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_2`
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id_2)),
|
from: public_mention(supply_account_id_2),
|
||||||
from_label: None,
|
to: Some(public_mention(recipient_account_id_2)),
|
||||||
to: Some(format_public_account_id(recipient_account_id_2)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -193,12 +188,9 @@ async fn amm_public() -> Result<()> {
|
|||||||
|
|
||||||
// Send creation tx
|
// Send creation tx
|
||||||
let subcommand = AmmProgramAgnosticSubcommand::New {
|
let subcommand = AmmProgramAgnosticSubcommand::New {
|
||||||
user_holding_a: Some(format_public_account_id(recipient_account_id_1)),
|
user_holding_a: public_mention(recipient_account_id_1),
|
||||||
user_holding_a_label: None,
|
user_holding_b: public_mention(recipient_account_id_2),
|
||||||
user_holding_b: Some(format_public_account_id(recipient_account_id_2)),
|
user_holding_lp: public_mention(user_holding_lp),
|
||||||
user_holding_b_label: None,
|
|
||||||
user_holding_lp: Some(format_public_account_id(user_holding_lp)),
|
|
||||||
user_holding_lp_label: None,
|
|
||||||
balance_a: 3,
|
balance_a: 3,
|
||||||
balance_b: 3,
|
balance_b: 3,
|
||||||
};
|
};
|
||||||
@ -239,13 +231,11 @@ async fn amm_public() -> Result<()> {
|
|||||||
// Make swap
|
// Make swap
|
||||||
|
|
||||||
let subcommand = AmmProgramAgnosticSubcommand::SwapExactInput {
|
let subcommand = AmmProgramAgnosticSubcommand::SwapExactInput {
|
||||||
user_holding_a: Some(format_public_account_id(recipient_account_id_1)),
|
user_holding_a: public_mention(recipient_account_id_1),
|
||||||
user_holding_a_label: None,
|
user_holding_b: public_mention(recipient_account_id_2),
|
||||||
user_holding_b: Some(format_public_account_id(recipient_account_id_2)),
|
|
||||||
user_holding_b_label: None,
|
|
||||||
amount_in: 2,
|
amount_in: 2,
|
||||||
min_amount_out: 1,
|
min_amount_out: 1,
|
||||||
token_definition: definition_account_id_1.to_string(),
|
token_definition: definition_account_id_1,
|
||||||
};
|
};
|
||||||
|
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?;
|
||||||
@ -284,13 +274,11 @@ async fn amm_public() -> Result<()> {
|
|||||||
// Make swap
|
// Make swap
|
||||||
|
|
||||||
let subcommand = AmmProgramAgnosticSubcommand::SwapExactInput {
|
let subcommand = AmmProgramAgnosticSubcommand::SwapExactInput {
|
||||||
user_holding_a: Some(format_public_account_id(recipient_account_id_1)),
|
user_holding_a: public_mention(recipient_account_id_1),
|
||||||
user_holding_a_label: None,
|
user_holding_b: public_mention(recipient_account_id_2),
|
||||||
user_holding_b: Some(format_public_account_id(recipient_account_id_2)),
|
|
||||||
user_holding_b_label: None,
|
|
||||||
amount_in: 2,
|
amount_in: 2,
|
||||||
min_amount_out: 1,
|
min_amount_out: 1,
|
||||||
token_definition: definition_account_id_2.to_string(),
|
token_definition: definition_account_id_2,
|
||||||
};
|
};
|
||||||
|
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?;
|
||||||
@ -329,12 +317,9 @@ async fn amm_public() -> Result<()> {
|
|||||||
// Add liquidity
|
// Add liquidity
|
||||||
|
|
||||||
let subcommand = AmmProgramAgnosticSubcommand::AddLiquidity {
|
let subcommand = AmmProgramAgnosticSubcommand::AddLiquidity {
|
||||||
user_holding_a: Some(format_public_account_id(recipient_account_id_1)),
|
user_holding_a: public_mention(recipient_account_id_1),
|
||||||
user_holding_a_label: None,
|
user_holding_b: public_mention(recipient_account_id_2),
|
||||||
user_holding_b: Some(format_public_account_id(recipient_account_id_2)),
|
user_holding_lp: public_mention(user_holding_lp),
|
||||||
user_holding_b_label: None,
|
|
||||||
user_holding_lp: Some(format_public_account_id(user_holding_lp)),
|
|
||||||
user_holding_lp_label: None,
|
|
||||||
min_amount_lp: 1,
|
min_amount_lp: 1,
|
||||||
max_amount_a: 2,
|
max_amount_a: 2,
|
||||||
max_amount_b: 2,
|
max_amount_b: 2,
|
||||||
@ -376,12 +361,9 @@ async fn amm_public() -> Result<()> {
|
|||||||
// Remove liquidity
|
// Remove liquidity
|
||||||
|
|
||||||
let subcommand = AmmProgramAgnosticSubcommand::RemoveLiquidity {
|
let subcommand = AmmProgramAgnosticSubcommand::RemoveLiquidity {
|
||||||
user_holding_a: Some(format_public_account_id(recipient_account_id_1)),
|
user_holding_a: public_mention(recipient_account_id_1),
|
||||||
user_holding_a_label: None,
|
user_holding_b: public_mention(recipient_account_id_2),
|
||||||
user_holding_b: Some(format_public_account_id(recipient_account_id_2)),
|
user_holding_lp: public_mention(user_holding_lp),
|
||||||
user_holding_b_label: None,
|
|
||||||
user_holding_lp: Some(format_public_account_id(user_holding_lp)),
|
|
||||||
user_holding_lp_label: None,
|
|
||||||
balance_lp: 2,
|
balance_lp: 2,
|
||||||
min_amount_a: 1,
|
min_amount_a: 1,
|
||||||
min_amount_b: 1,
|
min_amount_b: 1,
|
||||||
@ -457,14 +439,14 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create holding_a with a label
|
// Create holding_a with a label
|
||||||
let holding_a_label = "amm-holding-a-label".to_owned();
|
let holding_a_label = Label::new("amm-holding-a-label");
|
||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
account_id: holding_a_id,
|
account_id: holding_a_id,
|
||||||
} = wallet::cli::execute_subcommand(
|
} = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
cci: None,
|
cci: None,
|
||||||
label: Some(holding_a_label.clone()),
|
label: Some(Label::new(holding_a_label.clone())),
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
@ -502,14 +484,14 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create holding_b with a label
|
// Create holding_b with a label
|
||||||
let holding_b_label = "amm-holding-b-label".to_owned();
|
let holding_b_label = Label::new("amm-holding-b-label");
|
||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
account_id: holding_b_id,
|
account_id: holding_b_id,
|
||||||
} = wallet::cli::execute_subcommand(
|
} = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
cci: None,
|
cci: None,
|
||||||
label: Some(holding_b_label.clone()),
|
label: Some(Label::new(holding_b_label.clone())),
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
@ -518,14 +500,14 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create holding_lp with a label
|
// Create holding_lp with a label
|
||||||
let holding_lp_label = "amm-holding-lp-label".to_owned();
|
let holding_lp_label = Label::new("amm-holding-lp-label");
|
||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
account_id: holding_lp_id,
|
account_id: holding_lp_id,
|
||||||
} = wallet::cli::execute_subcommand(
|
} = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
cci: None,
|
cci: None,
|
||||||
label: Some(holding_lp_label.clone()),
|
label: Some(Label::new(holding_lp_label.clone())),
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
@ -535,10 +517,8 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
|
|
||||||
// Create token 1 and distribute to holding_a
|
// Create token 1 and distribute to holding_a
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id_1)),
|
definition_account_id: public_mention(definition_account_id_1),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id_1),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id_1)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TOKEN1".to_owned(),
|
name: "TOKEN1".to_owned(),
|
||||||
total_supply: 10,
|
total_supply: 10,
|
||||||
};
|
};
|
||||||
@ -546,10 +526,8 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||||
|
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id_1)),
|
from: public_mention(supply_account_id_1),
|
||||||
from_label: None,
|
to: Some(public_mention(holding_a_id)),
|
||||||
to: Some(format_public_account_id(holding_a_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -560,10 +538,8 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
|
|
||||||
// Create token 2 and distribute to holding_b
|
// Create token 2 and distribute to holding_b
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id_2)),
|
definition_account_id: public_mention(definition_account_id_2),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id_2),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id_2)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TOKEN2".to_owned(),
|
name: "TOKEN2".to_owned(),
|
||||||
total_supply: 10,
|
total_supply: 10,
|
||||||
};
|
};
|
||||||
@ -571,10 +547,8 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||||
|
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id_2)),
|
from: public_mention(supply_account_id_2),
|
||||||
from_label: None,
|
to: Some(public_mention(holding_b_id)),
|
||||||
to: Some(format_public_account_id(holding_b_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -585,12 +559,9 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
|||||||
|
|
||||||
// Create AMM pool using account labels instead of IDs
|
// Create AMM pool using account labels instead of IDs
|
||||||
let subcommand = AmmProgramAgnosticSubcommand::New {
|
let subcommand = AmmProgramAgnosticSubcommand::New {
|
||||||
user_holding_a: None,
|
user_holding_a: holding_a_label.into(),
|
||||||
user_holding_a_label: Some(holding_a_label),
|
user_holding_b: holding_b_label.into(),
|
||||||
user_holding_b: None,
|
user_holding_lp: holding_lp_label.into(),
|
||||||
user_holding_b_label: Some(holding_b_label),
|
|
||||||
user_holding_lp: None,
|
|
||||||
user_holding_lp_label: Some(holding_lp_label),
|
|
||||||
balance_a: 3,
|
balance_a: 3,
|
||||||
balance_b: 3,
|
balance_b: 3,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,8 +9,8 @@ use std::time::Duration;
|
|||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use ata_core::{compute_ata_seed, get_associated_token_account_id};
|
use ata_core::{compute_ata_seed, get_associated_token_account_id};
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
|
||||||
format_public_account_id, verify_commitment_is_in_state,
|
verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::program::Program;
|
use nssa::program::Program;
|
||||||
@ -68,10 +68,8 @@ async fn create_ata_initializes_holding_account() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::New {
|
Command::Token(TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TEST".to_owned(),
|
name: "TEST".to_owned(),
|
||||||
total_supply,
|
total_supply,
|
||||||
}),
|
}),
|
||||||
@ -85,8 +83,8 @@ async fn create_ata_initializes_holding_account() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_public_account_id(owner_account_id),
|
owner: public_mention(owner_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -132,10 +130,8 @@ async fn create_ata_is_idempotent() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::New {
|
Command::Token(TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TEST".to_owned(),
|
name: "TEST".to_owned(),
|
||||||
total_supply: 100,
|
total_supply: 100,
|
||||||
}),
|
}),
|
||||||
@ -149,8 +145,8 @@ async fn create_ata_is_idempotent() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_public_account_id(owner_account_id),
|
owner: public_mention(owner_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -162,8 +158,8 @@ async fn create_ata_is_idempotent() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_public_account_id(owner_account_id),
|
owner: public_mention(owner_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -212,10 +208,8 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::New {
|
Command::Token(TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TEST".to_owned(),
|
name: "TEST".to_owned(),
|
||||||
total_supply,
|
total_supply,
|
||||||
}),
|
}),
|
||||||
@ -240,16 +234,16 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_public_account_id(sender_account_id),
|
owner: public_mention(sender_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_public_account_id(recipient_account_id),
|
owner: public_mention(recipient_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -262,10 +256,8 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::Send {
|
Command::Token(TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id)),
|
from: public_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(public_mention(sender_ata_id)),
|
||||||
to: Some(format_public_account_id(sender_ata_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -282,9 +274,9 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Send {
|
Command::Ata(AtaSubcommand::Send {
|
||||||
from: format_public_account_id(sender_account_id),
|
from: public_mention(sender_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
to: recipient_ata_id.to_string(),
|
to: recipient_ata_id,
|
||||||
amount: transfer_amount,
|
amount: transfer_amount,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -320,8 +312,8 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Burn {
|
Command::Ata(AtaSubcommand::Burn {
|
||||||
holder: format_public_account_id(sender_account_id),
|
holder: public_mention(sender_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
amount: burn_amount,
|
amount: burn_amount,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -371,10 +363,8 @@ async fn create_ata_with_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::New {
|
Command::Token(TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TEST".to_owned(),
|
name: "TEST".to_owned(),
|
||||||
total_supply: 100,
|
total_supply: 100,
|
||||||
}),
|
}),
|
||||||
@ -388,8 +378,8 @@ async fn create_ata_with_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_private_account_id(owner_account_id),
|
owner: private_mention(owner_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -445,10 +435,8 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::New {
|
Command::Token(TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TEST".to_owned(),
|
name: "TEST".to_owned(),
|
||||||
total_supply,
|
total_supply,
|
||||||
}),
|
}),
|
||||||
@ -473,16 +461,16 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_private_account_id(sender_account_id),
|
owner: private_mention(sender_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_public_account_id(recipient_account_id),
|
owner: public_mention(recipient_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -495,10 +483,8 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::Send {
|
Command::Token(TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id)),
|
from: public_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(public_mention(sender_ata_id)),
|
||||||
to: Some(format_public_account_id(sender_ata_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -515,9 +501,9 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Send {
|
Command::Ata(AtaSubcommand::Send {
|
||||||
from: format_private_account_id(sender_account_id),
|
from: private_mention(sender_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
to: recipient_ata_id.to_string(),
|
to: recipient_ata_id,
|
||||||
amount: transfer_amount,
|
amount: transfer_amount,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -572,10 +558,8 @@ async fn burn_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::New {
|
Command::Token(TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "TEST".to_owned(),
|
name: "TEST".to_owned(),
|
||||||
total_supply,
|
total_supply,
|
||||||
}),
|
}),
|
||||||
@ -596,8 +580,8 @@ async fn burn_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Create {
|
Command::Ata(AtaSubcommand::Create {
|
||||||
owner: format_private_account_id(holder_account_id),
|
owner: private_mention(holder_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -610,10 +594,8 @@ async fn burn_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Token(TokenProgramAgnosticSubcommand::Send {
|
Command::Token(TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id)),
|
from: public_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(public_mention(holder_ata_id)),
|
||||||
to: Some(format_public_account_id(holder_ata_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -630,8 +612,8 @@ async fn burn_via_ata_private_owner() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Ata(AtaSubcommand::Burn {
|
Command::Ata(AtaSubcommand::Burn {
|
||||||
holder: format_private_account_id(holder_account_id),
|
holder: private_mention(holder_account_id),
|
||||||
token_definition: definition_account_id.to_string(),
|
token_definition: definition_account_id,
|
||||||
amount: burn_amount,
|
amount: burn_amount,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,18 +2,21 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx, private_mention,
|
||||||
format_private_account_id, format_public_account_id, verify_commitment_is_in_state,
|
public_mention, verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::{AccountId, program::Program};
|
use nssa::{AccountId, program::Program};
|
||||||
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::{
|
use wallet::{
|
||||||
Command, SubcommandReturnValue,
|
account::Label,
|
||||||
account::{AccountSubcommand, NewSubcommand},
|
cli::{
|
||||||
programs::native_token_transfer::AuthTransferSubcommand,
|
CliAccountMention, Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
programs::native_token_transfer::AuthTransferSubcommand,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -24,10 +27,8 @@ async fn private_transfer_to_owned_account() -> Result<()> {
|
|||||||
let to: AccountId = ctx.existing_private_accounts()[1];
|
let to: AccountId = ctx.existing_private_accounts()[1];
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
to: Some(private_mention(to)),
|
||||||
to: Some(format_private_account_id(to)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -66,10 +67,8 @@ async fn private_transfer_to_foreign_account() -> Result<()> {
|
|||||||
let to_vpk = Secp256k1Point::from_scalar(to_npk.0);
|
let to_vpk = Secp256k1Point::from_scalar(to_npk.0);
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
|
||||||
to_npk: Some(to_npk_string),
|
to_npk: Some(to_npk_string),
|
||||||
to_vpk: Some(hex::encode(to_vpk.0)),
|
to_vpk: Some(hex::encode(to_vpk.0)),
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -117,10 +116,8 @@ async fn deshielded_transfer_to_public_account() -> Result<()> {
|
|||||||
assert_eq!(from_acc.balance, 10000);
|
assert_eq!(from_acc.balance, 10000);
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
to: Some(public_mention(to)),
|
||||||
to: Some(format_public_account_id(to)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -173,22 +170,20 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get the keys for the newly created account
|
// Get the keys for the newly created account
|
||||||
let (to_keys, _, to_identifier) = ctx
|
let to = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.get_private_account(to_account_id)
|
.private_account(to_account_id)
|
||||||
.context("Failed to get private account")?;
|
.context("Failed to get private account")?;
|
||||||
|
|
||||||
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
to_npk: Some(hex::encode(to.key_chain.nullifier_public_key.0)),
|
||||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
to_vpk: Some(hex::encode(&to.key_chain.viewing_public_key.0)),
|
||||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
to_identifier: Some(to.identifier),
|
||||||
to_identifier: Some(to_identifier),
|
|
||||||
amount: 100,
|
amount: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -233,10 +228,8 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> {
|
|||||||
let to: AccountId = ctx.existing_private_accounts()[1];
|
let to: AccountId = ctx.existing_private_accounts()[1];
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(from)),
|
from: public_mention(from),
|
||||||
from_label: None,
|
to: Some(private_mention(to)),
|
||||||
to: Some(format_private_account_id(to)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -278,10 +271,8 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
|
|||||||
let from: AccountId = ctx.existing_public_accounts()[0];
|
let from: AccountId = ctx.existing_public_accounts()[0];
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(from)),
|
from: public_mention(from),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
|
||||||
to_npk: Some(to_npk_string),
|
to_npk: Some(to_npk_string),
|
||||||
to_vpk: Some(hex::encode(to_vpk.0)),
|
to_vpk: Some(hex::encode(to_vpk.0)),
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -341,22 +332,20 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get the newly created account's keys
|
// Get the newly created account's keys
|
||||||
let (to_keys, _, to_identifier) = ctx
|
let to = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.get_private_account(to_account_id)
|
.private_account(to_account_id)
|
||||||
.context("Failed to get private account")?;
|
.context("Failed to get private account")?;
|
||||||
|
|
||||||
// Send transfer using nullifier and viewing public keys
|
// Send transfer using nullifier and viewing public keys
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
to_npk: Some(hex::encode(to.key_chain.nullifier_public_key.0)),
|
||||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
to_vpk: Some(hex::encode(&to.key_chain.viewing_public_key.0)),
|
||||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
to_identifier: Some(to.identifier),
|
||||||
to_identifier: Some(to_identifier),
|
|
||||||
amount: 100,
|
amount: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -402,8 +391,7 @@ async fn initialize_private_account() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||||
account_id: Some(format_private_account_id(account_id)),
|
account_id: private_mention(account_id),
|
||||||
account_label: None,
|
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
@ -444,20 +432,17 @@ async fn private_transfer_using_from_label() -> Result<()> {
|
|||||||
let to: AccountId = ctx.existing_private_accounts()[1];
|
let to: AccountId = ctx.existing_private_accounts()[1];
|
||||||
|
|
||||||
// Assign a label to the sender account
|
// Assign a label to the sender account
|
||||||
let label = "private-sender-label".to_owned();
|
let label = Label::new("private-sender-label");
|
||||||
let command = Command::Account(AccountSubcommand::Label {
|
let command = Command::Account(AccountSubcommand::Label {
|
||||||
account_id: Some(format_private_account_id(from)),
|
account_id: private_mention(from),
|
||||||
account_label: None,
|
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
// Send using the label instead of account ID
|
// Send using the label instead of account ID
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: None,
|
from: CliAccountMention::Label(label),
|
||||||
from_label: Some(label),
|
to: Some(private_mention(to)),
|
||||||
to: Some(format_private_account_id(to)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -491,7 +476,7 @@ async fn initialize_private_account_using_label() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
// Create a new private account with a label
|
// Create a new private account with a label
|
||||||
let label = "init-private-label".to_owned();
|
let label = Label::new("init-private-label");
|
||||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
||||||
cci: None,
|
cci: None,
|
||||||
label: Some(label.clone()),
|
label: Some(label.clone()),
|
||||||
@ -503,8 +488,7 @@ async fn initialize_private_account_using_label() -> Result<()> {
|
|||||||
|
|
||||||
// Initialize using the label instead of account ID
|
// Initialize using the label instead of account ID
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||||
account_id: None,
|
account_id: label.into(),
|
||||||
account_label: Some(label),
|
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
@ -541,15 +525,12 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
|
|||||||
// Both transfers below will target this same node with distinct identifiers.
|
// Both transfers below will target this same node with distinct identifiers.
|
||||||
let chain_index = ctx.wallet_mut().create_private_accounts_key(None);
|
let chain_index = ctx.wallet_mut().create_private_accounts_key(None);
|
||||||
let (npk, vpk) = {
|
let (npk, vpk) = {
|
||||||
let node = ctx
|
let key_chain = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.private_key_tree
|
.private_account_key_chain_by_index(&chain_index)
|
||||||
.key_map
|
.expect("Failed to get private account key chain for chain index");
|
||||||
.get(&chain_index)
|
|
||||||
.expect("node was just inserted");
|
|
||||||
let key_chain = &node.value.0;
|
|
||||||
(
|
(
|
||||||
key_chain.nullifier_public_key,
|
key_chain.nullifier_public_key,
|
||||||
key_chain.viewing_public_key.clone(),
|
key_chain.viewing_public_key.clone(),
|
||||||
@ -568,10 +549,8 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(sender_0)),
|
from: public_mention(sender_0),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
|
||||||
to_npk: Some(npk_hex.clone()),
|
to_npk: Some(npk_hex.clone()),
|
||||||
to_vpk: Some(vpk_hex.clone()),
|
to_vpk: Some(vpk_hex.clone()),
|
||||||
to_identifier: Some(identifier_1),
|
to_identifier: Some(identifier_1),
|
||||||
@ -583,10 +562,8 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(
|
wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(sender_1)),
|
from: public_mention(sender_1),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
|
||||||
to_npk: Some(npk_hex),
|
to_npk: Some(npk_hex),
|
||||||
to_vpk: Some(vpk_hex),
|
to_vpk: Some(vpk_hex),
|
||||||
to_identifier: Some(identifier_2),
|
to_identifier: Some(identifier_2),
|
||||||
@ -620,21 +597,25 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
|
|||||||
assert_eq!(acc_2.balance, 200);
|
assert_eq!(acc_2.balance, 200);
|
||||||
|
|
||||||
// Both account ids must resolve to the same key node.
|
// Both account ids must resolve to the same key node.
|
||||||
let tree = &ctx.wallet().storage().user_data.private_key_tree;
|
let found_acc1 = ctx
|
||||||
let ci_1 = tree
|
.wallet()
|
||||||
.account_id_map
|
.storage()
|
||||||
.get(&account_id_1)
|
.key_chain()
|
||||||
.context("account_id_1 missing from private_key_tree.account_id_map")?;
|
.private_account(account_id_1)
|
||||||
let ci_2 = tree
|
.context("account_id_1 not found in key chain")?;
|
||||||
.account_id_map
|
let found_acc2 = ctx
|
||||||
.get(&account_id_2)
|
.wallet()
|
||||||
.context("account_id_2 missing from private_key_tree.account_id_map")?;
|
.storage()
|
||||||
|
.key_chain()
|
||||||
|
.private_account(account_id_2)
|
||||||
|
.context("account_id_2 not found in key chain")?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ci_1, ci_2,
|
found_acc1.chain_index, found_acc2.chain_index,
|
||||||
"identifiers 1 and 2 under the same NPK must share a single chain_index"
|
"identifiers 1 and 2 under the same NPK must share a single chain_index"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ci_1, &chain_index,
|
found_acc1.chain_index,
|
||||||
|
Some(chain_index),
|
||||||
"both accounts must resolve to the key node created at the start of the test"
|
"both accounts must resolve to the key node created at the start of the test"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id};
|
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::program::Program;
|
use nssa::program::Program;
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::{
|
use wallet::{
|
||||||
Command, SubcommandReturnValue,
|
account::Label,
|
||||||
account::{AccountSubcommand, NewSubcommand},
|
cli::{
|
||||||
programs::native_token_transfer::AuthTransferSubcommand,
|
CliAccountMention, Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
programs::native_token_transfer::AuthTransferSubcommand,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -17,10 +20,8 @@ async fn successful_transfer_to_existing_account() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(ctx.existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -67,8 +68,9 @@ pub async fn successful_transfer_to_new_account() -> Result<()> {
|
|||||||
let new_persistent_account_id = ctx
|
let new_persistent_account_id = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.account_ids()
|
.public_account_ids()
|
||||||
|
.map(|(account_id, _)| account_id)
|
||||||
.find(|acc_id| {
|
.find(|acc_id| {
|
||||||
*acc_id != ctx.existing_public_accounts()[0]
|
*acc_id != ctx.existing_public_accounts()[0]
|
||||||
&& *acc_id != ctx.existing_public_accounts()[1]
|
&& *acc_id != ctx.existing_public_accounts()[1]
|
||||||
@ -76,10 +78,8 @@ pub async fn successful_transfer_to_new_account() -> Result<()> {
|
|||||||
.expect("Failed to find newly created account in the wallet storage");
|
.expect("Failed to find newly created account in the wallet storage");
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(new_persistent_account_id)),
|
||||||
to: Some(format_public_account_id(new_persistent_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -115,10 +115,8 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(ctx.existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -156,10 +154,8 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
|
|||||||
|
|
||||||
// First transfer
|
// First transfer
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(ctx.existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -191,10 +187,8 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
|
|||||||
|
|
||||||
// Second transfer
|
// Second transfer
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(ctx.existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -241,8 +235,7 @@ async fn initialize_public_account() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||||
account_id: Some(format_public_account_id(account_id)),
|
account_id: public_mention(account_id),
|
||||||
account_label: None,
|
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
@ -267,20 +260,17 @@ async fn successful_transfer_using_from_label() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
// Assign a label to the sender account
|
// Assign a label to the sender account
|
||||||
let label = "sender-label".to_owned();
|
let label = Label::new("sender-label");
|
||||||
let command = Command::Account(AccountSubcommand::Label {
|
let command = Command::Account(AccountSubcommand::Label {
|
||||||
account_id: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
account_id: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
account_label: None,
|
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
// Send using the label instead of account ID
|
// Send using the label instead of account ID
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: None,
|
from: CliAccountMention::Label(label),
|
||||||
from_label: Some(label),
|
to: Some(public_mention(ctx.existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -315,20 +305,17 @@ async fn successful_transfer_using_to_label() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
// Assign a label to the receiver account
|
// Assign a label to the receiver account
|
||||||
let label = "receiver-label".to_owned();
|
let label = Label::new("receiver-label");
|
||||||
let command = Command::Account(AccountSubcommand::Label {
|
let command = Command::Account(AccountSubcommand::Label {
|
||||||
account_id: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
account_id: public_mention(ctx.existing_public_accounts()[1]),
|
||||||
account_label: None,
|
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
// Send using the label for the recipient
|
// Send using the label for the recipient
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(CliAccountMention::Label(label)),
|
||||||
to: None,
|
|
||||||
to_label: Some(label),
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
|
|||||||
@ -9,54 +9,61 @@ use std::time::Duration;
|
|||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use indexer_service_rpc::RpcClient as _;
|
use indexer_service_rpc::RpcClient as _;
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
|
||||||
format_public_account_id, verify_commitment_is_in_state,
|
verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::AccountId;
|
use nssa::AccountId;
|
||||||
use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcommand};
|
use wallet::{
|
||||||
|
account::Label,
|
||||||
|
cli::{CliAccountMention, Command, programs::native_token_transfer::AuthTransferSubcommand},
|
||||||
|
};
|
||||||
|
|
||||||
/// Maximum time to wait for the indexer to catch up to the sequencer.
|
/// Maximum time to wait for the indexer to catch up to the sequencer.
|
||||||
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
|
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
|
||||||
|
|
||||||
/// Poll the indexer until its last finalized block id reaches the sequencer's
|
/// Poll the indexer until its last finalized block id reaches the sequencer's
|
||||||
/// current last block id (and at least the genesis block has been advanced past),
|
/// current last block id or until [`L2_TO_L1_TIMEOUT_MILLIS`] elapses.
|
||||||
/// or until [`L2_TO_L1_TIMEOUT_MILLIS`] elapses. Returns the last indexer block
|
/// Returns the last indexer block id observed.
|
||||||
/// id observed.
|
async fn wait_for_indexer_to_catch_up(ctx: &TestContext) -> Result<u64> {
|
||||||
async fn wait_for_indexer_to_catch_up(ctx: &TestContext) -> u64 {
|
|
||||||
let timeout = Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS);
|
let timeout = Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS);
|
||||||
|
let block_id_to_catch_up =
|
||||||
|
sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client()).await?;
|
||||||
let mut last_ind: u64 = 1;
|
let mut last_ind: u64 = 1;
|
||||||
let inner = async {
|
let inner = async {
|
||||||
loop {
|
loop {
|
||||||
let seq = sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client())
|
|
||||||
.await
|
|
||||||
.unwrap_or(0);
|
|
||||||
let ind = ctx
|
let ind = ctx
|
||||||
.indexer_client()
|
.indexer_client()
|
||||||
.get_last_finalized_block_id()
|
.get_last_finalized_block_id()
|
||||||
.await
|
.await?
|
||||||
.unwrap_or(1);
|
.unwrap_or(0);
|
||||||
last_ind = ind;
|
last_ind = ind;
|
||||||
if ind >= seq && ind > 1 {
|
if ind >= block_id_to_catch_up {
|
||||||
info!("Indexer caught up: seq={seq}, ind={ind}");
|
let last_seq =
|
||||||
return ind;
|
sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client())
|
||||||
|
.await?;
|
||||||
|
info!(
|
||||||
|
"Indexer caught up. Indexer last block id: {ind}. Current sequencer last block id: {last_seq}"
|
||||||
|
);
|
||||||
|
return Ok(ind);
|
||||||
}
|
}
|
||||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
tokio::time::timeout(timeout, inner)
|
tokio::time::timeout(timeout, inner)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| {
|
.with_context(|| {
|
||||||
info!("Indexer catch-up timed out: ind={last_ind}");
|
format!(
|
||||||
last_ind
|
"Indexer failed to catch up within {L2_TO_L1_TIMEOUT_MILLIS} milliseconds. Last indexer block id observed: {last_ind}, but needed to catch up to at least {block_id_to_catch_up}"
|
||||||
})
|
)
|
||||||
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn indexer_test_run() -> Result<()> {
|
async fn indexer_test_run() -> Result<()> {
|
||||||
let ctx = TestContext::new().await?;
|
let ctx = TestContext::new().await?;
|
||||||
|
|
||||||
let last_block_indexer = wait_for_indexer_to_catch_up(&ctx).await;
|
let last_block_indexer = wait_for_indexer_to_catch_up(&ctx).await?;
|
||||||
|
|
||||||
let last_block_seq =
|
let last_block_seq =
|
||||||
sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client()).await?;
|
sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client()).await?;
|
||||||
@ -64,7 +71,7 @@ async fn indexer_test_run() -> Result<()> {
|
|||||||
info!("Last block on seq now is {last_block_seq}");
|
info!("Last block on seq now is {last_block_seq}");
|
||||||
info!("Last block on ind now is {last_block_indexer}");
|
info!("Last block on ind now is {last_block_indexer}");
|
||||||
|
|
||||||
assert!(last_block_indexer > 1);
|
assert!(last_block_indexer > 0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -74,11 +81,11 @@ async fn indexer_block_batching() -> Result<()> {
|
|||||||
let ctx = TestContext::new().await?;
|
let ctx = TestContext::new().await?;
|
||||||
|
|
||||||
info!("Waiting for indexer to parse blocks");
|
info!("Waiting for indexer to parse blocks");
|
||||||
let last_block_indexer = wait_for_indexer_to_catch_up(&ctx).await;
|
let last_block_indexer = wait_for_indexer_to_catch_up(&ctx).await?;
|
||||||
|
|
||||||
info!("Last block on ind now is {last_block_indexer}");
|
info!("Last block on ind now is {last_block_indexer}");
|
||||||
|
|
||||||
assert!(last_block_indexer > 1);
|
assert!(last_block_indexer > 0);
|
||||||
|
|
||||||
// Getting wide batch to fit all blocks (from latest backwards)
|
// Getting wide batch to fit all blocks (from latest backwards)
|
||||||
let mut block_batch = ctx.indexer_client().get_blocks(None, 100).await.unwrap();
|
let mut block_batch = ctx.indexer_client().get_blocks(None, 100).await.unwrap();
|
||||||
@ -105,10 +112,8 @@ async fn indexer_state_consistency() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(ctx.existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -142,10 +147,8 @@ async fn indexer_state_consistency() -> Result<()> {
|
|||||||
let to: AccountId = ctx.existing_private_accounts()[1];
|
let to: AccountId = ctx.existing_private_accounts()[1];
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
to: Some(private_mention(to)),
|
||||||
to: Some(format_private_account_id(to)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -172,7 +175,7 @@ async fn indexer_state_consistency() -> Result<()> {
|
|||||||
info!("Successfully transferred privately to owned account");
|
info!("Successfully transferred privately to owned account");
|
||||||
|
|
||||||
info!("Waiting for indexer to parse blocks");
|
info!("Waiting for indexer to parse blocks");
|
||||||
wait_for_indexer_to_catch_up(&ctx).await;
|
wait_for_indexer_to_catch_up(&ctx).await?;
|
||||||
|
|
||||||
let acc1_ind_state = ctx
|
let acc1_ind_state = ctx
|
||||||
.indexer_client()
|
.indexer_client()
|
||||||
@ -210,29 +213,25 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
// Assign labels to both accounts
|
// Assign labels to both accounts
|
||||||
let from_label = "idx-sender-label".to_owned();
|
let from_label = Label::new("idx-sender-label");
|
||||||
let to_label_str = "idx-receiver-label".to_owned();
|
let to_label = Label::new("idx-receiver-label");
|
||||||
|
|
||||||
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
||||||
account_id: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
account_id: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
account_label: None,
|
|
||||||
label: from_label.clone(),
|
label: from_label.clone(),
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd).await?;
|
||||||
|
|
||||||
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
||||||
account_id: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
account_id: public_mention(ctx.existing_public_accounts()[1]),
|
||||||
account_label: None,
|
label: to_label.clone(),
|
||||||
label: to_label_str.clone(),
|
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd).await?;
|
||||||
|
|
||||||
// Send using labels instead of account IDs
|
// Send using labels instead of account IDs
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: None,
|
from: CliAccountMention::Label(from_label),
|
||||||
from_label: Some(from_label),
|
to: Some(CliAccountMention::Label(to_label)),
|
||||||
to: None,
|
|
||||||
to_label: Some(to_label_str),
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -259,7 +258,7 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
|
|||||||
assert_eq!(acc_2_balance, 20100);
|
assert_eq!(acc_2_balance, 20100);
|
||||||
|
|
||||||
info!("Waiting for indexer to parse blocks");
|
info!("Waiting for indexer to parse blocks");
|
||||||
wait_for_indexer_to_catch_up(&ctx).await;
|
wait_for_indexer_to_catch_up(&ctx).await?;
|
||||||
|
|
||||||
let acc1_ind_state = ctx
|
let acc1_ind_state = ctx
|
||||||
.indexer_client()
|
.indexer_client()
|
||||||
|
|||||||
@ -5,82 +5,145 @@
|
|||||||
reason = "We don't care about these in tests"
|
reason = "We don't care about these in tests"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ffi::{CString, c_char},
|
||||||
|
fs::File,
|
||||||
|
io::Write as _,
|
||||||
|
net::SocketAddr,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use indexer_ffi::{
|
use indexer_ffi::{
|
||||||
IndexerServiceFFI, OperationStatus,
|
IndexerServiceFFI, OperationStatus, Runtime,
|
||||||
api::{
|
api::{
|
||||||
PointerResult,
|
PointerResult,
|
||||||
|
lifecycle::InitializedIndexerServiceFFIResult,
|
||||||
types::{FfiAccountId, FfiOption, FfiVec, account::FfiAccount, block::FfiBlock},
|
types::{FfiAccountId, FfiOption, FfiVec, account::FfiAccount, block::FfiBlock},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id,
|
BlockingTestContext, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention,
|
||||||
test_context_ffi::BlockingTestContextFFI, verify_commitment_is_in_state,
|
public_mention, verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::{debug, info};
|
||||||
use nssa::AccountId;
|
use nssa::AccountId;
|
||||||
use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcommand};
|
use tempfile::TempDir;
|
||||||
|
use wallet::{
|
||||||
|
account::Label,
|
||||||
|
cli::{Command, programs::native_token_transfer::AuthTransferSubcommand},
|
||||||
|
};
|
||||||
|
|
||||||
/// Maximum time to wait for the indexer to catch up to the sequencer.
|
/// Maximum time to wait for the indexer to catch up to the sequencer.
|
||||||
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
|
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
|
||||||
|
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
unsafe fn query_last_block(
|
unsafe fn query_last_block(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
) -> PointerResult<u64, OperationStatus>;
|
) -> PointerResult<u64, OperationStatus>;
|
||||||
|
|
||||||
unsafe fn query_block_vec(
|
unsafe fn query_block_vec(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
before: FfiOption<u64>,
|
before: FfiOption<u64>,
|
||||||
limit: u64,
|
limit: u64,
|
||||||
) -> PointerResult<FfiVec<FfiBlock>, OperationStatus>;
|
) -> PointerResult<FfiVec<FfiBlock>, OperationStatus>;
|
||||||
|
|
||||||
unsafe fn query_account(
|
unsafe fn query_account(
|
||||||
|
runtime: *const Runtime,
|
||||||
indexer: *const IndexerServiceFFI,
|
indexer: *const IndexerServiceFFI,
|
||||||
account_id: FfiAccountId,
|
account_id: FfiAccountId,
|
||||||
) -> PointerResult<FfiAccount, OperationStatus>;
|
) -> PointerResult<FfiAccount, OperationStatus>;
|
||||||
|
|
||||||
|
unsafe fn start_indexer(
|
||||||
|
runtime: *const Runtime,
|
||||||
|
config_path: *const c_char,
|
||||||
|
port: u16,
|
||||||
|
) -> InitializedIndexerServiceFFIResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_indexer_ffi(
|
||||||
|
runtime: &Runtime,
|
||||||
|
bedrock_addr: SocketAddr,
|
||||||
|
) -> Result<(IndexerServiceFFI, TempDir)> {
|
||||||
|
let temp_indexer_dir =
|
||||||
|
tempfile::tempdir().context("Failed to create temp dir for indexer home")?;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Using temp indexer home at {}",
|
||||||
|
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 config_json = serde_json::to_vec(&indexer_config)?;
|
||||||
|
let config_path = temp_indexer_dir.path().join("indexer_config.json");
|
||||||
|
let mut file = File::create(config_path.as_path())?;
|
||||||
|
file.write_all(&config_json)?;
|
||||||
|
file.flush()?;
|
||||||
|
|
||||||
|
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) };
|
||||||
|
|
||||||
|
if res.error.is_error() {
|
||||||
|
anyhow::bail!("Indexer FFI error {:?}", res.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
// SAFETY: lib function ensures validity of value.
|
||||||
|
unsafe { std::ptr::read(res.value) },
|
||||||
|
temp_indexer_dir,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare setup for tests.
|
||||||
|
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())?;
|
||||||
|
|
||||||
|
Ok((ctx, indexer_ffi, indexer_dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn indexer_test_run_ffi() -> Result<()> {
|
fn indexer_test_run_ffi() -> Result<()> {
|
||||||
let blocking_ctx = BlockingTestContextFFI::new()?;
|
let (ctx, indexer_ffi, _indexer_dir) = setup()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime();
|
|
||||||
|
|
||||||
// RUN OBSERVATION
|
// RUN OBSERVATION
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS));
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let last_block_indexer = blocking_ctx.ctx().get_last_block_indexer(runtime_wrapped)?;
|
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
|
||||||
let last_block_indexer_ffi_res = unsafe { query_last_block(blocking_ctx.indexer_ffi()) };
|
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
|
||||||
|
let last_block_indexer_ffi_res =
|
||||||
|
unsafe { query_last_block(&raw const runtime, &raw const indexer_ffi) };
|
||||||
|
|
||||||
assert!(last_block_indexer_ffi_res.error.is_ok());
|
assert!(last_block_indexer_ffi_res.error.is_ok());
|
||||||
|
|
||||||
let last_block_indexer_ffi = unsafe { *last_block_indexer_ffi_res.value };
|
let last_block_indexer_ffi = unsafe { *last_block_indexer_ffi_res.value };
|
||||||
|
|
||||||
info!("Last block on ind now is {last_block_indexer}");
|
|
||||||
info!("Last block on ind ffi now is {last_block_indexer_ffi}");
|
info!("Last block on ind ffi now is {last_block_indexer_ffi}");
|
||||||
|
|
||||||
assert!(last_block_indexer > 1);
|
|
||||||
assert!(last_block_indexer_ffi > 1);
|
assert!(last_block_indexer_ffi > 1);
|
||||||
|
|
||||||
assert_eq!(last_block_indexer, last_block_indexer_ffi);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn indexer_ffi_block_batching() -> Result<()> {
|
fn indexer_ffi_block_batching() -> Result<()> {
|
||||||
let blocking_ctx = BlockingTestContextFFI::new()?;
|
let (ctx, indexer_ffi, _indexer_dir) = setup()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime();
|
|
||||||
|
|
||||||
// WAIT
|
// WAIT
|
||||||
info!("Waiting for indexer to parse blocks");
|
info!("Waiting for indexer to parse blocks");
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS));
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let last_block_indexer_ffi_res = unsafe { query_last_block(blocking_ctx.indexer_ffi()) };
|
// 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 { query_last_block(&raw const runtime, &raw const indexer_ffi) };
|
||||||
|
|
||||||
assert!(last_block_indexer_ffi_res.error.is_ok());
|
assert!(last_block_indexer_ffi_res.error.is_ok());
|
||||||
|
|
||||||
@ -93,8 +156,14 @@ fn indexer_ffi_block_batching() -> Result<()> {
|
|||||||
let before_ffi = FfiOption::<u64>::from_none();
|
let before_ffi = FfiOption::<u64>::from_none();
|
||||||
let limit = 100;
|
let limit = 100;
|
||||||
|
|
||||||
let block_batch_ffi_res =
|
let block_batch_ffi_res = unsafe {
|
||||||
unsafe { query_block_vec(blocking_ctx.indexer_ffi(), before_ffi, limit) };
|
query_block_vec(
|
||||||
|
&raw const runtime,
|
||||||
|
&raw const indexer_ffi,
|
||||||
|
before_ffi,
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
assert!(block_batch_ffi_res.error.is_ok());
|
assert!(block_batch_ffi_res.error.is_ok());
|
||||||
|
|
||||||
@ -117,43 +186,37 @@ fn indexer_ffi_block_batching() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn indexer_ffi_state_consistency() -> Result<()> {
|
fn indexer_ffi_state_consistency() -> Result<()> {
|
||||||
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
let (mut ctx, indexer_ffi, _indexer_dir) = setup()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime_clone();
|
|
||||||
let indexer_ffi = blocking_ctx.indexer_ffi();
|
|
||||||
let ctx = blocking_ctx.ctx_mut();
|
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
from: public_mention(ctx.ctx().existing_public_accounts()[0]),
|
||||||
from_label: None,
|
to: Some(public_mention(ctx.ctx().existing_public_accounts()[1])),
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
ctx.block_on_mut(|ctx| wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
||||||
|
|
||||||
info!("Waiting for next block creation");
|
info!("Waiting for next block creation");
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_secs(
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
));
|
||||||
))
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
|
|
||||||
info!("Checking correct balance move");
|
info!("Checking correct balance move");
|
||||||
let acc_1_balance =
|
let acc_1_balance = ctx.block_on(|ctx| {
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance(
|
sequencer_service_rpc::RpcClient::get_account_balance(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[0],
|
ctx.existing_public_accounts()[0],
|
||||||
))?;
|
)
|
||||||
let acc_2_balance =
|
})?;
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance(
|
let acc_2_balance = ctx.block_on(|ctx| {
|
||||||
|
sequencer_service_rpc::RpcClient::get_account_balance(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[1],
|
ctx.existing_public_accounts()[1],
|
||||||
))?;
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
info!("Balance of sender: {acc_1_balance:#?}");
|
info!("Balance of sender: {acc_1_balance:#?}");
|
||||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||||
@ -161,68 +224,71 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
|||||||
assert_eq!(acc_1_balance, 9900);
|
assert_eq!(acc_1_balance, 9900);
|
||||||
assert_eq!(acc_2_balance, 20100);
|
assert_eq!(acc_2_balance, 20100);
|
||||||
|
|
||||||
let from: AccountId = ctx.existing_private_accounts()[0];
|
let from: AccountId = ctx.ctx().existing_private_accounts()[0];
|
||||||
let to: AccountId = ctx.existing_private_accounts()[1];
|
let to: AccountId = ctx.ctx().existing_private_accounts()[1];
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
to: Some(private_mention(to)),
|
||||||
to: Some(format_private_account_id(to)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
ctx.block_on_mut(|ctx| wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
||||||
|
|
||||||
info!("Waiting for next block creation");
|
info!("Waiting for next block creation");
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_secs(
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
));
|
||||||
))
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let new_commitment1 = ctx
|
let new_commitment1 = ctx
|
||||||
|
.ctx()
|
||||||
.wallet()
|
.wallet()
|
||||||
.get_private_account_commitment(from)
|
.get_private_account_commitment(from)
|
||||||
.context("Failed to get private account commitment for sender")?;
|
.context("Failed to get private account commitment for sender")?;
|
||||||
let commitment_check1 = runtime_wrapped.block_on(verify_commitment_is_in_state(
|
let commitment_check1 =
|
||||||
new_commitment1,
|
ctx.block_on(|ctx| verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()));
|
||||||
ctx.sequencer_client(),
|
|
||||||
));
|
|
||||||
assert!(commitment_check1);
|
assert!(commitment_check1);
|
||||||
|
|
||||||
let new_commitment2 = ctx
|
let new_commitment2 = ctx
|
||||||
|
.ctx()
|
||||||
.wallet()
|
.wallet()
|
||||||
.get_private_account_commitment(to)
|
.get_private_account_commitment(to)
|
||||||
.context("Failed to get private account commitment for receiver")?;
|
.context("Failed to get private account commitment for receiver")?;
|
||||||
let commitment_check2 = runtime_wrapped.block_on(verify_commitment_is_in_state(
|
let commitment_check2 =
|
||||||
new_commitment2,
|
ctx.block_on(|ctx| verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()));
|
||||||
ctx.sequencer_client(),
|
|
||||||
));
|
|
||||||
assert!(commitment_check2);
|
assert!(commitment_check2);
|
||||||
|
|
||||||
info!("Successfully transferred privately to owned account");
|
info!("Successfully transferred privately to owned account");
|
||||||
|
|
||||||
// WAIT
|
// WAIT
|
||||||
info!("Waiting for indexer to parse blocks");
|
info!("Waiting for indexer to parse blocks");
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS));
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let acc1_ind_state_ffi =
|
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
|
||||||
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[0]).into()) };
|
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
|
||||||
|
let acc1_ind_state_ffi = unsafe {
|
||||||
|
query_account(
|
||||||
|
&raw const runtime,
|
||||||
|
&raw const indexer_ffi,
|
||||||
|
(&ctx.ctx().existing_public_accounts()[0]).into(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
assert!(acc1_ind_state_ffi.error.is_ok());
|
assert!(acc1_ind_state_ffi.error.is_ok());
|
||||||
|
|
||||||
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
||||||
let acc1_ind_state: indexer_service_protocol::Account = acc1_ind_state_pre.into();
|
let acc1_ind_state: indexer_service_protocol::Account = acc1_ind_state_pre.into();
|
||||||
|
|
||||||
let acc2_ind_state_ffi =
|
let acc2_ind_state_ffi = unsafe {
|
||||||
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[1]).into()) };
|
query_account(
|
||||||
|
&raw const runtime,
|
||||||
|
&raw const indexer_ffi,
|
||||||
|
(&ctx.ctx().existing_public_accounts()[1]).into(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
assert!(acc2_ind_state_ffi.error.is_ok());
|
assert!(acc2_ind_state_ffi.error.is_ok());
|
||||||
|
|
||||||
@ -230,16 +296,18 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
|||||||
let acc2_ind_state: indexer_service_protocol::Account = acc2_ind_state_pre.into();
|
let acc2_ind_state: indexer_service_protocol::Account = acc2_ind_state_pre.into();
|
||||||
|
|
||||||
info!("Checking correct state transition");
|
info!("Checking correct state transition");
|
||||||
let acc1_seq_state =
|
let acc1_seq_state = ctx.block_on(|ctx| {
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account(
|
sequencer_service_rpc::RpcClient::get_account(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[0],
|
ctx.existing_public_accounts()[0],
|
||||||
))?;
|
)
|
||||||
let acc2_seq_state =
|
})?;
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account(
|
let acc2_seq_state = ctx.block_on(|ctx| {
|
||||||
|
sequencer_service_rpc::RpcClient::get_account(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[1],
|
ctx.existing_public_accounts()[1],
|
||||||
))?;
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
assert_eq!(acc1_ind_state, acc1_seq_state.into());
|
assert_eq!(acc1_ind_state, acc1_seq_state.into());
|
||||||
assert_eq!(acc2_ind_state, acc2_seq_state.into());
|
assert_eq!(acc2_ind_state, acc2_seq_state.into());
|
||||||
@ -251,83 +319,81 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
||||||
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
let (mut ctx, indexer_ffi, _indexer_dir) = setup()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime_clone();
|
|
||||||
let indexer_ffi = blocking_ctx.indexer_ffi();
|
|
||||||
let ctx = blocking_ctx.ctx_mut();
|
|
||||||
|
|
||||||
// Assign labels to both accounts
|
// Assign labels to both accounts
|
||||||
let from_label = "idx-sender-label".to_owned();
|
let from_label = Label::new("idx-sender-label");
|
||||||
let to_label_str = "idx-receiver-label".to_owned();
|
let to_label = Label::new("idx-receiver-label");
|
||||||
|
|
||||||
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
||||||
account_id: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
account_id: public_mention(ctx.ctx().existing_public_accounts()[0]),
|
||||||
account_label: None,
|
|
||||||
label: from_label.clone(),
|
label: from_label.clone(),
|
||||||
});
|
});
|
||||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?;
|
ctx.block_on_mut(|ctx| wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?;
|
||||||
|
|
||||||
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label {
|
||||||
account_id: Some(format_public_account_id(ctx.existing_public_accounts()[1])),
|
account_id: public_mention(ctx.ctx().existing_public_accounts()[1]),
|
||||||
account_label: None,
|
label: to_label.clone(),
|
||||||
label: to_label_str.clone(),
|
|
||||||
});
|
});
|
||||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?;
|
ctx.block_on_mut(|ctx| wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?;
|
||||||
|
|
||||||
// Send using labels instead of account IDs
|
// Send using labels instead of account IDs
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: None,
|
from: from_label.into(),
|
||||||
from_label: Some(from_label),
|
to: Some(to_label.into()),
|
||||||
to: None,
|
|
||||||
to_label: Some(to_label_str),
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
ctx.block_on_mut(|ctx| wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
||||||
|
|
||||||
info!("Waiting for next block creation");
|
info!("Waiting for next block creation");
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_secs(
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
));
|
||||||
))
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let acc_1_balance =
|
let acc_1_balance = ctx.block_on(|ctx| {
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance(
|
sequencer_service_rpc::RpcClient::get_account_balance(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[0],
|
ctx.existing_public_accounts()[0],
|
||||||
))?;
|
)
|
||||||
let acc_2_balance =
|
})?;
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance(
|
let acc_2_balance = ctx.block_on(|ctx| {
|
||||||
|
sequencer_service_rpc::RpcClient::get_account_balance(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[1],
|
ctx.existing_public_accounts()[1],
|
||||||
))?;
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
assert_eq!(acc_1_balance, 9900);
|
assert_eq!(acc_1_balance, 9900);
|
||||||
assert_eq!(acc_2_balance, 20100);
|
assert_eq!(acc_2_balance, 20100);
|
||||||
|
|
||||||
info!("Waiting for indexer to parse blocks");
|
info!("Waiting for indexer to parse blocks");
|
||||||
runtime_wrapped.block_on(async {
|
std::thread::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS));
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let acc1_ind_state_ffi =
|
// Safety: ctx runtime is valid for the lifetime of the returned Runtime
|
||||||
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[0]).into()) };
|
let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) };
|
||||||
|
let acc1_ind_state_ffi = unsafe {
|
||||||
|
query_account(
|
||||||
|
&raw const runtime,
|
||||||
|
&raw const indexer_ffi,
|
||||||
|
(&ctx.ctx().existing_public_accounts()[0]).into(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
assert!(acc1_ind_state_ffi.error.is_ok());
|
assert!(acc1_ind_state_ffi.error.is_ok());
|
||||||
|
|
||||||
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
||||||
let acc1_ind_state: indexer_service_protocol::Account = acc1_ind_state_pre.into();
|
let acc1_ind_state: indexer_service_protocol::Account = acc1_ind_state_pre.into();
|
||||||
|
|
||||||
let acc1_seq_state =
|
let acc1_seq_state = ctx.block_on(|ctx| {
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account(
|
sequencer_service_rpc::RpcClient::get_account(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
ctx.existing_public_accounts()[0],
|
ctx.existing_public_accounts()[0],
|
||||||
))?;
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
assert_eq!(acc1_ind_state, acc1_seq_state.into());
|
assert_eq!(acc1_ind_state, acc1_seq_state.into());
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,8 @@ use std::{str::FromStr as _, time::Duration};
|
|||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx, private_mention,
|
||||||
format_private_account_id, format_public_account_id, verify_commitment_is_in_state,
|
public_mention, verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||||
use log::info;
|
use log::info;
|
||||||
@ -59,22 +59,20 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get the keys for the newly created account
|
// Get the keys for the newly created account
|
||||||
let (to_keys, _, to_identifier) = ctx
|
let to_account = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.get_private_account(to_account_id)
|
.private_account(to_account_id)
|
||||||
.context("Failed to get private account")?;
|
.context("Failed to get private account")?;
|
||||||
|
|
||||||
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
|
||||||
to: None,
|
to: None,
|
||||||
to_label: None,
|
to_npk: Some(hex::encode(to_account.key_chain.nullifier_public_key.0)),
|
||||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
to_vpk: Some(hex::encode(&to_account.key_chain.viewing_public_key.0)),
|
||||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
to_identifier: Some(to_account.identifier),
|
||||||
to_identifier: Some(to_identifier),
|
|
||||||
amount: 100,
|
amount: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -145,10 +143,8 @@ async fn restore_keys_from_seed() -> Result<()> {
|
|||||||
|
|
||||||
// Send to first private account
|
// Send to first private account
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
to: Some(private_mention(to_account_id1)),
|
||||||
to: Some(format_private_account_id(to_account_id1)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -158,10 +154,8 @@ async fn restore_keys_from_seed() -> Result<()> {
|
|||||||
|
|
||||||
// Send to second private account
|
// Send to second private account
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(from)),
|
from: private_mention(from),
|
||||||
from_label: None,
|
to: Some(private_mention(to_account_id2)),
|
||||||
to: Some(format_private_account_id(to_account_id2)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -199,10 +193,8 @@ async fn restore_keys_from_seed() -> Result<()> {
|
|||||||
|
|
||||||
// Send to first public account
|
// Send to first public account
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(from)),
|
from: public_mention(from),
|
||||||
from_label: None,
|
to: Some(public_mention(to_account_id3)),
|
||||||
to: Some(format_public_account_id(to_account_id3)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -212,10 +204,8 @@ async fn restore_keys_from_seed() -> Result<()> {
|
|||||||
|
|
||||||
// Send to second public account
|
// Send to second public account
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(from)),
|
from: public_mention(from),
|
||||||
from_label: None,
|
to: Some(public_mention(to_account_id4)),
|
||||||
to: Some(format_public_account_id(to_account_id4)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -232,56 +222,50 @@ async fn restore_keys_from_seed() -> Result<()> {
|
|||||||
let acc1 = ctx
|
let acc1 = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.private_key_tree
|
.private_account(to_account_id1)
|
||||||
.get_node(to_account_id1)
|
|
||||||
.expect("Acc 1 should be restored");
|
.expect("Acc 1 should be restored");
|
||||||
|
|
||||||
let acc2 = ctx
|
let acc2 = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.private_key_tree
|
.private_account(to_account_id2)
|
||||||
.get_node(to_account_id2)
|
|
||||||
.expect("Acc 2 should be restored");
|
.expect("Acc 2 should be restored");
|
||||||
|
|
||||||
// Verify restored public accounts
|
// Verify restored public accounts
|
||||||
let _acc3 = ctx
|
let _acc3 = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.public_key_tree
|
.pub_account_signing_key(to_account_id3)
|
||||||
.get_node(to_account_id3)
|
|
||||||
.expect("Acc 3 should be restored");
|
.expect("Acc 3 should be restored");
|
||||||
|
|
||||||
let _acc4 = ctx
|
let _acc4 = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.public_key_tree
|
.pub_account_signing_key(to_account_id4)
|
||||||
.get_node(to_account_id4)
|
|
||||||
.expect("Acc 4 should be restored");
|
.expect("Acc 4 should be restored");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
acc1.value.1[0].1.program_owner,
|
acc1.account.program_owner,
|
||||||
Program::authenticated_transfer_program().id()
|
Program::authenticated_transfer_program().id()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
acc2.value.1[0].1.program_owner,
|
acc2.account.program_owner,
|
||||||
Program::authenticated_transfer_program().id()
|
Program::authenticated_transfer_program().id()
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(acc1.value.1[0].1.balance, 100);
|
assert_eq!(acc1.account.balance, 100);
|
||||||
assert_eq!(acc2.value.1[0].1.balance, 101);
|
assert_eq!(acc2.account.balance, 101);
|
||||||
|
|
||||||
info!("Tree checks passed, testing restored accounts can transact");
|
info!("Tree checks passed, testing restored accounts can transact");
|
||||||
|
|
||||||
// Test that restored accounts can send transactions
|
// Test that restored accounts can send transactions
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_private_account_id(to_account_id1)),
|
from: private_mention(to_account_id1),
|
||||||
from_label: None,
|
to: Some(private_mention(to_account_id2)),
|
||||||
to: Some(format_private_account_id(to_account_id2)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -290,10 +274,8 @@ async fn restore_keys_from_seed() -> Result<()> {
|
|||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(to_account_id3)),
|
from: public_mention(to_account_id3),
|
||||||
from_label: None,
|
to: Some(public_mention(to_account_id4)),
|
||||||
to: Some(format_public_account_id(to_account_id4)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -9,8 +9,8 @@ use std::time::Duration;
|
|||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use common::PINATA_BASE58;
|
use common::PINATA_BASE58;
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
|
||||||
format_public_account_id, verify_commitment_is_in_state,
|
verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
@ -42,8 +42,6 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()>
|
|||||||
anyhow::bail!("Expected RegisterAccount return value");
|
anyhow::bail!("Expected RegisterAccount return value");
|
||||||
};
|
};
|
||||||
|
|
||||||
let winner_account_id_formatted = format_public_account_id(winner_account_id);
|
|
||||||
|
|
||||||
let pinata_balance_pre = ctx
|
let pinata_balance_pre = ctx
|
||||||
.sequencer_client()
|
.sequencer_client()
|
||||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||||
@ -52,8 +50,7 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()>
|
|||||||
let claim_result = wallet::cli::execute_subcommand(
|
let claim_result = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
||||||
to: Some(winner_account_id_formatted),
|
to: public_mention(winner_account_id),
|
||||||
to_label: None,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -97,8 +94,6 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()
|
|||||||
anyhow::bail!("Expected RegisterAccount return value");
|
anyhow::bail!("Expected RegisterAccount return value");
|
||||||
};
|
};
|
||||||
|
|
||||||
let winner_account_id_formatted = format_private_account_id(winner_account_id);
|
|
||||||
|
|
||||||
let pinata_balance_pre = ctx
|
let pinata_balance_pre = ctx
|
||||||
.sequencer_client()
|
.sequencer_client()
|
||||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||||
@ -107,8 +102,7 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()
|
|||||||
let claim_result = wallet::cli::execute_subcommand(
|
let claim_result = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
||||||
to: Some(winner_account_id_formatted),
|
to: private_mention(winner_account_id),
|
||||||
to_label: None,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -139,8 +133,7 @@ async fn claim_pinata_to_existing_public_account() -> Result<()> {
|
|||||||
|
|
||||||
let pinata_prize = 150;
|
let pinata_prize = 150;
|
||||||
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
||||||
to: Some(format_public_account_id(ctx.existing_public_accounts()[0])),
|
to: public_mention(ctx.existing_public_accounts()[0]),
|
||||||
to_label: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let pinata_balance_pre = ctx
|
let pinata_balance_pre = ctx
|
||||||
@ -178,10 +171,7 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> {
|
|||||||
|
|
||||||
let pinata_prize = 150;
|
let pinata_prize = 150;
|
||||||
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
||||||
to: Some(format_private_account_id(
|
to: private_mention(ctx.existing_private_accounts()[0]),
|
||||||
ctx.existing_private_accounts()[0],
|
|
||||||
)),
|
|
||||||
to_label: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let pinata_balance_pre = ctx
|
let pinata_balance_pre = ctx
|
||||||
@ -241,12 +231,9 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
|
|||||||
anyhow::bail!("Expected RegisterAccount return value");
|
anyhow::bail!("Expected RegisterAccount return value");
|
||||||
};
|
};
|
||||||
|
|
||||||
let winner_account_id_formatted = format_private_account_id(winner_account_id);
|
|
||||||
|
|
||||||
// Initialize account under auth transfer program
|
// Initialize account under auth transfer program
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||||
account_id: Some(winner_account_id_formatted.clone()),
|
account_id: private_mention(winner_account_id),
|
||||||
account_label: None,
|
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
@ -261,8 +248,7 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
|
|||||||
|
|
||||||
// Claim pinata to the new private account
|
// Claim pinata to the new private account
|
||||||
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
|
||||||
to: Some(winner_account_id_formatted),
|
to: private_mention(winner_account_id),
|
||||||
to_label: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let pinata_balance_pre = ctx
|
let pinata_balance_pre = ctx
|
||||||
|
|||||||
@ -18,14 +18,19 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id};
|
use integration_tests::{
|
||||||
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
|
||||||
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::{
|
use wallet::{
|
||||||
Command, SubcommandReturnValue,
|
account::Label,
|
||||||
account::{AccountSubcommand, NewSubcommand},
|
cli::{
|
||||||
group::GroupSubcommand,
|
Command, SubcommandReturnValue,
|
||||||
programs::native_token_transfer::AuthTransferSubcommand,
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
group::GroupSubcommand,
|
||||||
|
programs::native_token_transfer::AuthTransferSubcommand,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a group, create a shared account from it, and verify registration.
|
/// Create a group, create a shared account from it, and verify registration.
|
||||||
@ -43,8 +48,8 @@ async fn group_create_and_shared_account_registration() -> Result<()> {
|
|||||||
assert!(
|
assert!(
|
||||||
ctx.wallet()
|
ctx.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.group_key_holder("test-group")
|
.group_key_holder(&Label::new("test-group"))
|
||||||
.is_some()
|
.is_some()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -69,10 +74,10 @@ async fn group_create_and_shared_account_registration() -> Result<()> {
|
|||||||
let entry = ctx
|
let entry = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.shared_private_account(&shared_account_id)
|
.shared_private_account(&shared_account_id)
|
||||||
.context("Shared account not found in storage")?;
|
.context("Shared account not found in storage")?;
|
||||||
assert_eq!(entry.group_label, "test-group");
|
assert_eq!(entry.group_label, Label::new("test-group"));
|
||||||
assert!(entry.pda_seed.is_none());
|
assert!(entry.pda_seed.is_none());
|
||||||
|
|
||||||
info!("Shared account registered: {shared_account_id}");
|
info!("Shared account registered: {shared_account_id}");
|
||||||
@ -98,8 +103,8 @@ async fn group_invite_join_key_agreement() -> Result<()> {
|
|||||||
let sealing_sk = ctx
|
let sealing_sk = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.sealing_secret_key
|
.sealing_secret_key()
|
||||||
.context("Sealing key not found")?;
|
.context("Sealing key not found")?;
|
||||||
let sealing_pk =
|
let sealing_pk =
|
||||||
key_protocol::key_management::group_key_holder::SealingPublicKey::from_scalar(sealing_sk);
|
key_protocol::key_management::group_key_holder::SealingPublicKey::from_scalar(sealing_sk);
|
||||||
@ -107,8 +112,8 @@ async fn group_invite_join_key_agreement() -> Result<()> {
|
|||||||
let holder = ctx
|
let holder = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.group_key_holder("alice-group")
|
.group_key_holder(&Label::new("alice-group"))
|
||||||
.context("Group not found")?;
|
.context("Group not found")?;
|
||||||
let sealed = holder.seal_for(&sealing_pk);
|
let sealed = holder.seal_for(&sealing_pk);
|
||||||
let sealed_hex = hex::encode(&sealed);
|
let sealed_hex = hex::encode(&sealed);
|
||||||
@ -124,14 +129,14 @@ async fn group_invite_join_key_agreement() -> Result<()> {
|
|||||||
let alice_holder = ctx
|
let alice_holder = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.group_key_holder("alice-group")
|
.group_key_holder(&Label::new("alice-group"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let bob_holder = ctx
|
let bob_holder = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.group_key_holder("bob-copy")
|
.group_key_holder(&Label::new("bob-copy"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let seed = [42_u8; 32];
|
let seed = [42_u8; 32];
|
||||||
@ -181,8 +186,7 @@ async fn fund_shared_account_from_public() -> Result<()> {
|
|||||||
|
|
||||||
// Initialize the shared account under auth-transfer
|
// Initialize the shared account under auth-transfer
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||||
account_id: Some(format!("Private/{shared_id}")),
|
account_id: private_mention(shared_id),
|
||||||
account_label: None,
|
|
||||||
});
|
});
|
||||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||||
|
|
||||||
@ -191,10 +195,8 @@ async fn fund_shared_account_from_public() -> Result<()> {
|
|||||||
// Fund from a public account
|
// Fund from a public account
|
||||||
let from_public = ctx.existing_public_accounts()[0];
|
let from_public = ctx.existing_public_accounts()[0];
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||||
from: Some(format_public_account_id(from_public)),
|
from: public_mention(from_public),
|
||||||
from_label: None,
|
to: Some(private_mention(shared_id)),
|
||||||
to: Some(format!("Private/{shared_id}")),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: None,
|
to_identifier: None,
|
||||||
@ -212,7 +214,7 @@ async fn fund_shared_account_from_public() -> Result<()> {
|
|||||||
let entry = ctx
|
let entry = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.shared_private_account(&shared_id)
|
.shared_private_account(&shared_id)
|
||||||
.context("Shared account not found after sync")?;
|
.context("Shared account not found after sync")?;
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,8 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use integration_tests::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, private_mention, public_mention,
|
||||||
format_public_account_id, verify_commitment_is_in_state,
|
verify_commitment_is_in_state,
|
||||||
};
|
};
|
||||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||||
use log::info;
|
use log::info;
|
||||||
@ -17,10 +17,13 @@ use nssa::program::Program;
|
|||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use token_core::{TokenDefinition, TokenHolding};
|
use token_core::{TokenDefinition, TokenHolding};
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::{
|
use wallet::{
|
||||||
Command, SubcommandReturnValue,
|
account::Label,
|
||||||
account::{AccountSubcommand, NewSubcommand},
|
cli::{
|
||||||
programs::token::TokenProgramAgnosticSubcommand,
|
Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
programs::token::TokenProgramAgnosticSubcommand,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -79,10 +82,8 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -128,10 +129,8 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
|||||||
// Transfer 7 tokens from supply_acc to recipient_account_id
|
// Transfer 7 tokens from supply_acc to recipient_account_id
|
||||||
let transfer_amount = 7;
|
let transfer_amount = 7;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id)),
|
from: public_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(public_mention(recipient_account_id)),
|
||||||
to: Some(format_public_account_id(recipient_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -176,10 +175,8 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
|||||||
// Burn 3 tokens from recipient_acc
|
// Burn 3 tokens from recipient_acc
|
||||||
let burn_amount = 3;
|
let burn_amount = 3;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
||||||
definition: Some(format_public_account_id(definition_account_id)),
|
definition: public_mention(definition_account_id),
|
||||||
definition_label: None,
|
holder: public_mention(recipient_account_id),
|
||||||
holder: Some(format_public_account_id(recipient_account_id)),
|
|
||||||
holder_label: None,
|
|
||||||
amount: burn_amount,
|
amount: burn_amount,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -222,10 +219,8 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
|||||||
// Mint 10 tokens at recipient_acc
|
// Mint 10 tokens at recipient_acc
|
||||||
let mint_amount = 10;
|
let mint_amount = 10;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||||
definition: Some(format_public_account_id(definition_account_id)),
|
definition: public_mention(definition_account_id),
|
||||||
definition_label: None,
|
holder: Some(public_mention(recipient_account_id)),
|
||||||
holder: Some(format_public_account_id(recipient_account_id)),
|
|
||||||
holder_label: None,
|
|
||||||
holder_npk: None,
|
holder_npk: None,
|
||||||
holder_vpk: None,
|
holder_vpk: None,
|
||||||
holder_identifier: None,
|
holder_identifier: None,
|
||||||
@ -329,10 +324,8 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: private_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_private_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -368,10 +361,8 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
|||||||
// Transfer 7 tokens from supply_acc to recipient_account_id
|
// Transfer 7 tokens from supply_acc to recipient_account_id
|
||||||
let transfer_amount = 7;
|
let transfer_amount = 7;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_private_account_id(supply_account_id)),
|
from: private_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(private_mention(recipient_account_id)),
|
||||||
to: Some(format_private_account_id(recipient_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -398,10 +389,8 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
|||||||
// Burn 3 tokens from recipient_acc
|
// Burn 3 tokens from recipient_acc
|
||||||
let burn_amount = 3;
|
let burn_amount = 3;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
||||||
definition: Some(format_public_account_id(definition_account_id)),
|
definition: public_mention(definition_account_id),
|
||||||
definition_label: None,
|
holder: private_mention(recipient_account_id),
|
||||||
holder: Some(format_private_account_id(recipient_account_id)),
|
|
||||||
holder_label: None,
|
|
||||||
amount: burn_amount,
|
amount: burn_amount,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -492,10 +481,8 @@ async fn create_token_with_private_definition() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_private_account_id(definition_account_id)),
|
definition_account_id: private_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -563,10 +550,8 @@ async fn create_token_with_private_definition() -> Result<()> {
|
|||||||
// Mint to public account
|
// Mint to public account
|
||||||
let mint_amount_public = 10;
|
let mint_amount_public = 10;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||||
definition: Some(format_private_account_id(definition_account_id)),
|
definition: private_mention(definition_account_id),
|
||||||
definition_label: None,
|
holder: Some(public_mention(recipient_account_id_public)),
|
||||||
holder: Some(format_public_account_id(recipient_account_id_public)),
|
|
||||||
holder_label: None,
|
|
||||||
holder_npk: None,
|
holder_npk: None,
|
||||||
holder_vpk: None,
|
holder_vpk: None,
|
||||||
holder_identifier: None,
|
holder_identifier: None,
|
||||||
@ -612,10 +597,8 @@ async fn create_token_with_private_definition() -> Result<()> {
|
|||||||
// Mint to private account
|
// Mint to private account
|
||||||
let mint_amount_private = 5;
|
let mint_amount_private = 5;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||||
definition: Some(format_private_account_id(definition_account_id)),
|
definition: private_mention(definition_account_id),
|
||||||
definition_label: None,
|
holder: Some(private_mention(recipient_account_id_private)),
|
||||||
holder: Some(format_private_account_id(recipient_account_id_private)),
|
|
||||||
holder_label: None,
|
|
||||||
holder_npk: None,
|
holder_npk: None,
|
||||||
holder_vpk: None,
|
holder_vpk: None,
|
||||||
holder_identifier: None,
|
holder_identifier: None,
|
||||||
@ -694,10 +677,8 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_private_account_id(definition_account_id)),
|
definition_account_id: private_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: private_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_private_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name,
|
name,
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -755,10 +736,8 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
|||||||
// Transfer tokens
|
// Transfer tokens
|
||||||
let transfer_amount = 7;
|
let transfer_amount = 7;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_private_account_id(supply_account_id)),
|
from: private_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(private_mention(recipient_account_id)),
|
||||||
to: Some(format_private_account_id(recipient_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -871,10 +850,8 @@ async fn shielded_token_transfer() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name,
|
name,
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -887,10 +864,8 @@ async fn shielded_token_transfer() -> Result<()> {
|
|||||||
// Perform shielded transfer: public supply -> private recipient
|
// Perform shielded transfer: public supply -> private recipient
|
||||||
let transfer_amount = 7;
|
let transfer_amount = 7;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_public_account_id(supply_account_id)),
|
from: public_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(private_mention(recipient_account_id)),
|
||||||
to: Some(format_private_account_id(recipient_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -998,10 +973,8 @@ async fn deshielded_token_transfer() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: private_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_private_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name,
|
name,
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -1014,10 +987,8 @@ async fn deshielded_token_transfer() -> Result<()> {
|
|||||||
// Perform deshielded transfer: private supply -> public recipient
|
// Perform deshielded transfer: private supply -> public recipient
|
||||||
let transfer_amount = 7;
|
let transfer_amount = 7;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: Some(format_private_account_id(supply_account_id)),
|
from: private_mention(supply_account_id),
|
||||||
from_label: None,
|
to: Some(public_mention(recipient_account_id)),
|
||||||
to: Some(format_public_account_id(recipient_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
@ -1109,10 +1080,8 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
|||||||
let name = "A NAME".to_owned();
|
let name = "A NAME".to_owned();
|
||||||
let total_supply = 37;
|
let total_supply = 37;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_private_account_id(definition_account_id)),
|
definition_account_id: private_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: private_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_private_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name,
|
name,
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -1139,22 +1108,23 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get keys for foreign mint (claiming path)
|
// Get keys for foreign mint (claiming path)
|
||||||
let (holder_keys, _, holder_identifier) = ctx
|
let holder = ctx
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.get_private_account(recipient_account_id)
|
.private_account(recipient_account_id)
|
||||||
.context("Failed to get private account keys")?;
|
.context("Failed to get private account keys")?;
|
||||||
|
|
||||||
|
let holder_keys = holder.key_chain;
|
||||||
|
let holder_identifier = holder.identifier;
|
||||||
|
|
||||||
// Mint using claiming path (foreign account)
|
// Mint using claiming path (foreign account)
|
||||||
let mint_amount = 9;
|
let mint_amount = 9;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||||
definition: Some(format_private_account_id(definition_account_id)),
|
definition: private_mention(definition_account_id),
|
||||||
definition_label: None,
|
|
||||||
holder: None,
|
holder: None,
|
||||||
holder_label: None,
|
|
||||||
holder_npk: Some(hex::encode(holder_keys.nullifier_public_key.0)),
|
holder_npk: Some(hex::encode(holder_keys.nullifier_public_key.0)),
|
||||||
holder_vpk: Some(hex::encode(holder_keys.viewing_public_key.0)),
|
holder_vpk: Some(hex::encode(&holder_keys.viewing_public_key.0)),
|
||||||
holder_identifier: Some(holder_identifier),
|
holder_identifier: Some(holder_identifier),
|
||||||
amount: mint_amount,
|
amount: mint_amount,
|
||||||
};
|
};
|
||||||
@ -1199,8 +1169,8 @@ async fn create_token_using_labels() -> Result<()> {
|
|||||||
let mut ctx = TestContext::new().await?;
|
let mut ctx = TestContext::new().await?;
|
||||||
|
|
||||||
// Create definition and supply accounts with labels
|
// Create definition and supply accounts with labels
|
||||||
let def_label = "token-definition-label".to_owned();
|
let def_label = Label::new("token-definition-label");
|
||||||
let supply_label = "token-supply-label".to_owned();
|
let supply_label = Label::new("token-supply-label");
|
||||||
|
|
||||||
let result = wallet::cli::execute_subcommand(
|
let result = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
@ -1221,7 +1191,7 @@ async fn create_token_using_labels() -> Result<()> {
|
|||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
cci: None,
|
cci: None,
|
||||||
label: Some(supply_label.clone()),
|
label: Some(Label::new(supply_label.clone())),
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -1236,10 +1206,8 @@ async fn create_token_using_labels() -> Result<()> {
|
|||||||
let name = "LABELED TOKEN".to_owned();
|
let name = "LABELED TOKEN".to_owned();
|
||||||
let total_supply = 100;
|
let total_supply = 100;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: None,
|
definition_account_id: def_label.into(),
|
||||||
definition_account_label: Some(def_label),
|
supply_account_id: supply_label.into(),
|
||||||
supply_account_id: None,
|
|
||||||
supply_account_label: Some(supply_label),
|
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -1303,7 +1271,7 @@ async fn transfer_token_using_from_label() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create supply account with a label
|
// Create supply account with a label
|
||||||
let supply_label = "token-supply-sender".to_owned();
|
let supply_label = Label::new("token-supply-sender");
|
||||||
let result = wallet::cli::execute_subcommand(
|
let result = wallet::cli::execute_subcommand(
|
||||||
ctx.wallet_mut(),
|
ctx.wallet_mut(),
|
||||||
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
@ -1338,10 +1306,8 @@ async fn transfer_token_using_from_label() -> Result<()> {
|
|||||||
// Create token
|
// Create token
|
||||||
let total_supply = 50;
|
let total_supply = 50;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
definition_account_id: Some(format_public_account_id(definition_account_id)),
|
definition_account_id: public_mention(definition_account_id),
|
||||||
definition_account_label: None,
|
supply_account_id: public_mention(supply_account_id),
|
||||||
supply_account_id: Some(format_public_account_id(supply_account_id)),
|
|
||||||
supply_account_label: None,
|
|
||||||
name: "LABEL TEST TOKEN".to_owned(),
|
name: "LABEL TEST TOKEN".to_owned(),
|
||||||
total_supply,
|
total_supply,
|
||||||
};
|
};
|
||||||
@ -1353,10 +1319,8 @@ async fn transfer_token_using_from_label() -> Result<()> {
|
|||||||
// Transfer token using from_label instead of from
|
// Transfer token using from_label instead of from
|
||||||
let transfer_amount = 20;
|
let transfer_amount = 20;
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||||
from: None,
|
from: supply_label.into(),
|
||||||
from_label: Some(supply_label),
|
to: Some(public_mention(recipient_account_id)),
|
||||||
to: Some(format_public_account_id(recipient_account_id)),
|
|
||||||
to_label: None,
|
|
||||||
to_npk: None,
|
to_npk: None,
|
||||||
to_vpk: None,
|
to_vpk: None,
|
||||||
to_identifier: Some(0),
|
to_identifier: Some(0),
|
||||||
|
|||||||
@ -14,11 +14,8 @@ use std::time::{Duration, Instant};
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
use common::transaction::NSSATransaction;
|
use common::transaction::NSSATransaction;
|
||||||
use integration_tests::{
|
use integration_tests::{TestContext, config::SequencerPartialConfig};
|
||||||
TestContext,
|
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||||
config::{InitialData, SequencerPartialConfig},
|
|
||||||
};
|
|
||||||
use key_protocol::key_management::{KeyChain, ephemeral_key_holder::EphemeralKeyHolder};
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::{
|
use nssa::{
|
||||||
Account, AccountId, PrivacyPreservingTransaction, PrivateKey, PublicKey, PublicTransaction,
|
Account, AccountId, PrivacyPreservingTransaction, PrivateKey, PublicKey, PublicTransaction,
|
||||||
@ -31,6 +28,7 @@ use nssa_core::{
|
|||||||
account::{AccountWithMetadata, Nonce, data::Data},
|
account::{AccountWithMetadata, Nonce, data::Data},
|
||||||
encryption::ViewingPublicKey,
|
encryption::ViewingPublicKey,
|
||||||
};
|
};
|
||||||
|
use sequencer_core::config::GenesisTransaction;
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
|
|
||||||
@ -81,7 +79,7 @@ impl TpsTestManager {
|
|||||||
program.id(),
|
program.id(),
|
||||||
[pair[0].1, pair[1].1].to_vec(),
|
[pair[0].1, pair[1].1].to_vec(),
|
||||||
[Nonce(0_u128)].to_vec(),
|
[Nonce(0_u128)].to_vec(),
|
||||||
amount,
|
authenticated_transfer_core::Instruction::Transfer { amount },
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let witness_set =
|
let witness_set =
|
||||||
@ -96,28 +94,14 @@ impl TpsTestManager {
|
|||||||
/// Generates a sequencer configuration with initial balance in a number of public accounts.
|
/// Generates a sequencer configuration with initial balance in a number of public accounts.
|
||||||
/// The transactions generated with the function `build_public_txs` will be valid in a node
|
/// The transactions generated with the function `build_public_txs` will be valid in a node
|
||||||
/// started with the config from this method.
|
/// started with the config from this method.
|
||||||
fn generate_initial_data(&self) -> InitialData {
|
fn generate_genesis(&self) -> Vec<GenesisTransaction> {
|
||||||
// Create public public keypairs
|
self.public_keypairs
|
||||||
let public_accounts = self
|
|
||||||
.public_keypairs
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(key, _)| (key.clone(), 10))
|
.map(|(_, account_id)| GenesisTransaction::SupplyPublicAccount {
|
||||||
.collect();
|
account_id: *account_id,
|
||||||
|
balance: 10,
|
||||||
// Generate an initial commitment to be used with the privacy preserving transaction
|
})
|
||||||
// created with the `build_privacy_transaction` function.
|
.collect()
|
||||||
let key_chain = KeyChain::new_os_random();
|
|
||||||
let account = Account {
|
|
||||||
balance: 100,
|
|
||||||
nonce: Nonce(0xdead_beef),
|
|
||||||
program_owner: Program::authenticated_transfer_program().id(),
|
|
||||||
data: Data::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
InitialData {
|
|
||||||
public_accounts,
|
|
||||||
private_accounts: vec![(key_chain, account)],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn generate_sequencer_partial_config() -> SequencerPartialConfig {
|
const fn generate_sequencer_partial_config() -> SequencerPartialConfig {
|
||||||
@ -139,7 +123,7 @@ pub async fn tps_test() -> Result<()> {
|
|||||||
let tps_test = TpsTestManager::new(target_tps, num_transactions);
|
let tps_test = TpsTestManager::new(target_tps, num_transactions);
|
||||||
let ctx = TestContext::builder()
|
let ctx = TestContext::builder()
|
||||||
.with_sequencer_partial_config(TpsTestManager::generate_sequencer_partial_config())
|
.with_sequencer_partial_config(TpsTestManager::generate_sequencer_partial_config())
|
||||||
.with_initial_data(tps_test.generate_initial_data())
|
.with_genesis(tps_test.generate_genesis())
|
||||||
.build()
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -166,7 +150,7 @@ pub async fn tps_test() -> Result<()> {
|
|||||||
loop {
|
loop {
|
||||||
assert!(
|
assert!(
|
||||||
now.elapsed().as_millis() <= target_time.as_millis(),
|
now.elapsed().as_millis() <= target_time.as_millis(),
|
||||||
"TPS test failed by timeout"
|
"TPS test failed by timeout, transactions processed {i}/{num_transactions}"
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx_obj = ctx
|
let tx_obj = ctx
|
||||||
@ -250,7 +234,10 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
|||||||
);
|
);
|
||||||
let (output, proof) = circuit::execute_and_prove(
|
let (output, proof) = circuit::execute_and_prove(
|
||||||
vec![sender_pre, recipient_pre],
|
vec![sender_pre, recipient_pre],
|
||||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer {
|
||||||
|
amount: balance_to_move,
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
vec![
|
vec![
|
||||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||||
ssk: sender_ss,
|
ssk: sender_ss,
|
||||||
|
|||||||
@ -24,6 +24,7 @@ use log::info;
|
|||||||
use nssa::{Account, AccountId, PrivateKey, PublicKey, program::Program};
|
use nssa::{Account, AccountId, PrivateKey, PublicKey, program::Program};
|
||||||
use nssa_core::program::DEFAULT_PROGRAM_ID;
|
use nssa_core::program::DEFAULT_PROGRAM_ID;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
use wallet::account::HumanReadableAccount;
|
||||||
use wallet_ffi::{
|
use wallet_ffi::{
|
||||||
FfiAccount, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey,
|
FfiAccount, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey,
|
||||||
FfiTransferResult, FfiU128, WalletHandle, error,
|
FfiTransferResult, FfiU128, WalletHandle, error,
|
||||||
@ -53,11 +54,24 @@ unsafe extern "C" {
|
|||||||
out_account_id: *mut FfiBytes32,
|
out_account_id: *mut FfiBytes32,
|
||||||
) -> error::WalletFfiError;
|
) -> error::WalletFfiError;
|
||||||
|
|
||||||
|
fn wallet_ffi_import_public_account(
|
||||||
|
handle: *mut WalletHandle,
|
||||||
|
private_key_hex: *const c_char,
|
||||||
|
) -> error::WalletFfiError;
|
||||||
|
|
||||||
fn wallet_ffi_create_private_accounts_key(
|
fn wallet_ffi_create_private_accounts_key(
|
||||||
handle: *mut WalletHandle,
|
handle: *mut WalletHandle,
|
||||||
out_keys: *mut FfiPrivateAccountKeys,
|
out_keys: *mut FfiPrivateAccountKeys,
|
||||||
) -> error::WalletFfiError;
|
) -> error::WalletFfiError;
|
||||||
|
|
||||||
|
fn wallet_ffi_import_private_account(
|
||||||
|
handle: *mut WalletHandle,
|
||||||
|
key_chain_json: *const c_char,
|
||||||
|
chain_index: *const c_char,
|
||||||
|
identifier: *const FfiU128,
|
||||||
|
account_state_json: *const c_char,
|
||||||
|
) -> error::WalletFfiError;
|
||||||
|
|
||||||
fn wallet_ffi_list_accounts(
|
fn wallet_ffi_list_accounts(
|
||||||
handle: *mut WalletHandle,
|
handle: *mut WalletHandle,
|
||||||
out_list: *mut FfiAccountList,
|
out_list: *mut FfiAccountList,
|
||||||
@ -191,13 +205,59 @@ fn new_wallet_ffi_with_test_context_config(
|
|||||||
let storage_path = CString::new(storage_path.to_str().unwrap())?;
|
let storage_path = CString::new(storage_path.to_str().unwrap())?;
|
||||||
let password = CString::new(ctx.ctx().wallet_password())?;
|
let password = CString::new(ctx.ctx().wallet_password())?;
|
||||||
|
|
||||||
Ok(unsafe {
|
let wallet_ffi_handle = unsafe {
|
||||||
wallet_ffi_create_new(
|
wallet_ffi_create_new(
|
||||||
config_path.as_ptr(),
|
config_path.as_ptr(),
|
||||||
storage_path.as_ptr(),
|
storage_path.as_ptr(),
|
||||||
password.as_ptr(),
|
password.as_ptr(),
|
||||||
)
|
)
|
||||||
})
|
};
|
||||||
|
|
||||||
|
// Import accounts from source wallet
|
||||||
|
let source_wallet = ctx.ctx().wallet();
|
||||||
|
let source_key_chain = source_wallet.storage().key_chain();
|
||||||
|
|
||||||
|
for (account_id, _chain_index) in source_key_chain.public_account_ids() {
|
||||||
|
let private_key_hex = source_wallet
|
||||||
|
.get_account_public_signing_key(account_id)
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
let private_key_hex = CString::new(private_key_hex)?;
|
||||||
|
unsafe { wallet_ffi_import_public_account(wallet_ffi_handle, private_key_hex.as_ptr()) }
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (account_id, _chain_index) in source_key_chain.private_account_ids() {
|
||||||
|
let account = source_key_chain.private_account(account_id).unwrap();
|
||||||
|
let key_chain_json = CString::new(serde_json::to_string(account.key_chain)?)?;
|
||||||
|
let account_state_json = CString::new(serde_json::to_string(
|
||||||
|
&HumanReadableAccount::from(account.account.clone()),
|
||||||
|
)?)?;
|
||||||
|
|
||||||
|
let chain_index = account
|
||||||
|
.chain_index
|
||||||
|
.map(|chain_index| CString::new(chain_index.to_string()))
|
||||||
|
.transpose()?;
|
||||||
|
let chain_index_ptr = chain_index
|
||||||
|
.as_ref()
|
||||||
|
.map_or(std::ptr::null(), |value| value.as_ptr());
|
||||||
|
let identifier = FfiU128 {
|
||||||
|
data: account.identifier.to_le_bytes(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
wallet_ffi_import_private_account(
|
||||||
|
wallet_ffi_handle,
|
||||||
|
key_chain_json.as_ptr(),
|
||||||
|
chain_index_ptr,
|
||||||
|
&raw const identifier,
|
||||||
|
account_state_json.as_ptr(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(wallet_ffi_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_wallet_ffi_with_default_config(password: &str) -> Result<*mut WalletHandle> {
|
fn new_wallet_ffi_with_default_config(password: &str) -> Result<*mut WalletHandle> {
|
||||||
@ -405,7 +465,7 @@ fn test_wallet_ffi_get_balance_public() -> Result<()> {
|
|||||||
|
|
||||||
let balance = unsafe {
|
let balance = unsafe {
|
||||||
let mut out_balance: [u8; 16] = [0; 16];
|
let mut out_balance: [u8; 16] = [0; 16];
|
||||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
let ffi_account_id = FfiBytes32::from(account_id);
|
||||||
wallet_ffi_get_balance(
|
wallet_ffi_get_balance(
|
||||||
wallet_ffi_handle,
|
wallet_ffi_handle,
|
||||||
&raw const ffi_account_id,
|
&raw const ffi_account_id,
|
||||||
@ -435,7 +495,7 @@ fn test_wallet_ffi_get_account_public() -> Result<()> {
|
|||||||
let mut out_account = FfiAccount::default();
|
let mut out_account = FfiAccount::default();
|
||||||
|
|
||||||
let account: Account = unsafe {
|
let account: Account = unsafe {
|
||||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
let ffi_account_id = FfiBytes32::from(account_id);
|
||||||
wallet_ffi_get_account_public(
|
wallet_ffi_get_account_public(
|
||||||
wallet_ffi_handle,
|
wallet_ffi_handle,
|
||||||
&raw const ffi_account_id,
|
&raw const ffi_account_id,
|
||||||
@ -451,7 +511,7 @@ fn test_wallet_ffi_get_account_public() -> Result<()> {
|
|||||||
);
|
);
|
||||||
assert_eq!(account.balance, 10000);
|
assert_eq!(account.balance, 10000);
|
||||||
assert!(account.data.is_empty());
|
assert!(account.data.is_empty());
|
||||||
assert_eq!(account.nonce.0, 0);
|
assert_eq!(account.nonce.0, 1);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
wallet_ffi_free_account_data(&raw mut out_account);
|
wallet_ffi_free_account_data(&raw mut out_account);
|
||||||
@ -472,7 +532,7 @@ fn test_wallet_ffi_get_account_private() -> Result<()> {
|
|||||||
let mut out_account = FfiAccount::default();
|
let mut out_account = FfiAccount::default();
|
||||||
|
|
||||||
let account: Account = unsafe {
|
let account: Account = unsafe {
|
||||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
let ffi_account_id = FfiBytes32::from(account_id);
|
||||||
wallet_ffi_get_account_private(
|
wallet_ffi_get_account_private(
|
||||||
wallet_ffi_handle,
|
wallet_ffi_handle,
|
||||||
&raw const ffi_account_id,
|
&raw const ffi_account_id,
|
||||||
@ -488,7 +548,6 @@ fn test_wallet_ffi_get_account_private() -> Result<()> {
|
|||||||
);
|
);
|
||||||
assert_eq!(account.balance, 10000);
|
assert_eq!(account.balance, 10000);
|
||||||
assert!(account.data.is_empty());
|
assert!(account.data.is_empty());
|
||||||
assert_eq!(account.nonce, 0_u128.into());
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
wallet_ffi_free_account_data(&raw mut out_account);
|
wallet_ffi_free_account_data(&raw mut out_account);
|
||||||
@ -509,7 +568,7 @@ fn test_wallet_ffi_get_public_account_keys() -> Result<()> {
|
|||||||
let mut out_key = FfiPublicAccountKey::default();
|
let mut out_key = FfiPublicAccountKey::default();
|
||||||
|
|
||||||
let key: PublicKey = unsafe {
|
let key: PublicKey = unsafe {
|
||||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
let ffi_account_id = FfiBytes32::from(account_id);
|
||||||
wallet_ffi_get_public_account_key(
|
wallet_ffi_get_public_account_key(
|
||||||
wallet_ffi_handle,
|
wallet_ffi_handle,
|
||||||
&raw const ffi_account_id,
|
&raw const ffi_account_id,
|
||||||
@ -548,7 +607,7 @@ fn test_wallet_ffi_get_private_account_keys() -> Result<()> {
|
|||||||
let mut keys = FfiPrivateAccountKeys::default();
|
let mut keys = FfiPrivateAccountKeys::default();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
let ffi_account_id = FfiBytes32::from(account_id);
|
||||||
wallet_ffi_get_private_account_keys(
|
wallet_ffi_get_private_account_keys(
|
||||||
wallet_ffi_handle,
|
wallet_ffi_handle,
|
||||||
&raw const ffi_account_id,
|
&raw const ffi_account_id,
|
||||||
@ -557,15 +616,15 @@ fn test_wallet_ffi_get_private_account_keys() -> Result<()> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
let key_chain = &ctx
|
let account = &ctx
|
||||||
.ctx()
|
.ctx()
|
||||||
.wallet()
|
.wallet()
|
||||||
.storage()
|
.storage()
|
||||||
.user_data
|
.key_chain()
|
||||||
.get_private_account(account_id)
|
.private_account(account_id)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.0;
|
|
||||||
|
|
||||||
|
let key_chain = account.key_chain;
|
||||||
let expected_npk = &key_chain.nullifier_public_key;
|
let expected_npk = &key_chain.nullifier_public_key;
|
||||||
let expected_vpk = &key_chain.viewing_public_key;
|
let expected_vpk = &key_chain.viewing_public_key;
|
||||||
|
|
||||||
@ -587,7 +646,7 @@ fn test_wallet_ffi_account_id_to_base58() -> Result<()> {
|
|||||||
let private_key = PrivateKey::new_os_random();
|
let private_key = PrivateKey::new_os_random();
|
||||||
let public_key = PublicKey::new_from_private_key(&private_key);
|
let public_key = PublicKey::new_from_private_key(&private_key);
|
||||||
let account_id = AccountId::from(&public_key);
|
let account_id = AccountId::from(&public_key);
|
||||||
let ffi_bytes: FfiBytes32 = (&account_id).into();
|
let ffi_bytes: FfiBytes32 = account_id.into();
|
||||||
let ptr = unsafe { wallet_ffi_account_id_to_base58(&raw const ffi_bytes) };
|
let ptr = unsafe { wallet_ffi_account_id_to_base58(&raw const ffi_bytes) };
|
||||||
|
|
||||||
let ffi_result = unsafe { CStr::from_ptr(ptr).to_str()? };
|
let ffi_result = unsafe { CStr::from_ptr(ptr).to_str()? };
|
||||||
@ -744,8 +803,8 @@ fn test_wallet_ffi_transfer_public() -> Result<()> {
|
|||||||
let ctx = BlockingTestContext::new()?;
|
let ctx = BlockingTestContext::new()?;
|
||||||
let home = tempfile::tempdir()?;
|
let home = tempfile::tempdir()?;
|
||||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||||
let from: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
let from: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into();
|
||||||
let to: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[1]).into();
|
let to: FfiBytes32 = ctx.ctx().existing_public_accounts()[1].into();
|
||||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||||
|
|
||||||
let mut transfer_result = FfiTransferResult::default();
|
let mut transfer_result = FfiTransferResult::default();
|
||||||
@ -797,12 +856,12 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
|
|||||||
let ctx = BlockingTestContext::new()?;
|
let ctx = BlockingTestContext::new()?;
|
||||||
let home = tempfile::tempdir()?;
|
let home = tempfile::tempdir()?;
|
||||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||||
let from: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
let from: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into();
|
||||||
let (to, to_keys) = unsafe {
|
let (to, to_keys) = unsafe {
|
||||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||||
let account_id = nssa::AccountId::from((&out_keys.npk(), 0_u128));
|
let account_id = nssa::AccountId::from((&out_keys.npk(), 0_u128));
|
||||||
let to: FfiBytes32 = (&account_id).into();
|
let to: FfiBytes32 = account_id.into();
|
||||||
(to, out_keys)
|
(to, out_keys)
|
||||||
};
|
};
|
||||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||||
@ -871,8 +930,8 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
|
|||||||
let ctx = BlockingTestContext::new()?;
|
let ctx = BlockingTestContext::new()?;
|
||||||
let home = tempfile::tempdir()?;
|
let home = tempfile::tempdir()?;
|
||||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||||
let from: FfiBytes32 = (&ctx.ctx().existing_private_accounts()[0]).into();
|
let from: FfiBytes32 = ctx.ctx().existing_private_accounts()[0].into();
|
||||||
let to: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
let to: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into();
|
||||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||||
|
|
||||||
let mut transfer_result = FfiTransferResult::default();
|
let mut transfer_result = FfiTransferResult::default();
|
||||||
@ -883,8 +942,9 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
|
|||||||
&raw const to,
|
&raw const to,
|
||||||
&raw const amount,
|
&raw const amount,
|
||||||
&raw mut transfer_result,
|
&raw mut transfer_result,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
info!("Waiting for next block creation");
|
info!("Waiting for next block creation");
|
||||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||||
@ -892,9 +952,9 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
|
|||||||
// Sync private account local storage with onchain encrypted state
|
// Sync private account local storage with onchain encrypted state
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut current_height = 0;
|
let mut current_height = 0;
|
||||||
wallet_ffi_get_current_block_height(wallet_ffi_handle, &raw mut current_height);
|
wallet_ffi_get_current_block_height(wallet_ffi_handle, &raw mut current_height).unwrap();
|
||||||
wallet_ffi_sync_to_block(wallet_ffi_handle, current_height);
|
wallet_ffi_sync_to_block(wallet_ffi_handle, current_height).unwrap();
|
||||||
};
|
}
|
||||||
|
|
||||||
let from_balance = unsafe {
|
let from_balance = unsafe {
|
||||||
let mut out_balance: [u8; 16] = [0; 16];
|
let mut out_balance: [u8; 16] = [0; 16];
|
||||||
@ -931,12 +991,12 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
|
|||||||
let home = tempfile::tempdir()?;
|
let home = tempfile::tempdir()?;
|
||||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||||
|
|
||||||
let from: FfiBytes32 = (&ctx.ctx().existing_private_accounts()[0]).into();
|
let from: FfiBytes32 = ctx.ctx().existing_private_accounts()[0].into();
|
||||||
let (to, to_keys) = unsafe {
|
let (to, to_keys) = unsafe {
|
||||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||||
let account_id = nssa::AccountId::from((&out_keys.npk(), 0_u128));
|
let account_id = nssa::AccountId::from((&out_keys.npk(), 0_u128));
|
||||||
let to: FfiBytes32 = (&account_id).into();
|
let to: FfiBytes32 = account_id.into();
|
||||||
(to, out_keys)
|
(to, out_keys)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,10 @@ license = { workspace = true }
|
|||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
test_utils = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nssa.workspace = true
|
nssa.workspace = true
|
||||||
nssa_core.workspace = true
|
nssa_core.workspace = true
|
||||||
|
|||||||
@ -36,7 +36,7 @@ impl EphemeralKeyHolder {
|
|||||||
&self,
|
&self,
|
||||||
receiver_viewing_public_key: &ViewingPublicKey,
|
receiver_viewing_public_key: &ViewingPublicKey,
|
||||||
) -> SharedSecretKey {
|
) -> SharedSecretKey {
|
||||||
SharedSecretKey::new(&self.ephemeral_secret_key, receiver_viewing_public_key)
|
SharedSecretKey::new(self.ephemeral_secret_key, receiver_viewing_public_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ pub fn produce_one_sided_shared_secret_receiver(
|
|||||||
let mut esk = [0; 32];
|
let mut esk = [0; 32];
|
||||||
OsRng.fill_bytes(&mut esk);
|
OsRng.fill_bytes(&mut esk);
|
||||||
(
|
(
|
||||||
SharedSecretKey::new(&esk, vpk),
|
SharedSecretKey::new(esk, vpk),
|
||||||
EphemeralPublicKey::from_scalar(esk),
|
EphemeralPublicKey::from_scalar(esk),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@ pub type SealingSecretKey = Scalar;
|
|||||||
/// `Debug` is implemented manually to redact the GMS; formatting this value with `{:?}`
|
/// `Debug` is implemented manually to redact the GMS; formatting this value with `{:?}`
|
||||||
/// will not leak the secret. Code that formats through `{:#?}` on containing types is
|
/// will not leak the secret. Code that formats through `{:#?}` on containing types is
|
||||||
/// safe for the same reason.
|
/// safe for the same reason.
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct GroupKeyHolder {
|
pub struct GroupKeyHolder {
|
||||||
gms: [u8; 32],
|
gms: [u8; 32],
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ impl GroupKeyHolder {
|
|||||||
let mut ephemeral_scalar: Scalar = [0_u8; 32];
|
let mut ephemeral_scalar: Scalar = [0_u8; 32];
|
||||||
OsRng.fill_bytes(&mut ephemeral_scalar);
|
OsRng.fill_bytes(&mut ephemeral_scalar);
|
||||||
let ephemeral_pubkey = Secp256k1Point::from_scalar(ephemeral_scalar);
|
let ephemeral_pubkey = Secp256k1Point::from_scalar(ephemeral_scalar);
|
||||||
let shared = SharedSecretKey::new(&ephemeral_scalar, &recipient_key.0);
|
let shared = SharedSecretKey::new(ephemeral_scalar, &recipient_key.0);
|
||||||
let aes_key = Self::seal_kdf(&shared);
|
let aes_key = Self::seal_kdf(&shared);
|
||||||
let cipher = Aes256Gcm::new(&aes_key.into());
|
let cipher = Aes256Gcm::new(&aes_key.into());
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ impl GroupKeyHolder {
|
|||||||
///
|
///
|
||||||
/// Returns `Err` if the ciphertext is too short, the ECDH point is invalid, or the
|
/// Returns `Err` if the ciphertext is too short, the ECDH point is invalid, or the
|
||||||
/// AES-GCM authentication tag doesn't verify (wrong key or tampered data).
|
/// AES-GCM authentication tag doesn't verify (wrong key or tampered data).
|
||||||
pub fn unseal(sealed: &[u8], own_key: &SealingSecretKey) -> Result<Self, SealError> {
|
pub fn unseal(sealed: &[u8], own_key: SealingSecretKey) -> Result<Self, SealError> {
|
||||||
const HEADER_LEN: usize = 33 + 12;
|
const HEADER_LEN: usize = 33 + 12;
|
||||||
const MIN_LEN: usize = HEADER_LEN + 16;
|
const MIN_LEN: usize = HEADER_LEN + 16;
|
||||||
if sealed.len() < MIN_LEN {
|
if sealed.len() < MIN_LEN {
|
||||||
@ -407,7 +407,7 @@ mod tests {
|
|||||||
let recipient_vsk = recipient_keys.viewing_secret_key;
|
let recipient_vsk = recipient_keys.viewing_secret_key;
|
||||||
|
|
||||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||||
let restored = GroupKeyHolder::unseal(&sealed, &recipient_vsk).expect("unseal");
|
let restored = GroupKeyHolder::unseal(&sealed, recipient_vsk).expect("unseal");
|
||||||
|
|
||||||
assert_eq!(restored.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
assert_eq!(restored.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
||||||
|
|
||||||
@ -438,7 +438,7 @@ mod tests {
|
|||||||
.viewing_secret_key;
|
.viewing_secret_key;
|
||||||
|
|
||||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||||
let result = GroupKeyHolder::unseal(&sealed, &wrong_vsk);
|
let result = GroupKeyHolder::unseal(&sealed, wrong_vsk);
|
||||||
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ mod tests {
|
|||||||
let last = sealed.len() - 1;
|
let last = sealed.len() - 1;
|
||||||
sealed[last] ^= 0xFF;
|
sealed[last] ^= 0xFF;
|
||||||
|
|
||||||
let result = GroupKeyHolder::unseal(&sealed, &recipient_vsk);
|
let result = GroupKeyHolder::unseal(&sealed, recipient_vsk);
|
||||||
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +481,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unseal_too_short_fails() {
|
fn unseal_too_short_fails() {
|
||||||
let vsk: SealingSecretKey = [7_u8; 32];
|
let vsk: SealingSecretKey = [7_u8; 32];
|
||||||
let result = GroupKeyHolder::unseal(&[0_u8; 10], &vsk);
|
let result = GroupKeyHolder::unseal(&[0_u8; 10], vsk);
|
||||||
assert!(matches!(result, Err(super::SealError::TooShort)));
|
assert!(matches!(result, Err(super::SealError::TooShort)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,7 +537,7 @@ mod tests {
|
|||||||
|
|
||||||
let sealed = alice_holder.seal_for(&SealingPublicKey::from_bytes(bob_vpk.0));
|
let sealed = alice_holder.seal_for(&SealingPublicKey::from_bytes(bob_vpk.0));
|
||||||
let bob_holder =
|
let bob_holder =
|
||||||
GroupKeyHolder::unseal(&sealed, &bob_vsk).expect("Bob should unseal the GMS");
|
GroupKeyHolder::unseal(&sealed, bob_vsk).expect("Bob should unseal the GMS");
|
||||||
|
|
||||||
// Key agreement: both derive identical NPK and AccountId
|
// Key agreement: both derive identical NPK and AccountId
|
||||||
let bob_npk = bob_holder
|
let bob_npk = bob_holder
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use k256::{Scalar, elliptic_curve::PrimeField as _};
|
use k256::{Scalar, elliptic_curve::PrimeField as _};
|
||||||
use nssa_core::{Identifier, NullifierPublicKey, encryption::ViewingPublicKey};
|
use nssa_core::{Identifier, NullifierPublicKey, encryption::ViewingPublicKey};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -9,8 +11,9 @@ use crate::key_management::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(any(test, feature = "test_utils"), derive(PartialEq, Eq))]
|
||||||
pub struct ChildKeysPrivate {
|
pub struct ChildKeysPrivate {
|
||||||
pub value: (KeyChain, Vec<(Identifier, nssa::Account)>),
|
pub value: (KeyChain, BTreeMap<Identifier, nssa::Account>),
|
||||||
pub ccc: [u8; 32],
|
pub ccc: [u8; 32],
|
||||||
/// Can be [`None`] if root.
|
/// Can be [`None`] if root.
|
||||||
pub cci: Option<u32>,
|
pub cci: Option<u32>,
|
||||||
@ -47,7 +50,7 @@ impl ChildKeysPrivate {
|
|||||||
viewing_secret_key: vsk,
|
viewing_secret_key: vsk,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
vec![],
|
BTreeMap::from_iter([(0, nssa::Account::default())]),
|
||||||
),
|
),
|
||||||
ccc,
|
ccc,
|
||||||
cci: None,
|
cci: None,
|
||||||
@ -97,7 +100,7 @@ impl ChildKeysPrivate {
|
|||||||
viewing_secret_key: vsk,
|
viewing_secret_key: vsk,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
vec![],
|
BTreeMap::from_iter([(0, nssa::Account::default())]),
|
||||||
),
|
),
|
||||||
ccc,
|
ccc,
|
||||||
cci: Some(cci),
|
cci: Some(cci),
|
||||||
@ -115,7 +118,7 @@ impl KeyTreeNode for ChildKeysPrivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
fn account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
||||||
self.value.1.iter().map(|(identifier, _)| {
|
self.value.1.keys().map(|identifier| {
|
||||||
nssa::AccountId::from((&self.value.0.nullifier_public_key, *identifier))
|
nssa::AccountId::from((&self.value.0.nullifier_public_key, *identifier))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::key_management::key_tree::traits::KeyTreeNode;
|
use crate::key_management::key_tree::traits::KeyTreeNode;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(any(test, feature = "test_utils"), derive(PartialEq, Eq))]
|
||||||
pub struct ChildKeysPublic {
|
pub struct ChildKeysPublic {
|
||||||
pub csk: nssa::PrivateKey,
|
pub csk: nssa::PrivateKey,
|
||||||
pub cpk: nssa::PublicKey,
|
pub cpk: nssa::PublicKey,
|
||||||
|
|||||||
@ -21,6 +21,7 @@ pub mod traits;
|
|||||||
pub const DEPTH_SOFT_CAP: u32 = 20;
|
pub const DEPTH_SOFT_CAP: u32 = 20;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(any(test, feature = "test_utils"), derive(PartialEq, Eq))]
|
||||||
pub struct KeyTree<N: KeyTreeNode> {
|
pub struct KeyTree<N: KeyTreeNode> {
|
||||||
pub key_map: BTreeMap<ChainIndex, N>,
|
pub key_map: BTreeMap<ChainIndex, N>,
|
||||||
pub account_id_map: BTreeMap<nssa::AccountId, ChainIndex>,
|
pub account_id_map: BTreeMap<nssa::AccountId, ChainIndex>,
|
||||||
@ -297,7 +298,13 @@ impl KeyTree<ChildKeysPrivate> {
|
|||||||
println!("Cleanup of tree at depth {i}");
|
println!("Cleanup of tree at depth {i}");
|
||||||
for id in ChainIndex::chain_ids_at_depth(i) {
|
for id in ChainIndex::chain_ids_at_depth(i) {
|
||||||
if let Some(node) = self.key_map.get(&id).cloned() {
|
if let Some(node) = self.key_map.get(&id).cloned() {
|
||||||
if node.value.1.is_empty() {
|
if node.value.1.is_empty()
|
||||||
|
|| node
|
||||||
|
.value
|
||||||
|
.1
|
||||||
|
.iter()
|
||||||
|
.all(|(_, acc)| acc == &nssa::Account::default())
|
||||||
|
{
|
||||||
let account_ids = node.account_ids();
|
let account_ids = node.account_ids();
|
||||||
self.key_map.remove(&id);
|
self.key_map.remove(&id);
|
||||||
for addr in account_ids {
|
for addr in account_ids {
|
||||||
@ -531,49 +538,49 @@ mod tests {
|
|||||||
.key_map
|
.key_map
|
||||||
.get_mut(&ChainIndex::from_str("/1").unwrap())
|
.get_mut(&ChainIndex::from_str("/1").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
acc.value.1.push((
|
acc.value.1.insert(
|
||||||
0,
|
0,
|
||||||
nssa::Account {
|
nssa::Account {
|
||||||
balance: 2,
|
balance: 2,
|
||||||
..nssa::Account::default()
|
..nssa::Account::default()
|
||||||
},
|
},
|
||||||
));
|
);
|
||||||
|
|
||||||
let acc = tree
|
let acc = tree
|
||||||
.key_map
|
.key_map
|
||||||
.get_mut(&ChainIndex::from_str("/2").unwrap())
|
.get_mut(&ChainIndex::from_str("/2").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
acc.value.1.push((
|
acc.value.1.insert(
|
||||||
0,
|
0,
|
||||||
nssa::Account {
|
nssa::Account {
|
||||||
balance: 3,
|
balance: 3,
|
||||||
..nssa::Account::default()
|
..nssa::Account::default()
|
||||||
},
|
},
|
||||||
));
|
);
|
||||||
|
|
||||||
let acc = tree
|
let acc = tree
|
||||||
.key_map
|
.key_map
|
||||||
.get_mut(&ChainIndex::from_str("/0/1").unwrap())
|
.get_mut(&ChainIndex::from_str("/0/1").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
acc.value.1.push((
|
acc.value.1.insert(
|
||||||
0,
|
0,
|
||||||
nssa::Account {
|
nssa::Account {
|
||||||
balance: 5,
|
balance: 5,
|
||||||
..nssa::Account::default()
|
..nssa::Account::default()
|
||||||
},
|
},
|
||||||
));
|
);
|
||||||
|
|
||||||
let acc = tree
|
let acc = tree
|
||||||
.key_map
|
.key_map
|
||||||
.get_mut(&ChainIndex::from_str("/1/0").unwrap())
|
.get_mut(&ChainIndex::from_str("/1/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
acc.value.1.push((
|
acc.value.1.insert(
|
||||||
0,
|
0,
|
||||||
nssa::Account {
|
nssa::Account {
|
||||||
balance: 6,
|
balance: 6,
|
||||||
..nssa::Account::default()
|
..nssa::Account::default()
|
||||||
},
|
},
|
||||||
));
|
);
|
||||||
|
|
||||||
// Update account_id_map for nodes that now have entries
|
// Update account_id_map for nodes that now have entries
|
||||||
for chain_index_str in ["/1", "/2", "/0/1", "/1/0"] {
|
for chain_index_str in ["/1", "/2", "/0/1", "/1/0"] {
|
||||||
@ -605,15 +612,15 @@ mod tests {
|
|||||||
assert_eq!(key_set, key_set_res);
|
assert_eq!(key_set, key_set_res);
|
||||||
|
|
||||||
let acc = &tree.key_map[&ChainIndex::from_str("/1").unwrap()];
|
let acc = &tree.key_map[&ChainIndex::from_str("/1").unwrap()];
|
||||||
assert_eq!(acc.value.1[0].1.balance, 2);
|
assert_eq!(acc.value.1[&0].balance, 2);
|
||||||
|
|
||||||
let acc = &tree.key_map[&ChainIndex::from_str("/2").unwrap()];
|
let acc = &tree.key_map[&ChainIndex::from_str("/2").unwrap()];
|
||||||
assert_eq!(acc.value.1[0].1.balance, 3);
|
assert_eq!(acc.value.1[&0].balance, 3);
|
||||||
|
|
||||||
let acc = &tree.key_map[&ChainIndex::from_str("/0/1").unwrap()];
|
let acc = &tree.key_map[&ChainIndex::from_str("/0/1").unwrap()];
|
||||||
assert_eq!(acc.value.1[0].1.balance, 5);
|
assert_eq!(acc.value.1[&0].balance, 5);
|
||||||
|
|
||||||
let acc = &tree.key_map[&ChainIndex::from_str("/1/0").unwrap()];
|
let acc = &tree.key_map[&ChainIndex::from_str("/1/0").unwrap()];
|
||||||
assert_eq!(acc.value.1[0].1.balance, 6);
|
assert_eq!(acc.value.1[&0].balance, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,8 +12,8 @@ pub mod secret_holders;
|
|||||||
|
|
||||||
pub type PublicAccountSigningKey = [u8; 32];
|
pub type PublicAccountSigningKey = [u8; 32];
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
/// Private account keychain.
|
||||||
/// Entrypoint to key management.
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct KeyChain {
|
pub struct KeyChain {
|
||||||
pub secret_spending_key: SecretSpendingKey,
|
pub secret_spending_key: SecretSpendingKey,
|
||||||
pub private_key_holder: PrivateKeyHolder,
|
pub private_key_holder: PrivateKeyHolder,
|
||||||
@ -72,7 +72,7 @@ impl KeyChain {
|
|||||||
index: Option<u32>,
|
index: Option<u32>,
|
||||||
) -> SharedSecretKey {
|
) -> SharedSecretKey {
|
||||||
SharedSecretKey::new(
|
SharedSecretKey::new(
|
||||||
&self.secret_spending_key.generate_viewing_secret_key(index),
|
self.secret_spending_key.generate_viewing_secret_key(index),
|
||||||
ephemeral_public_key_sender,
|
ephemeral_public_key_sender,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,14 +17,14 @@ pub struct SeedHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
/// Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct SecretSpendingKey(pub [u8; 32]);
|
pub struct SecretSpendingKey(pub [u8; 32]);
|
||||||
|
|
||||||
pub type ViewingSecretKey = Scalar;
|
pub type ViewingSecretKey = Scalar;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
|
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
|
||||||
/// for recepient.
|
/// for recepient.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct PrivateKeyHolder {
|
pub struct PrivateKeyHolder {
|
||||||
pub nullifier_secret_key: NullifierSecretKey,
|
pub nullifier_secret_key: NullifierSecretKey,
|
||||||
pub viewing_secret_key: ViewingSecretKey,
|
pub viewing_secret_key: ViewingSecretKey,
|
||||||
|
|||||||
@ -1,417 +0,0 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use k256::AffinePoint;
|
|
||||||
use nssa::{Account, AccountId};
|
|
||||||
use nssa_core::Identifier;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::key_management::{
|
|
||||||
KeyChain,
|
|
||||||
group_key_holder::GroupKeyHolder,
|
|
||||||
key_tree::{KeyTreePrivate, KeyTreePublic, chain_index::ChainIndex},
|
|
||||||
secret_holders::SeedHolder,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type PublicKey = AffinePoint;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct UserPrivateAccountData {
|
|
||||||
pub key_chain: KeyChain,
|
|
||||||
pub accounts: Vec<(Identifier, Account)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata for a shared account (GMS-derived), stored alongside the cached plaintext state.
|
|
||||||
/// The group label and identifier (or PDA seed) are needed to re-derive keys during sync.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct SharedAccountEntry {
|
|
||||||
pub group_label: String,
|
|
||||||
pub identifier: Identifier,
|
|
||||||
/// For PDA accounts, the seed and program ID used to derive keys via `derive_keys_for_pda`.
|
|
||||||
/// `None` for regular shared accounts (keys derived from identifier via derivation seed).
|
|
||||||
#[serde(default)]
|
|
||||||
pub pda_seed: Option<nssa_core::program::PdaSeed>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub pda_program_id: Option<nssa_core::program::ProgramId>,
|
|
||||||
pub account: Account,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct NSSAUserData {
|
|
||||||
/// Default public accounts.
|
|
||||||
pub default_pub_account_signing_keys: BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
|
||||||
/// Default private accounts.
|
|
||||||
pub default_user_private_accounts: BTreeMap<AccountId, UserPrivateAccountData>,
|
|
||||||
/// Tree of public keys.
|
|
||||||
pub public_key_tree: KeyTreePublic,
|
|
||||||
/// Tree of private keys.
|
|
||||||
pub private_key_tree: KeyTreePrivate,
|
|
||||||
/// Group key holders for shared account management, keyed by a human-readable label.
|
|
||||||
pub group_key_holders: BTreeMap<String, GroupKeyHolder>,
|
|
||||||
/// Cached plaintext state of shared private accounts (PDAs and regular shared accounts),
|
|
||||||
/// keyed by `AccountId`. Each entry stores the group label and identifier needed
|
|
||||||
/// to re-derive keys during sync.
|
|
||||||
pub shared_private_accounts: BTreeMap<nssa::AccountId, SharedAccountEntry>,
|
|
||||||
/// Dedicated sealing secret key for GMS distribution. Generated once via
|
|
||||||
/// `wallet group new-sealing-key`. The corresponding public key is shared with
|
|
||||||
/// group members so they can seal GMS for this wallet.
|
|
||||||
pub sealing_secret_key: Option<nssa_core::encryption::Scalar>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NSSAUserData {
|
|
||||||
fn valid_public_key_transaction_pairing_check(
|
|
||||||
accounts_keys_map: &BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
|
||||||
) -> bool {
|
|
||||||
let mut check_res = true;
|
|
||||||
for (account_id, key) in accounts_keys_map {
|
|
||||||
let expected_account_id =
|
|
||||||
nssa::AccountId::from(&nssa::PublicKey::new_from_private_key(key));
|
|
||||||
if &expected_account_id != account_id {
|
|
||||||
println!("{expected_account_id}, {account_id}");
|
|
||||||
check_res = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check_res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn valid_private_key_transaction_pairing_check(
|
|
||||||
accounts_keys_map: &BTreeMap<AccountId, UserPrivateAccountData>,
|
|
||||||
) -> bool {
|
|
||||||
let mut check_res = true;
|
|
||||||
for (account_id, entry) in accounts_keys_map {
|
|
||||||
let any_match = entry.accounts.iter().any(|(identifier, _)| {
|
|
||||||
nssa::AccountId::from((&entry.key_chain.nullifier_public_key, *identifier))
|
|
||||||
== *account_id
|
|
||||||
});
|
|
||||||
if !any_match {
|
|
||||||
println!("No matching entry found for account_id {account_id}");
|
|
||||||
check_res = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check_res
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_accounts(
|
|
||||||
default_accounts_keys: BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
|
||||||
default_accounts_key_chains: BTreeMap<AccountId, UserPrivateAccountData>,
|
|
||||||
public_key_tree: KeyTreePublic,
|
|
||||||
private_key_tree: KeyTreePrivate,
|
|
||||||
) -> Result<Self> {
|
|
||||||
if !Self::valid_public_key_transaction_pairing_check(&default_accounts_keys) {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Key transaction pairing check not satisfied, there are public account_ids, which are not derived from keys"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Self::valid_private_key_transaction_pairing_check(&default_accounts_key_chains) {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Key transaction pairing check not satisfied, there are private account_ids, which are not derived from keys"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
default_pub_account_signing_keys: default_accounts_keys,
|
|
||||||
default_user_private_accounts: default_accounts_key_chains,
|
|
||||||
public_key_tree,
|
|
||||||
private_key_tree,
|
|
||||||
group_key_holders: BTreeMap::new(),
|
|
||||||
shared_private_accounts: BTreeMap::new(),
|
|
||||||
sealing_secret_key: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generated new private key for public transaction signatures.
|
|
||||||
///
|
|
||||||
/// Returns the `account_id` of new account.
|
|
||||||
pub fn generate_new_public_transaction_private_key(
|
|
||||||
&mut self,
|
|
||||||
parent_cci: Option<ChainIndex>,
|
|
||||||
) -> (nssa::AccountId, ChainIndex) {
|
|
||||||
match parent_cci {
|
|
||||||
Some(parent_cci) => self
|
|
||||||
.public_key_tree
|
|
||||||
.generate_new_public_node(&parent_cci)
|
|
||||||
.expect("Parent must be present in a tree"),
|
|
||||||
None => self
|
|
||||||
.public_key_tree
|
|
||||||
.generate_new_public_node_layered()
|
|
||||||
.expect("Search for new node slot failed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the signing key for public transaction signatures.
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_pub_account_signing_key(
|
|
||||||
&self,
|
|
||||||
account_id: nssa::AccountId,
|
|
||||||
) -> Option<&nssa::PrivateKey> {
|
|
||||||
self.default_pub_account_signing_keys
|
|
||||||
.get(&account_id)
|
|
||||||
.or_else(|| self.public_key_tree.get_node(account_id).map(Into::into))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new receiving key node and returns its `ChainIndex`.
|
|
||||||
pub fn create_private_accounts_key(&mut self, parent_cci: Option<ChainIndex>) -> ChainIndex {
|
|
||||||
match parent_cci {
|
|
||||||
Some(parent_cci) => self
|
|
||||||
.private_key_tree
|
|
||||||
.create_private_accounts_key_node(&parent_cci)
|
|
||||||
.expect("Parent must be present in a tree"),
|
|
||||||
None => self
|
|
||||||
.private_key_tree
|
|
||||||
.create_private_accounts_key_node_layered()
|
|
||||||
.expect("Search for new node slot failed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers an additional identifier on an existing private key node, deriving and recording
|
|
||||||
/// the corresponding `AccountId`. Returns `None` if the node does not exist or the identifier
|
|
||||||
/// is already registered.
|
|
||||||
pub fn register_identifier_on_private_key_chain(
|
|
||||||
&mut self,
|
|
||||||
cci: &ChainIndex,
|
|
||||||
identifier: Identifier,
|
|
||||||
) -> Option<nssa::AccountId> {
|
|
||||||
self.private_key_tree
|
|
||||||
.register_identifier_on_node(cci, identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the key chain and account data for the given private account ID.
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_private_account(
|
|
||||||
&self,
|
|
||||||
account_id: nssa::AccountId,
|
|
||||||
) -> Option<(KeyChain, nssa_core::account::Account, Identifier)> {
|
|
||||||
// Check default accounts
|
|
||||||
if let Some(entry) = self.default_user_private_accounts.get(&account_id) {
|
|
||||||
for (identifier, account) in &entry.accounts {
|
|
||||||
let expected_id =
|
|
||||||
nssa::AccountId::from((&entry.key_chain.nullifier_public_key, *identifier));
|
|
||||||
if expected_id == account_id {
|
|
||||||
return Some((entry.key_chain.clone(), account.clone(), *identifier));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// Check tree
|
|
||||||
if let Some(node) = self.private_key_tree.get_node(account_id) {
|
|
||||||
let key_chain = &node.value.0;
|
|
||||||
for (identifier, account) in &node.value.1 {
|
|
||||||
let expected_id =
|
|
||||||
nssa::AccountId::from((&key_chain.nullifier_public_key, *identifier));
|
|
||||||
if expected_id == account_id {
|
|
||||||
return Some((key_chain.clone(), account.clone(), *identifier));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
|
||||||
self.public_account_ids().chain(self.private_account_ids())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn public_account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
|
||||||
self.default_pub_account_signing_keys
|
|
||||||
.keys()
|
|
||||||
.copied()
|
|
||||||
.chain(self.public_key_tree.account_id_map.keys().copied())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn private_account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
|
||||||
self.default_user_private_accounts
|
|
||||||
.keys()
|
|
||||||
.copied()
|
|
||||||
.chain(self.private_key_tree.account_id_map.keys().copied())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `GroupKeyHolder` for the given label, if it exists.
|
|
||||||
#[must_use]
|
|
||||||
pub fn group_key_holder(&self, label: &str) -> Option<&GroupKeyHolder> {
|
|
||||||
self.group_key_holders.get(label)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts or replaces a `GroupKeyHolder` under the given label.
|
|
||||||
///
|
|
||||||
/// If a holder already exists under this label, it is silently replaced and the old
|
|
||||||
/// GMS is lost. Callers must ensure label uniqueness across groups.
|
|
||||||
pub fn insert_group_key_holder(&mut self, label: String, holder: GroupKeyHolder) {
|
|
||||||
self.group_key_holders.insert(label, holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the cached account for a shared private account, if it exists.
|
|
||||||
#[must_use]
|
|
||||||
pub fn shared_private_account(
|
|
||||||
&self,
|
|
||||||
account_id: &nssa::AccountId,
|
|
||||||
) -> Option<&SharedAccountEntry> {
|
|
||||||
self.shared_private_accounts.get(account_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts or replaces a shared private account entry.
|
|
||||||
pub fn insert_shared_private_account(
|
|
||||||
&mut self,
|
|
||||||
account_id: nssa::AccountId,
|
|
||||||
entry: SharedAccountEntry,
|
|
||||||
) {
|
|
||||||
self.shared_private_accounts.insert(account_id, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the cached account state for a shared private account.
|
|
||||||
pub fn update_shared_private_account_state(
|
|
||||||
&mut self,
|
|
||||||
account_id: &nssa::AccountId,
|
|
||||||
account: nssa_core::account::Account,
|
|
||||||
) {
|
|
||||||
if let Some(entry) = self.shared_private_accounts.get_mut(account_id) {
|
|
||||||
entry.account = account;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterates over all shared private accounts.
|
|
||||||
pub fn shared_private_accounts_iter(
|
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = (&nssa::AccountId, &SharedAccountEntry)> {
|
|
||||||
self.shared_private_accounts.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for NSSAUserData {
|
|
||||||
fn default() -> Self {
|
|
||||||
let (seed_holder, _mnemonic) = SeedHolder::new_mnemonic("");
|
|
||||||
Self::new_with_accounts(
|
|
||||||
BTreeMap::new(),
|
|
||||||
BTreeMap::new(),
|
|
||||||
KeyTreePublic::new(&seed_holder),
|
|
||||||
KeyTreePrivate::new(&seed_holder),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn group_key_holder_storage_round_trip() {
|
|
||||||
let mut user_data = NSSAUserData::default();
|
|
||||||
assert!(user_data.group_key_holder("test-group").is_none());
|
|
||||||
|
|
||||||
let holder = GroupKeyHolder::from_gms([42_u8; 32]);
|
|
||||||
user_data.insert_group_key_holder(String::from("test-group"), holder.clone());
|
|
||||||
|
|
||||||
let retrieved = user_data
|
|
||||||
.group_key_holder("test-group")
|
|
||||||
.expect("should exist");
|
|
||||||
assert_eq!(retrieved.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn group_key_holders_default_empty() {
|
|
||||||
let user_data = NSSAUserData::default();
|
|
||||||
assert!(user_data.group_key_holders.is_empty());
|
|
||||||
assert!(user_data.shared_private_accounts.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn shared_account_entry_serde_round_trip() {
|
|
||||||
use nssa_core::program::PdaSeed;
|
|
||||||
|
|
||||||
let entry = SharedAccountEntry {
|
|
||||||
group_label: String::from("test-group"),
|
|
||||||
identifier: 42,
|
|
||||||
pda_seed: None,
|
|
||||||
pda_program_id: None,
|
|
||||||
account: nssa_core::account::Account::default(),
|
|
||||||
};
|
|
||||||
let encoded = bincode::serialize(&entry).expect("serialize");
|
|
||||||
let decoded: SharedAccountEntry = bincode::deserialize(&encoded).expect("deserialize");
|
|
||||||
assert_eq!(decoded.group_label, "test-group");
|
|
||||||
assert_eq!(decoded.identifier, 42);
|
|
||||||
assert!(decoded.pda_seed.is_none());
|
|
||||||
|
|
||||||
let pda_entry = SharedAccountEntry {
|
|
||||||
group_label: String::from("pda-group"),
|
|
||||||
identifier: u128::MAX,
|
|
||||||
pda_seed: Some(PdaSeed::new([7_u8; 32])),
|
|
||||||
pda_program_id: Some([9; 8]),
|
|
||||||
account: nssa_core::account::Account::default(),
|
|
||||||
};
|
|
||||||
let pda_encoded = bincode::serialize(&pda_entry).expect("serialize pda");
|
|
||||||
let pda_decoded: SharedAccountEntry =
|
|
||||||
bincode::deserialize(&pda_encoded).expect("deserialize pda");
|
|
||||||
assert_eq!(pda_decoded.group_label, "pda-group");
|
|
||||||
assert_eq!(pda_decoded.identifier, u128::MAX);
|
|
||||||
assert_eq!(pda_decoded.pda_seed.unwrap(), PdaSeed::new([7_u8; 32]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn shared_account_entry_none_pda_seed_round_trips() {
|
|
||||||
// Verify that an entry with pda_seed=None serializes and deserializes correctly,
|
|
||||||
// confirming the #[serde(default)] attribute works for backward compatibility.
|
|
||||||
let entry = SharedAccountEntry {
|
|
||||||
group_label: String::from("old"),
|
|
||||||
identifier: 1,
|
|
||||||
pda_seed: None,
|
|
||||||
pda_program_id: None,
|
|
||||||
account: nssa_core::account::Account::default(),
|
|
||||||
};
|
|
||||||
let encoded = bincode::serialize(&entry).expect("serialize");
|
|
||||||
let decoded: SharedAccountEntry = bincode::deserialize(&encoded).expect("deserialize");
|
|
||||||
assert_eq!(decoded.group_label, "old");
|
|
||||||
assert_eq!(decoded.identifier, 1);
|
|
||||||
assert!(decoded.pda_seed.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn shared_account_derives_consistent_keys_from_group() {
|
|
||||||
use nssa_core::program::PdaSeed;
|
|
||||||
|
|
||||||
let mut user_data = NSSAUserData::default();
|
|
||||||
let gms_holder = GroupKeyHolder::from_gms([42_u8; 32]);
|
|
||||||
user_data.insert_group_key_holder(String::from("my-group"), gms_holder);
|
|
||||||
|
|
||||||
let holder = user_data.group_key_holder("my-group").unwrap();
|
|
||||||
|
|
||||||
// Regular shared account: derive via tag
|
|
||||||
let tag = [1_u8; 32];
|
|
||||||
let keys_a = holder.derive_keys_for_shared_account(&tag);
|
|
||||||
let keys_b = holder.derive_keys_for_shared_account(&tag);
|
|
||||||
assert_eq!(
|
|
||||||
keys_a.generate_nullifier_public_key(),
|
|
||||||
keys_b.generate_nullifier_public_key(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// PDA shared account: derive via seed
|
|
||||||
let seed = PdaSeed::new([2_u8; 32]);
|
|
||||||
let pda_keys_a = holder.derive_keys_for_pda(&[9; 8], &seed);
|
|
||||||
let pda_keys_b = holder.derive_keys_for_pda(&[9; 8], &seed);
|
|
||||||
assert_eq!(
|
|
||||||
pda_keys_a.generate_nullifier_public_key(),
|
|
||||||
pda_keys_b.generate_nullifier_public_key(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// PDA and shared derivations don't collide
|
|
||||||
assert_ne!(
|
|
||||||
keys_a.generate_nullifier_public_key(),
|
|
||||||
pda_keys_a.generate_nullifier_public_key(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_account() {
|
|
||||||
let mut user_data = NSSAUserData::default();
|
|
||||||
|
|
||||||
let chain_index = user_data.create_private_accounts_key(Some(ChainIndex::root()));
|
|
||||||
|
|
||||||
let is_key_chain_generated = user_data
|
|
||||||
.private_key_tree
|
|
||||||
.key_map
|
|
||||||
.contains_key(&chain_index);
|
|
||||||
assert!(is_key_chain_generated);
|
|
||||||
|
|
||||||
let key_chain = &user_data.private_key_tree.key_map[&chain_index].value.0;
|
|
||||||
println!("{key_chain:#?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
#![expect(clippy::print_stdout, reason = "TODO: fix later")]
|
#![expect(clippy::print_stdout, reason = "TODO: fix later")]
|
||||||
|
|
||||||
pub mod key_management;
|
pub mod key_management;
|
||||||
pub mod key_protocol_core;
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@ risc0-binfmt = "3.0.2"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
token_core.workspace = true
|
token_core.workspace = true
|
||||||
|
authenticated_transfer_core.workspace = true
|
||||||
test_program_methods.workspace = true
|
test_program_methods.workspace = true
|
||||||
|
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
|||||||
@ -17,7 +17,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{SharedSecretKey, encryption::Scalar};
|
use crate::{SharedSecretKey, encryption::Scalar};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, BorshSerialize, BorshDeserialize,
|
||||||
|
)]
|
||||||
pub struct Secp256k1Point(pub Vec<u8>);
|
pub struct Secp256k1Point(pub Vec<u8>);
|
||||||
|
|
||||||
impl std::fmt::Debug for Secp256k1Point {
|
impl std::fmt::Debug for Secp256k1Point {
|
||||||
@ -56,8 +58,8 @@ impl From<&EphemeralSecretKey> for EphemeralPublicKey {
|
|||||||
impl SharedSecretKey {
|
impl SharedSecretKey {
|
||||||
/// Creates a new shared secret key from a scalar and a point.
|
/// Creates a new shared secret key from a scalar and a point.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(scalar: &Scalar, point: &Secp256k1Point) -> Self {
|
pub fn new(scalar: Scalar, point: &Secp256k1Point) -> Self {
|
||||||
let scalar = k256::Scalar::from_repr((*scalar).into()).unwrap();
|
let scalar = k256::Scalar::from_repr(scalar.into()).unwrap();
|
||||||
let point: [u8; 33] = point.0.clone().try_into().unwrap();
|
let point: [u8; 33] = point.0.clone().try_into().unwrap();
|
||||||
|
|
||||||
let encoded = EncodedPoint::from_bytes(point).unwrap();
|
let encoded = EncodedPoint::from_bytes(point).unwrap();
|
||||||
|
|||||||
@ -24,6 +24,8 @@ pub mod program;
|
|||||||
#[cfg(feature = "host")]
|
#[cfg(feature = "host")]
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
|
pub const GENESIS_BLOCK_ID: BlockId = 1;
|
||||||
|
|
||||||
pub type BlockId = u64;
|
pub type BlockId = u64;
|
||||||
/// Unix timestamp in milliseconds.
|
/// Unix timestamp in milliseconds.
|
||||||
pub type Timestamp = u64;
|
pub type Timestamp = u64;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const PRIVATE_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/Private/\x00\
|
|||||||
|
|
||||||
pub type Identifier = u128;
|
pub type Identifier = u128;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
#[cfg_attr(any(feature = "host", test), derive(Hash))]
|
#[cfg_attr(any(feature = "host", test), derive(Hash))]
|
||||||
pub struct NullifierPublicKey(pub [u8; 32]);
|
pub struct NullifierPublicKey(pub [u8; 32]);
|
||||||
|
|
||||||
|
|||||||
@ -636,7 +636,6 @@ pub fn validate_execution(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 8. Total balance is preserved
|
// 8. Total balance is preserved
|
||||||
|
|
||||||
let Some(total_balance_pre_states) =
|
let Some(total_balance_pre_states) =
|
||||||
WrappedBalanceSum::from_balances(pre_states.iter().map(|pre| pre.account.balance))
|
WrappedBalanceSum::from_balances(pre_states.iter().map(|pre| pre.account.balance))
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
pub use nssa_core::{
|
pub use nssa_core::{
|
||||||
SharedSecretKey,
|
GENESIS_BLOCK_ID, SharedSecretKey,
|
||||||
account::{Account, AccountId, Data},
|
account::{Account, AccountId, Data},
|
||||||
encryption::EphemeralPublicKey,
|
encryption::EphemeralPublicKey,
|
||||||
program::ProgramId,
|
program::ProgramId,
|
||||||
|
|||||||
@ -17,6 +17,26 @@ pub struct MerkleTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MerkleTree {
|
impl MerkleTree {
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
// Adjust capacity to ensure power of two
|
||||||
|
let capacity = capacity.next_power_of_two();
|
||||||
|
let total_depth = usize::try_from(capacity.trailing_zeros()).expect("u32 fits in usize");
|
||||||
|
|
||||||
|
let nodes = default_values::DEFAULT_VALUES[..=total_depth]
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(level, default_value)| std::iter::repeat_n(default_value, 1 << level))
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
nodes,
|
||||||
|
capacity,
|
||||||
|
length: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root(&self) -> Node {
|
pub fn root(&self) -> Node {
|
||||||
let root_index = self.root_index();
|
let root_index = self.root_index();
|
||||||
*self.get_node(root_index)
|
*self.get_node(root_index)
|
||||||
@ -49,26 +69,6 @@ impl MerkleTree {
|
|||||||
self.nodes[index] = node;
|
self.nodes[index] = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
|
||||||
// Adjust capacity to ensure power of two
|
|
||||||
let capacity = capacity.next_power_of_two();
|
|
||||||
let total_depth = usize::try_from(capacity.trailing_zeros()).expect("u32 fits in usize");
|
|
||||||
|
|
||||||
let nodes = default_values::DEFAULT_VALUES[..=total_depth]
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.enumerate()
|
|
||||||
.flat_map(|(level, default_value)| std::iter::repeat_n(default_value, 1 << level))
|
|
||||||
.copied()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
nodes,
|
|
||||||
capacity,
|
|
||||||
length: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reallocates storage of Merkle tree for double capacity.
|
/// Reallocates storage of Merkle tree for double capacity.
|
||||||
/// The current tree is embedded into the new tree as a subtree.
|
/// The current tree is embedded into the new tree as a subtree.
|
||||||
fn reallocate_to_double_capacity(&mut self) {
|
fn reallocate_to_double_capacity(&mut self) {
|
||||||
|
|||||||
@ -228,11 +228,14 @@ mod tests {
|
|||||||
let expected_sender_pre = sender.clone();
|
let expected_sender_pre = sender.clone();
|
||||||
|
|
||||||
let esk = [3; 32];
|
let esk = [3; 32];
|
||||||
let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.vpk());
|
let shared_secret = SharedSecretKey::new(esk, &recipient_keys.vpk());
|
||||||
|
|
||||||
let (output, proof) = execute_and_prove(
|
let (output, proof) = execute_and_prove(
|
||||||
vec![sender, recipient],
|
vec![sender, recipient],
|
||||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer {
|
||||||
|
amount: balance_to_move,
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
vec![
|
vec![
|
||||||
InputAccountIdentity::Public,
|
InputAccountIdentity::Public,
|
||||||
InputAccountIdentity::PrivateUnauthorized {
|
InputAccountIdentity::PrivateUnauthorized {
|
||||||
@ -322,14 +325,17 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let esk_1 = [3; 32];
|
let esk_1 = [3; 32];
|
||||||
let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.vpk());
|
let shared_secret_1 = SharedSecretKey::new(esk_1, &sender_keys.vpk());
|
||||||
|
|
||||||
let esk_2 = [5; 32];
|
let esk_2 = [5; 32];
|
||||||
let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.vpk());
|
let shared_secret_2 = SharedSecretKey::new(esk_2, &recipient_keys.vpk());
|
||||||
|
|
||||||
let (output, proof) = execute_and_prove(
|
let (output, proof) = execute_and_prove(
|
||||||
vec![sender_pre, recipient],
|
vec![sender_pre, recipient],
|
||||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer {
|
||||||
|
amount: balance_to_move,
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
vec![
|
vec![
|
||||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||||
ssk: shared_secret_1,
|
ssk: shared_secret_1,
|
||||||
@ -397,7 +403,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let esk = [3; 32];
|
let esk = [3; 32];
|
||||||
let shared_secret = SharedSecretKey::new(&esk, &account_keys.vpk());
|
let shared_secret = SharedSecretKey::new(esk, &account_keys.vpk());
|
||||||
|
|
||||||
let program_with_deps = ProgramWithDependencies::new(
|
let program_with_deps = ProgramWithDependencies::new(
|
||||||
validity_window_chain_caller,
|
validity_window_chain_caller,
|
||||||
@ -428,7 +434,7 @@ mod tests {
|
|||||||
let keys = test_private_account_keys_1();
|
let keys = test_private_account_keys_1();
|
||||||
let npk = keys.npk();
|
let npk = keys.npk();
|
||||||
let seed = PdaSeed::new([42; 32]);
|
let seed = PdaSeed::new([42; 32]);
|
||||||
let shared_secret_pda = SharedSecretKey::new(&[55; 32], &keys.vpk());
|
let shared_secret_pda = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||||
|
|
||||||
// PDA (new, private PDA) — AccountId derived from auth_transfer_proxy's program ID
|
// PDA (new, private PDA) — AccountId derived from auth_transfer_proxy's program ID
|
||||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk);
|
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk);
|
||||||
@ -465,7 +471,7 @@ mod tests {
|
|||||||
let keys = test_private_account_keys_1();
|
let keys = test_private_account_keys_1();
|
||||||
let npk = keys.npk();
|
let npk = keys.npk();
|
||||||
let seed = PdaSeed::new([42; 32]);
|
let seed = PdaSeed::new([42; 32]);
|
||||||
let shared_secret_pda = SharedSecretKey::new(&[55; 32], &keys.vpk());
|
let shared_secret_pda = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||||
|
|
||||||
// PDA (new, private PDA)
|
// PDA (new, private PDA)
|
||||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk);
|
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk);
|
||||||
@ -518,7 +524,7 @@ mod tests {
|
|||||||
let shared_keys = test_private_account_keys_1();
|
let shared_keys = test_private_account_keys_1();
|
||||||
let shared_npk = shared_keys.npk();
|
let shared_npk = shared_keys.npk();
|
||||||
let shared_identifier: u128 = 42;
|
let shared_identifier: u128 = 42;
|
||||||
let shared_secret = SharedSecretKey::new(&[55; 32], &shared_keys.vpk());
|
let shared_secret = SharedSecretKey::new([55; 32], &shared_keys.vpk());
|
||||||
|
|
||||||
// Sender: public account with balance, owned by auth-transfer
|
// Sender: public account with balance, owned by auth-transfer
|
||||||
let sender_id = AccountId::new([99; 32]);
|
let sender_id = AccountId::new([99; 32]);
|
||||||
|
|||||||
@ -250,7 +250,7 @@ pub mod tests {
|
|||||||
let account_id = nssa_core::account::AccountId::from((&npk, 0));
|
let account_id = nssa_core::account::AccountId::from((&npk, 0));
|
||||||
let commitment = Commitment::new(&account_id, &account);
|
let commitment = Commitment::new(&account_id, &account);
|
||||||
let esk = [3; 32];
|
let esk = [3; 32];
|
||||||
let shared_secret = SharedSecretKey::new(&esk, &vpk);
|
let shared_secret = SharedSecretKey::new(esk, &vpk);
|
||||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||||
let ciphertext = EncryptionScheme::encrypt(&account, 0, &shared_secret, &commitment, 2);
|
let ciphertext = EncryptionScheme::encrypt(&account, 0, &shared_secret, &commitment, 2);
|
||||||
let encrypted_account_data =
|
let encrypted_account_data =
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user