Skip to content

Commit 1bde7ea

Browse files
authored
Merge pull request #441 from xch-dev/single-sided-fix
Fix single sided offer NFTs and DIDs
2 parents 7652e89 + 5e9cff5 commit 1bde7ea

File tree

8 files changed

+127
-31
lines changed

8 files changed

+127
-31
lines changed

crates/sage-wallet/src/database.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ pub async fn insert_puzzle(
121121
});
122122

123123
if coin_state.spent_height.is_some()
124-
&& (row.created_height.is_none() || row.created_height > coin_state.created_height)
124+
&& (row.created_height.is_none()
125+
|| row.created_height > coin_state.created_height
126+
|| row.is_owned)
125127
{
126128
return Ok(());
127129
}
@@ -174,7 +176,9 @@ pub async fn insert_puzzle(
174176
});
175177

176178
if coin_state.spent_height.is_some()
177-
&& (row.created_height.is_none() || row.created_height > coin_state.created_height)
179+
&& (row.created_height.is_none()
180+
|| row.created_height > coin_state.created_height
181+
|| row.is_owned)
178182
{
179183
return Ok(());
180184
}

crates/sage-wallet/src/queues/puzzle_queue.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{
1919
pub struct PuzzleQueue {
2020
db: Database,
2121
genesis_challenge: Bytes32,
22+
batch_size_per_peer: usize,
2223
state: Arc<Mutex<PeerState>>,
2324
sync_sender: mpsc::Sender<SyncEvent>,
2425
command_sender: mpsc::Sender<SyncCommand>,
@@ -28,13 +29,15 @@ impl PuzzleQueue {
2829
pub fn new(
2930
db: Database,
3031
genesis_challenge: Bytes32,
32+
batch_size_per_peer: usize,
3133
state: Arc<Mutex<PeerState>>,
3234
sync_sender: mpsc::Sender<SyncEvent>,
3335
command_sender: mpsc::Sender<SyncCommand>,
3436
) -> Self {
3537
Self {
3638
db,
3739
genesis_challenge,
40+
batch_size_per_peer,
3841
state,
3942
sync_sender,
4043
command_sender,
@@ -55,7 +58,10 @@ impl PuzzleQueue {
5558
return Ok(());
5659
}
5760

58-
let coin_states = self.db.unsynced_coin_states(peers.len() * 5).await?;
61+
let coin_states = self
62+
.db
63+
.unsynced_coin_states(peers.len() * self.batch_size_per_peer)
64+
.await?;
5965

6066
if coin_states.is_empty() {
6167
return Ok(());
@@ -68,7 +74,7 @@ impl PuzzleQueue {
6874
let mut coin_states_iter = coin_states.into_iter();
6975

7076
for peer in peers {
71-
for _ in 0..5 {
77+
for _ in 0..self.batch_size_per_peer {
7278
let Some(coin_state) = coin_states_iter.next() else {
7379
break;
7480
};

crates/sage-wallet/src/sync_manager.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ impl SyncManager {
407407
PuzzleQueue::new(
408408
wallet.db.clone(),
409409
wallet.genesis_challenge,
410+
self.options.puzzle_batch_size_per_peer,
410411
self.state.clone(),
411412
self.event_sender.clone(),
412413
self.command_sender.clone(),

crates/sage-wallet/src/sync_manager/options.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub struct SyncOptions {
77
pub dns_batch_size: usize,
88
pub connection_batch_size: usize,
99
pub max_peer_age_seconds: u64,
10+
pub puzzle_batch_size_per_peer: usize,
1011
pub timeouts: Timeouts,
1112
pub testing: bool,
1213
}

crates/sage-wallet/src/sync_manager/peer_discovery.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{
22
cmp::Reverse,
3+
collections::HashMap,
34
net::{IpAddr, SocketAddr},
45
str::FromStr,
56
time::{Duration, SystemTime, UNIX_EPOCH},
@@ -409,9 +410,19 @@ impl SyncManager {
409410
let mut rng = rand::thread_rng();
410411

411412
// Sort so user managed are deprioritized, then by height, then randomly
413+
let mut peer_rng = HashMap::new();
414+
415+
for (peer, _) in &peers {
416+
peer_rng.insert(peer.socket_addr(), rng.gen_range(0..100));
417+
}
418+
412419
peers.sort_by_key(|(peer, height)| {
413420
let peer_info = state.peer(peer.socket_addr().ip()).expect("peer not found");
414-
(peer_info.user_managed, *height, rng.gen_range(0..100))
421+
(
422+
peer_info.user_managed,
423+
*height,
424+
peer_rng[&peer.socket_addr()],
425+
)
415426
});
416427

417428
let count = state.peer_count() - self.options.target_peers + 1;

crates/sage-wallet/src/test.rs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,37 @@ pub struct TestWallet {
4646
pub events: Receiver<SyncEvent>,
4747
pub index: u32,
4848
pub state: Arc<Mutex<PeerState>>,
49+
pub options: SyncOptions,
4950
}
5051

5152
impl TestWallet {
5253
pub async fn new(balance: u64) -> anyhow::Result<Self> {
53-
let sim = PeerSimulator::new().await?;
54-
Self::with_sim(Arc::new(sim), balance, 0).await
54+
Self::new_with_options(balance, default_test_options()).await
5555
}
5656

5757
pub async fn next(&self, balance: u64) -> anyhow::Result<Self> {
58-
Self::with_sim(self.sim.clone(), balance, self.index + 1).await
58+
self.next_with_options(balance, default_test_options())
59+
.await
60+
}
61+
62+
pub async fn new_with_options(balance: u64, options: SyncOptions) -> anyhow::Result<Self> {
63+
let sim = PeerSimulator::new().await?;
64+
Self::with_sim(Arc::new(sim), balance, 0, options).await
65+
}
66+
67+
pub async fn next_with_options(
68+
&self,
69+
balance: u64,
70+
options: SyncOptions,
71+
) -> anyhow::Result<Self> {
72+
Self::with_sim(self.sim.clone(), balance, self.index + 1, options).await
5973
}
6074

6175
async fn with_sim(
6276
sim: Arc<PeerSimulator>,
6377
balance: u64,
6478
key_index: u32,
79+
options: SyncOptions,
6580
) -> anyhow::Result<Self> {
6681
let db_index = {
6782
let mut lock = INDEX.lock().await;
@@ -119,23 +134,7 @@ impl TestWallet {
119134
));
120135

121136
let (mut sync_manager, sender, events) = SyncManager::new(
122-
SyncOptions {
123-
target_peers: 0,
124-
discover_peers: false,
125-
dns_batch_size: 0,
126-
connection_batch_size: 0,
127-
max_peer_age_seconds: 0,
128-
timeouts: Timeouts {
129-
sync_delay: Duration::from_millis(100),
130-
nft_uri_delay: Duration::from_millis(100),
131-
cat_delay: Duration::from_millis(100),
132-
puzzle_delay: Duration::from_millis(100),
133-
transaction_delay: Duration::from_millis(100),
134-
offer_delay: Duration::from_millis(100),
135-
..Default::default()
136-
},
137-
testing: true,
138-
},
137+
options,
139138
state.clone(),
140139
Some(wallet.clone()),
141140
TESTNET11.clone(),
@@ -164,6 +163,7 @@ impl TestWallet {
164163
events,
165164
index: key_index,
166165
state,
166+
options,
167167
};
168168

169169
test.consume_until(|event| matches!(event, SyncEvent::Subscribed))
@@ -173,6 +173,11 @@ impl TestWallet {
173173
Ok(test)
174174
}
175175

176+
pub async fn resync(&mut self) -> anyhow::Result<()> {
177+
*self = Self::with_sim(self.sim.clone(), 0, self.index, self.options).await?;
178+
Ok(())
179+
}
180+
176181
pub async fn transact(&self, coin_spends: Vec<CoinSpend>) -> anyhow::Result<()> {
177182
let spend_bundle = self
178183
.wallet
@@ -231,3 +236,24 @@ impl TestWallet {
231236
.await;
232237
}
233238
}
239+
240+
pub fn default_test_options() -> SyncOptions {
241+
SyncOptions {
242+
target_peers: 0,
243+
discover_peers: false,
244+
dns_batch_size: 0,
245+
connection_batch_size: 0,
246+
max_peer_age_seconds: 0,
247+
puzzle_batch_size_per_peer: 5,
248+
timeouts: Timeouts {
249+
sync_delay: Duration::from_millis(100),
250+
nft_uri_delay: Duration::from_millis(100),
251+
cat_delay: Duration::from_millis(100),
252+
puzzle_delay: Duration::from_millis(100),
253+
transaction_delay: Duration::from_millis(100),
254+
offer_delay: Duration::from_millis(100),
255+
..Default::default()
256+
},
257+
testing: true,
258+
}
259+
}

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

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub use unlock_assets::*;
2121

2222
#[cfg(test)]
2323
mod tests {
24+
use std::time::Duration;
25+
2426
use chia::{
2527
clvm_traits::{FromClvm, ToClvm},
2628
protocol::{Bytes32, Program},
@@ -30,7 +32,10 @@ mod tests {
3032
use indexmap::indexmap;
3133
use test_log::test;
3234

33-
use crate::{MakerSide, RequestedNft, TakerSide, TestWallet, WalletNftMint};
35+
use crate::{
36+
default_test_options, MakerSide, RequestedNft, SyncOptions, TakerSide, TestWallet,
37+
Timeouts, WalletNftMint,
38+
};
3439

3540
use super::aggregate_offers;
3641

@@ -738,8 +743,22 @@ mod tests {
738743

739744
#[test(tokio::test)]
740745
async fn test_offer_nft_single_sided() -> anyhow::Result<()> {
746+
let options = default_test_options();
747+
741748
let mut alice = TestWallet::new(2).await?;
742-
let mut bob = alice.next(0).await?;
749+
let mut bob = alice
750+
.next_with_options(
751+
0,
752+
SyncOptions {
753+
puzzle_batch_size_per_peer: 1,
754+
timeouts: Timeouts {
755+
puzzle_delay: Duration::from_secs(1),
756+
..options.timeouts
757+
},
758+
..options
759+
},
760+
)
761+
.await?;
743762

744763
let (coin_spends, did) = alice.wallet.create_did(0, false, true).await?;
745764
alice.transact(coin_spends).await?;
@@ -790,16 +809,43 @@ mod tests {
790809
.wallet
791810
.sign_take_offer(offer, &bob.agg_sig, bob.master_sk.clone())
792811
.await?;
812+
assert_eq!(spend_bundle.coin_spends.len(), 3);
793813
bob.push_bundle(spend_bundle).await?;
794814

795815
// We need to wait for both wallets to sync in this case
796816
bob.wait_for_coins().await;
797817

818+
// Resync to make sure the NFT is spendable even if it wasn't pending previously
819+
bob.resync().await?;
820+
bob.wait_for_puzzles().await;
821+
bob.wait_for_puzzles().await;
822+
798823
// Check balances
799-
assert_ne!(
800-
bob.wallet.db.spendable_nft(nft.info.launcher_id).await?,
801-
None
802-
);
824+
let nft = bob
825+
.wallet
826+
.db
827+
.spendable_nft(nft.info.launcher_id)
828+
.await?
829+
.expect("NFT should be spendable");
830+
831+
let coin_id = bob
832+
.wallet
833+
.db
834+
.nft_row(nft.info.launcher_id)
835+
.await?
836+
.expect("NFT should exist")
837+
.coin_id;
838+
839+
let is_spent = bob
840+
.wallet
841+
.db
842+
.coin_state(coin_id)
843+
.await?
844+
.expect("coin should exist")
845+
.spent_height
846+
.is_some();
847+
848+
assert!(!is_spent);
803849

804850
Ok(())
805851
}

crates/sage/src/sage.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ impl Sage {
220220
max_peer_age_seconds: 3600 * 8,
221221
dns_batch_size: 10,
222222
connection_batch_size: 30,
223+
puzzle_batch_size_per_peer: 5,
223224
timeouts: Timeouts::default(),
224225
testing: false,
225226
},

0 commit comments

Comments
 (0)