mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-16 13:09:33 +00:00
updating logic
This commit is contained in:
parent
a43314a213
commit
54f6d4922b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4392,6 +4392,7 @@ dependencies = [
|
||||
"pyo3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -298,6 +298,7 @@ async fn claim_funds_from_vault_to_private(
|
||||
],
|
||||
instruction_data,
|
||||
&program_with_dependencies,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.context("Failed to submit private vault claim transaction")?;
|
||||
|
||||
@ -9,10 +9,10 @@ source venv/bin/activate
|
||||
export KEYCARD_PIN=111111
|
||||
|
||||
echo "=== Test: wallet keycard get-private-keys path 10 ==="
|
||||
wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/10"
|
||||
wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/10" --reveal
|
||||
|
||||
echo "=== Test: wallet keycard get-private-keys path 11 ==="
|
||||
wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/11"
|
||||
wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/11" --reveal
|
||||
|
||||
echo ""
|
||||
echo "=== All get-private-keys tests finished ==="
|
||||
|
||||
@ -83,17 +83,17 @@ echo "Test: wallet account get --account-id \"m/44'/60'/0'/0/0\""
|
||||
wallet account get --account-id "Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo"
|
||||
|
||||
echo "=== Test: account get path 0 ==="
|
||||
wallet account get --key-path "m/44'/60'/0'/0/0"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/0"
|
||||
echo "=== Test: account get path 1 ==="
|
||||
wallet account get --key-path "m/44'/60'/0'/0/1"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/1"
|
||||
echo ""
|
||||
echo "=== Test (1): Shielded auth-transfer to owned private account ==="
|
||||
|
||||
wallet auth-transfer send --amount 2 \
|
||||
--from-key-path "m/44'/60'/0'/0/0" \
|
||||
--from "m/44'/60'/0'/0/0" \
|
||||
--to-npk "55204e2934045b044f06d8222b454d46b54788f33c7dec4f6733d441703bb0e6" \
|
||||
--to-vpk "02a8626b0c0ad9383c5678dad48c3969b4174fb377cdb03a6259648032c774cec8"
|
||||
echo "Shielded auth-transfer sent"
|
||||
|
||||
sleep 5
|
||||
wallet account get --key-path "m/44'/60'/0'/0/0"
|
||||
sleep 15
|
||||
wallet account get --account-id "m/44'/60'/0'/0/0"
|
||||
@ -32,7 +32,9 @@ export KEYCARD_PIN=111111
|
||||
echo ""
|
||||
echo "=== Keycard setup ==="
|
||||
wallet keycard available
|
||||
wallet keycard load --mnemonic "fashion degree mountain wool question damp current pond grow dolphin chronic then"
|
||||
export KEYCARD_MNEMONIC="fashion degree mountain wool question damp current pond grow dolphin chronic then"
|
||||
wallet keycard load
|
||||
unset KEYCARD_MNEMONIC
|
||||
|
||||
# =============================================================================
|
||||
# Create non-keycard wallet accounts
|
||||
@ -49,10 +51,10 @@ wallet account new public --label amm-lp-fund 2>/dev/null || true
|
||||
# (1) Create LEZ token — definition AND supply via keycard paths
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (1) Create LEZ token (keycard def=path2, supply=path1) ==="
|
||||
echo "=== (1) Create LEZ token (keycard def=path2, supply=path3) ==="
|
||||
wallet token new \
|
||||
--definition-key-path "m/44'/60'/0'/0/2" \
|
||||
--supply-key-path "m/44'/60'/0'/0/3" \
|
||||
--definition-account-id "m/44'/60'/0'/0/2" \
|
||||
--supply-account-id "m/44'/60'/0'/0/3" \
|
||||
--name LEZ \
|
||||
--total-supply 100000
|
||||
echo "LEZ token created"
|
||||
@ -61,29 +63,29 @@ echo "LEZ token created"
|
||||
# (2) Create LEE token — definition AND supply via keycard paths
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (2) Create LEE token (keycard def=path4, supply=path3) ==="
|
||||
echo "=== (2) Create LEE token (keycard def=path4, supply=path5) ==="
|
||||
wallet token new \
|
||||
--definition-key-path "m/44'/60'/0'/0/4" \
|
||||
--supply-key-path "m/44'/60'/0'/0/5" \
|
||||
--definition-account-id "m/44'/60'/0'/0/4" \
|
||||
--supply-account-id "m/44'/60'/0'/0/5" \
|
||||
--name LEE \
|
||||
--total-supply 100000
|
||||
echo "LEE token created"
|
||||
|
||||
sleep 15
|
||||
|
||||
LEZ_DEF_ID=$(wallet account id --key-path "m/44'/60'/0'/0/2")
|
||||
LEE_DEF_ID=$(wallet account id --key-path "m/44'/60'/0'/0/4")
|
||||
LEZ_DEF_ID=$(wallet account id --account-id "m/44'/60'/0'/0/2")
|
||||
LEE_DEF_ID=$(wallet account id --account-id "m/44'/60'/0'/0/4")
|
||||
echo "LEZ definition ID: $LEZ_DEF_ID"
|
||||
echo "LEE definition ID: $LEE_DEF_ID"
|
||||
|
||||
echo "Keycard path 2 (LEZ definition) state:"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/2"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/2"
|
||||
echo "Keycard path 3 (LEZ supply) state:"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/3"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/3"
|
||||
echo "Keycard path 4 (LEE definition) state:"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/4"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/4"
|
||||
echo "Keycard path 5 (LEE supply) state:"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/5"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/5"
|
||||
|
||||
# =============================================================================
|
||||
# Initialize token holding accounts
|
||||
@ -91,31 +93,36 @@ wallet account get --key-path "m/44'/60'/0'/0/5"
|
||||
echo ""
|
||||
echo "=== Initialize token holding accounts ==="
|
||||
|
||||
# Keycard path 6: LEZ holding
|
||||
wallet token init \
|
||||
--definition-account-id "Public/$LEZ_DEF_ID" \
|
||||
--holder-key-path "m/44'/60'/0'/0/6"
|
||||
echo "LEZ holding initialized for keycard path 4"
|
||||
# Keycard path 6: LEZ holding (mint 0 to initialize)
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder "m/44'/60'/0'/0/6" \
|
||||
--amount 0
|
||||
echo "LEZ holding initialized for keycard path 6"
|
||||
|
||||
# Keycard path 7: LEE holding
|
||||
wallet token init \
|
||||
--definition-account-id "Public/$LEE_DEF_ID" \
|
||||
--holder-key-path "m/44'/60'/0'/0/7"
|
||||
echo "LEE holding initialized for keycard path 5"
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/4" \
|
||||
--holder "m/44'/60'/0'/0/7" \
|
||||
--amount 0
|
||||
echo "LEE holding initialized for keycard path 7"
|
||||
|
||||
# pub-receiver: public LEZ holding (for token transfer test)
|
||||
wallet token init \
|
||||
--definition-account-id "Public/$LEZ_DEF_ID" \
|
||||
--holder-account-label pub-receiver
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder pub-receiver \
|
||||
--amount 0
|
||||
echo "LEZ holding initialized for pub-receiver"
|
||||
|
||||
# AMM seed accounts
|
||||
wallet token init \
|
||||
--definition-account-id "Public/$LEZ_DEF_ID" \
|
||||
--holder-account-label amm-lez-fund
|
||||
wallet token init \
|
||||
--definition-account-id "Public/$LEE_DEF_ID" \
|
||||
--holder-account-label amm-lee-fund
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder amm-lez-fund \
|
||||
--amount 0
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/4" \
|
||||
--holder amm-lee-fund \
|
||||
--amount 0
|
||||
echo "AMM seed holdings initialized"
|
||||
|
||||
# =============================================================================
|
||||
@ -125,39 +132,39 @@ echo ""
|
||||
echo "=== Fund keycard holdings and AMM seed accounts ==="
|
||||
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/3" \
|
||||
--to-key-path "m/44'/60'/0'/0/6" \
|
||||
--from "m/44'/60'/0'/0/3" \
|
||||
--to "m/44'/60'/0'/0/6" \
|
||||
--amount 20000
|
||||
echo "Transferred 20000 LEZ → keycard path 4"
|
||||
echo "Transferred 20000 LEZ → keycard path 6"
|
||||
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/5" \
|
||||
--to-key-path "m/44'/60'/0'/0/7" \
|
||||
--from "m/44'/60'/0'/0/5" \
|
||||
--to "m/44'/60'/0'/0/7" \
|
||||
--amount 20000
|
||||
echo "Transferred 20000 LEE → keycard path 5"
|
||||
echo "Transferred 20000 LEE → keycard path 7"
|
||||
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/3" \
|
||||
--to-label amm-lez-fund \
|
||||
--from "m/44'/60'/0'/0/3" \
|
||||
--to amm-lez-fund \
|
||||
--amount 10000
|
||||
echo "Transferred 10000 LEZ → amm-lez-fund"
|
||||
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/5" \
|
||||
--to-label amm-lee-fund \
|
||||
--from "m/44'/60'/0'/0/5" \
|
||||
--to amm-lee-fund \
|
||||
--amount 10000
|
||||
echo "Transferred 10000 LEE → amm-lee-fund"
|
||||
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 6 (LEZ holding) state (balance should be 20000):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
echo "Keycard path 7 (LEE holding) state (balance should be 20000):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/7"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/7"
|
||||
echo "amm-lez-fund state (balance should be 10000):"
|
||||
wallet account get --account-label amm-lez-fund
|
||||
wallet account get --account-id amm-lez-fund
|
||||
echo "amm-lee-fund state (balance should be 10000):"
|
||||
wallet account get --account-label amm-lee-fund
|
||||
wallet account get --account-id amm-lee-fund
|
||||
|
||||
# =============================================================================
|
||||
# (3) Token transfer: keycard path 6 (LEZ) → public account
|
||||
@ -165,17 +172,17 @@ wallet account get --account-label amm-lee-fund
|
||||
echo ""
|
||||
echo "=== (3) Token transfer: keycard path 6 → pub-receiver (public) ==="
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/6" \
|
||||
--to-label pub-receiver \
|
||||
--from "m/44'/60'/0'/0/6" \
|
||||
--to pub-receiver \
|
||||
--amount 1000
|
||||
echo "Transferred 1000 LEZ: keycard path 6 → pub-receiver"
|
||||
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 6 (LEZ) state (balance should be 19000):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
echo "pub-receiver state (balance should be 1000):"
|
||||
wallet account get --account-label pub-receiver
|
||||
wallet account get --account-id pub-receiver
|
||||
|
||||
# =============================================================================
|
||||
# (4) Token transfer: keycard path 6 (LEZ) → private account (shielded)
|
||||
@ -186,8 +193,8 @@ PRIV_RECEIVER=$(wallet account new private | grep -o 'Private/[^[:space:]]*' | h
|
||||
echo "Fresh private receiver account: $PRIV_RECEIVER"
|
||||
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/6" \
|
||||
--to "$PRIV_RECEIVER" \
|
||||
--from "m/44'/60'/0'/0/6" \
|
||||
--to "$PRIV_RECEIVER" \
|
||||
--amount 500
|
||||
echo "Shielded transfer of 500 LEZ: keycard path 6 → $PRIV_RECEIVER"
|
||||
|
||||
@ -196,7 +203,7 @@ wallet account sync-private
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 6 (LEZ) state (balance should be 18500):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
echo "priv-receiver state (balance should be 500):"
|
||||
wallet account get --account-id "$PRIV_RECEIVER"
|
||||
|
||||
@ -206,17 +213,17 @@ wallet account get --account-id "$PRIV_RECEIVER"
|
||||
echo ""
|
||||
echo "=== (5) Token mint: keycard def path 2 mints 2000 LEZ to keycard path 6 ==="
|
||||
wallet token mint \
|
||||
--definition-key-path "m/44'/60'/0'/0/2" \
|
||||
--holder-key-path "m/44'/60'/0'/0/6" \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder "m/44'/60'/0'/0/6" \
|
||||
--amount 2000
|
||||
echo "Minted 2000 LEZ to keycard path 4"
|
||||
echo "Minted 2000 LEZ to keycard path 6"
|
||||
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 2 (LEZ definition) state (total supply should have increased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/2"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/2"
|
||||
echo "Keycard path 6 (LEZ holding) state (balance should be 20500):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
|
||||
# =============================================================================
|
||||
# (6) Token burn with keycard — holder is keycard path 6
|
||||
@ -224,17 +231,17 @@ wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
echo ""
|
||||
echo "=== (6) Token burn: keycard path 6 burns 500 LEZ ==="
|
||||
wallet token burn \
|
||||
--definition "Public/$LEZ_DEF_ID" \
|
||||
--holder-key-path "m/44'/60'/0'/0/6" \
|
||||
--definition "Public/$LEZ_DEF_ID" \
|
||||
--holder "m/44'/60'/0'/0/6" \
|
||||
--amount 500
|
||||
echo "Burned 500 LEZ from keycard path 4"
|
||||
echo "Burned 500 LEZ from keycard path 6"
|
||||
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 2 (LEZ definition) state (total supply should reflect burn):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/2"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/2"
|
||||
echo "Keycard path 6 (LEZ holding) state (balance should be 20000):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
|
||||
# =============================================================================
|
||||
# (7) Create AMM pool for LEZ/LEE — without keycard
|
||||
@ -243,9 +250,9 @@ echo ""
|
||||
echo "=== (7) Create AMM pool for LEZ/LEE (without keycard) ==="
|
||||
|
||||
wallet amm new \
|
||||
--user-holding-a-label amm-lez-fund \
|
||||
--user-holding-b-label amm-lee-fund \
|
||||
--user-holding-lp-label amm-lp-fund \
|
||||
--user-holding-a amm-lez-fund \
|
||||
--user-holding-b amm-lee-fund \
|
||||
--user-holding-lp amm-lp-fund \
|
||||
--balance-a 10000 \
|
||||
--balance-b 10000
|
||||
echo "AMM pool created for LEZ/LEE"
|
||||
@ -253,12 +260,12 @@ echo "AMM pool created for LEZ/LEE"
|
||||
sleep 15
|
||||
|
||||
echo "amm-lez-fund state (balance should be 0 — contributed to pool):"
|
||||
wallet account get --account-label amm-lez-fund
|
||||
wallet account get --account-id amm-lez-fund
|
||||
echo "amm-lee-fund state (balance should be 0 — contributed to pool):"
|
||||
wallet account get --account-label amm-lee-fund
|
||||
wallet account get --account-id amm-lee-fund
|
||||
echo "Initial LP holding state (should hold initial LP tokens):"
|
||||
wallet account get --account-label amm-lp-fund
|
||||
LP_DEF_ID=$(wallet account get --account-label amm-lp-fund | grep -o '"definition_id":"[^"]*"' | awk -F'"' '{print $4}')
|
||||
wallet account get --account-id amm-lp-fund
|
||||
LP_DEF_ID=$(wallet account get --account-id amm-lp-fund | grep -o '"definition_id":"[^"]*"' | awk -F'"' '{print $4}')
|
||||
echo "LP token definition ID: $LP_DEF_ID"
|
||||
|
||||
# =============================================================================
|
||||
@ -268,8 +275,8 @@ echo "LP token definition ID: $LP_DEF_ID"
|
||||
echo ""
|
||||
echo "=== (8) Swap: keycard path 7 sells 500 LEE, keycard path 6 receives LEZ ==="
|
||||
wallet amm swap-exact-input \
|
||||
--user-holding-a-key-path "m/44'/60'/0'/0/6" \
|
||||
--user-holding-b-key-path "m/44'/60'/0'/0/7" \
|
||||
--user-holding-a "m/44'/60'/0'/0/6" \
|
||||
--user-holding-b "m/44'/60'/0'/0/7" \
|
||||
--amount-in 500 \
|
||||
--min-amount-out 1 \
|
||||
--token-definition "$LEE_DEF_ID"
|
||||
@ -278,31 +285,32 @@ echo "Swap LEE → LEZ complete via keycard"
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 6 (LEZ holding) state (balance should have increased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
echo "Keycard path 7 (LEE holding) state (balance should have decreased by 500):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/7"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/7"
|
||||
|
||||
# =============================================================================
|
||||
# (9) Add liquidity — keycard accounts for holding A (path 6), B (path 7), LP (path 8)
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (9) Initialize LP holding (keycard path 8) before add-liquidity ==="
|
||||
wallet token init \
|
||||
--definition-account-id "Public/$LP_DEF_ID" \
|
||||
--holder-key-path "m/44'/60'/0'/0/8"
|
||||
wallet token mint \
|
||||
--definition "Public/$LP_DEF_ID" \
|
||||
--holder "m/44'/60'/0'/0/8" \
|
||||
--amount 0
|
||||
echo "Keycard path 8 (LP holding) initialized"
|
||||
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 8 (LP holding) state (after init):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/8"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/8"
|
||||
|
||||
echo ""
|
||||
echo "=== (9) Add liquidity (keycard path 6=LEZ, path 7=LEE, path 8=LP) ==="
|
||||
wallet amm add-liquidity \
|
||||
--user-holding-a-key-path "m/44'/60'/0'/0/6" \
|
||||
--user-holding-b-key-path "m/44'/60'/0'/0/7" \
|
||||
--user-holding-lp-key-path "m/44'/60'/0'/0/8" \
|
||||
--user-holding-a "m/44'/60'/0'/0/6" \
|
||||
--user-holding-b "m/44'/60'/0'/0/7" \
|
||||
--user-holding-lp "m/44'/60'/0'/0/8" \
|
||||
--max-amount-a 1000 \
|
||||
--max-amount-b 1000 \
|
||||
--min-amount-lp 1
|
||||
@ -311,11 +319,11 @@ echo "Add liquidity complete via keycard"
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 6 (LEZ holding) state (balance should have decreased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
echo "Keycard path 7 (LEE holding) state (balance should have decreased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/7"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/7"
|
||||
echo "Keycard path 8 (LP holding) state (should have received LP tokens):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/8"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/8"
|
||||
|
||||
# =============================================================================
|
||||
# (10) Remove liquidity — keycard accounts for holding A (path 6), B (path 7), LP (path 8)
|
||||
@ -323,9 +331,9 @@ wallet account get --key-path "m/44'/60'/0'/0/8"
|
||||
echo ""
|
||||
echo "=== (10) Remove liquidity (keycard path 6=LEZ, path 7=LEE, path 8=LP) ==="
|
||||
wallet amm remove-liquidity \
|
||||
--user-holding-a-key-path "m/44'/60'/0'/0/6" \
|
||||
--user-holding-b-key-path "m/44'/60'/0'/0/7" \
|
||||
--user-holding-lp-key-path "m/44'/60'/0'/0/8" \
|
||||
--user-holding-a "m/44'/60'/0'/0/6" \
|
||||
--user-holding-b "m/44'/60'/0'/0/7" \
|
||||
--user-holding-lp "m/44'/60'/0'/0/8" \
|
||||
--balance-lp 500 \
|
||||
--min-amount-a 1 \
|
||||
--min-amount-b 1
|
||||
@ -334,22 +342,22 @@ echo "Remove liquidity complete via keycard"
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 6 (LEZ holding) state (balance should have increased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/6"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/6"
|
||||
echo "Keycard path 7 (LEE holding) state (balance should have increased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/7"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/7"
|
||||
echo "Keycard path 8 (LP holding) state (balance should have decreased):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/8"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/8"
|
||||
|
||||
# =============================================================================
|
||||
# (11) ATA create — keycard path 9 as owner for LEZ
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (11) ATA create: keycard path 9 as owner, LEZ token ==="
|
||||
ATA_OWNER_ID=$(wallet account id --key-path "m/44'/60'/0'/0/9")
|
||||
ATA_OWNER_ID=$(wallet account id --account-id "m/44'/60'/0'/0/9")
|
||||
echo "ATA owner (keycard path 9): $ATA_OWNER_ID"
|
||||
|
||||
wallet ata create \
|
||||
--key-path "m/44'/60'/0'/0/9" \
|
||||
--owner "m/44'/60'/0'/0/9" \
|
||||
--token-definition "$LEZ_DEF_ID"
|
||||
echo "ATA created for keycard path 9 / LEZ"
|
||||
|
||||
@ -362,8 +370,8 @@ wallet account get --account-id "Public/$LEZ_ATA_ID"
|
||||
|
||||
# Fund the ATA from LEZ supply (path 3) — setup for tests 12 and 13
|
||||
wallet token send \
|
||||
--from-key-path "m/44'/60'/0'/0/3" \
|
||||
--to "Public/$LEZ_ATA_ID" \
|
||||
--from "m/44'/60'/0'/0/3" \
|
||||
--to "Public/$LEZ_ATA_ID" \
|
||||
--amount 3000
|
||||
echo "Funded keycard path 9 ATA with 3000 LEZ"
|
||||
|
||||
@ -373,11 +381,11 @@ echo "ATA state after funding (balance should be 3000):"
|
||||
wallet account get --account-id "Public/$LEZ_ATA_ID"
|
||||
|
||||
# =============================================================================
|
||||
# (12) ATA send — keycard path 7's ATA → pub-receiver's ATA
|
||||
# (12) ATA send — keycard path 9's ATA → pub-receiver's ATA
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (12) ATA send: keycard path 7's ATA → pub-receiver's ATA ==="
|
||||
PUB_RECEIVER_ID=$(wallet account id --account-label pub-receiver)
|
||||
echo "=== (12) ATA send: keycard path 9's ATA → pub-receiver's ATA ==="
|
||||
PUB_RECEIVER_ID=$(wallet account id --account-id pub-receiver)
|
||||
wallet ata create \
|
||||
--owner "Public/$PUB_RECEIVER_ID" \
|
||||
--token-definition "$LEZ_DEF_ID"
|
||||
@ -391,7 +399,7 @@ echo "pub-receiver ATA state (should be initialized with zero balance):"
|
||||
wallet account get --account-id "Public/$PUB_RECEIVER_ATA_ID"
|
||||
|
||||
wallet ata send \
|
||||
--from-key-path "m/44'/60'/0'/0/9" \
|
||||
--from "m/44'/60'/0'/0/9" \
|
||||
--token-definition "$LEZ_DEF_ID" \
|
||||
--to "$PUB_RECEIVER_ATA_ID" \
|
||||
--amount 500
|
||||
@ -405,12 +413,12 @@ echo "pub-receiver ATA state (balance should be 500):"
|
||||
wallet account get --account-id "Public/$PUB_RECEIVER_ATA_ID"
|
||||
|
||||
# =============================================================================
|
||||
# (13) ATA burn — keycard path 7's ATA burns 200 LEZ
|
||||
# (13) ATA burn — keycard path 9's ATA burns 200 LEZ
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (13) ATA burn: keycard path 7's ATA burns 200 LEZ ==="
|
||||
echo "=== (13) ATA burn: keycard path 9's ATA burns 200 LEZ ==="
|
||||
wallet ata burn \
|
||||
--key-path "m/44'/60'/0'/0/9" \
|
||||
--holder "m/44'/60'/0'/0/9" \
|
||||
--token-definition "$LEZ_DEF_ID" \
|
||||
--amount 200
|
||||
echo "Burned 200 LEZ from keycard path 9 ATA"
|
||||
@ -420,7 +428,7 @@ sleep 15
|
||||
echo "Keycard path 9 ATA state (balance should be 2300):"
|
||||
wallet account get --account-id "Public/$LEZ_ATA_ID"
|
||||
echo "LEZ definition state (total supply should reflect burn):"
|
||||
wallet account get --key-path "m/44'/60'/0'/0/2"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/2"
|
||||
|
||||
echo ""
|
||||
echo "=== All keycard token + AMM + ATA tests finished ==="
|
||||
|
||||
@ -13,4 +13,5 @@ nssa_core.workspace = true
|
||||
pyo3.workspace = true
|
||||
log.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
serde_json.workspace = true
|
||||
zeroize = "1"
|
||||
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
||||
use nssa::{AccountId, PublicKey, Signature};
|
||||
use nssa_core::NullifierPublicKey;
|
||||
use pyo3::{prelude::*, types::PyAny};
|
||||
use zeroize::Zeroizing;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod python_path;
|
||||
@ -215,26 +216,39 @@ impl KeycardWallet {
|
||||
&self,
|
||||
py: Python,
|
||||
path: &str,
|
||||
) -> PyResult<([u8; 32], [u8; 32])> {
|
||||
) -> PyResult<(Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>)> {
|
||||
let (raw_nsk, raw_vsk): (Vec<u8>, Vec<u8>) = self
|
||||
.instance
|
||||
.bind(py)
|
||||
.call_method1("get_private_keys_for_path", (path,))?
|
||||
.extract()?;
|
||||
|
||||
let nsk: [u8; 32] = raw_nsk.try_into().map_err(|vec: Vec<u8>| {
|
||||
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
|
||||
"expected 32-byte NSK from keycard, got {} bytes",
|
||||
vec.len()
|
||||
))
|
||||
})?;
|
||||
let raw_nsk = Zeroizing::new(raw_nsk);
|
||||
let raw_vsk = Zeroizing::new(raw_vsk);
|
||||
|
||||
let vsk: [u8; 32] = raw_vsk.try_into().map_err(|vec: Vec<u8>| {
|
||||
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
|
||||
"expected 32-byte VSK from keycard, got {} bytes",
|
||||
vec.len()
|
||||
))
|
||||
})?;
|
||||
let nsk = {
|
||||
if raw_nsk.len() != 32 {
|
||||
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
|
||||
"expected 32-byte NSK from keycard, got {} bytes",
|
||||
raw_nsk.len()
|
||||
)));
|
||||
}
|
||||
let mut arr = Zeroizing::new([0u8; 32]);
|
||||
arr.copy_from_slice(&raw_nsk);
|
||||
arr
|
||||
};
|
||||
|
||||
let vsk = {
|
||||
if raw_vsk.len() != 32 {
|
||||
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
|
||||
"expected 32-byte VSK from keycard, got {} bytes",
|
||||
raw_vsk.len()
|
||||
)));
|
||||
}
|
||||
let mut arr = Zeroizing::new([0u8; 32]);
|
||||
arr.copy_from_slice(&raw_vsk);
|
||||
arr
|
||||
};
|
||||
|
||||
Ok((nsk, vsk))
|
||||
}
|
||||
@ -242,7 +256,7 @@ impl KeycardWallet {
|
||||
pub fn get_private_keys_for_path_with_connect(
|
||||
pin: &str,
|
||||
path: &str,
|
||||
) -> PyResult<([u8; 32], [u8; 32])> {
|
||||
) -> PyResult<(Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>)> {
|
||||
Python::with_gil(|py| {
|
||||
python_path::add_python_path(py)?;
|
||||
|
||||
@ -268,7 +282,7 @@ impl KeycardWallet {
|
||||
key_path: &str,
|
||||
) -> PyResult<String> {
|
||||
let (nsk, _vsk) = Self::get_private_keys_for_path_with_connect(pin, key_path)?;
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let npk = NullifierPublicKey::from(&*nsk);
|
||||
|
||||
Ok(format!("Private/{}", AccountId::from((&npk, 0_u128))))
|
||||
}
|
||||
|
||||
@ -15,6 +15,12 @@ use crate::{
|
||||
/// Represents generic chain CLI subcommand.
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum AccountSubcommand {
|
||||
/// Resolve an account mention and print just the account ID (no privacy prefix).
|
||||
Id {
|
||||
/// Account id with privacy prefix, label, or BIP-32 key path.
|
||||
#[arg(long)]
|
||||
account_id: CliAccountMention,
|
||||
},
|
||||
/// Get account data.
|
||||
Get {
|
||||
/// Flag to get raw account data.
|
||||
@ -261,6 +267,14 @@ impl WalletSubcommand for AccountSubcommand {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
Self::Id { account_id } => {
|
||||
let resolved = account_id.resolve(wallet_core.storage())?;
|
||||
let id = match resolved {
|
||||
AccountIdWithPrivacy::Public(id) | AccountIdWithPrivacy::Private(id) => id,
|
||||
};
|
||||
println!("{id}");
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
Self::Get {
|
||||
raw,
|
||||
keys,
|
||||
|
||||
@ -16,6 +16,18 @@ pub enum KeycardSubcommand {
|
||||
Disconnect,
|
||||
Init,
|
||||
Load,
|
||||
/// Retrieve the private keys (NSK, VSK) for a given BIP-32 key path.
|
||||
///
|
||||
/// Prints raw key material to stdout — intended for debugging only.
|
||||
/// Requires --reveal to confirm intent.
|
||||
GetPrivateKeys {
|
||||
/// BIP-32 derivation path, e.g. `m/44'/60'/0'/0/0`.
|
||||
#[arg(long)]
|
||||
key_path: String,
|
||||
/// Confirm that raw NSK and VSK should be disclosed on stdout.
|
||||
#[arg(long)]
|
||||
reveal: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl WalletSubcommand for KeycardSubcommand {
|
||||
@ -131,6 +143,26 @@ impl WalletSubcommand for KeycardSubcommand {
|
||||
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
Self::GetPrivateKeys { key_path, reveal } => {
|
||||
if !reveal {
|
||||
eprintln!(
|
||||
"WARNING: pass --reveal to print NSK and VSK. \
|
||||
Disclosing either key fully compromises the account's privacy."
|
||||
);
|
||||
return Ok(SubcommandReturnValue::Empty);
|
||||
}
|
||||
eprintln!(
|
||||
"WARNING: NSK and VSK are being printed to stdout. \
|
||||
Any terminal log, scrollback, or screen recording captures these keys."
|
||||
);
|
||||
let pin = read_pin()?;
|
||||
let (nsk, vsk) =
|
||||
KeycardWallet::get_private_keys_for_path_with_connect(&pin, &key_path)
|
||||
.map_err(anyhow::Error::from)?;
|
||||
println!("NSK: {}", hex::encode(&*nsk));
|
||||
println!("VSK: {}", hex::encode(&*vsk));
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ impl CliAccountMention {
|
||||
Self::KeyPath(path) => {
|
||||
let pin = read_pin()?;
|
||||
let id_str =
|
||||
keycard_wallet::KeycardWallet::get_account_id_for_path_with_connect(&pin, path)
|
||||
keycard_wallet::KeycardWallet::get_public_account_id_for_path_with_connect(&pin, path)
|
||||
.map_err(anyhow::Error::from)?;
|
||||
AccountIdWithPrivacy::from_str(&id_str)
|
||||
.map_err(|e| anyhow::anyhow!("Invalid account id from keycard: {e}"))
|
||||
@ -159,46 +159,6 @@ impl CliAccountMention {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve to an [`AccountSigner`] for a sender — must sign, never `Foreign`.
|
||||
pub fn to_signer(&self, wallet_core: &WalletCore) -> Result<crate::signing::AccountSigner> {
|
||||
if let Self::KeyPath(path) = self {
|
||||
return Ok(crate::signing::AccountSigner::Keycard(path.clone()));
|
||||
}
|
||||
let account = self.resolve(wallet_core.storage())?;
|
||||
match account {
|
||||
AccountIdWithPrivacy::Public(id) => Ok(crate::signing::AccountSigner::Local(id)),
|
||||
AccountIdWithPrivacy::Private(_) => {
|
||||
anyhow::bail!("Private accounts not supported as senders here")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve to an [`AccountSigner`] for a recipient — returns `Foreign` when the account
|
||||
/// has no local key and no keycard path, meaning no signature or nonce is required.
|
||||
pub fn to_recipient_signer(
|
||||
&self,
|
||||
wallet_core: &WalletCore,
|
||||
) -> Result<crate::signing::AccountSigner> {
|
||||
if let Self::KeyPath(path) = self {
|
||||
return Ok(crate::signing::AccountSigner::Keycard(path.clone()));
|
||||
}
|
||||
let account = self.resolve(wallet_core.storage())?;
|
||||
match account {
|
||||
AccountIdWithPrivacy::Public(id) => Ok(
|
||||
match wallet_core
|
||||
.storage()
|
||||
.key_chain()
|
||||
.pub_account_signing_key(id)
|
||||
{
|
||||
Some(_) => crate::signing::AccountSigner::Local(id),
|
||||
None => crate::signing::AccountSigner::Foreign,
|
||||
},
|
||||
),
|
||||
AccountIdWithPrivacy::Private(_) => {
|
||||
anyhow::bail!("Private accounts not supported as recipients here")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CliAccountMention {
|
||||
|
||||
@ -131,22 +131,25 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
balance_a,
|
||||
balance_b,
|
||||
} => {
|
||||
let user_holding_a = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let user_holding_b = user_holding_b.resolve(wallet_core.storage())?;
|
||||
let user_holding_lp = user_holding_lp.resolve(wallet_core.storage())?;
|
||||
match (user_holding_a, user_holding_b, user_holding_lp) {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
let lp_id = user_holding_lp.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id, lp_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(user_holding_a),
|
||||
AccountIdWithPrivacy::Public(user_holding_b),
|
||||
AccountIdWithPrivacy::Public(user_holding_lp),
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
AccountIdWithPrivacy::Public(lp),
|
||||
) => {
|
||||
Amm(wallet_core)
|
||||
.send_new_definition(
|
||||
user_holding_a,
|
||||
user_holding_b,
|
||||
user_holding_lp,
|
||||
a,
|
||||
b,
|
||||
lp,
|
||||
balance_a,
|
||||
balance_b,
|
||||
&user_holding_a,
|
||||
&user_holding_b,
|
||||
&user_holding_lp,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -165,20 +168,22 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
min_amount_out,
|
||||
token_definition,
|
||||
} => {
|
||||
let user_holding_a = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let user_holding_b = user_holding_b.resolve(wallet_core.storage())?;
|
||||
match (user_holding_a, user_holding_b) {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(user_holding_a),
|
||||
AccountIdWithPrivacy::Public(user_holding_b),
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
) => {
|
||||
Amm(wallet_core)
|
||||
.send_swap_exact_input(
|
||||
user_holding_a,
|
||||
user_holding_b,
|
||||
a,
|
||||
b,
|
||||
amount_in,
|
||||
min_amount_out,
|
||||
token_definition,
|
||||
&user_holding_a,
|
||||
&user_holding_b,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -197,20 +202,22 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
max_amount_in,
|
||||
token_definition,
|
||||
} => {
|
||||
let user_holding_a = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let user_holding_b = user_holding_b.resolve(wallet_core.storage())?;
|
||||
match (user_holding_a, user_holding_b) {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(user_holding_a),
|
||||
AccountIdWithPrivacy::Public(user_holding_b),
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
) => {
|
||||
Amm(wallet_core)
|
||||
.send_swap_exact_output(
|
||||
user_holding_a,
|
||||
user_holding_b,
|
||||
a,
|
||||
b,
|
||||
exact_amount_out,
|
||||
max_amount_in,
|
||||
token_definition,
|
||||
&user_holding_a,
|
||||
&user_holding_b,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -230,23 +237,25 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
max_amount_a,
|
||||
max_amount_b,
|
||||
} => {
|
||||
let user_holding_a = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let user_holding_b = user_holding_b.resolve(wallet_core.storage())?;
|
||||
let user_holding_lp = user_holding_lp.resolve(wallet_core.storage())?;
|
||||
match (user_holding_a, user_holding_b, user_holding_lp) {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
let lp_id = user_holding_lp.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id, lp_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(user_holding_a),
|
||||
AccountIdWithPrivacy::Public(user_holding_b),
|
||||
AccountIdWithPrivacy::Public(user_holding_lp),
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
AccountIdWithPrivacy::Public(lp),
|
||||
) => {
|
||||
Amm(wallet_core)
|
||||
.send_add_liquidity(
|
||||
user_holding_a,
|
||||
user_holding_b,
|
||||
user_holding_lp,
|
||||
a,
|
||||
b,
|
||||
lp,
|
||||
min_amount_lp,
|
||||
max_amount_a,
|
||||
max_amount_b,
|
||||
&user_holding_a,
|
||||
&user_holding_b,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -266,23 +275,24 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
min_amount_a,
|
||||
min_amount_b,
|
||||
} => {
|
||||
let user_holding_a = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let user_holding_b = user_holding_b.resolve(wallet_core.storage())?;
|
||||
let user_holding_lp = user_holding_lp.resolve(wallet_core.storage())?;
|
||||
match (user_holding_a, user_holding_b, user_holding_lp) {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
let lp_id = user_holding_lp.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id, lp_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(user_holding_a),
|
||||
AccountIdWithPrivacy::Public(user_holding_b),
|
||||
AccountIdWithPrivacy::Public(user_holding_lp),
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
AccountIdWithPrivacy::Public(lp),
|
||||
) => {
|
||||
Amm(wallet_core)
|
||||
.send_remove_liquidity(
|
||||
user_holding_a,
|
||||
user_holding_b,
|
||||
user_holding_lp,
|
||||
a,
|
||||
b,
|
||||
lp,
|
||||
balance_lp,
|
||||
min_amount_a,
|
||||
min_amount_b,
|
||||
&user_holding_lp,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@ -91,13 +91,13 @@ impl WalletSubcommand for AtaSubcommand {
|
||||
owner,
|
||||
token_definition,
|
||||
} => {
|
||||
let owner = owner.resolve(wallet_core.storage())?;
|
||||
let owner_resolved = owner.resolve(wallet_core.storage())?;
|
||||
let definition_id = token_definition;
|
||||
|
||||
match owner {
|
||||
match owner_resolved {
|
||||
AccountIdWithPrivacy::Public(owner_id) => {
|
||||
Ata(wallet_core)
|
||||
.send_create(owner_id, definition_id)
|
||||
.send_create(owner_id, definition_id, &owner)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
@ -127,14 +127,14 @@ impl WalletSubcommand for AtaSubcommand {
|
||||
to,
|
||||
amount,
|
||||
} => {
|
||||
let from = from.resolve(wallet_core.storage())?;
|
||||
let from_resolved = from.resolve(wallet_core.storage())?;
|
||||
let definition_id = token_definition;
|
||||
let to_id = to;
|
||||
|
||||
match from {
|
||||
match from_resolved {
|
||||
AccountIdWithPrivacy::Public(from_id) => {
|
||||
Ata(wallet_core)
|
||||
.send_transfer(from_id, definition_id, to_id, amount)
|
||||
.send_transfer(from_id, definition_id, to_id, amount, &from)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
@ -163,13 +163,13 @@ impl WalletSubcommand for AtaSubcommand {
|
||||
token_definition,
|
||||
amount,
|
||||
} => {
|
||||
let holder = holder.resolve(wallet_core.storage())?;
|
||||
let holder_resolved = holder.resolve(wallet_core.storage())?;
|
||||
let definition_id = token_definition;
|
||||
|
||||
match holder {
|
||||
match holder_resolved {
|
||||
AccountIdWithPrivacy::Public(holder_id) => {
|
||||
Ata(wallet_core)
|
||||
.send_burn(holder_id, definition_id, amount)
|
||||
.send_burn(holder_id, definition_id, amount, &holder)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
}
|
||||
AccountIdWithPrivacy::Private(account_id) => {
|
||||
let (tx_hash, secret) = NativeTokenTransfer(wallet_core)
|
||||
.register_account_private(account_id)
|
||||
.register_account_private(account_id, &None)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
@ -151,6 +151,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
from_mention: from_account,
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -175,6 +176,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
to_vpk,
|
||||
to_identifier,
|
||||
amount,
|
||||
from_mention: from_account,
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -247,6 +249,8 @@ pub enum NativeTokenTransferProgramSubcommandShielded {
|
||||
/// amount - amount of balance to move.
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
#[arg(skip)]
|
||||
from_mention: CliAccountMention,
|
||||
},
|
||||
/// Send native token transfer from `from` to `to` for `amount`.
|
||||
///
|
||||
@ -267,6 +271,8 @@ pub enum NativeTokenTransferProgramSubcommandShielded {
|
||||
/// amount - amount of balance to move.
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
#[arg(skip)]
|
||||
from_mention: CliAccountMention,
|
||||
},
|
||||
}
|
||||
|
||||
@ -318,7 +324,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
match self {
|
||||
Self::PrivateOwned { from, to, amount } => {
|
||||
let (tx_hash, [secret_from, secret_to]) = NativeTokenTransfer(wallet_core)
|
||||
.send_private_transfer_to_owned_account(from, to, amount)
|
||||
.send_private_transfer_to_owned_account(from, to, amount, &None)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
@ -363,6 +369,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
to_vpk,
|
||||
to_identifier.unwrap_or_else(rand::random),
|
||||
amount,
|
||||
&None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -393,9 +400,9 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
Self::ShieldedOwned { from, to, amount } => {
|
||||
Self::ShieldedOwned { from, to, amount, from_mention } => {
|
||||
let (tx_hash, secret) = NativeTokenTransfer(wallet_core)
|
||||
.send_shielded_transfer(from, to, amount)
|
||||
.send_shielded_transfer(from, to, amount, &from_mention)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
@ -421,6 +428,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
to_vpk,
|
||||
to_identifier,
|
||||
amount,
|
||||
from_mention,
|
||||
} => {
|
||||
let to_npk_res = hex::decode(to_npk)?;
|
||||
let mut to_npk = [0; 32];
|
||||
@ -440,6 +448,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
to_vpk,
|
||||
to_identifier.unwrap_or_else(rand::random),
|
||||
amount,
|
||||
&from_mention,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -467,7 +476,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
||||
}
|
||||
Self::Deshielded { from, to, amount } => {
|
||||
let (tx_hash, secret) = NativeTokenTransfer(wallet_core)
|
||||
.send_deshielded_transfer(from, to, amount)
|
||||
.send_deshielded_transfer(from, to, amount, &None)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
|
||||
@ -114,16 +114,18 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
name,
|
||||
total_supply,
|
||||
} => {
|
||||
let def_mention = definition_account_id.clone();
|
||||
let sup_mention = supply_account_id.clone();
|
||||
let definition_account_id = definition_account_id.resolve(wallet_core.storage())?;
|
||||
let supply_account_id = supply_account_id.resolve(wallet_core.storage())?;
|
||||
let underlying_subcommand = match (definition_account_id, supply_account_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(definition_account_id),
|
||||
AccountIdWithPrivacy::Public(supply_account_id),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
) => TokenProgramSubcommand::Create(
|
||||
CreateNewTokenProgramSubcommand::NewPublicDefPublicSupp {
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
definition_account_id: def_mention,
|
||||
supply_account_id: sup_mention,
|
||||
name,
|
||||
total_supply,
|
||||
},
|
||||
@ -173,6 +175,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
to_identifier,
|
||||
amount,
|
||||
} => {
|
||||
let from_mention = from.clone();
|
||||
let to_mention = to.clone();
|
||||
let from = from.resolve(wallet_core.storage())?;
|
||||
let to = to
|
||||
.map(|account_mention| account_mention.resolve(wallet_core.storage()))
|
||||
@ -192,11 +196,11 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
anyhow::bail!("List of public keys is uncomplete");
|
||||
}
|
||||
(Some(to), None, None) => match (from, to) {
|
||||
(AccountIdWithPrivacy::Public(from), AccountIdWithPrivacy::Public(to)) => {
|
||||
(AccountIdWithPrivacy::Public(_), AccountIdWithPrivacy::Public(_)) => {
|
||||
TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::TransferToken {
|
||||
sender_account_id: from,
|
||||
recipient_account_id: to,
|
||||
sender_account_id: from_mention,
|
||||
recipient_account_id: to_mention.expect("matched Some branch"),
|
||||
balance_to_move: amount,
|
||||
},
|
||||
)
|
||||
@ -259,15 +263,16 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
holder,
|
||||
amount,
|
||||
} => {
|
||||
let holder_mention = holder.clone();
|
||||
let definition = definition.resolve(wallet_core.storage())?;
|
||||
let holder = holder.resolve(wallet_core.storage())?;
|
||||
let underlying_subcommand = match (definition, holder) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(definition),
|
||||
AccountIdWithPrivacy::Public(holder),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
) => TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::BurnToken {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
holder_account_id: holder_mention,
|
||||
amount,
|
||||
}),
|
||||
(
|
||||
@ -312,6 +317,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
holder_identifier,
|
||||
amount,
|
||||
} => {
|
||||
let def_mention = definition.clone();
|
||||
let hol_mention = holder.clone();
|
||||
let definition = definition.resolve(wallet_core.storage())?;
|
||||
let holder = holder
|
||||
.map(|account_mention| account_mention.resolve(wallet_core.storage()))
|
||||
@ -332,12 +339,12 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
}
|
||||
(Some(holder), None, None) => match (definition, holder) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(definition),
|
||||
AccountIdWithPrivacy::Public(holder),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
) => TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::MintToken {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder,
|
||||
definition_account_id: def_mention,
|
||||
holder_account_id: hol_mention.expect("matched Some branch"),
|
||||
amount,
|
||||
},
|
||||
),
|
||||
@ -430,9 +437,9 @@ pub enum TokenProgramSubcommandPublic {
|
||||
// Transfer tokens using the token program
|
||||
TransferToken {
|
||||
#[arg(short, long)]
|
||||
sender_account_id: AccountId,
|
||||
sender_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
recipient_account_id: AccountId,
|
||||
recipient_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
@ -441,16 +448,16 @@ pub enum TokenProgramSubcommandPublic {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: AccountId,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: AccountId,
|
||||
holder_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
MintToken {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: AccountId,
|
||||
definition_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
holder_account_id: AccountId,
|
||||
holder_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
amount: u128,
|
||||
},
|
||||
@ -620,9 +627,9 @@ pub enum CreateNewTokenProgramSubcommand {
|
||||
/// Definition - public, supply - public.
|
||||
NewPublicDefPublicSupp {
|
||||
#[arg(short, long)]
|
||||
definition_account_id: AccountId,
|
||||
definition_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
supply_account_id: AccountId,
|
||||
supply_account_id: CliAccountMention,
|
||||
#[arg(short, long)]
|
||||
name: String,
|
||||
#[arg(short, long)]
|
||||
@ -680,11 +687,18 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
} => {
|
||||
let sender = sender_account_id.resolve(wallet_core.storage())?;
|
||||
let recipient = recipient_account_id.resolve(wallet_core.storage())?;
|
||||
let (AccountIdWithPrivacy::Public(sender_id), AccountIdWithPrivacy::Public(recipient_id)) = (sender, recipient) else {
|
||||
anyhow::bail!("Only public accounts supported for token transfer");
|
||||
};
|
||||
Token(wallet_core)
|
||||
.send_transfer_transaction(
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
sender_id,
|
||||
recipient_id,
|
||||
balance_to_move,
|
||||
&sender_account_id,
|
||||
&recipient_account_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
@ -694,8 +708,12 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let holder = holder_account_id.resolve(wallet_core.storage())?;
|
||||
let AccountIdWithPrivacy::Public(holder_id) = holder else {
|
||||
anyhow::bail!("Only public holder account supported for token burn");
|
||||
};
|
||||
Token(wallet_core)
|
||||
.send_burn_transaction(definition_account_id, holder_account_id, amount)
|
||||
.send_burn_transaction(definition_account_id, holder_id, amount, &holder_account_id)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
@ -704,8 +722,13 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
holder_account_id,
|
||||
amount,
|
||||
} => {
|
||||
let definition = definition_account_id.resolve(wallet_core.storage())?;
|
||||
let holder = holder_account_id.resolve(wallet_core.storage())?;
|
||||
let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(holder_id)) = (definition, holder) else {
|
||||
anyhow::bail!("Only public accounts supported for token mint");
|
||||
};
|
||||
Token(wallet_core)
|
||||
.send_mint_transaction(definition_account_id, holder_account_id, amount)
|
||||
.send_mint_transaction(def_id, holder_id, amount, &definition_account_id, &holder_account_id)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
@ -1307,12 +1330,19 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand {
|
||||
name,
|
||||
total_supply,
|
||||
} => {
|
||||
let definition = definition_account_id.resolve(wallet_core.storage())?;
|
||||
let supply = supply_account_id.resolve(wallet_core.storage())?;
|
||||
let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(sup_id)) = (definition, supply) else {
|
||||
anyhow::bail!("Only public accounts supported for new token definition");
|
||||
};
|
||||
Token(wallet_core)
|
||||
.send_new_definition(
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
def_id,
|
||||
sup_id,
|
||||
name,
|
||||
total_supply,
|
||||
&definition_account_id,
|
||||
&supply_account_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
|
||||
@ -22,70 +22,6 @@ pub fn read_pin() -> anyhow::Result<zeroize::Zeroizing<String>> {
|
||||
|
||||
/// Read the mnemonic phrase without echoing it.
|
||||
///
|
||||
/// Exactly one of `id` or `label` must be `Some`. If `id` is provided it is
|
||||
/// returned as-is; if `label` is provided it is resolved via
|
||||
/// [`resolve_account_label`]. Any other combination returns an error.
|
||||
pub fn resolve_id_or_label(
|
||||
id: Option<String>,
|
||||
label: Option<String>,
|
||||
labels: &HashMap<String, Label>,
|
||||
user_data: &NSSAUserData,
|
||||
key_path: Option<&str>,
|
||||
) -> Result<String> {
|
||||
match (id, label, key_path) {
|
||||
(Some(id), None, None) => Ok(id),
|
||||
(None, Some(label), None) => resolve_account_label(&label, labels, user_data),
|
||||
(None, None, Some(key_path)) => resolve_keycard_id(key_path),
|
||||
_ => anyhow::bail!("provide exactly one of account id, account label or keycard path"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_keycard_id(key_path: &str) -> Result<String> {
|
||||
let pin = read_pin()?;
|
||||
KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path)
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
|
||||
/// Resolve an account label to its full `Privacy/id` string representation.
|
||||
///
|
||||
/// Looks up the label in the labels map and determines whether the account is
|
||||
/// public or private by checking the user data key trees.
|
||||
pub fn resolve_account_label(
|
||||
label: &str,
|
||||
labels: &HashMap<String, Label>,
|
||||
user_data: &NSSAUserData,
|
||||
) -> Result<String> {
|
||||
let account_id_str = labels
|
||||
.iter()
|
||||
.find(|(_, l)| l.to_string() == label)
|
||||
.map(|(k, _)| k.clone())
|
||||
.ok_or_else(|| anyhow::anyhow!("No account found with label '{label}'"))?;
|
||||
|
||||
let account_id: nssa::AccountId = account_id_str.parse()?;
|
||||
|
||||
let privacy = if user_data
|
||||
.public_key_tree
|
||||
.account_id_map
|
||||
.contains_key(&account_id)
|
||||
|| user_data
|
||||
.default_pub_account_signing_keys
|
||||
.contains_key(&account_id)
|
||||
{
|
||||
"Public"
|
||||
} else if user_data
|
||||
.private_key_tree
|
||||
.account_id_map
|
||||
.contains_key(&account_id)
|
||||
|| user_data
|
||||
.default_user_private_accounts
|
||||
.contains_key(&account_id)
|
||||
{
|
||||
"Private"
|
||||
} else {
|
||||
anyhow::bail!("Account with label '{label}' not found in wallet");
|
||||
};
|
||||
|
||||
Ok(format!("{privacy}/{account_id_str}"))
|
||||
/// Checks `KEYCARD_MNEMONIC` first for non-interactive callers. Falls back to
|
||||
/// a TTY prompt so the phrase never appears in argv, shell history, or `ps`.
|
||||
pub fn read_mnemonic() -> anyhow::Result<zeroize::Zeroizing<String>> {
|
||||
|
||||
@ -28,7 +28,6 @@ use nssa_core::{
|
||||
program::InstructionData,
|
||||
};
|
||||
pub use privacy_preserving_tx::PrivacyPreservingAccount;
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder};
|
||||
use storage::Storage;
|
||||
use tokio::io::AsyncWriteExt as _;
|
||||
@ -575,38 +574,41 @@ impl WalletCore {
|
||||
|
||||
let mut pre_states = acc_manager.pre_states();
|
||||
|
||||
let keycard_account = if let Some(key_path_str) = key_path.as_deref() {
|
||||
let account_id = {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path_str)?
|
||||
};
|
||||
let (acc_id, _) =
|
||||
parse_addr_with_privacy_prefix(&account_id).expect("Valid parsing of account id");
|
||||
let account_id: AccountId = acc_id.parse().expect("Expect a valid Account Id");
|
||||
let (keycard_account, keycard_pin, keycard_path) = if let Some(key_path_str) = key_path.as_deref() {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let account_id_str =
|
||||
KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path_str)?;
|
||||
let account_id: AccountId =
|
||||
match account_id_str.parse::<AccountIdWithPrivacy>().expect("Valid parsing of account id") {
|
||||
AccountIdWithPrivacy::Public(id) | AccountIdWithPrivacy::Private(id) => id,
|
||||
};
|
||||
let account = self
|
||||
.get_account_public(account_id)
|
||||
.await
|
||||
.expect("Expect valid account");
|
||||
Some(AccountWithMetadata {
|
||||
account,
|
||||
is_authorized: true,
|
||||
account_id,
|
||||
})
|
||||
let pin_str = pin.as_str().to_owned();
|
||||
(
|
||||
Some(AccountWithMetadata {
|
||||
account,
|
||||
is_authorized: true,
|
||||
account_id,
|
||||
}),
|
||||
Some(pin_str),
|
||||
Some(key_path_str.to_owned()),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let mut nonces: Vec<Nonce> = acc_manager.public_account_nonces().into_iter().collect();
|
||||
|
||||
let mut account_ids: Vec<AccountId> = acc_manager.public_account_ids();
|
||||
|
||||
let mut visibility_mask = acc_manager.visibility_mask().to_vec();
|
||||
|
||||
if let Some(acc) = keycard_account.as_ref() {
|
||||
if acc_manager.public_account_ids().contains(&acc.account_id) {
|
||||
if let Some(pre) = pre_states
|
||||
@ -619,7 +621,6 @@ impl WalletCore {
|
||||
} else {
|
||||
nonces.push(acc.account.nonce);
|
||||
account_ids.push(acc.account_id);
|
||||
visibility_mask.push(0);
|
||||
pre_states.push(acc.clone());
|
||||
}
|
||||
}
|
||||
@ -652,12 +653,35 @@ impl WalletCore {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
let witness_set = if let (Some(pin), Some(path)) =
|
||||
(keycard_pin.as_deref(), keycard_path.as_deref())
|
||||
{
|
||||
let hash = message.hash();
|
||||
let local_auth = acc_manager.public_account_auth();
|
||||
let mut sigs: Vec<(Signature, PublicKey)> = local_auth
|
||||
.iter()
|
||||
.map(|&key| (Signature::new(key, &hash), PublicKey::new_from_private_key(key)))
|
||||
.collect();
|
||||
let keycard_sig = pyo3::Python::with_gil(|py| {
|
||||
let mut ctx = crate::signing::KeycardSessionContext::new(pin);
|
||||
let result = ctx
|
||||
.get_or_connect(py)
|
||||
.and_then(|w| w.sign_message_for_path(py, path, &hash));
|
||||
ctx.close(py);
|
||||
result
|
||||
})
|
||||
.map_err(ExecutionFailureKind::KeycardError)?;
|
||||
sigs.push(keycard_sig);
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_raw_parts(
|
||||
sigs, proof,
|
||||
)
|
||||
} else {
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
|
||||
&message,
|
||||
proof,
|
||||
&acc_manager.public_account_auth(),
|
||||
);
|
||||
)
|
||||
};
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
let shared_secrets: Vec<_> = private_account_keys
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
use amm_core::{compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda};
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use nssa::{AccountId, program::Program};
|
||||
use nssa::{AccountId, program::Program, public_transaction::WitnessSet};
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use token_core::TokenHolding;
|
||||
|
||||
use crate::{ExecutionFailureKind, WalletCore};
|
||||
use crate::{
|
||||
ExecutionFailureKind, WalletCore,
|
||||
cli::CliAccountMention,
|
||||
helperfunctions::read_pin,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
pub struct Amm<'wallet>(pub &'wallet WalletCore);
|
||||
|
||||
impl Amm<'_> {
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "each parameter is distinct; grouping into a struct would add unnecessary indirection"
|
||||
)]
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_new_definition(
|
||||
&self,
|
||||
user_holding_a: AccountId,
|
||||
@ -19,8 +22,9 @@ impl Amm<'_> {
|
||||
user_holding_lp: AccountId,
|
||||
balance_a: u128,
|
||||
balance_b: u128,
|
||||
key_path_a: Option<&str>,
|
||||
key_path_b: Option<&str>,
|
||||
a_mention: &CliAccountMention,
|
||||
b_mention: &CliAccountMention,
|
||||
lp_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let program = Program::amm();
|
||||
let amm_program_id = Program::amm().id();
|
||||
@ -64,106 +68,40 @@ impl Amm<'_> {
|
||||
user_holding_lp,
|
||||
];
|
||||
|
||||
// Check if LP has a stored key to determine if LP nonce is needed — before message creation
|
||||
let lp_sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(user_holding_lp);
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(a_mention, user_holding_a, self.0)
|
||||
.and_then(|()| groups.add_sender(b_mention, user_holding_b, self.0))
|
||||
.and_then(|()| groups.add_recipient(lp_mention, user_holding_lp, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let mut nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_a, user_holding_b])
|
||||
.await
|
||||
let mut nonces = self.0.get_accounts_nonces(vec![user_holding_a, user_holding_b]).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
if lp_sk.is_some() {
|
||||
let lp_nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_lp])
|
||||
.await
|
||||
if groups.signing_ids().contains(&user_holding_lp) {
|
||||
let lp_nonces = self.0.get_accounts_nonces(vec![user_holding_lp]).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
if lp_nonces.is_empty() {
|
||||
nonces.push(nssa_core::account::Nonce(0));
|
||||
} else {
|
||||
nonces.extend(lp_nonces);
|
||||
}
|
||||
nonces.push(lp_nonces.into_iter().next().unwrap_or(nssa_core::account::Nonce(0)));
|
||||
} else {
|
||||
println!(
|
||||
"Liquidity pool tokens receiver's account ({user_holding_lp}) private key not found in wallet. Proceeding with only liquidity provider's keys."
|
||||
);
|
||||
}
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let pin = if key_path_a.is_some() || key_path_b.is_some() {
|
||||
Some(crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?)
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
None
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let (sig_a, pk_a) = if let Some(kp) = key_path_a {
|
||||
keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
pin.as_ref().unwrap(),
|
||||
kp,
|
||||
&msg_hash,
|
||||
)?
|
||||
} else {
|
||||
let sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(user_holding_a)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
(
|
||||
nssa::Signature::new(sk, &msg_hash),
|
||||
nssa::PublicKey::new_from_private_key(sk),
|
||||
)
|
||||
};
|
||||
|
||||
let (sig_b, pk_b) = if let Some(kp) = key_path_b {
|
||||
keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
pin.as_ref().unwrap(),
|
||||
kp,
|
||||
&msg_hash,
|
||||
)?
|
||||
} else {
|
||||
let sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(user_holding_b)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
(
|
||||
nssa::Signature::new(sk, &msg_hash),
|
||||
nssa::PublicKey::new_from_private_key(sk),
|
||||
)
|
||||
};
|
||||
|
||||
let mut sigs = vec![sig_a, sig_b];
|
||||
let mut pks = vec![pk_a, pk_b];
|
||||
|
||||
if let Some(sk_lp) = lp_sk {
|
||||
sigs.push(nssa::Signature::new(sk_lp, &msg_hash));
|
||||
pks.push(nssa::PublicKey::new_from_private_key(sk_lp));
|
||||
}
|
||||
|
||||
let witness_set = nssa::public_transaction::WitnessSet::from_list(&message, &sigs, &pks)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -172,7 +110,7 @@ impl Amm<'_> {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "To fix later")]
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_swap_exact_input(
|
||||
&self,
|
||||
user_holding_a: AccountId,
|
||||
@ -180,8 +118,8 @@ impl Amm<'_> {
|
||||
swap_amount_in: u128,
|
||||
min_amount_out: u128,
|
||||
token_definition_id_in: AccountId,
|
||||
user_holding_a_key_path: Option<&str>,
|
||||
user_holding_b_key_path: Option<&str>,
|
||||
a_mention: &CliAccountMention,
|
||||
b_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let instruction = amm_core::Instruction::SwapExactInput {
|
||||
swap_amount_in,
|
||||
@ -222,59 +160,36 @@ impl Amm<'_> {
|
||||
user_holding_b,
|
||||
];
|
||||
|
||||
let account_id_auth = if definition_token_a_id == token_definition_id_in {
|
||||
user_holding_a
|
||||
let (account_id_auth, seller_mention) = if definition_token_a_id == token_definition_id_in {
|
||||
(user_holding_a, a_mention)
|
||||
} else if definition_token_b_id == token_definition_id_in {
|
||||
user_holding_b
|
||||
(user_holding_b, b_mention)
|
||||
} else {
|
||||
return Err(ExecutionFailureKind::AccountDataError(
|
||||
token_definition_id_in,
|
||||
));
|
||||
return Err(ExecutionFailureKind::AccountDataError(token_definition_id_in));
|
||||
};
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![account_id_auth])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(seller_mention, account_id_auth, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let seller_key_path = if definition_token_a_id == token_definition_id_in {
|
||||
user_holding_a_key_path
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
user_holding_b_key_path
|
||||
};
|
||||
let witness_set = if let Some(kp) = seller_key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp, &msg_hash,
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
} else {
|
||||
let signing_key = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(account_id_auth)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -283,7 +198,7 @@ impl Amm<'_> {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "To fix later")]
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_swap_exact_output(
|
||||
&self,
|
||||
user_holding_a: AccountId,
|
||||
@ -291,8 +206,8 @@ impl Amm<'_> {
|
||||
exact_amount_out: u128,
|
||||
max_amount_in: u128,
|
||||
token_definition_id_in: AccountId,
|
||||
user_holding_a_key_path: Option<&str>,
|
||||
user_holding_b_key_path: Option<&str>,
|
||||
a_mention: &CliAccountMention,
|
||||
b_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let instruction = amm_core::Instruction::SwapExactOutput {
|
||||
exact_amount_out,
|
||||
@ -333,63 +248,36 @@ impl Amm<'_> {
|
||||
user_holding_b,
|
||||
];
|
||||
|
||||
let account_id_auth = if definition_token_a_id == token_definition_id_in {
|
||||
user_holding_a
|
||||
let (account_id_auth, seller_mention) = if definition_token_a_id == token_definition_id_in {
|
||||
(user_holding_a, a_mention)
|
||||
} else if definition_token_b_id == token_definition_id_in {
|
||||
user_holding_b
|
||||
(user_holding_b, b_mention)
|
||||
} else {
|
||||
return Err(ExecutionFailureKind::AccountDataError(
|
||||
token_definition_id_in,
|
||||
));
|
||||
return Err(ExecutionFailureKind::AccountDataError(token_definition_id_in));
|
||||
};
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![account_id_auth])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(seller_mention, account_id_auth, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let witness_set = if let (Some(kp_a), Some(kp_b)) =
|
||||
(user_holding_a_key_path, user_holding_b_key_path)
|
||||
{
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (sig_1, pk_1) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp_a, &msg_hash,
|
||||
)?;
|
||||
let (sig_2, pk_2) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp_b, &msg_hash,
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(
|
||||
&message,
|
||||
&[sig_1, sig_2],
|
||||
&[pk_1, pk_2],
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
let signing_key = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(account_id_auth)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -398,10 +286,7 @@ impl Amm<'_> {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "each parameter is distinct; grouping into a struct would add unnecessary indirection"
|
||||
)]
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_add_liquidity(
|
||||
&self,
|
||||
user_holding_a: AccountId,
|
||||
@ -410,8 +295,8 @@ impl Amm<'_> {
|
||||
min_amount_liquidity: u128,
|
||||
max_amount_to_add_token_a: u128,
|
||||
max_amount_to_add_token_b: u128,
|
||||
key_path_a: Option<&str>,
|
||||
key_path_b: Option<&str>,
|
||||
a_mention: &CliAccountMention,
|
||||
b_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let instruction = amm_core::Instruction::AddLiquidity {
|
||||
min_amount_liquidity,
|
||||
@ -455,78 +340,29 @@ impl Amm<'_> {
|
||||
user_holding_lp,
|
||||
];
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_a, user_holding_b])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(a_mention, user_holding_a, self.0)
|
||||
.and_then(|()| groups.add_sender(b_mention, user_holding_b, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let pin = if key_path_a.is_some() || key_path_b.is_some() {
|
||||
Some(crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?)
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
None
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let (sig_a, pk_a) = if let Some(kp) = key_path_a {
|
||||
keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
pin.as_ref().unwrap(),
|
||||
kp,
|
||||
&msg_hash,
|
||||
)?
|
||||
} else {
|
||||
let sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(user_holding_a)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
(
|
||||
nssa::Signature::new(sk, &msg_hash),
|
||||
nssa::PublicKey::new_from_private_key(sk),
|
||||
)
|
||||
};
|
||||
|
||||
let (sig_b, pk_b) = if let Some(kp) = key_path_b {
|
||||
keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
pin.as_ref().unwrap(),
|
||||
kp,
|
||||
&msg_hash,
|
||||
)?
|
||||
} else {
|
||||
let sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(user_holding_b)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
(
|
||||
nssa::Signature::new(sk, &msg_hash),
|
||||
nssa::PublicKey::new_from_private_key(sk),
|
||||
)
|
||||
};
|
||||
|
||||
let witness_set = nssa::public_transaction::WitnessSet::from_list(
|
||||
&message,
|
||||
&[sig_a, sig_b],
|
||||
&[pk_a, pk_b],
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -535,10 +371,7 @@ impl Amm<'_> {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "each parameter is distinct; grouping into a struct would add unnecessary indirection"
|
||||
)]
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_remove_liquidity(
|
||||
&self,
|
||||
user_holding_a: AccountId,
|
||||
@ -547,7 +380,7 @@ impl Amm<'_> {
|
||||
remove_liquidity_amount: u128,
|
||||
min_amount_to_remove_token_a: u128,
|
||||
min_amount_to_remove_token_b: u128,
|
||||
key_path_lp: Option<&str>,
|
||||
lp_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let instruction = amm_core::Instruction::RemoveLiquidity {
|
||||
remove_liquidity_amount,
|
||||
@ -591,44 +424,28 @@ impl Amm<'_> {
|
||||
user_holding_lp,
|
||||
];
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_lp])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(lp_mention, user_holding_lp, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let witness_set = if let Some(kp) = key_path_lp {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp, &msg_hash,
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
let signing_key_lp = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(user_holding_lp)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key_lp])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
|
||||
@ -4,11 +4,18 @@ use ata_core::{compute_ata_seed, get_associated_token_account_id};
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use nssa::{
|
||||
AccountId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
|
||||
public_transaction::WitnessSet,
|
||||
};
|
||||
use nssa_core::SharedSecretKey;
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
|
||||
use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore};
|
||||
use crate::{
|
||||
ExecutionFailureKind, PrivacyPreservingAccount, WalletCore,
|
||||
cli::CliAccountMention,
|
||||
helperfunctions::read_pin,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
|
||||
pub struct Ata<'wallet>(pub &'wallet WalletCore);
|
||||
|
||||
@ -17,7 +24,7 @@ impl Ata<'_> {
|
||||
&self,
|
||||
owner_id: AccountId,
|
||||
definition_id: AccountId,
|
||||
key_path: Option<&str>,
|
||||
owner_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let program = Program::ata();
|
||||
let ata_program_id = program.id();
|
||||
@ -27,54 +34,31 @@ impl Ata<'_> {
|
||||
);
|
||||
|
||||
let account_ids = vec![owner_id, definition_id, ata_id];
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![owner_id])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let instruction = ata_core::Instruction::Create { ata_program_id };
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)?;
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(owner_mention, owner_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let witness_set = if let Some(kp) = key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp, &msg_hash,
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
let Some(signing_key) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(owner_id)
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self.0.sequencer_client.send_transaction(NSSATransaction::Public(tx)).await?)
|
||||
}
|
||||
|
||||
pub async fn send_transfer(
|
||||
@ -83,7 +67,7 @@ impl Ata<'_> {
|
||||
definition_id: AccountId,
|
||||
recipient_id: AccountId,
|
||||
amount: u128,
|
||||
key_path: Option<&str>,
|
||||
owner_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let program = Program::ata();
|
||||
let ata_program_id = program.id();
|
||||
@ -93,57 +77,31 @@ impl Ata<'_> {
|
||||
);
|
||||
|
||||
let account_ids = vec![owner_id, sender_ata_id, recipient_id];
|
||||
let instruction = ata_core::Instruction::Transfer { ata_program_id, amount };
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![owner_id])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(owner_mention, owner_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let instruction = ata_core::Instruction::Transfer {
|
||||
ata_program_id,
|
||||
amount,
|
||||
};
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)?;
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let witness_set = if let Some(kp) = key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp, &msg_hash,
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
let Some(signing_key) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(owner_id)
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self.0.sequencer_client.send_transaction(NSSATransaction::Public(tx)).await?)
|
||||
}
|
||||
|
||||
pub async fn send_burn(
|
||||
@ -151,7 +109,7 @@ impl Ata<'_> {
|
||||
owner_id: AccountId,
|
||||
definition_id: AccountId,
|
||||
amount: u128,
|
||||
key_path: Option<&str>,
|
||||
owner_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let program = Program::ata();
|
||||
let ata_program_id = program.id();
|
||||
@ -161,57 +119,31 @@ impl Ata<'_> {
|
||||
);
|
||||
|
||||
let account_ids = vec![owner_id, holder_ata_id, definition_id];
|
||||
let instruction = ata_core::Instruction::Burn { ata_program_id, amount };
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![owner_id])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(owner_mention, owner_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let instruction = ata_core::Instruction::Burn {
|
||||
ata_program_id,
|
||||
amount,
|
||||
};
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)?;
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let witness_set = if let Some(kp) = key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin, kp, &msg_hash,
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
let Some(signing_key) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(owner_id)
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self.0.sequencer_client.send_transaction(NSSATransaction::Public(tx)).await?)
|
||||
}
|
||||
|
||||
pub async fn send_create_private_owner(
|
||||
@ -230,18 +162,15 @@ impl Ata<'_> {
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
|
||||
let accounts = vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(owner_id),
|
||||
self.0
|
||||
.resolve_private_account(owner_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(definition_id),
|
||||
PrivacyPreservingAccount::Public(ata_id),
|
||||
];
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
accounts,
|
||||
instruction_data,
|
||||
&ata_with_token_dependency(),
|
||||
&None,
|
||||
)
|
||||
.send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency(), &None)
|
||||
.await
|
||||
.map(|(hash, mut secrets)| {
|
||||
let secret = secrets.pop().expect("expected owner's secret");
|
||||
@ -270,18 +199,15 @@ impl Ata<'_> {
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
|
||||
let accounts = vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(owner_id),
|
||||
self.0
|
||||
.resolve_private_account(owner_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(sender_ata_id),
|
||||
PrivacyPreservingAccount::Public(recipient_id),
|
||||
];
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
accounts,
|
||||
instruction_data,
|
||||
&ata_with_token_dependency(),
|
||||
&None,
|
||||
)
|
||||
.send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency(), &None)
|
||||
.await
|
||||
.map(|(hash, mut secrets)| {
|
||||
let secret = secrets.pop().expect("expected owner's secret");
|
||||
@ -309,18 +235,15 @@ impl Ata<'_> {
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
|
||||
let accounts = vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(owner_id),
|
||||
self.0
|
||||
.resolve_private_account(owner_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(holder_ata_id),
|
||||
PrivacyPreservingAccount::Public(definition_id),
|
||||
];
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
accounts,
|
||||
instruction_data,
|
||||
&ata_with_token_dependency(),
|
||||
&None,
|
||||
)
|
||||
.send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency(), &None)
|
||||
.await
|
||||
.map(|(hash, mut secrets)| {
|
||||
let secret = secrets.pop().expect("expected owner's secret");
|
||||
|
||||
@ -11,7 +11,7 @@ use sequencer_service_rpc::RpcClient as _;
|
||||
use super::NativeTokenTransfer;
|
||||
use crate::{
|
||||
ExecutionFailureKind, cli::CliAccountMention, helperfunctions::read_pin,
|
||||
signing::KeycardSessionContext,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
|
||||
impl NativeTokenTransfer<'_> {
|
||||
@ -23,30 +23,26 @@ impl NativeTokenTransfer<'_> {
|
||||
from_mention: &CliAccountMention,
|
||||
to_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let from_signer = from_mention.to_signer(self.0).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
let to_signer = to_mention.to_recipient_signer(self.0).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
|
||||
let account_ids = vec![from, to];
|
||||
let signing_ids: Vec<AccountId> = if to_signer.needs_signature() {
|
||||
vec![from, to]
|
||||
} else {
|
||||
vec![from]
|
||||
};
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(from_mention, from, self.0)
|
||||
.and_then(|()| groups.add_recipient(to_mention, to, self.0))
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(signing_ids)
|
||||
.get_accounts_nonces(groups.signing_ids())
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
vec![from, to],
|
||||
nonces,
|
||||
AuthTransferInstruction::Transfer {
|
||||
amount: balance_to_move,
|
||||
@ -54,7 +50,7 @@ impl NativeTokenTransfer<'_> {
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let pin = if from_mention.is_keycard() || to_mention.is_keycard() {
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
@ -67,30 +63,11 @@ impl NativeTokenTransfer<'_> {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let witness_set = pyo3::Python::with_gil(|py| -> pyo3::PyResult<WitnessSet> {
|
||||
let mut ctx = KeycardSessionContext::new(&pin);
|
||||
let hash = message.hash();
|
||||
let sigs = groups.sign_all(&message.hash(), &pin).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
|
||||
let (from_sig, from_pk) = from_signer
|
||||
.sign(self.0, &mut ctx, py, &hash)
|
||||
.expect("from signer always produces a signature")
|
||||
.map_err(|e| pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))?;
|
||||
|
||||
let sigs_and_keys = match to_signer
|
||||
.sign(self.0, &mut ctx, py, &hash)
|
||||
.transpose()
|
||||
.map_err(|e| pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))?
|
||||
{
|
||||
Some((to_sig, to_pk)) => vec![(from_sig, from_pk), (to_sig, to_pk)],
|
||||
None => vec![(from_sig, from_pk)],
|
||||
};
|
||||
|
||||
ctx.close(py);
|
||||
Ok(WitnessSet::from_raw_parts(sigs_and_keys))
|
||||
})
|
||||
.map_err(ExecutionFailureKind::KeycardError)?;
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
@ -119,11 +96,16 @@ impl NativeTokenTransfer<'_> {
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let signer = account_mention.to_signer(self.0).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(account_mention, from, self.0)
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
let pin = if account_mention.is_keycard() {
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
@ -136,21 +118,11 @@ impl NativeTokenTransfer<'_> {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let witness_set = pyo3::Python::with_gil(|py| -> pyo3::PyResult<WitnessSet> {
|
||||
let mut ctx = KeycardSessionContext::new(&pin);
|
||||
let hash = message.hash();
|
||||
let sigs = groups.sign_all(&message.hash(), &pin).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
|
||||
let (sig, pk) = signer
|
||||
.sign(self.0, &mut ctx, py, &hash)
|
||||
.expect("account signer always produces a signature")
|
||||
.map_err(|e| pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))?;
|
||||
|
||||
ctx.close(py);
|
||||
Ok(WitnessSet::from_raw_parts(vec![(sig, pk)]))
|
||||
})
|
||||
.map_err(ExecutionFailureKind::KeycardError)?;
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
|
||||
@ -3,7 +3,7 @@ use nssa::AccountId;
|
||||
use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey};
|
||||
|
||||
use super::{NativeTokenTransfer, auth_transfer_preparation};
|
||||
use crate::{ExecutionFailureKind, PrivacyPreservingAccount};
|
||||
use crate::{ExecutionFailureKind, PrivacyPreservingAccount, cli::CliAccountMention};
|
||||
|
||||
impl NativeTokenTransfer<'_> {
|
||||
pub async fn send_shielded_transfer(
|
||||
@ -11,9 +11,10 @@ impl NativeTokenTransfer<'_> {
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
from_key_path: &Option<String>,
|
||||
from_mention: &CliAccountMention,
|
||||
) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move);
|
||||
let key_path = from_mention.key_path().map(str::to_owned);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx_with_pre_check(
|
||||
@ -26,7 +27,7 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
from_key_path,
|
||||
&key_path,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
@ -45,9 +46,10 @@ impl NativeTokenTransfer<'_> {
|
||||
to_vpk: ViewingPublicKey,
|
||||
to_identifier: Identifier,
|
||||
balance_to_move: u128,
|
||||
from_key_path: &Option<String>,
|
||||
from_mention: &CliAccountMention,
|
||||
) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move);
|
||||
let key_path = from_mention.key_path().map(str::to_owned);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx_with_pre_check(
|
||||
@ -62,7 +64,7 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
from_key_path,
|
||||
&key_path,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
|
||||
@ -1,90 +1,56 @@
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use keycard_wallet::KeycardWallet;
|
||||
use nssa::{AccountId, PublicKey, Signature, program::Program, public_transaction::WitnessSet};
|
||||
use nssa::{AccountId, program::Program, public_transaction::WitnessSet};
|
||||
use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey};
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use token_core::Instruction;
|
||||
|
||||
use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore};
|
||||
use crate::{
|
||||
ExecutionFailureKind, PrivacyPreservingAccount, WalletCore,
|
||||
cli::CliAccountMention,
|
||||
helperfunctions::read_pin,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
|
||||
pub struct Token<'wallet>(pub &'wallet WalletCore);
|
||||
|
||||
impl Token<'_> {
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_new_definition(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
supply_account_id: AccountId,
|
||||
name: String,
|
||||
total_supply: u128,
|
||||
definition_key_path: Option<&str>,
|
||||
supply_key_path: Option<&str>,
|
||||
definition_mention: &CliAccountMention,
|
||||
supply_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let account_ids = vec![definition_account_id, supply_account_id];
|
||||
let program_id = nssa::program::Program::token().id();
|
||||
let instruction = Instruction::NewFungibleDefinition { name, total_supply };
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(account_ids.clone())
|
||||
.await
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(definition_mention, definition_account_id, self.0)
|
||||
.and_then(|()| groups.add_sender(supply_mention, supply_account_id, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(program_id, account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let pin = if definition_key_path.is_some() || supply_key_path.is_some() {
|
||||
Some(crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?)
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
None
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let (sig_def, pk_def) = if let Some(kp) = definition_key_path {
|
||||
KeycardWallet::sign_message_for_path_with_connect(pin.as_ref().unwrap(), kp, &msg_hash)?
|
||||
} else {
|
||||
let sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
(
|
||||
Signature::new(sk, &msg_hash),
|
||||
PublicKey::new_from_private_key(sk),
|
||||
)
|
||||
};
|
||||
|
||||
let (sig_sup, pk_sup) = if let Some(kp) = supply_key_path {
|
||||
KeycardWallet::sign_message_for_path_with_connect(pin.as_ref().unwrap(), kp, &msg_hash)?
|
||||
} else {
|
||||
let sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(supply_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
(
|
||||
Signature::new(sk, &msg_hash),
|
||||
PublicKey::new_from_private_key(sk),
|
||||
)
|
||||
};
|
||||
|
||||
let witness_set = nssa::public_transaction::WitnessSet::from_list(
|
||||
&message,
|
||||
&[sig_def, sig_sup],
|
||||
&[pk_def, pk_sup],
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -108,7 +74,9 @@ impl Token<'_> {
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(supply_account_id),
|
||||
self.0
|
||||
.resolve_private_account(supply_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -138,7 +106,9 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(supply_account_id),
|
||||
],
|
||||
instruction_data,
|
||||
@ -169,8 +139,12 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(supply_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
self.0
|
||||
.resolve_private_account(supply_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -190,79 +164,36 @@ impl Token<'_> {
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
sender_key_path: Option<String>,
|
||||
sender_mention: &CliAccountMention,
|
||||
recipient_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let account_ids = vec![sender_account_id, recipient_account_id];
|
||||
let program_id = nssa::program::Program::token().id();
|
||||
let instruction = Instruction::Transfer {
|
||||
amount_to_transfer: amount,
|
||||
};
|
||||
let instruction = Instruction::Transfer { amount_to_transfer: amount };
|
||||
|
||||
let mut nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![sender_account_id])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(sender_mention, sender_account_id, self.0)
|
||||
.and_then(|()| groups.add_recipient(recipient_mention, recipient_account_id, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let private_keys = if sender_key_path.is_none() {
|
||||
let mut private_keys = Vec::new();
|
||||
let sender_sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(sender_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
private_keys.push(sender_sk);
|
||||
let message = nssa::public_transaction::Message::try_new(program_id, account_ids, nonces, instruction).unwrap();
|
||||
|
||||
if let Some(recipient_sk) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(recipient_account_id)
|
||||
{
|
||||
private_keys.push(recipient_sk);
|
||||
let recipient_nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![recipient_account_id])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
nonces.extend(recipient_nonces);
|
||||
} else {
|
||||
println!(
|
||||
"Receiver's account ({recipient_account_id}) private key not found in wallet. Proceeding with only sender's key."
|
||||
);
|
||||
}
|
||||
private_keys
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
Vec::new()
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = if let Some(sender_key_path) = sender_key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
let (signature, public_key) = KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin,
|
||||
&sender_key_path,
|
||||
&message.hash(),
|
||||
)?;
|
||||
WitnessSet::from_list(&message, &[signature], &[public_key])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
} else {
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys)
|
||||
};
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -286,8 +217,12 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(sender_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(recipient_account_id),
|
||||
self.0
|
||||
.resolve_private_account(sender_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
self.0
|
||||
.resolve_private_account(recipient_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -319,7 +254,9 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(sender_account_id),
|
||||
self.0
|
||||
.resolve_private_account(sender_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::PrivateForeign {
|
||||
npk: recipient_npk,
|
||||
vpk: recipient_vpk,
|
||||
@ -354,7 +291,9 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(sender_account_id),
|
||||
self.0
|
||||
.resolve_private_account(sender_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(recipient_account_id),
|
||||
],
|
||||
instruction_data,
|
||||
@ -376,7 +315,6 @@ impl Token<'_> {
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
sender_key_path: Option<String>,
|
||||
) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> {
|
||||
let instruction = Instruction::Transfer {
|
||||
amount_to_transfer: amount,
|
||||
@ -388,11 +326,13 @@ impl Token<'_> {
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(sender_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(recipient_account_id),
|
||||
self.0
|
||||
.resolve_private_account(recipient_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
&sender_key_path,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
@ -442,129 +382,40 @@ impl Token<'_> {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_initialize_account(
|
||||
&self,
|
||||
definition_account: PrivacyPreservingAccount,
|
||||
holder_account: PrivacyPreservingAccount,
|
||||
key_path: &Option<String>,
|
||||
) -> Result<(HashType, Vec<SharedSecretKey>), ExecutionFailureKind> {
|
||||
let instruction = Instruction::InitializeAccount;
|
||||
|
||||
if definition_account.is_public() && holder_account.is_public() {
|
||||
let PrivacyPreservingAccount::Public(definition_account_id) = definition_account else {
|
||||
unreachable!()
|
||||
};
|
||||
let PrivacyPreservingAccount::Public(holder_account_id) = holder_account else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![holder_account_id])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
Program::token().id(),
|
||||
vec![definition_account_id, holder_account_id],
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.expect("Instruction should serialize");
|
||||
|
||||
let witness_set = if let Some(key_path) = key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let (signature, pub_key) =
|
||||
keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin,
|
||||
key_path,
|
||||
&message.hash(),
|
||||
)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[signature], &[pub_key])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
} else {
|
||||
let signing_key = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(holder_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
};
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
let hash = self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?;
|
||||
Ok((hash, vec![]))
|
||||
} else {
|
||||
let instruction_data =
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![definition_account, holder_account],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
key_path,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
holder_key_path: Option<&str>,
|
||||
holder_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let account_ids = vec![definition_account_id, holder_account_id];
|
||||
let instruction = Instruction::Burn {
|
||||
amount_to_burn: amount,
|
||||
};
|
||||
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![holder_account_id])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(holder_mention, holder_account_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
Program::token().id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.expect("Instruction should serialize");
|
||||
let message = nssa::public_transaction::Message::try_new(Program::token().id(), account_ids, nonces, instruction)
|
||||
.expect("Instruction should serialize");
|
||||
|
||||
let msg_hash = message.hash();
|
||||
let witness_set = if let Some(kp) = holder_key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
let (sig, pk) = KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?;
|
||||
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
let signing_key = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(holder_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -588,8 +439,12 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
self.0
|
||||
.resolve_private_account(holder_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -619,7 +474,9 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(holder_account_id),
|
||||
],
|
||||
instruction_data,
|
||||
@ -652,7 +509,9 @@ impl Token<'_> {
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
self.0
|
||||
.resolve_private_account(holder_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -673,78 +532,37 @@ impl Token<'_> {
|
||||
definition_account_id: AccountId,
|
||||
holder_account_id: AccountId,
|
||||
amount: u128,
|
||||
definition_key_path: Option<&str>,
|
||||
definition_mention: &CliAccountMention,
|
||||
holder_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let account_ids = vec![definition_account_id, holder_account_id];
|
||||
let instruction = Instruction::Mint {
|
||||
amount_to_mint: amount,
|
||||
};
|
||||
|
||||
let mut nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![definition_account_id])
|
||||
.await
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(definition_mention, definition_account_id, self.0)
|
||||
.and_then(|()| groups.add_recipient(holder_mention, holder_account_id, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let private_keys = if definition_key_path.is_none() {
|
||||
let mut private_keys = Vec::new();
|
||||
let definition_sk = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||
private_keys.push(definition_sk);
|
||||
let message = nssa::public_transaction::Message::try_new(Program::token().id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
if let Some(holder_sk) = self
|
||||
.0
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(holder_account_id)
|
||||
{
|
||||
private_keys.push(holder_sk);
|
||||
let holder_nonce: Vec<nssa_core::account::Nonce> = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![holder_account_id])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
nonces.extend(holder_nonce);
|
||||
} else {
|
||||
println!(
|
||||
"Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key."
|
||||
);
|
||||
}
|
||||
private_keys
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
Vec::new()
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
Program::token().id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = if let Some(definition_key_path) = definition_key_path {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
let (signature, public_key) = KeycardWallet::sign_message_for_path_with_connect(
|
||||
&pin,
|
||||
definition_key_path,
|
||||
&message.hash(),
|
||||
)?;
|
||||
WitnessSet::from_list(&message, &[signature], &[public_key])
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?
|
||||
} else {
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys)
|
||||
};
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
@ -768,8 +586,12 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
self.0
|
||||
.resolve_private_account(holder_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -801,7 +623,9 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::PrivateForeign {
|
||||
npk: holder_npk,
|
||||
vpk: holder_vpk,
|
||||
@ -836,7 +660,9 @@ impl Token<'_> {
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::PrivateOwned(definition_account_id),
|
||||
self.0
|
||||
.resolve_private_account(definition_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
PrivacyPreservingAccount::Public(holder_account_id),
|
||||
],
|
||||
instruction_data,
|
||||
@ -869,7 +695,9 @@ impl Token<'_> {
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(definition_account_id),
|
||||
PrivacyPreservingAccount::PrivateOwned(holder_account_id),
|
||||
self.0
|
||||
.resolve_private_account(holder_account_id)
|
||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?,
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
@ -922,4 +750,5 @@ impl Token<'_> {
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,60 +1,116 @@
|
||||
use anyhow::Result;
|
||||
use keycard_wallet::{KeycardWallet, python_path};
|
||||
use nssa::{AccountId, PublicKey, Signature};
|
||||
use nssa::{AccountId, PrivateKey, PublicKey, Signature};
|
||||
use pyo3::Python;
|
||||
|
||||
use crate::WalletCore;
|
||||
use crate::{WalletCore, cli::CliAccountMention};
|
||||
|
||||
/// How a single account participates in signing a transaction.
|
||||
/// Groups transaction signers by type to minimise Python GIL acquisition.
|
||||
///
|
||||
/// Created from [`crate::cli::CliAccountMention`] via `to_signer` / `to_recipient_signer`.
|
||||
/// Used inside `Python::with_gil` blocks — does not cross async boundaries.
|
||||
pub enum AccountSigner {
|
||||
/// Account is in the local wallet; key is looked up from storage at sign time.
|
||||
Local(AccountId),
|
||||
/// Account is on a Keycard at the given BIP32 path.
|
||||
Keycard(String),
|
||||
/// Foreign account — no signature or nonce required.
|
||||
Foreign,
|
||||
/// Local signers are signed in pure Rust; all keycard signers share a single Python session
|
||||
/// with one `connect` / `close_session` pair.
|
||||
#[derive(Default)]
|
||||
pub struct SigningGroups {
|
||||
local: Vec<(AccountId, PrivateKey)>,
|
||||
keycard: Vec<(AccountId, String)>,
|
||||
}
|
||||
|
||||
impl AccountSigner {
|
||||
impl SigningGroups {
|
||||
#[must_use]
|
||||
pub const fn needs_signature(&self) -> bool {
|
||||
!matches!(self, Self::Foreign)
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sign `hash` and return `(Signature, PublicKey)`, or `None` for `Foreign`.
|
||||
pub fn sign(
|
||||
&self,
|
||||
/// Add a sender. Keycard paths are queued for the hardware session; local accounts
|
||||
/// have their signing key resolved eagerly. Errors if no key is found.
|
||||
pub fn add_sender(
|
||||
&mut self,
|
||||
mention: &CliAccountMention,
|
||||
account_id: AccountId,
|
||||
wallet_core: &WalletCore,
|
||||
ctx: &mut KeycardSessionContext,
|
||||
py: Python<'_>,
|
||||
hash: &[u8; 32],
|
||||
) -> Option<Result<(Signature, PublicKey)>> {
|
||||
match self {
|
||||
Self::Local(id) => {
|
||||
let key = wallet_core
|
||||
.storage()
|
||||
.key_chain()
|
||||
.pub_account_signing_key(*id);
|
||||
Some(key.map_or_else(
|
||||
|| Err(anyhow::anyhow!("signing key not found for account {id}")),
|
||||
|key| {
|
||||
Ok((
|
||||
Signature::new(key, hash),
|
||||
PublicKey::new_from_private_key(key),
|
||||
))
|
||||
},
|
||||
))
|
||||
}
|
||||
Self::Keycard(path) => Some(
|
||||
ctx.get_or_connect(py)
|
||||
.and_then(|w| w.sign_message_for_path(py, path, hash))
|
||||
.map_err(anyhow::Error::from),
|
||||
),
|
||||
Self::Foreign => None,
|
||||
) -> Result<()> {
|
||||
if let CliAccountMention::KeyPath(path) = mention {
|
||||
self.keycard.push((account_id, path.clone()));
|
||||
return Ok(());
|
||||
}
|
||||
let key = wallet_core
|
||||
.storage()
|
||||
.key_chain()
|
||||
.pub_account_signing_key(account_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("signing key not found for account {account_id}"))?
|
||||
.clone();
|
||||
self.local.push((account_id, key));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a recipient. Same as [`add_sender`] but silently skips accounts with no local
|
||||
/// key and no keycard path — they are foreign and require neither a signature nor a nonce.
|
||||
pub fn add_recipient(
|
||||
&mut self,
|
||||
mention: &CliAccountMention,
|
||||
account_id: AccountId,
|
||||
wallet_core: &WalletCore,
|
||||
) -> Result<()> {
|
||||
if let CliAccountMention::KeyPath(path) = mention {
|
||||
self.keycard.push((account_id, path.clone()));
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(key) = wallet_core
|
||||
.storage()
|
||||
.key_chain()
|
||||
.pub_account_signing_key(account_id)
|
||||
{
|
||||
self.local.push((account_id, key.clone()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` when a PIN is required (at least one keycard signer is present).
|
||||
#[must_use]
|
||||
pub const fn needs_pin(&self) -> bool {
|
||||
!self.keycard.is_empty()
|
||||
}
|
||||
|
||||
/// Account IDs that require a nonce (every non-foreign signer).
|
||||
#[must_use]
|
||||
pub fn signing_ids(&self) -> Vec<AccountId> {
|
||||
self.local
|
||||
.iter()
|
||||
.map(|(id, _)| *id)
|
||||
.chain(self.keycard.iter().map(|(id, _)| *id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Sign `hash` for every account in the group.
|
||||
///
|
||||
/// Local accounts are signed in pure Rust. Keycard accounts share one Python session.
|
||||
pub fn sign_all(&self, hash: &[u8; 32], pin: &str) -> Result<Vec<(Signature, PublicKey)>> {
|
||||
let mut sigs: Vec<(Signature, PublicKey)> = self
|
||||
.local
|
||||
.iter()
|
||||
.map(|(_, key)| {
|
||||
(
|
||||
Signature::new(key, hash),
|
||||
PublicKey::new_from_private_key(key),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !self.keycard.is_empty() {
|
||||
pyo3::Python::with_gil(|py| -> pyo3::PyResult<()> {
|
||||
python_path::add_python_path(py)?;
|
||||
let wallet = KeycardWallet::new(py)?;
|
||||
wallet.connect(py, pin)?;
|
||||
for (_, path) in &self.keycard {
|
||||
sigs.push(wallet.sign_message_for_path(py, path, hash)?);
|
||||
}
|
||||
drop(wallet.close_session(py));
|
||||
Ok(())
|
||||
})
|
||||
.map_err(anyhow::Error::from)?;
|
||||
}
|
||||
|
||||
Ok(sigs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user