Skip to content

Commit 8b9fbcf

Browse files
authored
Merge pull request #698 from xch-dev/arbor
Add Arbor Wallet compatibility
2 parents 0542f92 + a22a753 commit 8b9fbcf

11 files changed

+142
-17
lines changed

.sqlx/query-3d10c346c9307a0fb2f71cadcf04881e163e2bf96d5c54b475dedcf8c981dcbc.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-91054392bb590bd729d257989bee20e41f5e7b40a1434df439f0ccca126ee8ac.json renamed to .sqlx/query-881596d83c520b3f27ed2e82f09d0d9c136cb5c929e4b14ffa0f0f4422339b99.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-23a3a5e21db7039a455a7fae4ad0a5eb866a5176fb5df024ac8b0897f82d3008.json renamed to .sqlx/query-8d85ede79ebf0aadc828252ef02b66f4018f81f4b5d471c0d2cb329bc9a77e86.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-bad04dbba3a2f7b238f59b1badae2b9c2098511951341f83e01db561430733ff.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/sage-database/src/tables/p2_puzzles.rs

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use chia::{bls::PublicKey, clvm_utils::ToTreeHash, protocol::Bytes32};
2-
use chia_wallet_sdk::driver::{ClawbackV2, OptionType, OptionUnderlying};
2+
use chia_wallet_sdk::{
3+
driver::{ClawbackV2, OptionType, OptionUnderlying},
4+
types::{puzzles::P2DelegatedConditionsArgs, Mod},
5+
};
36
use sqlx::{query, SqliteExecutor};
47

58
use crate::{Convert, Database, DatabaseError, DatabaseTx, Result};
@@ -9,13 +12,15 @@ pub enum P2PuzzleKind {
912
PublicKey,
1013
Clawback,
1114
Option,
15+
Arbor,
1216
}
1317

1418
#[derive(Debug, Clone, Copy)]
1519
pub enum P2Puzzle {
1620
PublicKey(PublicKey),
1721
Clawback(Clawback),
1822
Option(Underlying),
23+
Arbor(PublicKey),
1924
}
2025

2126
#[derive(Debug, Clone, Copy)]
@@ -100,6 +105,13 @@ impl Database {
100105
strike_type: underlying.strike_type,
101106
}))
102107
}
108+
P2PuzzleKind::Arbor => {
109+
let Some(key) = arbor_key(&self.pool, puzzle_hash).await? else {
110+
return Err(DatabaseError::PublicKeyNotFound);
111+
};
112+
113+
Ok(P2Puzzle::Arbor(key))
114+
}
103115
}
104116
}
105117

@@ -162,10 +174,14 @@ impl DatabaseTx<'_> {
162174
pub async fn insert_option_p2_puzzle(&mut self, underlying: OptionUnderlying) -> Result<()> {
163175
insert_option_p2_puzzle(&mut *self.tx, underlying).await
164176
}
177+
178+
pub async fn insert_arbor_p2_puzzle(&mut self, key: PublicKey) -> Result<()> {
179+
insert_arbor_p2_puzzle(&mut *self.tx, key).await
180+
}
165181
}
166182

