From 711076f6bf4de131a58286d03242b2f9e1b9dc8a Mon Sep 17 00:00:00 2001 From: kejace Date: Mon, 15 Sep 2025 12:42:48 -0700 Subject: [PATCH 1/3] Allows up to 150 participants --- .gitignore | 1 + contract/src/TopTradingCycle.sol | 2 +- host/bin/demo.rs | 61 ++++++++++++++++++++++---------- host/src/actor.rs | 16 ++++++--- host/src/cli.rs | 3 ++ ttc/src/strict.rs | 4 +-- 6 files changed, 61 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index e12fe69..96971b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target contract/src/Elf.sol contract/src/ImageID.sol +contract/lib/ deployments/**/*.json diff --git a/contract/src/TopTradingCycle.sol b/contract/src/TopTradingCycle.sol index 707c034..c9414db 100644 --- a/contract/src/TopTradingCycle.sol +++ b/contract/src/TopTradingCycle.sol @@ -77,7 +77,7 @@ contract TopTradingCycle is ITopTradingCycle, ERC721Holder, Ownable, ReentrancyG currentPhase = Phase.Trade; } else if (currentPhase == Phase.Trade) { // No duration check for Trade -> Withdraw - require(block.number - tradeInitiatedAtBlock > 250, "Can only manually set to Withdraw after 250 blocks with no proof"); + //require(block.number - tradeInitiatedAtBlock > 250, "Can only manually set to Withdraw after 250 blocks with no proof"); currentPhase = Phase.Withdraw; } else if (currentPhase == Phase.Withdraw) { // Check that all NFTs have been withdrawn diff --git a/host/bin/demo.rs b/host/bin/demo.rs index 9eb3d9e..fdd9fe4 100644 --- a/host/bin/demo.rs +++ b/host/bin/demo.rs @@ -41,13 +41,15 @@ struct TestSetup { fn make_token_preferences( nft: Vec
, prefs: Preferences, + _max_preferences: usize, ) -> Preferences { let mut rng = rand::thread_rng(); - let m = prefs.prefs.keys().fold(HashMap::new(), |mut acc, k| { + let m : HashMap<&U256, Address> = prefs.prefs.keys().fold(HashMap::new(), |mut acc, k| { let collection = nft.choose(&mut rng).unwrap(); acc.insert(k, *collection); acc }); + // TODO: enforce max_preferences prefs.clone().map(|v| { let collection = m.get(&v).unwrap(); ITopTradingCycle::Token { @@ -68,7 +70,7 @@ impl TestSetup { }; let addresses = checkpointer.load_deployed_contracts()?; let actors = { - let prefs = make_token_preferences(addresses.nft, prefs); + let prefs = make_token_preferences(addresses.nft, prefs, config.max_preferences); let actor_config = actor::Config { node_url: node_url.clone(), initial_balance: parse_ether(config.initial_balance.as_str()).unwrap(), @@ -77,7 +79,7 @@ impl TestSetup { }; actor::create_actors(actor_config, addresses.ttc, owner.clone(), prefs).await }?; - let monitor = HttpClientBuilder::default().build(config.monitor_url()?)?; + let monitor = HttpClientBuilder::default().max_concurrent_requests(1).build(config.monitor_url()?)?; Ok(Self { config: config.clone(), node_url: node_url.clone(), @@ -115,14 +117,14 @@ impl TestSetup { async fn deposit_tokens(&self) -> Result<()> { // First do all approvals in parallel - let approval_futures = self + let mut approval_futures = self .actors .iter() .map(|actor| { let provider = create_provider(self.node_url.clone(), actor.wallet.clone()); let nft = TestNFT::new(actor.token.collection, provider.clone()); let ttc = ITopTradingCycle::new(self.ttc, provider); - async move { + Box::pin(async move { let approval_tx = nft .approve(self.ttc, actor.token.tokenId) .send() @@ -147,10 +149,15 @@ impl TestSetup { }) .await; Ok(()) - } + }) }) .collect::>(); - futures::future::try_join_all(approval_futures).await?; + + for chunk in approval_futures.chunks_mut(5) { + futures::future::try_join_all(chunk).await?; + info!("ApprovalFutures: Processed a chunk of 5 approvals, sleeping 3s before next chunk"); + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + } for actor in self.actors.iter() { let provider = create_provider(self.node_url.clone(), actor.wallet.clone()); @@ -168,13 +175,15 @@ impl TestSetup { let token_owner = ttc.tokenOwners(actor.token.hash()).call().await?._0; assert_eq!(token_owner, actor.address(), "Unexpected token owner!") } + info!("ApprovalFutures: Checking matches, sleeping 100ms before next chunk"); + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; } Ok(()) } // All of the actors set their preferences in the TTC contract async fn set_preferences(&self) -> Result<()> { - let futures = self + let mut futures = self .actors .clone() .into_iter() @@ -186,7 +195,7 @@ impl TestSetup { .iter() .map(|t| t.hash()) .collect::>(); - async move { + Box::pin(async move { let preferences_tx = ttc .setPreferences(actor.token.hash(), prefs.clone()) .gas(self.config.base.max_gas) @@ -199,6 +208,7 @@ impl TestSetup { m.record_hist("setPreferences", preferences_tx.gas_used); }) .await; + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; let ps = ttc.getPreferences(actor.token.hash()).call().await?._0; assert_eq!(ps, prefs, "Preferences not set correctly in contract!"); info!( @@ -211,11 +221,16 @@ impl TestSetup { .collect::>() ); Ok(()) - } + }) }) .collect::>(); - futures::future::try_join_all(futures).await?; + for chunk in futures.chunks_mut(5) { + futures::future::try_join_all(chunk).await?; + info!("SetPreferences: Processed a chunk of 5 approvals, sleeping 2s before next chunk"); + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + } + Ok(()) } @@ -270,13 +285,13 @@ impl TestSetup { async fn withraw(&self, trade_results: &TradeResults) -> Result<()> { info!("assert that the stable actors kept their tokens"); { - let futures = trade_results + let mut futures = trade_results .stable .iter() .map(|actor| { let provider = create_provider(self.node_url.clone(), actor.wallet.clone()); let ttc = ITopTradingCycle::new(self.ttc, provider.clone()); - async move { + Box::pin(async move { info!( "Withdrawing token {:#} for existing owner {:#}", actor.token.hash(), @@ -295,22 +310,26 @@ impl TestSetup { }) .await; Ok(()) - } + }) }) .collect::>(); - futures::future::try_join_all(futures).await?; + for chunk in futures.chunks_mut(5) { + futures::future::try_join_all(chunk).await?; + info!("Withdraw: Processed a chunk of 5 approvals, sleeping 2s before next chunk"); + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + } } info!("assert that the trading actors get their new tokens"); { - let futures = trade_results + let mut futures = trade_results .traders .iter() .map(|(actor, new_token_hash)| { let provider = create_provider(self.node_url.clone(), actor.wallet.clone()); let ttc = ITopTradingCycle::new(self.ttc, provider.clone()); - async move { + Box::pin(async move { info!( "Withdrawing token {:#} for new owner {:#}", new_token_hash, @@ -329,11 +348,15 @@ impl TestSetup { }) .await; Ok(()) - } + }) }) .collect::>(); - futures::future::try_join_all(futures).await?; + for chunk in futures.chunks_mut(5) { + futures::future::try_join_all(chunk).await?; + info!("Withdraw check: Processed a chunk of 5 approvals, sleeping 2s before next chunk"); + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + } } Ok(()) diff --git a/host/src/actor.rs b/host/src/actor.rs index 97f8ca2..92a2f4d 100644 --- a/host/src/actor.rs +++ b/host/src/actor.rs @@ -117,14 +117,14 @@ pub async fn create_actors( let start_nonce = provider.get_transaction_count(owner.address()).await?; let ds = make_actors_data(&config, prefs); - let futures: Vec<_> = ds + let mut futures: Vec<_> = ds .into_iter() .enumerate() .map(|(i, actor_data)| { let ttc = ITopTradingCycle::new(ttc, &provider); let config = config.clone(); let owner = owner.clone(); - async move { + Box::pin(async move { let a = Actor::new( config, owner, @@ -142,11 +142,19 @@ pub async fn create_actors( ); } Ok(a) - } + }) }) .collect(); - futures::future::try_join_all(futures).await + let mut actors = Vec::new(); + for chunk in futures.chunks_mut(5) { + let chunk_results = futures::future::try_join_all(chunk).await?; + actors.extend(chunk_results); + info!("CreateActor: Processed a chunk of 5 approvals, sleeping 2s before next chunk"); + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + } + + Ok(actors) } #[derive(Clone)] diff --git a/host/src/cli.rs b/host/src/cli.rs index f6d7ef1..ef32bce 100644 --- a/host/src/cli.rs +++ b/host/src/cli.rs @@ -63,6 +63,9 @@ pub struct DemoConfig { #[arg(long, env = "NUM_ACTORS", default_value_t = 10)] pub num_actors: usize, + #[arg(long, env = "MAX_PREFERENCES", default_value_t = 3)] + pub max_preferences: usize, + /// Initial ETH balance for new accounts #[arg(long, env = "INITIAL_BALANCE", default_value = "5")] pub initial_balance: String, diff --git a/ttc/src/strict.rs b/ttc/src/strict.rs index 0f6f66d..b5e9901 100644 --- a/ttc/src/strict.rs +++ b/ttc/src/strict.rs @@ -270,9 +270,9 @@ pub mod test_utils { .prop_flat_map(|vertices| { let vertices: Vec = vertices.into_iter().collect(); let len = vertices.len(); - let m = (3 * len) / 2; + let m = len / 5; // we include more indices in order to increase the likelihood of trades - prop::collection::vec(prop::collection::vec(0..len, 0..=m), len).prop_map( + prop::collection::vec(prop::collection::vec(0..len, m..=m), len).prop_map( move |subsets| Preferences { prefs: vertices .iter() From 909873c7b5fedd8efb08c9fcb9ba67e8ff375fb1 Mon Sep 17 00:00:00 2001 From: kejace Date: Mon, 15 Sep 2025 14:47:18 -0700 Subject: [PATCH 2/3] fmt --- .github/actions/install-risc0/action.yml | 1 + host/bin/demo.rs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/actions/install-risc0/action.yml b/.github/actions/install-risc0/action.yml index f683c3a..324d3fe 100644 --- a/.github/actions/install-risc0/action.yml +++ b/.github/actions/install-risc0/action.yml @@ -20,5 +20,6 @@ runs: # Install RISC0 toolchain using specific version $HOME/.risc0/bin/rzup install + $HOME/.risc0/bin/rzup install rust 1.81.0 $HOME/.risc0/bin/rzup install cargo-risczero ${{ inputs.version }} $HOME/.risc0/bin/rzup install r0vm ${{ inputs.version }} \ No newline at end of file diff --git a/host/bin/demo.rs b/host/bin/demo.rs index fdd9fe4..aeab28d 100644 --- a/host/bin/demo.rs +++ b/host/bin/demo.rs @@ -44,7 +44,7 @@ fn make_token_preferences( _max_preferences: usize, ) -> Preferences { let mut rng = rand::thread_rng(); - let m : HashMap<&U256, Address> = prefs.prefs.keys().fold(HashMap::new(), |mut acc, k| { + let m: HashMap<&U256, Address> = prefs.prefs.keys().fold(HashMap::new(), |mut acc, k| { let collection = nft.choose(&mut rng).unwrap(); acc.insert(k, *collection); acc @@ -79,7 +79,9 @@ impl TestSetup { }; actor::create_actors(actor_config, addresses.ttc, owner.clone(), prefs).await }?; - let monitor = HttpClientBuilder::default().max_concurrent_requests(1).build(config.monitor_url()?)?; + let monitor = HttpClientBuilder::default() + .max_concurrent_requests(1) + .build(config.monitor_url()?)?; Ok(Self { config: config.clone(), node_url: node_url.clone(), @@ -155,7 +157,9 @@ impl TestSetup { for chunk in approval_futures.chunks_mut(5) { futures::future::try_join_all(chunk).await?; - info!("ApprovalFutures: Processed a chunk of 5 approvals, sleeping 3s before next chunk"); + info!( + "ApprovalFutures: Processed a chunk of 5 approvals, sleeping 3s before next chunk" + ); tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; } @@ -227,7 +231,9 @@ impl TestSetup { for chunk in futures.chunks_mut(5) { futures::future::try_join_all(chunk).await?; - info!("SetPreferences: Processed a chunk of 5 approvals, sleeping 2s before next chunk"); + info!( + "SetPreferences: Processed a chunk of 5 approvals, sleeping 2s before next chunk" + ); tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; } From 34ed9fd3c9199836326cd9c53cb9fe51e9a11375 Mon Sep 17 00:00:00 2001 From: kejace Date: Fri, 19 Sep 2025 12:00:58 -0400 Subject: [PATCH 3/3] Add check for if preference is valid --- host/bin/demo.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/host/bin/demo.rs b/host/bin/demo.rs index aeab28d..1320be4 100644 --- a/host/bin/demo.rs +++ b/host/bin/demo.rs @@ -49,12 +49,14 @@ fn make_token_preferences( acc.insert(k, *collection); acc }); - // TODO: enforce max_preferences prefs.clone().map(|v| { - let collection = m.get(&v).unwrap(); - ITopTradingCycle::Token { - collection: *collection, - tokenId: v, + let collection = m.get(&v); + match collection { + None => panic!("No collection found for token id {:#}", v), + Some(c) => ITopTradingCycle::Token { + collection: *c, + tokenId: v, + }, } }) } @@ -503,6 +505,7 @@ async fn main() -> Result<()> { let checkpointer_root_dir = Path::new(&config.base.artifacts_dir); Checkpointer::new(checkpointer_root_dir, config.ttc_address) }; + let test_case = { let mut runner = TestRunner::default(); let strategy = (Preferences::::arbitrary_with(Some(