diff --git a/configuration/Dockerfile b/configuration/Dockerfile index f343f6dd..c7b81961 100644 --- a/configuration/Dockerfile +++ b/configuration/Dockerfile @@ -1,5 +1,4 @@ FROM nginx:latest - # Install additional CA certificates if needed COPY ./configuration/trust/* /usr/local/share/ca-certificates/ RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates || true diff --git a/configuration/nginx.conf b/configuration/nginx.conf index 2dc0be2b..862d6340 100644 --- a/configuration/nginx.conf +++ b/configuration/nginx.conf @@ -1,6 +1,9 @@ server { - # Increase timeouts to handle long uploads - client_max_body_size 500M; + client_max_body_size 30G; + # Increase if you need to upload larger files + client_body_timeout 3600s; + send_timeout 3600s; + keepalive_timeout 75s; location / { root /var/www/html; dav_methods PUT DELETE MKCOL COPY MOVE; diff --git a/docker-compose.yml b/docker-compose.yml index c440f0a0..b5bd1257 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -206,7 +206,7 @@ services: stdin_open: true # keep stdin open, so we can print things in docker compose up tty: true # required for logs to print in colour environment: - - NF4_RUN_MODE=${NF4_RUN_MODE:-base_sepolia} + - NF4_RUN_MODE=${NF4_RUN_MODE:-plume_testnet} - RUST_BACKTRACE=${RUST_BACKTRACE:-0} - NF4_SIGNING_KEY=${PROPOSER_SIGNING_KEY} - NF4_MOCK_PROVER=${NF4_MOCK_PROVER:-false} @@ -332,8 +332,8 @@ services: command: mongod --quiet --logpath /dev/null networks: - nightfall_network - # volumes: - # - mongodb_proposer_data:/data/db + volumes: + - mongodb_proposer_data:/data/db environment: - NF4_RUN_MODE=${NF4_RUN_MODE:-development} @@ -419,7 +419,7 @@ services: stdin_open: true # keep stdin open, so we can print things in docker compose up tty: true # required for logs to print in colour environment: - - NF4_RUN_MODE=${NF4_RUN_MODE:-base_sepolia} + - NF4_RUN_MODE=${NF4_RUN_MODE:-plume_testnet} - NF4_CONTRACTS__DEPLOY_CONTRACTS=${NF4_CONTRACTS__DEPLOY_CONTRACTS:-true} - RUST_BACKTRACE=${RUST_BACKTRACE:-0} - NF4_SIGNING_KEY=${DEPLOYER_SIGNING_KEY} @@ -438,9 +438,9 @@ services: build: dockerfile: configuration/Dockerfile context: . - # restart: unless-stopped - # ports: - # - "8080:80" replace with a port that is exposed for configuration service if required + restart: unless-stopped + ports: + - "8080:80" platform: linux/amd64 # Required for building on M1 Macs volumes: - type: bind @@ -555,7 +555,7 @@ services: volumes: # mongodb_client_data: # mongodb_client2_data: - # mongodb_proposer_data: + mongodb_proposer_data: address_data: networks: diff --git a/nightfall.toml b/nightfall.toml index 66da1bb1..86bf5ce5 100644 --- a/nightfall.toml +++ b/nightfall.toml @@ -203,6 +203,86 @@ certificate_policies = [ "0x06032d0607000000000000000000000000000000000000000000000000000000", ] +[plume_testnet] +# This is the plume_testnet environment - add an appropriate configuration here, using development as a template +signing_key = "Key not set" #key (0) +azure_vault_url = "vault url is not set" +azure_key_name = "key name not set" +log_app_only = true +test_x509_certificates = true +mock_prover = false +genesis_block = 0 +ethereum_client_url = "wss://testnet-rpc.plume.org" +configuration_url = "http://35.225.105.10:8080" # The name of the proposer config to use can be overridden by env var NF4_PROPOSER_CONFIG + +[plume_testnet.network] +chain_id = 98867 + +[plume_testnet.nightfall_client] +url = "http://client:3000" +log_level = "info" +wallet_type = "local" +db_url = "mongodb://nf4_db_client:27017" +max_event_listener_attempts = 10 +webhook_url = "http://172.18.0.250:8080/webhook" # The webhook URL for the client to send notifications to the propose +max_queue_size = 1000 + +[plume_testnet.nightfall_proposer] +url = "http://35.225.105.10:3001" +log_level = "info" +wallet_type = "local" +db_url = "mongodb://nf4_db_proposer:27017" +block_assembly_max_wait_secs = 120 +block_assembly_target_fill_ratio = 0.25 +block_assembly_initial_interval_secs = 15 +max_event_listener_attempts = 10 +block_size = 64 + + +[plume_testnet.nightfall_deployer] +log_level = "info" +default_proposer_address = "0xd1Fe296737888f2310EDD943bc6342a41d8Da409" +default_proposer_url = "http://35.225.105.10:3001" +proposer_stake = 4 +proposer_ding = 3 # how much to fine a proposer for not making a block +proposer_exit_penalty = 2 # how much to fine a proposer for deregister during its turn +proposer_cooling_blocks = 4 # how many blocks before a de-registered proposer can register again +proposer_rotation_blocks = 4 # how many blocks before we rotate proposers + +[plume_testnet.nightfall_test] +log_level = "info" + +[plume_testnet.owners] +vk_provider_owner = "0x32fE75423eFaC83B636E41115152A0Fe6D14C7c2" +x509_owner = "0x32fE75423eFaC83B636E41115152A0Fe6D14C7c2" +verifier_owner = "0x32fE75423eFaC83B636E41115152A0Fe6D14C7c2" +round_robin_owner = "0x32fE75423eFaC83B636E41115152A0Fe6D14C7c2" +nightfall_owner = "0x32fE75423eFaC83B636E41115152A0Fe6D14C7c2" + +[plume_testnet.contracts] +assets = "./blockchain_assets/artifacts" +rust_bindings = "./nightfall_bindings" +addresses_file = "configuration/toml/addresses.toml" +deployment_file = "./blockchain_assets/logs/deployer.s.sol" +deploy_contracts = true + +[plume_testnet.contracts.contract_addresses] # Contract addresses for the development network. These are used if deploy_contracts is false and they are not empty +nightfall = "" +round_robin = "" +x509 = "" + +[plume_testnet.certificates] +authority_key_identifier = "0xA469FF28BFAB9C4DB09220B24038D6F18EA57F75" +modulus = "0x009DEA9DCA80BFA87C29232B18D6C0072898922A7E7E224A7FF638F61851B5F36392E7FBFDBFF3A0AE409763E2A04CDD3DC692A6DE447391FFE6722456957DD7F031B8D9A7999579F6F4258490AE6E9D629BC40815F689C58037C03B46502243BFD29B02116454453810D160DE1D8C8DDD624B30A25A011185E60BCA9BF71181DD3256112F1EFDBECF19E77AF9640EDE4DB8FF51855E6B490424FC4F5631DD9551D7CD762420E3AFA0B20E6B403A0CB71FA16861F8C591B2BD7BDD564EC6D5A17A932E310876D1D65AF3F3F213D1C49086F32C7C8A0F53750127DF8709F6035688E02E613F1C57A525A21DD83FA27D0622FC0EFC76ABA114194A7FDA1B0879013D0790F3B8D387ACA238FC37135F9BA6BB0C87A972143568B010B62EE8BA71C78202858170F292596AD95DD4FA2DC8E9ABA359B8F511B5F3894906F3FD0A22CA3DEB2E67B2A97CD2B847AC73BE28F69996A4CF51B6FD87B9F932F6049F886AC5A7725755693842DF00795A9D00C76E2C4446BDDA5E595CBE8CDF51E050632DB110D155343188A57F273B4334E5DA5EC556AD3CADC3327268DC0C528FE41F837A393B5B2F76E476CFA64A2A24BA71F5F7078F5360EBF316D4275AB292B031B9CF8787ACB009D3DC5DCD5038C05E1B2225909E596DFE2E968CFAE077FDFF540E3F78FC464966BB19E280DE34F81079B9DCA111904CDC7C5B6FD5CD44A215B0B5A6A9" +exponent = 65537 +extended_key_usages = [ + "0x06082b0601050507030400000000000000000000000000000000000000000000", + "0x06082b0601050507030800000000000000000000000000000000000000000000", +] +certificate_policies = [ + "0x06032d0607000000000000000000000000000000000000000000000000000000", +] + [production] # This is the production environment - add an appropriate configuration here, using development as a template diff --git a/nightfall_proposer/src/driven/nightfall_contract.rs b/nightfall_proposer/src/driven/nightfall_contract.rs index 3f3bbcdd..c9d1d25e 100644 --- a/nightfall_proposer/src/driven/nightfall_contract.rs +++ b/nightfall_proposer/src/driven/nightfall_contract.rs @@ -5,7 +5,7 @@ use crate::{ ports::contracts::NightfallContract, }; use alloy::primitives::I256; -use configuration::addresses::get_addresses; +use configuration::{addresses::get_addresses, settings::get_settings}; use lib::blockchain_client::BlockchainClientConnection; use log::info; use nightfall_bindings::artifacts::Nightfall; @@ -29,11 +29,25 @@ impl NightfallContract for Nightfall::NightfallCalls { let nightfall = Nightfall::new(nightfall_address, client); // Convert the block transactions to the Nightfall format let blk: Nightfall::Block = block.into(); + let nonce = blockchain_client.get_transaction_count(signer.address()).await.map_err(|_| NightfallContractError::TransactionError)?; + let gas_price = blockchain_client.get_gas_price().await.map_err(|_| NightfallContractError::TransactionError)?; + let max_fee_per_gas = gas_price * 2; + let max_priority_fee_per_gas = gas_price; + let gas_limit = 5000000u64; - let receipt = nightfall + + let raw_tx = nightfall .propose_block(blk) - .from(signer.address()) - .send() + .nonce(nonce) + .gas(gas_limit) + .max_fee_per_gas(max_fee_per_gas) + .max_priority_fee_per_gas(max_priority_fee_per_gas) + .chain_id(get_settings().network.chain_id) // Linea testnet chain ID + .build_raw_transaction(signer).await + .map_err(|_| NightfallContractError::TransactionError)?; + + let receipt = blockchain_client + .send_raw_transaction(&raw_tx) .await .map_err(|_| NightfallContractError::TransactionError)? .get_receipt() diff --git a/nightfall_proposer/src/drivers/blockchain/nightfall_event_listener.rs b/nightfall_proposer/src/drivers/blockchain/nightfall_event_listener.rs index dc1ea7d5..f3608338 100644 --- a/nightfall_proposer/src/drivers/blockchain/nightfall_event_listener.rs +++ b/nightfall_proposer/src/drivers/blockchain/nightfall_event_listener.rs @@ -117,6 +117,58 @@ where ]) .from_block(start_block as u64); + { + + let latest_block = blockchain_client + .get_block_number() + .await + .expect("could not get latest block number"); + + + if latest_block >= start_block as u64 { + let past_events = blockchain_client + .get_logs(&events_filter.clone().to_block(latest_block)) + .await + .expect("could not get past events"); + log::info!("Found {} past events to process", past_events.len()); + for evt in past_events { + let event = match Nightfall::NightfallEvents::decode_log(&evt.inner) { + Ok(e) => e, + Err(e) => { + warn!("Failed to decode log: {e:?}"); + continue; // Skip malformed events + } + }; + let result = process_events::(event.data, evt).await; + match result { + Ok(_) => continue, + Err(e) => { + match e { + // we're missing blocks, so we need to re-synchronise + EventHandlerError::MissingBlocks(n) => { + warn!("Missing blocks. Last contiguous block was {n}. Restarting event listener"); + restart_event_listener::(start_block).await; + return Err(EventHandlerError::StreamTerminated); + } + + EventHandlerError::BlockHashError(expected, found) => { + warn!( + "Block hash mismatch: expected {expected:?}, found {found:?}. Restarting event listener" + ); + restart_event_listener::(start_block).await; + return Err(EventHandlerError::StreamTerminated); + } + + _ => panic!("Error processing event: {e:?}"), + } + } + } + } + } else { + println!("Start block {} is greater than latest block {}. No past events to process.", start_block, latest_block); + } + } + // Subscribe to the combined events filter let events_subscription = blockchain_client .subscribe_logs(&events_filter) diff --git a/nightfall_proposer/src/drivers/rest/proposers.rs b/nightfall_proposer/src/drivers/rest/proposers.rs index c7256dc9..1fd005fb 100644 --- a/nightfall_proposer/src/drivers/rest/proposers.rs +++ b/nightfall_proposer/src/drivers/rest/proposers.rs @@ -66,14 +66,39 @@ async fn handle_add_proposer(url: String) -> Result .await; let blockchain_client = read_connection.get_client(); let caller = read_connection.get_address(); + let signer = read_connection.get_signer(); let client = blockchain_client.root(); let proposer_manager = RoundRobin::new(get_addresses().round_robin, client.clone()); - // add the proposer - let tx = proposer_manager + + let nonce = blockchain_client.get_transaction_count(caller).await.map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToAddProposer + })?; + let gas_price = blockchain_client.get_gas_price().await.map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToAddProposer + })?; + let max_fee_per_gas = gas_price * 2; + let max_priority_fee_per_gas = gas_price; + let gas_limit = 5000000u64; + + + let raw_tx = proposer_manager .add_proposer(url) .value(U256::from(get_settings().nightfall_deployer.proposer_stake)) - .from(caller) - .send() + .nonce(nonce) + .gas(gas_limit) + .max_fee_per_gas(max_fee_per_gas) + .max_priority_fee_per_gas(max_priority_fee_per_gas) + .chain_id(get_settings().network.chain_id) // Linea testnet chain ID + .build_raw_transaction(signer).await + .map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToAddProposer + })?; + // add the proposer + let tx = blockchain_client + .send_raw_transaction(&raw_tx) .await .map_err(|e| { warn!("{e}"); @@ -110,6 +135,7 @@ async fn handle_remove_proposer() -> Result { .await; let blockchain_client = read_connection.get_client(); let signer_address = read_connection.get_address(); + let signer = read_connection.get_signer(); let client = blockchain_client.root(); let proposer_manager = RoundRobin::new(get_addresses().round_robin, client.clone()); @@ -134,11 +160,34 @@ async fn handle_remove_proposer() -> Result { } } - // remove the proposer - let tx = proposer_manager + let nonce = blockchain_client.get_transaction_count(signer_address).await.map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToRemoveProposer + })?; + let gas_price = blockchain_client.get_gas_price().await.map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToRemoveProposer + })?; + let max_fee_per_gas = gas_price * 2; + let max_priority_fee_per_gas = gas_price; + let gas_limit = 5000000u64; + + + let raw_tx = proposer_manager .remove_proposer() - .from(signer_address) - .send() + .nonce(nonce) + .gas(gas_limit) + .max_fee_per_gas(max_fee_per_gas) + .max_priority_fee_per_gas(max_priority_fee_per_gas) + .chain_id(get_settings().network.chain_id) // Linea testnet chain ID + .build_raw_transaction(signer).await + .map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToRemoveProposer + })?; + // add the proposer + let tx = blockchain_client + .send_raw_transaction(&raw_tx) .await .map_err(|_e| { warn!("Failed to remove proposer"); @@ -176,12 +225,37 @@ async fn handle_withdraw(amount: u64) -> Result { .await; let blockchain_client = read_connection.get_client(); let caller = read_connection.get_address(); + let signer = read_connection.get_signer(); let proposer_manager = RoundRobin::new(get_addresses().round_robin, blockchain_client.root()); // attemp to withdraw the stake - let tx = proposer_manager + let nonce = blockchain_client.get_transaction_count(caller).await .map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToWithdrawStake + })?; + let gas_price = blockchain_client.get_gas_price().await .map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToWithdrawStake + })?; + let max_fee_per_gas = gas_price * 2; + let max_priority_fee_per_gas = gas_price; + let gas_limit = 5000000u64; + + + let raw_tx = proposer_manager .withdraw(U256::from(amount)) - .from(caller) - .send() + .nonce(nonce) + .gas(gas_limit) + .max_fee_per_gas(max_fee_per_gas) + .max_priority_fee_per_gas(max_priority_fee_per_gas) + .chain_id(get_settings().network.chain_id) // Linea testnet chain ID + .build_raw_transaction(signer).await + .map_err(|e| { + warn!("{e}"); + ProposerRejection::FailedToWithdrawStake + })?; + // add the proposer + let tx = blockchain_client + .send_raw_transaction(&raw_tx) .await .map_err(|e| { warn!("{e}");