167183
async fn custody_p2_puzzle_hashes(conn: impl SqliteExecutor<'_>) -> Result<Vec<Bytes32>> {
168-
query!("SELECT hash FROM p2_puzzles WHERE kind = 0")
184+
query!("SELECT hash FROM p2_puzzles WHERE kind IN (0, 3)")
169185
.fetch_all(conn)
170186
.await?
171187
.into_iter()
@@ -200,7 +216,7 @@ async fn is_custody_p2_puzzle_hash(
200216
let puzzle_hash = puzzle_hash.as_ref();
201217

202218
Ok(query!(
203-
"SELECT COUNT(*) AS count FROM p2_puzzles WHERE hash = ? AND kind = 0",
219+
"SELECT COUNT(*) AS count FROM p2_puzzles WHERE hash = ? AND kind IN (0, 3)",
204220
puzzle_hash
205221
)
206222
.fetch_one(conn)
@@ -405,6 +421,30 @@ async fn insert_option_p2_puzzle(
405421
Ok(())
406422
}
407423

424+
async fn insert_arbor_p2_puzzle(conn: impl SqliteExecutor<'_>, key: PublicKey) -> Result<()> {
425+
let p2_puzzle_hash = P2DelegatedConditionsArgs::new(key)
426+
.curry_tree_hash()
427+
.to_vec();
428+
let key = key.to_bytes();
429+
let key = key.as_ref();
430+
431+
query!(
432+
"
433+
INSERT OR IGNORE INTO p2_puzzles (hash, kind) VALUES (?, 3);
434+
435+
INSERT OR IGNORE INTO p2_arbor (p2_puzzle_id, key)
436+
VALUES ((SELECT id FROM p2_puzzles WHERE hash = ?), ?);
437+
",
438+
p2_puzzle_hash,
439+
p2_puzzle_hash,
440+
key,
441+
)
442+
.execute(conn)
443+
.await?;
444+
445+
Ok(())
446+
}
447+
408448
async fn p2_puzzle_kind(
409449
conn: impl SqliteExecutor<'_>,
410450
p2_puzzle_hash: Bytes32,
@@ -419,6 +459,7 @@ async fn p2_puzzle_kind(
419459
0 => P2PuzzleKind::PublicKey,
420460
1 => P2PuzzleKind::Clawback,
421461
2 => P2PuzzleKind::Option,
462+
3 => P2PuzzleKind::Arbor,
422463
_ => return Err(DatabaseError::InvalidEnumVariant),
423464
})
424465
}
@@ -496,6 +537,27 @@ async fn underlying_launcher_id(
496537
.convert()
497538
}
498539

540+
async fn arbor_key(
541+
conn: impl SqliteExecutor<'_>,
542+
p2_puzzle_hash: Bytes32,
543+
) -> Result<Option<PublicKey>> {
544+
let p2_puzzle_hash = p2_puzzle_hash.as_ref();
545+
546+
let row = query!(
547+
"
548+
SELECT key
549+
FROM p2_puzzles
550+
INNER JOIN p2_arbor ON p2_arbor.p2_puzzle_id = p2_puzzles.id
551+
WHERE p2_puzzles.hash = ?
552+
",
553+
p2_puzzle_hash
554+
)
555+
.fetch_optional(conn)
556+
.await?;
557+
558+
row.map(|row| row.key.convert()).transpose()
559+
}
560+
499561
async fn derivation(
500562
conn: impl SqliteExecutor<'_>,
501563
public_key: PublicKey,

crates/sage-wallet/src/wallet.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use chia_puzzles::SETTLEMENT_PAYMENT_HASH;
1313
use chia_wallet_sdk::{
1414
driver::{
1515
Action, Cat, ClawbackV2, Deltas, DriverError, Id, Layer, OptionUnderlying, Outputs,
16-
Relation, SettlementLayer, SpendContext, SpendKind, SpendWithConditions, SpendableAsset,
17-
Spends, StandardLayer,
16+
P2DelegatedConditionsLayer, Relation, SettlementLayer, SpendContext, SpendKind,
17+
SpendWithConditions, SpendableAsset, Spends, StandardLayer,
1818
},
1919
signer::AggSigConstants,
20+
types::puzzles::P2DelegatedConditionsSolution,
2021
utils::select_coins,
2122
};
2223
use indexmap::IndexMap;
@@ -354,6 +355,13 @@ impl Wallet {
354355

355356
underlying.clawback_spend(ctx, spend)
356357
}
358+
P2Puzzle::Arbor(key) => P2DelegatedConditionsLayer::new(*key)
359+
.construct_spend(
360+
ctx,
361+
P2DelegatedConditionsSolution::new(
362+
spend.finish().into_iter().collect(),
363+
),
364+
),
357365
}
358366
}
359367
SpendKind::Settlement(spend) => SettlementLayer

crates/sage-wallet/src/wallet/signing.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ impl Wallet {
4444
continue;
4545
};
4646
let Some(derivation) = self.db.derivation(required.public_key).await? else {
47-
if partial {
48-
continue;
49-
}
50-
return Err(WalletError::UnknownPublicKey);
47+
continue;
5148
};
5249
derivations.insert(required.public_key, derivation);
5350
}
@@ -75,9 +72,18 @@ impl Wallet {
7572
let RequiredSignature::Bls(required) = required else {
7673
continue;
7774
};
78-
let Some(sk) = secret_keys.get(&required.public_key).cloned() else {
79-
continue;
75+
76+
let sk = if required.public_key == master_sk.public_key() {
77+
master_sk.clone()
78+
} else if let Some(sk) = secret_keys.get(&required.public_key).cloned() {
79+
sk
80+
} else {
81+
if partial {
82+
continue;
83+
}
84+
return Err(WalletError::UnknownPublicKey);
8085
};
86+
8187
aggregated_signature += &sign(&sk, required.message());
8288
}
8389

crates/sage/src/endpoints/keys.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ impl Sage {
216216
}
217217
}
218218

219+
tx.insert_arbor_p2_puzzle(master_pk).await?;
220+
219221
tx.commit().await?;
220222

221223
if req.login {

crates/sage/src/endpoints/wallet_connect.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use chia::{
66
protocol::{Bytes32, Coin, CoinSpend, SpendBundle},
77
puzzles::{DeriveSynthetic, Proof},
88
};
9-
use chia_wallet_sdk::driver::{ClawbackV2, Layer, OptionUnderlying, SpendContext, StandardLayer};
9+
use chia_wallet_sdk::driver::{
10+
ClawbackV2, Layer, OptionUnderlying, P2DelegatedConditionsLayer, SpendContext, StandardLayer,
11+
};
1012
use sage_api::wallet_connect::{
1113
self, AssetCoinType, FilterUnlockedCoins, FilterUnlockedCoinsResponse, GetAssetCoins,
1214
GetAssetCoinsResponse, LineageProof, SendTransactionImmediately,
@@ -104,6 +106,9 @@ impl Sage {
104106
);
105107
underlying.into_1_of_n().construct_puzzle(&mut ctx)?
106108
}
109+
P2Puzzle::Arbor(key) => {
110+
P2DelegatedConditionsLayer::new(key).construct_puzzle(&mut ctx)?
111+
}
107112
};
108113

109114
let (puzzle, proof) = match req.kind {

migrations/0004_arbor.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* P2 arbor is a p2 puzzle with kind = 3
3+
*/
4+
CREATE TABLE p2_arbor (
5+
id INTEGER NOT NULL PRIMARY KEY,
6+
p2_puzzle_id INTEGER NOT NULL UNIQUE,
7+
key BLOB NOT NULL,
8+
FOREIGN KEY (p2_puzzle_id) REFERENCES p2_puzzles(id) ON DELETE CASCADE
9+
);

0 commit comments

Comments
 (0)