Skip to content

Commit

Permalink
feat: Implement the new specification of PoRA mine. (#307)
Browse files Browse the repository at this point in the history
* feat: Support faster PoRA specification

* Support subtasks in PoRA mine

* Fix test fails

* Check the contract version
  • Loading branch information
bruno-valante authored Dec 28, 2024
1 parent bb74143 commit 40d4355
Show file tree
Hide file tree
Showing 15 changed files with 419 additions and 48 deletions.
2 changes: 1 addition & 1 deletion common/spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub const TB: usize = 1024 * GB;

pub const BYTES_PER_SECTOR: usize = 256;
pub const BYTES_PER_SEAL: usize = 4 * KB;
pub const BYTES_PER_SCRATCHPAD: usize = 64 * KB;
pub const BYTES_PER_SCRATCHPAD: usize = 16 * KB;
pub const BYTES_PER_LOAD: usize = 256 * KB;
pub const BYTES_PER_PRICING: usize = 8 * GB;
pub const BYTES_PER_MAX_MINING_RANGE: usize = 8 * TB;
Expand Down
18 changes: 13 additions & 5 deletions node/miner/src/mine.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use contract_interface::zgs_flow::MineContext;
use contract_interface::pora_mine::MineContext;
use ethereum_types::{H256, U256};
use rand::{self, Rng};
use std::time;
Expand Down Expand Up @@ -35,16 +35,23 @@ pub struct PoraService {
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct PoraPuzzle {
context: MineContext,
target_quality: U256,
pora_target: U256,
max_shards: u64,
subtask_digest: H256,
}

impl PoraPuzzle {
pub fn new(context: MineContext, target_quality: U256, max_shards: u64) -> Self {
pub fn new(
context: MineContext,
pora_target: U256,
max_shards: u64,
subtask_digest: H256,
) -> Self {
Self {
context,
target_quality,
pora_target,
max_shards,
subtask_digest,
}
}

Expand Down Expand Up @@ -255,7 +262,8 @@ impl PoraService {
miner_id: &self.miner_id,
mine_range_config: &self.mine_range,
context: &puzzle.context,
target_quality: &puzzle.target_quality,
subtask_digest: &puzzle.subtask_digest,
pora_target: &puzzle.pora_target,
loader: &*self.loader,
})
}
Expand Down
20 changes: 13 additions & 7 deletions node/miner/src/pora.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use super::metrics::*;
use crate::recall_range::RecallRange;
use crate::{MineRangeConfig, PoraLoader};
use blake2::{Blake2b512, Digest};
use contract_interface::zgs_flow::MineContext;
use contract_interface::pora_mine::MineContext;
use ethereum_types::{H256, U256};
use ethers::utils::keccak256;
use lighthouse_metrics::inc_counter;
use storage::log_store::MineLoadChunk;
use tiny_keccak::{Hasher, Keccak};
Expand All @@ -24,7 +25,8 @@ pub(crate) struct Miner<'a> {
pub range: RecallRange,
pub miner_id: &'a H256,
pub context: &'a MineContext,
pub target_quality: &'a U256,
pub subtask_digest: &'a H256,
pub pora_target: &'a U256,
pub loader: &'a dyn PoraLoader,
pub mine_range_config: &'a MineRangeConfig,
}
Expand Down Expand Up @@ -106,11 +108,11 @@ impl<'a> Miner<'a> {
.range
.difficulty_scale_x64(self.context.flow_length.as_u64());

if quality <= (self.target_quality / difficulty_scale_x64) << 64 {
if quality <= (self.pora_target / difficulty_scale_x64) << 64 {
debug!(
"Find a PoRA valid answer, quality: {}, target_quality {}, scale {:.3}",
"Find a PoRA valid answer, quality: {}, pora_target {}, scale {:.3}",
U256::MAX / quality,
U256::MAX / self.target_quality,
U256::MAX / self.pora_target,
difficulty_scale_x64.as_u128() as f64 / u64::MAX as f64
);
inc_counter(&HIT_COUNT);
Expand Down Expand Up @@ -138,7 +140,7 @@ impl<'a> Miner<'a> {
let mut hasher = Blake2b512::new();
hasher.update(self.miner_id);
hasher.update(nonce);
hasher.update(self.context.digest);
hasher.update(self.subtask_digest);
hasher.update(self.range.digest());
hasher.finalize().into()
};
Expand All @@ -148,7 +150,11 @@ impl<'a> Miner<'a> {
let mut scratch_pad =
[[0u8; BLAKE2B_OUTPUT_BYTES]; BYTES_PER_SCRATCHPAD / BLAKE2B_OUTPUT_BYTES];
for scratch_pad_cell in scratch_pad.iter_mut() {
digest = Blake2b512::new().chain_update(digest).finalize().into();
let output0 = keccak256(digest);
digest[..32].copy_from_slice(&output0);
let output1 = keccak256(digest);
digest[32..].copy_from_slice(&output1);

*scratch_pad_cell = digest;
}

Expand Down
2 changes: 1 addition & 1 deletion node/miner/src/sealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl Sealer {

async fn update_flow_length(&mut self) -> Result<()> {
let recent_context = self.flow_contract.make_context_with_result().call().await?;
debug!(target: "seal", "Recent context is {:?}", recent_context);
debug!("Recent context is {:?}", recent_context);

let recent_flow_length = recent_context.flow_length.as_u64();
if self.last_context_flow_length < recent_flow_length {
Expand Down
1 change: 1 addition & 0 deletions node/miner/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl MineService {
msg_recv.resubscribe(),
provider.clone(),
&config,
miner_id,
);

let mine_answer_receiver = PoraService::spawn(
Expand Down
64 changes: 46 additions & 18 deletions node/miner/src/watcher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(unused)]

use contract_interface::{zgs_flow::MineContext, PoraMine, ZgsFlow};
use contract_interface::{zgs_flow::MineContext, PoraMine, WorkerContext, ZgsFlow};
use ethereum_types::{Address, H256, U256};
use ethers::{
contract::Contract,
Expand Down Expand Up @@ -28,6 +28,8 @@ lazy_static! {
H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap();
}

const PORA_VERSION: u64 = 1;

pub struct MineContextWatcher {
provider: Arc<Provider<RetryClient<Http>>>,
flow_contract: ZgsFlow<Provider<RetryClient<Http>>>,
Expand All @@ -36,6 +38,7 @@ pub struct MineContextWatcher {
mine_context_sender: broadcast::Sender<MineContextMessage>,
last_report: MineContextMessage,
query_interval: Duration,
miner_id: H256,

msg_recv: broadcast::Receiver<MinerMessage>,
}
Expand All @@ -46,6 +49,7 @@ impl MineContextWatcher {
msg_recv: broadcast::Receiver<MinerMessage>,
provider: Arc<Provider<RetryClient<Http>>>,
config: &MinerConfig,
miner_id: H256,
) -> broadcast::Receiver<MineContextMessage> {
let mine_contract = PoraMine::new(config.mine_address, provider.clone());
let flow_contract = ZgsFlow::new(config.flow_address, provider.clone());
Expand All @@ -60,6 +64,7 @@ impl MineContextWatcher {
msg_recv,
last_report: None,
query_interval: config.context_query_interval,
miner_id,
};
executor.spawn(
async move { Box::pin(watcher.start()).await },
Expand Down Expand Up @@ -105,33 +110,56 @@ impl MineContextWatcher {
}

async fn query_recent_context(&mut self) -> Result<(), String> {
let context_call = self.flow_contract.make_context_with_result();
let valid_call = self.mine_contract.can_submit();
let quality_call = self.mine_contract.pora_target();
let shards_call = self.mine_contract.max_shards();

let (context, can_submit, quality, max_shards) = try_join!(
context_call.call(),
valid_call.call(),
quality_call.call(),
shards_call.call()
)
.map_err(|e| format!("Failed to query mining context: {:?}", e))?;
let report = if can_submit && context.digest != EMPTY_HASH.0 {
Some(PoraPuzzle::new(context, quality, max_shards))
} else {
None
};
let report = self.fetch_pora_puzzle().await?;

if report == self.last_report {
return Ok(());
}

debug!("Update pora puzzle: {:?}", report);

self.mine_context_sender
.send(report.clone())
.map_err(|e| format!("Failed to send out the most recent mine context: {:?}", e))?;
self.last_report = report;

Ok(())
}

async fn fetch_pora_puzzle(&self) -> Result<Option<PoraPuzzle>, String> {
let pora_version = self
.mine_contract
.pora_version()
.call()
.await
.map_err(|e| format!("Failed to query mining version: {:?}", e))?;

if pora_version != PORA_VERSION {
return Ok(None);
}

let miner_id = self.miner_id.0;
let WorkerContext {
context,
pora_target,
subtask_digest,
max_shards,
} = self
.mine_contract
.compute_worker_context(miner_id)
.call()
.await
.map_err(|e| format!("Failed to query mining context: {:?}", e))?;

if pora_target.is_zero() || context.digest == EMPTY_HASH.0 {
return Ok(None);
}

Ok(Some(PoraPuzzle::new(
context,
pora_target,
max_shards,
H256(subtask_digest),
)))
}
}
2 changes: 1 addition & 1 deletion storage-contracts-abis/0g-storage-contracts-rev
Original file line number Diff line number Diff line change
@@ -1 +1 @@
30f0f921a80078e43d578c82b746677bcf06d786
1e931c7b168f9bc2b55f7b8fd96946e35b373048
4 changes: 2 additions & 2 deletions storage-contracts-abis/ChunkLinearReward.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions storage-contracts-abis/FixedPrice.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions storage-contracts-abis/FixedPriceFlow.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions storage-contracts-abis/Flow.json

Large diffs are not rendered by default.

Loading

0 comments on commit 40d4355

Please sign in to comment.