Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bento/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ Bento supports optional Proof of Verifiable Work for enhanced security:

```bash
# Enable POVW
export POVW_LOG_ID="0x0000000000000000000000000000000000000000"
export REWARDS_ADDRESS="0x0000000000000000000000000000000000000000"

# Start agents with POVW support
cargo run -p workflow -- join --task-stream join
Expand Down Expand Up @@ -285,7 +285,7 @@ When POVW is enabled:
| `DATABASE_URL` | PostgreSQL connection string | Required |
| `REDIS_URL` | Redis connection string | Required |
| `RISC0_DEV_MODE` | Enable development mode | `false` |
| `POVW_LOG_ID` | POVW log identifier | Required to enable POVW |
| `REWARDS_ADDRESS`| Rewards address for POVW | Required to enable POVW |

### Agent Configuration

Expand Down
3 changes: 2 additions & 1 deletion compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ x-base-environment: &base-environment
RUST_LOG: ${RUST_LOG:-info}
RISC0_HOME: /usr/local/risc0
RUST_BACKTRACE: 1
POVW_LOG_ID: ${POVW_LOG_ID:-}
REWARDS_ADDRESS: ${REWARDS_ADDRESS:-${POVW_LOG_ID:-}}
POVW_LOG_ID: ${REWARDS_ADDRESS:-${POVW_LOG_ID:-}}
x-agent-common: &agent-common
runtime: nvidia
build:
Expand Down
52 changes: 26 additions & 26 deletions crates/boundless-cli/src/commands/povw/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,13 @@ const HOUR: Duration = Duration::from_secs(60 * 60);
#[non_exhaustive]
#[derive(Args, Clone, Debug)]
pub struct PovwClaim {
// TODO(povw): Support providing multiple log IDs.
/// Work log ID for the reward claim.
// TODO(povw): Support providing multiple rewards addresses.
/// Rewards address for the reward claim.
///
/// State for submitted updates is retrieved from the chain using the ID. Note that initiating
/// the claim can be done for any log ID and does not require authorization.
/// Work log ID for the reward claim.
///
/// State for submitted updates is retrieved from the chain using the ID. Note that initiating
/// the claim can be done for any log ID and does not require authorization.
#[arg(short, long)]
pub log_id: PovwLogId,
/// State for submitted updates is retrieved from the chain using the address. Note that initiating
/// the claim can be done for any rewards address and does not require authorization.
#[arg(short, long = "rewards-address", alias = "log-id")]
pub rewards_address: PovwLogId,

// TODO: Deprecate and/or remove this when history support works without the Beacon API.
/// URL for an Ethereum Beacon chain (i.e. consensus chain) API.
Expand Down Expand Up @@ -141,20 +137,24 @@ impl PovwClaim {
// Determine the commit range for which we can mint. This is the difference between the
// recoreded work log commit on the accounting contract and on the mint contract.
let initial_commit = Digest::from(
*povw_mint.workLogCommit(self.log_id.into()).call().await.with_context(|| {
format!(
"Failed to call IPovwMint.workLogCommit on {}",
deployment.povw_mint_address
)
})?,
*povw_mint.workLogCommit(self.rewards_address.into()).call().await.with_context(
|| {
format!(
"Failed to call IPovwMint.workLogCommit on {}",
deployment.povw_mint_address
)
},
)?,
);
let final_commit = Digest::from(
*povw_accounting.workLogCommit(self.log_id.into()).call().await.with_context(|| {
format!(
"Failed to call IPovwAccounting.workLogCommit on {}",
deployment.povw_accounting_address
)
})?,
*povw_accounting.workLogCommit(self.rewards_address.into()).call().await.with_context(
|| {
format!(
"Failed to call IPovwAccounting.workLogCommit on {}",
deployment.povw_accounting_address
)
},
)?,
);
tracing::debug!(%initial_commit, %final_commit, "Commit range for mint");

Expand All @@ -167,7 +167,7 @@ impl PovwClaim {
tracing::info!("Searching for work log update events in the past {} days", self.days);
let update_events = search_work_log_updated(
&povw_accounting,
self.log_id,
self.rewards_address,
initial_commit,
final_commit,
latest_block_number,
Expand Down Expand Up @@ -256,7 +256,7 @@ impl PovwClaim {

tracing::info!("Building input data for Mint Calculator guest");
let mint_input = mint_calculator_prover
.build_input(event_block_numbers, [self.log_id])
.build_input(event_block_numbers, [self.rewards_address])
.await
.context("Failed to build input for Mint Calculator Guest")?;

Expand Down Expand Up @@ -365,7 +365,7 @@ async fn block_number_near_timestamp(
/// found [WorkLogUpdated] events along with the block number at which they were emitted.
async fn search_work_log_updated(
povw_accounting: &IPovwAccountingInstance<impl Provider>,
log_id: PovwLogId,
rewards_address: PovwLogId,
initial_commit: Digest,
final_commit: Digest,
upper_limit_block_number: u64,
Expand All @@ -392,7 +392,7 @@ async fn search_work_log_updated(
let filter = Filter::new()
.address(*povw_accounting.address())
.event_signature(WorkLogUpdated::SIGNATURE_HASH)
.topic1(Address::from(log_id));
.topic1(Address::from(rewards_address));

tracing::debug!(%initial_commit, %final_commit, %upper_limit_block_number, %lower_limit_block_number, "Searching for WorkLogUpdated events");
search_events(
Expand Down
20 changes: 10 additions & 10 deletions crates/boundless-cli/src/commands/povw/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ use super::{State, WorkReceipt};
#[non_exhaustive]
#[derive(Args, Clone, Debug)]
pub struct PovwPrepare {
/// Create a new work log with the given work log identifier.
/// Create a new work log with the given rewards address.
///
/// The work log identifier is a 160-bit public key hash (i.e. an Ethereum address) which is
/// The rewards address is a 160-bit public key hash (i.e. an Ethereum address) which is
/// used to identify the work log. A work log is a collection of work claims, including their
/// value and nonces. A single work log can only include a nonce (and so a receipt) once.
///
/// A prover may have one or more work logs, and may set the work log ID equal to their onchain
/// prover address, or to a new address just used as the work log ID.
/// A prover may have one or more work logs, and may set the rewards address equal to their onchain
/// prover address, or to a new address just used as the rewards address.
/// If this not set, then the state file must exist.
#[arg(short, long = "new")]
new_log_id: Option<PovwLogId>,
#[arg(short, long = "new", alias = "new-log-id")]
new_rewards_address: Option<PovwLogId>,

/// Path of the work log state file to load the work log and store the prepared update.
#[arg(short, long, env = "POVW_STATE_PATH")]
Expand Down Expand Up @@ -74,17 +74,17 @@ impl PovwPrepare {
/// Run the [PovwPrepare] command.
pub async fn run(&self) -> Result<()> {
// Load the existing state, if provided.
let mut state = if let Some(log_id) = self.new_log_id {
let mut state = if let Some(log_id) = self.new_rewards_address {
if self.state.exists() {
bail!("File already exists at the state path; refusing to overwrite");
}
tracing::info!("Initializing a new work log with ID {log_id:x}");
tracing::info!("Initializing a new work log with rewards address {log_id:x}");
State::new(log_id)
} else {
let state = State::load(&self.state).await.context("Failed to load state file")?;
tracing::info!("Loaded work log state from {}", self.state.display(),);
tracing::debug!(commit = %state.work_log.commit(), "Loaded work log commit");
tracing::info!("Preparing work log update for log ID: {:x}", state.log_id);
tracing::info!("Preparing work log update for rewards address: {:x}", state.log_id);
state
};

Expand Down Expand Up @@ -236,7 +236,7 @@ fn check_work_receipt<T: Borrow<WorkReceipt>>(
// NOTE: If nonce_max does not have the same log ID as nonce_min, the exec will fail.
ensure!(
work_claim.nonce_min.log == log_id,
"Receipt has a log ID that does not match the work log: receipt: {:x}, work log: {:x}",
"Receipt has a rewards address that does not match the work log: receipt: {:x}, work log: {:x}",
work_claim.nonce_min.log,
log_id
);
Expand Down
21 changes: 13 additions & 8 deletions crates/boundless-cli/src/commands/povw/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,18 @@ pub struct PovwSubmit {
#[arg(short, long, env = "POVW_STATE_PATH")]
pub state: PathBuf,

/// Private key used to sign work log updates. Should have an address equal to the work log ID.
/// Private key used to sign work log updates. Should have an address equal to the rewards address.
///
/// If this option is not set, the value of the private key from global config will be used.
#[clap(long, env = "POVW_PRIVATE_KEY", hide_env_values = true)]
pub povw_private_key: Option<PrivateKeySigner>,

/// The address to assign any PoVW rewards to. If not provided, defaults to the work log ID.
#[clap(
long = "rewards-address-private-key",
alias = "povw-private-key",
env = "REWARDS_ADDRESS_PRIVATE_KEY",
hide_env_values = true
)]
pub rewards_private_key: Option<PrivateKeySigner>,

/// The address to assign any PoVW rewards to. If not provided, defaults to the rewards address.
#[clap(short, long, env = "POVW_VALUE_RECIPIENT")]
pub value_recipient: Option<Address>,

Expand All @@ -64,18 +69,18 @@ impl PovwSubmit {
/// Run the [PovwSubmit] command.
pub async fn run(&self, global_config: &GlobalConfig) -> anyhow::Result<()> {
let tx_signer = global_config.require_private_key()?;
let work_log_signer = self.povw_private_key.as_ref().unwrap_or(&tx_signer);
let work_log_signer = self.rewards_private_key.as_ref().unwrap_or(&tx_signer);
let rpc_url = global_config.require_rpc_url()?;

// Load the state and check to make sure the private key matches.
let mut state = State::load(&self.state)
.await
.with_context(|| format!("Failed to load state from {}", self.state.display()))?;
tracing::info!("Submitting work log update for log ID: {:x}", state.log_id);
tracing::info!("Submitting work log update for rewards address: {:x}", state.log_id);

ensure!(
Address::from(state.log_id) == work_log_signer.address(),
"Signer does not match the state log ID: signer: {}, state: {}",
"Signer does not match the rewards address: signer: {}, state: {}",
work_log_signer.address(),
state.log_id
);
Expand Down
12 changes: 6 additions & 6 deletions crates/boundless-cli/tests/povw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ async fn prove_and_send_update() -> anyhow::Result<()> {
.env("PRIVATE_KEY", format!("{:#x}", tx_signer.to_bytes()))
.env("RISC0_DEV_MODE", "1")
.env("RPC_URL", ctx.anvil.lock().await.endpoint_url().as_str())
.env("POVW_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()))
.env("REWARDS_ADDRESS_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()))
.assert()
.success()
// 4. Confirm that the command logs success
Expand Down Expand Up @@ -271,7 +271,7 @@ async fn claim_reward_multi_epoch() -> anyhow::Result<()> {
.env("PRIVATE_KEY", format!("{:#x}", tx_signer.to_bytes()))
.env("RISC0_DEV_MODE", "1")
.env("RPC_URL", ctx.anvil.lock().await.endpoint_url().as_str())
.env("POVW_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()));
.env("REWARDS_ADDRESS_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()));

let result = cmd.assert().success().stdout(contains("Work log update confirmed"));

Expand All @@ -291,7 +291,7 @@ async fn claim_reward_multi_epoch() -> anyhow::Result<()> {
// Run the claim command to mint the accumulated rewards
println!("Running claim command");
let mut cmd = Command::cargo_bin("boundless")?;
cmd.args(["povw", "claim", "--log-id", &format!("{:#x}", log_id)])
cmd.args(["povw", "claim", "--rewards-address", &format!("{:#x}", log_id)])
.env("NO_COLOR", "1")
.env("RUST_LOG", "boundless_cli=debug,info")
.env("POVW_ACCOUNTING_ADDRESS", format!("{:#x}", ctx.povw_accounting.address()))
Expand Down Expand Up @@ -383,7 +383,7 @@ async fn claim_on_partially_finalized_epochs() -> anyhow::Result<()> {
.env("PRIVATE_KEY", format!("{:#x}", tx_signer.to_bytes()))
.env("RISC0_DEV_MODE", "1")
.env("RPC_URL", ctx.anvil.lock().await.endpoint_url().as_str())
.env("POVW_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()));
.env("REWARDS_ADDRESS_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()));

let result = cmd.assert().success().stdout(contains("Work log update confirmed"));

Expand Down Expand Up @@ -434,7 +434,7 @@ async fn claim_on_partially_finalized_epochs() -> anyhow::Result<()> {
.env("PRIVATE_KEY", format!("{:#x}", tx_signer.to_bytes()))
.env("RISC0_DEV_MODE", "1")
.env("RPC_URL", ctx.anvil.lock().await.endpoint_url().as_str())
.env("POVW_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()));
.env("REWARDS_ADDRESS_PRIVATE_KEY", format!("{:#x}", work_log_signer.to_bytes()));

let result = cmd.assert().success().stdout(contains("Work log update confirmed"));

Expand All @@ -448,7 +448,7 @@ async fn claim_on_partially_finalized_epochs() -> anyhow::Result<()> {
// Will warn about the second epoch.
println!("Running claim command");
let mut cmd = Command::cargo_bin("boundless")?;
cmd.args(["povw", "claim", "--log-id", &format!("{:#x}", log_id)])
cmd.args(["povw", "claim", "--rewards-address", &format!("{:#x}", log_id)])
.env("NO_COLOR", "1")
.env("RUST_LOG", "boundless_cli=debug,info")
.env("POVW_ACCOUNTING_ADDRESS", format!("{:#x}", ctx.povw_accounting.address()))
Expand Down
Loading
Loading