Support multiple mine submission (#124)

* Support multiple mine submission

* Update
This commit is contained in:
bruno-valante 2024-07-12 17:31:53 +08:00 committed by GitHub
parent ba8d065e73
commit cca14e246e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 93 additions and 29 deletions

@ -1 +1 @@
Subproject commit faee29c2343036e1172321194569639ebeed6e01 Subproject commit 25bc14a27441e8fb26e4d42d7c8c885f92d6c74a

1
Cargo.lock generated
View File

@ -4527,6 +4527,7 @@ dependencies = [
"ethers", "ethers",
"hex", "hex",
"lazy_static", "lazy_static",
"lighthouse_metrics",
"network", "network",
"rand 0.8.5", "rand 0.8.5",
"shared_types", "shared_types",

View File

@ -10,6 +10,7 @@ zgs_spec = { path = "../../common/spec" }
zgs_seal = { path = "../../common/zgs_seal" } zgs_seal = { path = "../../common/zgs_seal" }
task_executor = { path = "../../common/task_executor" } task_executor = { path = "../../common/task_executor" }
contract-interface = { path = "../../common/contract-interface" } contract-interface = { path = "../../common/contract-interface" }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
ethereum-types = "0.14" ethereum-types = "0.14"
tokio = { version = "1.19.2", features = ["full"] } tokio = { version = "1.19.2", features = ["full"] }
tracing = "0.1.35" tracing = "0.1.35"

View File

@ -6,6 +6,7 @@ extern crate lazy_static;
mod config; mod config;
mod loader; mod loader;
mod metrics;
mod mine; mod mine;
mod miner_id; mod miner_id;
pub mod pora; pub mod pora;

32
node/miner/src/metrics.rs Normal file
View File

@ -0,0 +1,32 @@
use lighthouse_metrics::{try_create_int_counter, IntCounter, Result};
lazy_static! {
pub static ref SCRATCH_PAD_ITER_COUNT: Result<IntCounter> = try_create_int_counter(
"miner_scratch_pad_iter",
"Number of scratch pad iterations for PoRA"
);
pub static ref LOADING_COUNT: Result<IntCounter> = try_create_int_counter(
"miner_loading_iter",
"Number of loading iterations for PoRA"
);
pub static ref PAD_MIX_COUNT: Result<IntCounter> = try_create_int_counter(
"miner_mix_iter",
"Number of mix sealed data with scratch pad iterations for PoRA"
);
pub static ref HIT_COUNT: Result<IntCounter> =
try_create_int_counter("miner_hit", "Number of hit for PoRA");
}
pub fn report() -> String {
let s = |counter: &Result<IntCounter>| match counter {
Ok(x) => format!("{}", x.get()),
Err(_) => "n/a".to_string(),
};
format!(
"scratch pad: {}, loading: {}, pad_mix: {}, hit: {}",
s(&SCRATCH_PAD_ITER_COUNT),
s(&LOADING_COUNT),
s(&PAD_MIX_COUNT),
s(&HIT_COUNT)
)
}

View File

@ -6,15 +6,16 @@ use task_executor::TaskExecutor;
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tokio::time::{sleep, Duration, Instant}; use tokio::time::{sleep, Duration, Instant};
use storage::config::ShardConfig;
use zgs_spec::{SECTORS_PER_LOAD, SECTORS_PER_MAX_MINING_RANGE, SECTORS_PER_PRICING}; use zgs_spec::{SECTORS_PER_LOAD, SECTORS_PER_MAX_MINING_RANGE, SECTORS_PER_PRICING};
use super::metrics;
use crate::recall_range::RecallRange; use crate::recall_range::RecallRange;
use crate::{ use crate::{
pora::{AnswerWithoutProof, Miner}, pora::{AnswerWithoutProof, Miner},
watcher::MineContextMessage, watcher::MineContextMessage,
MinerConfig, MinerMessage, PoraLoader, MinerConfig, MinerMessage, PoraLoader,
}; };
use storage::config::ShardConfig;
use std::sync::Arc; use std::sync::Arc;
@ -122,7 +123,6 @@ impl PoraService {
let cpu_percent: u64 = self.cpu_percentage; let cpu_percent: u64 = self.cpu_percentage;
let diastole = sleep(Duration::from_secs(0)); let diastole = sleep(Duration::from_secs(0));
tokio::pin!(diastole); tokio::pin!(diastole);
// info!("CPU percent {}", cpu_percent);
loop { loop {
tokio::select! { tokio::select! {
@ -157,19 +157,21 @@ impl PoraService {
} }
maybe_msg = self.mine_context_receiver.recv() => { maybe_msg = self.mine_context_receiver.recv() => {
trace!("PoraService receives context={:?}", maybe_msg);
if let Some(msg) = maybe_msg { if let Some(msg) = maybe_msg {
debug!("Update mine service: {:?}", msg); info!("Update mine service: {:?}", msg);
info!("Mine iterations statistics: {}", metrics::report());
self.puzzle = msg.map(|(context, target_quality)| PoraPuzzle { self.puzzle = msg.map(|(context, target_quality)| PoraPuzzle {
context, target_quality context, target_quality
}); });
} else {
warn!("Mine context channel closed.");
} }
} }
() = &mut diastole, if !diastole.is_elapsed() => { () = &mut diastole, if !diastole.is_elapsed() => {
} }
_ = async {}, if mining_enabled && cpu_percent > 0 && self.as_miner().is_some() && diastole.is_elapsed() => { _ = async {}, if mining_enabled && cpu_percent > 0 && self.as_miner().map_or(false, |miner| miner.range.mining_length > 0) && diastole.is_elapsed() => {
let nonce = H256(rand::thread_rng().gen()); let nonce = H256(rand::thread_rng().gen());
let miner = self.as_miner().unwrap(); let miner = self.as_miner().unwrap();

View File

@ -1,8 +1,10 @@
use super::metrics::*;
use crate::recall_range::RecallRange; use crate::recall_range::RecallRange;
use crate::{MineRangeConfig, PoraLoader}; use crate::{MineRangeConfig, PoraLoader};
use blake2::{Blake2b512, Digest}; use blake2::{Blake2b512, Digest};
use contract_interface::zgs_flow::MineContext; use contract_interface::zgs_flow::MineContext;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use lighthouse_metrics::inc_counter;
use storage::log_store::MineLoadChunk; use storage::log_store::MineLoadChunk;
use tiny_keccak::{Hasher, Keccak}; use tiny_keccak::{Hasher, Keccak};
use zgs_spec::{BYTES_PER_SCRATCHPAD, BYTES_PER_SEAL, SECTORS_PER_LOAD, SECTORS_PER_SEAL}; use zgs_spec::{BYTES_PER_SCRATCHPAD, BYTES_PER_SEAL, SECTORS_PER_LOAD, SECTORS_PER_SEAL};
@ -58,16 +60,13 @@ impl<'a> Miner<'a> {
} }
pub async fn iteration(&self, nonce: H256) -> Option<AnswerWithoutProof> { pub async fn iteration(&self, nonce: H256) -> Option<AnswerWithoutProof> {
inc_counter(&SCRATCH_PAD_ITER_COUNT);
let ScratchPad { let ScratchPad {
scratch_pad, scratch_pad,
recall_seed, recall_seed,
pad_seed, pad_seed,
} = self.make_scratch_pad(&nonce); } = self.make_scratch_pad(&nonce);
if self.range.mining_length == 0 {
return None;
}
let recall_position = self.range.load_position(recall_seed)?; let recall_position = self.range.load_position(recall_seed)?;
if !self.mine_range_config.is_covered(recall_position).unwrap() { if !self.mine_range_config.is_covered(recall_position).unwrap() {
trace!( trace!(
@ -77,6 +76,7 @@ impl<'a> Miner<'a> {
return None; return None;
} }
inc_counter(&LOADING_COUNT);
let MineLoadChunk { let MineLoadChunk {
loaded_chunk, loaded_chunk,
avalibilities, avalibilities,
@ -95,6 +95,7 @@ impl<'a> Miner<'a> {
.zip(avalibilities.into_iter()) .zip(avalibilities.into_iter())
.filter_map(|(data, avaliable)| avaliable.then_some(data)) .filter_map(|(data, avaliable)| avaliable.then_some(data))
{ {
inc_counter(&PAD_MIX_COUNT);
// Rust can optimize this loop well. // Rust can optimize this loop well.
for (x, y) in sealed_data.iter_mut().zip(scratch_pad.iter()) { for (x, y) in sealed_data.iter_mut().zip(scratch_pad.iter()) {
*x ^= y; *x ^= y;
@ -111,6 +112,7 @@ impl<'a> Miner<'a> {
U256::MAX / self.target_quality, U256::MAX / self.target_quality,
quality_scale quality_scale
); );
inc_counter(&HIT_COUNT);
// Undo mix data when find a valid solition // Undo mix data when find a valid solition
for (x, y) in sealed_data.iter_mut().zip(scratch_pad.iter()) { for (x, y) in sealed_data.iter_mut().zip(scratch_pad.iter()) {
*x ^= y; *x ^= y;

View File

@ -143,7 +143,7 @@ impl Submitter {
SUBMISSION_RETIES SUBMISSION_RETIES
))?; ))?;
info!("Submit PoRA sucess"); info!("Submit PoRA success");
debug!("Receipt: {:?}", receipt); debug!("Receipt: {:?}", receipt);
Ok(()) Ok(())

View File

@ -106,13 +106,13 @@ impl MineContextWatcher {
async fn query_recent_context(&mut self) -> Result<(), String> { async fn query_recent_context(&mut self) -> Result<(), String> {
let context_call = self.flow_contract.make_context_with_result(); let context_call = self.flow_contract.make_context_with_result();
let epoch_call = self.mine_contract.last_mined_epoch(); let valid_call = self.mine_contract.can_submit();
let quality_call = self.mine_contract.target_quality(); let quality_call = self.mine_contract.pora_target();
let (context, epoch, quality) = let (context, can_submit, quality) =
try_join!(context_call.call(), epoch_call.call(), quality_call.call()) try_join!(context_call.call(), valid_call.call(), quality_call.call())
.map_err(|e| format!("Failed to query mining context: {:?}", e))?; .map_err(|e| format!("Failed to query mining context: {:?}", e))?;
let report = if context.epoch > epoch && context.digest != EMPTY_HASH.0 { let report = if can_submit && context.digest != EMPTY_HASH.0 {
Some((context, quality)) Some((context, quality))
} else { } else {
None None

View File

@ -35,7 +35,7 @@ class MineTest(TestFramework):
self.log.info("flow address: %s", self.contract.address()) self.log.info("flow address: %s", self.contract.address())
self.log.info("mine address: %s", self.mine_contract.address()) self.log.info("mine address: %s", self.mine_contract.address())
quality = int(2**256 / 400 / estimate_st_performance()) quality = int(2**256 / 100 / estimate_st_performance())
self.mine_contract.set_quality(quality) self.mine_contract.set_quality(quality)
self.log.info("Submit the first data chunk") self.log.info("Submit the first data chunk")
@ -49,14 +49,14 @@ class MineTest(TestFramework):
self.contract.update_context() self.contract.update_context()
self.log.info("Wait for the first mine answer") self.log.info("Wait for the first mine answer")
wait_until(lambda: self.mine_contract.last_mined_epoch() == 1, timeout=180) wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 1 and not self.mine_contract.can_submit(), timeout=180)
self.log.info("Wait for the second mine context release") self.log.info("Wait for the second mine context release")
wait_until(lambda: int(blockchain.eth_blockNumber(), 16) >= start_epoch + 2, timeout=180) wait_until(lambda: int(blockchain.eth_blockNumber(), 16) >= start_epoch + 2, timeout=180)
self.contract.update_context() self.contract.update_context()
self.log.info("Wait for the second mine answer") self.log.info("Wait for the second mine answer")
wait_until(lambda: self.mine_contract.last_mined_epoch() == 2, timeout=180) wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 2 and not self.mine_contract.can_submit(), timeout=180)
self.nodes[0].miner_stop() self.nodes[0].miner_stop()
self.log.info("Wait for the third mine context release") self.log.info("Wait for the third mine context release")
@ -69,7 +69,7 @@ class MineTest(TestFramework):
self.nodes[0].miner_start() self.nodes[0].miner_start()
self.log.info("Wait for the third mine answer") self.log.info("Wait for the third mine answer")
wait_until(lambda: self.mine_contract.last_mined_epoch() == 3, timeout=180) wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 3 and not self.mine_contract.can_submit(), timeout=180)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -45,8 +45,8 @@ class MineTest(TestFramework):
self.log.info("flow address: %s", self.contract.address()) self.log.info("flow address: %s", self.contract.address())
self.log.info("mine address: %s", self.mine_contract.address()) self.log.info("mine address: %s", self.mine_contract.address())
quality = int(2**256 / 5 / estimate_st_performance()) difficulty = int(2**256 / 5 / estimate_st_performance())
self.mine_contract.set_quality(quality) self.mine_contract.set_quality(difficulty)
SECTORS_PER_PRICING = int(8 * ( 2 ** 30 ) / 256) SECTORS_PER_PRICING = int(8 * ( 2 ** 30 ) / 256)
@ -67,10 +67,10 @@ class MineTest(TestFramework):
self.contract.update_context() self.contract.update_context()
self.log.info("Wait for mine answer") self.log.info("Wait for mine answer")
wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 1, timeout=120) wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 1 and not self.mine_contract.can_submit(), timeout=120)
rewards = self.reward_contract.reward_distributes() rewards = self.reward_contract.reward_distributes()
assert_equal(len(self.reward_contract.reward_distributes()), start_epoch + 1) assert_equal(len(rewards), 2)
firstReward = rewards[0].args.amount firstReward = rewards[0].args.amount
self.log.info("Received reward %d Gwei", firstReward / (10**9)) self.log.info("Received reward %d Gwei", firstReward / (10**9))
@ -87,10 +87,12 @@ class MineTest(TestFramework):
self.contract.update_context() self.contract.update_context()
self.log.info("Wait for mine answer") self.log.info("Wait for mine answer")
wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 2) wait_until(lambda: self.mine_contract.last_mined_epoch() == start_epoch + 2 and not self.mine_contract.can_submit())
assert_equal(self.contract.epoch(), start_epoch + 2)
rewards = self.reward_contract.reward_distributes() rewards = self.reward_contract.reward_distributes()
assert_equal(len(self.reward_contract.reward_distributes()), start_epoch + 2) assert_equal(len(rewards), 4)
secondReward = rewards[1].args.amount secondReward = rewards[2].args.amount
self.log.info("Received reward %d Gwei", secondReward / (10**9)) self.log.info("Received reward %d Gwei", secondReward / (10**9))
assert_greater_than(secondReward, 100 * firstReward / (start_epoch + 1)) assert_greater_than(secondReward, 100 * firstReward / (start_epoch + 1))

View File

@ -285,18 +285,27 @@ class BlockchainNode(TestNode):
def deploy_no_market(): def deploy_no_market():
self.log.debug("Start deploy contracts") self.log.debug("Start deploy contracts")
dummy_market_contract, _ = deploy_contract("DummyMarket", []) dummy_market_contract, _ = deploy_contract("DummyMarket", [])
self.log.debug("DummyMarket deployed") self.log.debug("DummyMarket deployed")
dummy_reward_contract, _ = deploy_contract("DummyReward", []) dummy_reward_contract, _ = deploy_contract("DummyReward", [])
self.log.debug("DummyReward deployed") self.log.debug("DummyReward deployed")
flow_contract, _ = deploy_contract("Flow", [mine_period, 0]) flow_contract, _ = deploy_contract("Flow", [mine_period, 0])
self.log.debug("Flow deployed") self.log.debug("Flow deployed")
mine_contract, _ = deploy_contract("PoraMineTest", [0]) mine_contract, _ = deploy_contract("PoraMineTest", [0])
self.log.debug("Mine deployed") self.log.debug("Mine deployed")
mine_contract.functions.initialize(1, 1, flow_contract.address, dummy_reward_contract.address).transact(TX_PARAMS)
mine_contract.functions.initialize(1, flow_contract.address, dummy_reward_contract.address).transact(TX_PARAMS)
mine_contract.functions.setDifficultyAdjustRatio(1).transact(TX_PARAMS)
mine_contract.functions.setTargetSubmissions(2).transact(TX_PARAMS)
self.log.debug("Mine Initialized") self.log.debug("Mine Initialized")
flow_initialize_hash = (flow_contract.get_function_by_signature('initialize(address)'))(dummy_market_contract.address).transact(TX_PARAMS) flow_initialize_hash = (flow_contract.get_function_by_signature('initialize(address)'))(dummy_market_contract.address).transact(TX_PARAMS)
self.log.debug("Flow Initialized") self.log.debug("Flow Initialized")
self.wait_for_transaction_receipt(w3, flow_initialize_hash) self.wait_for_transaction_receipt(w3, flow_initialize_hash)
self.log.info("All contracts deployed") self.log.info("All contracts deployed")
@ -309,22 +318,33 @@ class BlockchainNode(TestNode):
LIFETIME_MONTH = 1 LIFETIME_MONTH = 1
self.log.debug("Start deploy contracts") self.log.debug("Start deploy contracts")
mine_contract, _ = deploy_contract("PoraMineTest", [0]) mine_contract, _ = deploy_contract("PoraMineTest", [0])
self.log.debug("Mine deployed") self.log.debug("Mine deployed")
market_contract, _ = deploy_contract("FixedPrice", []) market_contract, _ = deploy_contract("FixedPrice", [])
self.log.debug("Market deployed") self.log.debug("Market deployed")
reward_contract, _ =deploy_contract("OnePoolReward", [LIFETIME_MONTH]) reward_contract, _ =deploy_contract("OnePoolReward", [LIFETIME_MONTH])
self.log.debug("Reward deployed") self.log.debug("Reward deployed")
flow_contract, _ = deploy_contract("FixedPriceFlow", [mine_period, 0]) flow_contract, _ = deploy_contract("FixedPriceFlow", [mine_period, 0])
self.log.debug("Flow deployed") self.log.debug("Flow deployed")
mine_contract.functions.initialize(1, 1, flow_contract.address, reward_contract.address).transact(TX_PARAMS)
mine_contract.functions.initialize(1, flow_contract.address, reward_contract.address).transact(TX_PARAMS)
mine_contract.functions.setDifficultyAdjustRatio(1).transact(TX_PARAMS)
mine_contract.functions.setTargetSubmissions(2).transact(TX_PARAMS)
self.log.debug("Mine Initialized") self.log.debug("Mine Initialized")
market_contract.functions.initialize(LIFETIME_MONTH, flow_contract.address, reward_contract.address).transact(TX_PARAMS) market_contract.functions.initialize(LIFETIME_MONTH, flow_contract.address, reward_contract.address).transact(TX_PARAMS)
self.log.debug("Market Initialized") self.log.debug("Market Initialized")
reward_contract.functions.initialize(market_contract.address, mine_contract.address).transact(TX_PARAMS) reward_contract.functions.initialize(market_contract.address, mine_contract.address).transact(TX_PARAMS)
self.log.debug("Reward Initialized") self.log.debug("Reward Initialized")
flow_initialize_hash = (flow_contract.get_function_by_signature('initialize(address)'))(market_contract.address).transact(TX_PARAMS) flow_initialize_hash = (flow_contract.get_function_by_signature('initialize(address)'))(market_contract.address).transact(TX_PARAMS)
self.log.debug("Flow Initialized") self.log.debug("Flow Initialized")
self.wait_for_transaction_receipt(w3, flow_initialize_hash) self.wait_for_transaction_receipt(w3, flow_initialize_hash)
self.log.info("All contracts deployed") self.log.info("All contracts deployed")

View File

@ -90,6 +90,9 @@ class MineContractProxy(ContractProxy):
def last_mined_epoch(self, node_idx=0): def last_mined_epoch(self, node_idx=0):
return self._call("lastMinedEpoch", node_idx) return self._call("lastMinedEpoch", node_idx)
def can_submit(self, node_idx=0):
return self._call("canSubmit", node_idx)
def set_quality(self, quality, node_idx=0): def set_quality(self, quality, node_idx=0):
return self._send("setQuality", node_idx, _targetQuality=quality) return self._send("setQuality", node_idx, _targetQuality=quality)