mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
darkfid: support merge mining for a DAO
This commit is contained in:
@@ -85,7 +85,7 @@ pub struct DarkfiNode {
|
|||||||
/// HTTP JSON-RPC connection tracker
|
/// HTTP JSON-RPC connection tracker
|
||||||
mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
|
mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
|
||||||
/// Merge mining block templates
|
/// Merge mining block templates
|
||||||
mm_blocktemplates: Mutex<HashMap<[u8; 32], (BlockInfo, f64, SecretKey)>>,
|
mm_blocktemplates: Mutex<HashMap<Vec<u8>, (BlockInfo, f64, SecretKey)>>,
|
||||||
/// PowRewardV1 ZK data
|
/// PowRewardV1 ZK data
|
||||||
powrewardv1_zk: PowRewardV1Zk,
|
powrewardv1_zk: PowRewardV1Zk,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,11 @@ use darkfi::{
|
|||||||
util::encoding::base64,
|
util::encoding::base64,
|
||||||
validator::consensus::Proposal,
|
validator::consensus::Proposal,
|
||||||
};
|
};
|
||||||
use darkfi_sdk::crypto::PublicKey;
|
use darkfi_sdk::{
|
||||||
use darkfi_serial::serialize_async;
|
crypto::{pasta_prelude::PrimeField, FuncId, PublicKey},
|
||||||
|
pasta::pallas,
|
||||||
|
};
|
||||||
|
use darkfi_serial::{deserialize_async, serialize_async};
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
use tinyjson::JsonValue;
|
use tinyjson::JsonValue;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
@@ -89,13 +92,13 @@ impl DarkfiNode {
|
|||||||
// merge mining.
|
// merge mining.
|
||||||
//
|
//
|
||||||
// **Request:**
|
// **Request:**
|
||||||
// * `address` : A wallet address on the merge mined chain
|
// * `address` : A base-64 encoded wallet address mining configuration on the merge mined chain
|
||||||
// * `aux_hash`: Merge mining job that is currently being polled
|
// * `aux_hash`: Merge mining job that is currently being polled
|
||||||
// * `height` : Monero height
|
// * `height` : Monero height
|
||||||
// * `prev_id` : Hash of the previous Monero block
|
// * `prev_id` : Hash of the previous Monero block
|
||||||
//
|
//
|
||||||
// **Response:**
|
// **Response:**
|
||||||
// * `aux_blob`: The hex-encoded wallet address blob
|
// * `aux_blob`: The hex-encoded wallet address mining configuration blob
|
||||||
// * `aux_diff`: Mining difficulty (decimal number)
|
// * `aux_diff`: Mining difficulty (decimal number)
|
||||||
// * `aux_hash`: A 32-byte hex-encoded hash of merge mined block
|
// * `aux_hash`: A 32-byte hex-encoded hash of merge mined block
|
||||||
//
|
//
|
||||||
@@ -112,7 +115,7 @@ impl DarkfiNode {
|
|||||||
return JsonError::new(InvalidParams, None, id).into()
|
return JsonError::new(InvalidParams, None, id).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse address
|
// Parse address mining configuration
|
||||||
let Some(address) = params.get("address") else {
|
let Some(address) = params.get("address") else {
|
||||||
return JsonError::new(InvalidParams, Some("missing address".to_string()), id).into()
|
return JsonError::new(InvalidParams, Some("missing address".to_string()), id).into()
|
||||||
};
|
};
|
||||||
@@ -120,10 +123,65 @@ impl DarkfiNode {
|
|||||||
return JsonError::new(InvalidParams, Some("invalid address format".to_string()), id)
|
return JsonError::new(InvalidParams, Some("invalid address format".to_string()), id)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
let Ok(address) = PublicKey::from_str(address) else {
|
let Some(address_bytes) = base64::decode(address) else {
|
||||||
return JsonError::new(InvalidParams, Some("invalid address format".to_string()), id)
|
return JsonError::new(InvalidParams, Some("invalid address format".to_string()), id)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
|
let Ok((recipient, spend_hook, user_data)) =
|
||||||
|
deserialize_async::<(PublicKey, Option<String>, Option<String>)>(&address_bytes).await
|
||||||
|
else {
|
||||||
|
return JsonError::new(InvalidParams, Some("invalid address format".to_string()), id)
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
let spend_hook = match spend_hook {
|
||||||
|
Some(s) => match FuncId::from_str(&s) {
|
||||||
|
Ok(s) => Some(s),
|
||||||
|
Err(_) => {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid address format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let user_data: Option<pallas::Base> = match user_data {
|
||||||
|
Some(u) => {
|
||||||
|
let Ok(bytes) = bs58::decode(&u).into_vec() else {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid address format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
let bytes: [u8; 32] = match bytes.try_into() {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid address format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match pallas::Base::from_repr(bytes).into() {
|
||||||
|
Some(v) => Some(v),
|
||||||
|
None => {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid address format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
// Parse aux_hash
|
// Parse aux_hash
|
||||||
let Some(aux_hash) = params.get("aux_hash") else {
|
let Some(aux_hash) = params.get("aux_hash") else {
|
||||||
@@ -184,7 +242,6 @@ impl DarkfiNode {
|
|||||||
return JsonError::new(ErrorCode::InternalError, None, id).into()
|
return JsonError::new(ErrorCode::InternalError, None, id).into()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let address_bytes = address.to_bytes();
|
|
||||||
if let Some((block, difficulty, _)) = mm_blocktemplates.get(&address_bytes) {
|
if let Some((block, difficulty, _)) = mm_blocktemplates.get(&address_bytes) {
|
||||||
let last_proposal = match extended_fork.last_proposal() {
|
let last_proposal = match extended_fork.last_proposal() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
@@ -217,9 +274,17 @@ impl DarkfiNode {
|
|||||||
|
|
||||||
// At this point, we should query the Validator for a new blocktemplate.
|
// At this point, we should query the Validator for a new blocktemplate.
|
||||||
// We first need to construct `MinerRewardsRecipientConfig` from the
|
// We first need to construct `MinerRewardsRecipientConfig` from the
|
||||||
// address provided to us through the RPC.
|
// address configuration provided to us through the RPC.
|
||||||
let recipient_config =
|
let recipient_str = format!("{recipient}");
|
||||||
MinerRewardsRecipientConfig { recipient: address, spend_hook: None, user_data: None };
|
let spend_hook_str = match spend_hook {
|
||||||
|
Some(spend_hook) => format!("{spend_hook}"),
|
||||||
|
None => String::from("-"),
|
||||||
|
};
|
||||||
|
let user_data_str = match user_data {
|
||||||
|
Some(user_data) => bs58::encode(user_data.to_repr()).into_string(),
|
||||||
|
None => String::from("-"),
|
||||||
|
};
|
||||||
|
let recipient_config = MinerRewardsRecipientConfig { recipient, spend_hook, user_data };
|
||||||
|
|
||||||
// Now let's try to construct the blocktemplate.
|
// Now let's try to construct the blocktemplate.
|
||||||
// Find the difficulty. Note we cast it to f64 here.
|
// Find the difficulty. Note we cast it to f64 here.
|
||||||
@@ -260,10 +325,11 @@ impl DarkfiNode {
|
|||||||
// Now we have the blocktemplate. We'll mark it down in memory,
|
// Now we have the blocktemplate. We'll mark it down in memory,
|
||||||
// and then ship it to RPC.
|
// and then ship it to RPC.
|
||||||
let blockhash = blocktemplate.header.template_hash();
|
let blockhash = blocktemplate.header.template_hash();
|
||||||
mm_blocktemplates.insert(address_bytes, (blocktemplate, difficulty, block_signing_secret));
|
mm_blocktemplates
|
||||||
|
.insert(address_bytes.clone(), (blocktemplate, difficulty, block_signing_secret));
|
||||||
info!(
|
info!(
|
||||||
target: "darkfid::rpc_xmr::xmr_merge_mining_get_aux_block",
|
target: "darkfid::rpc_xmr::xmr_merge_mining_get_aux_block",
|
||||||
"[RPC-XMR] Created new blocktemplate: address={address}, aux_hash={blockhash}, height={height}, prev_id={prev_id}"
|
"[RPC-XMR] Created new blocktemplate: address={recipient_str}, spend_hook={spend_hook_str}, user_data={user_data_str}, aux_hash={blockhash}, height={height}, prev_id={prev_id}"
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = JsonValue::from(HashMap::from([
|
let response = JsonValue::from(HashMap::from([
|
||||||
@@ -308,7 +374,7 @@ impl DarkfiNode {
|
|||||||
return JsonError::new(InvalidParams, None, id).into()
|
return JsonError::new(InvalidParams, None, id).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse address from aux_blob
|
// Parse address mining configuration from aux_blob
|
||||||
let Some(aux_blob) = params.get("aux_blob") else {
|
let Some(aux_blob) = params.get("aux_blob") else {
|
||||||
return JsonError::new(InvalidParams, Some("missing aux_blob".to_string()), id).into()
|
return JsonError::new(InvalidParams, Some("missing aux_blob".to_string()), id).into()
|
||||||
};
|
};
|
||||||
@@ -316,15 +382,54 @@ impl DarkfiNode {
|
|||||||
return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
|
return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
let mut address_bytes = [0u8; 32];
|
let Ok(address_bytes) = hex::decode(aux_blob) else {
|
||||||
if hex::decode_to_slice(aux_blob, &mut address_bytes).is_err() {
|
|
||||||
return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
|
return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
if PublicKey::from_bytes(address_bytes).is_err() {
|
let Ok((_, spend_hook, user_data)) =
|
||||||
|
deserialize_async::<(PublicKey, Option<String>, Option<String>)>(&address_bytes).await
|
||||||
|
else {
|
||||||
return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
|
return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
|
if let Some(spend_hook) = spend_hook {
|
||||||
|
if FuncId::from_str(&spend_hook).is_err() {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid aux_blob format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(user_data) = user_data {
|
||||||
|
let Ok(bytes) = bs58::decode(&user_data).into_vec() else {
|
||||||
|
return JsonError::new(InvalidParams, Some("invalid address format".to_string()), id)
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
let bytes: [u8; 32] = match bytes.try_into() {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid aux_blob format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _: pallas::Base = match pallas::Base::from_repr(bytes).into() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
return JsonError::new(
|
||||||
|
InvalidParams,
|
||||||
|
Some("invalid aux_blob format".to_string()),
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Parse aux_hash
|
// Parse aux_hash
|
||||||
let Some(aux_hash) = params.get("aux_hash") else {
|
let Some(aux_hash) = params.get("aux_hash") else {
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ pub async fn clean_mm_blocktemplates(node: &DarkfiNodePtr) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This job doesn't reference something so we drop it
|
// This job doesn't reference something so we drop it
|
||||||
dropped_templates.push(*key);
|
dropped_templates.push(key.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop jobs not referencing active forks or last confirmed block
|
// Drop jobs not referencing active forks or last confirmed block
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
|
|
||||||
let default_address = SubCommand::with_name("default-address")
|
let default_address = SubCommand::with_name("default-address")
|
||||||
.about("Set the default address in the wallet")
|
.about("Set the default address in the wallet")
|
||||||
.arg(index);
|
.arg(index.clone());
|
||||||
|
|
||||||
let secrets =
|
let secrets =
|
||||||
SubCommand::with_name("secrets").about("Print all the secret keys from the wallet");
|
SubCommand::with_name("secrets").about("Print all the secret keys from the wallet");
|
||||||
@@ -164,6 +164,14 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
|
|
||||||
let coins = SubCommand::with_name("coins").about("Print all the coins in the wallet");
|
let coins = SubCommand::with_name("coins").about("Print all the coins in the wallet");
|
||||||
|
|
||||||
|
let spend_hook = Arg::with_name("spend-hook").help("Optional contract spend hook to use");
|
||||||
|
|
||||||
|
let user_data = Arg::with_name("user-data").help("Optional user data to use");
|
||||||
|
|
||||||
|
let mining_config = SubCommand::with_name("mining-config")
|
||||||
|
.about("Print a wallet address mining configuration")
|
||||||
|
.args(&[index, spend_hook.clone(), user_data.clone()]);
|
||||||
|
|
||||||
let wallet = SubCommand::with_name("wallet").about("Wallet operations").subcommands(vec![
|
let wallet = SubCommand::with_name("wallet").about("Wallet operations").subcommands(vec![
|
||||||
initialize,
|
initialize,
|
||||||
keygen,
|
keygen,
|
||||||
@@ -175,6 +183,7 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
import_secrets,
|
import_secrets,
|
||||||
tree,
|
tree,
|
||||||
coins,
|
coins,
|
||||||
|
mining_config,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Spend
|
// Spend
|
||||||
@@ -193,10 +202,6 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
|
|
||||||
let recipient = Arg::with_name("recipient").help("Recipient address");
|
let recipient = Arg::with_name("recipient").help("Recipient address");
|
||||||
|
|
||||||
let spend_hook = Arg::with_name("spend-hook").help("Optional contract spend hook to use");
|
|
||||||
|
|
||||||
let user_data = Arg::with_name("user-data").help("Optional user data to use");
|
|
||||||
|
|
||||||
let half_split = Arg::with_name("half-split")
|
let half_split = Arg::with_name("half-split")
|
||||||
.long("half-split")
|
.long("half-split")
|
||||||
.help("Split the output coin into two equal halves");
|
.help("Split the output coin into two equal halves");
|
||||||
@@ -302,7 +307,7 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
.about("Create a generic proposal for a DAO")
|
.about("Create a generic proposal for a DAO")
|
||||||
.args(&[name.clone(), duration, user_data.clone()]);
|
.args(&[name.clone(), duration, user_data.clone()]);
|
||||||
|
|
||||||
let proposals = SubCommand::with_name("proposals").about("List DAO proposals").args(&[name]);
|
let proposals = SubCommand::with_name("proposals").about("List DAO proposals").arg(&name);
|
||||||
|
|
||||||
let bulla = Arg::with_name("bulla").help("Bulla identifier for the proposal");
|
let bulla = Arg::with_name("bulla").help("Bulla identifier for the proposal");
|
||||||
|
|
||||||
@@ -337,6 +342,9 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
let spend_hook_cmd = SubCommand::with_name("spend-hook")
|
let spend_hook_cmd = SubCommand::with_name("spend-hook")
|
||||||
.about("Print the DAO contract base64-encoded spend hook");
|
.about("Print the DAO contract base64-encoded spend hook");
|
||||||
|
|
||||||
|
let mining_config =
|
||||||
|
SubCommand::with_name("mining-config").about("Print a DAO mining configuration").arg(name);
|
||||||
|
|
||||||
let dao = SubCommand::with_name("dao").about("DAO functionalities").subcommands(vec![
|
let dao = SubCommand::with_name("dao").about("DAO functionalities").subcommands(vec![
|
||||||
create,
|
create,
|
||||||
view,
|
view,
|
||||||
@@ -352,6 +360,7 @@ pub fn generate_completions(shell: &str) -> Result<String> {
|
|||||||
vote,
|
vote,
|
||||||
exec,
|
exec,
|
||||||
spend_hook_cmd,
|
spend_hook_cmd,
|
||||||
|
mining_config,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// AttachFee
|
// AttachFee
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ use rusqlite::types::Value;
|
|||||||
|
|
||||||
use darkfi::{
|
use darkfi::{
|
||||||
tx::{ContractCallLeaf, Transaction, TransactionBuilder},
|
tx::{ContractCallLeaf, Transaction, TransactionBuilder},
|
||||||
util::parse::{decode_base10, encode_base10},
|
util::{
|
||||||
|
encoding::base64,
|
||||||
|
parse::{decode_base10, encode_base10},
|
||||||
|
},
|
||||||
zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
|
zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
|
||||||
zkas::ZkBinary,
|
zkas::ZkBinary,
|
||||||
Error, Result,
|
Error, Result,
|
||||||
@@ -55,6 +58,7 @@ use darkfi_money_contract::{
|
|||||||
use darkfi_sdk::{
|
use darkfi_sdk::{
|
||||||
bridgetree,
|
bridgetree,
|
||||||
crypto::{
|
crypto::{
|
||||||
|
pasta_prelude::PrimeField,
|
||||||
poseidon_hash,
|
poseidon_hash,
|
||||||
smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
|
smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
|
||||||
util::{fp_mod_fv, fp_to_u64},
|
util::{fp_mod_fv, fp_to_u64},
|
||||||
@@ -1907,6 +1911,28 @@ impl Drk {
|
|||||||
Ok(balmap)
|
Ok(balmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch known unspent balances from the wallet for the given DAO name.
|
||||||
|
pub async fn dao_mining_config(&self, name: &str, output: &mut Vec<String>) -> Result<()> {
|
||||||
|
let dao = self.get_dao_by_name(name).await?;
|
||||||
|
let recipient = dao.params.dao.notes_public_key;
|
||||||
|
let spend_hook = format!(
|
||||||
|
"{}",
|
||||||
|
FuncRef { contract_id: *DAO_CONTRACT_ID, func_code: DaoFunction::Exec as u8 }
|
||||||
|
.to_func_id()
|
||||||
|
);
|
||||||
|
let user_data = bs58::encode(dao.bulla().inner().to_repr()).into_string();
|
||||||
|
output.push(String::from("DarkFi TOML configuration:"));
|
||||||
|
output.push(format!("recipient = \"{recipient}\""));
|
||||||
|
output.push(format!("spend_hook = \"{spend_hook}\""));
|
||||||
|
output.push(format!("user_data = \"{user_data}\""));
|
||||||
|
output.push(String::from("\nP2Pool wallet address to use:"));
|
||||||
|
output.push(
|
||||||
|
base64::encode(&serialize(&(recipient, Some(spend_hook), Some(user_data)))).to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch all known DAO proposalss from the wallet.
|
/// Fetch all known DAO proposalss from the wallet.
|
||||||
pub async fn get_proposals(&self) -> Result<Vec<ProposalRecord>> {
|
pub async fn get_proposals(&self) -> Result<Vec<ProposalRecord>> {
|
||||||
let rows = match self.wallet.query_multiple(&DAO_PROPOSALS_TABLE, &[], &[]) {
|
let rows = match self.wallet.query_multiple(&DAO_PROPOSALS_TABLE, &[], &[]) {
|
||||||
|
|||||||
@@ -150,7 +150,8 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
|
|||||||
lc.push(prefix.clone() + "wallet secrets");
|
lc.push(prefix.clone() + "wallet secrets");
|
||||||
lc.push(prefix.clone() + "wallet import-secrets");
|
lc.push(prefix.clone() + "wallet import-secrets");
|
||||||
lc.push(prefix.clone() + "wallet tree");
|
lc.push(prefix.clone() + "wallet tree");
|
||||||
lc.push(prefix + "wallet coins");
|
lc.push(prefix.clone() + "wallet coins");
|
||||||
|
lc.push(prefix + "wallet mining-config");
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +194,8 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
|
|||||||
lc.push(prefix.clone() + "dao proposal-import");
|
lc.push(prefix.clone() + "dao proposal-import");
|
||||||
lc.push(prefix.clone() + "dao vote");
|
lc.push(prefix.clone() + "dao vote");
|
||||||
lc.push(prefix.clone() + "dao exec");
|
lc.push(prefix.clone() + "dao exec");
|
||||||
lc.push(prefix + "dao spend-hook");
|
lc.push(prefix.clone() + "dao spend-hook");
|
||||||
|
lc.push(prefix + "dao mining-config");
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,13 +335,14 @@ fn hints(buffer: &str) -> Option<(String, i32, bool)> {
|
|||||||
let bold = false;
|
let bold = false;
|
||||||
match last {
|
match last {
|
||||||
"completions " => Some(("<shell>".to_string(), color, bold)),
|
"completions " => Some(("<shell>".to_string(), color, bold)),
|
||||||
"wallet " => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), color, bold)),
|
"wallet " => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins|mining-config)".to_string(), color, bold)),
|
||||||
"wallet default-address " => Some(("<index>".to_string(), color, bold)),
|
"wallet default-address " => Some(("<index>".to_string(), color, bold)),
|
||||||
|
"wallet mining-config " => Some(("<index> [spend_hook] [user_data]".to_string(), color, bold)),
|
||||||
"unspend " => Some(("<coin>".to_string(), color, bold)),
|
"unspend " => Some(("<coin>".to_string(), color, bold)),
|
||||||
"transfer " => Some(("[--half-split] <amount> <token> <recipient> [spend_hook] [user_data]".to_string(), color, bold)),
|
"transfer " => Some(("[--half-split] <amount> <token> <recipient> [spend_hook] [user_data]".to_string(), color, bold)),
|
||||||
"otc " => Some(("(init|join|inspect|sign)".to_string(), color, bold)),
|
"otc " => Some(("(init|join|inspect|sign)".to_string(), color, bold)),
|
||||||
"otc init " => Some(("<value_pair> <token_pair>".to_string(), color, bold)),
|
"otc init " => Some(("<value_pair> <token_pair>".to_string(), color, bold)),
|
||||||
"dao " => Some(("(create|view|import|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook)".to_string(), color, bold)),
|
"dao " => Some(("(create|view|import|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook|mining-config)".to_string(), color, bold)),
|
||||||
"dao create " => Some(("<proposer-limit> <quorum> <early-exec-quorum> <approval-ratio> <gov-token-id>".to_string(), color, bold)),
|
"dao create " => Some(("<proposer-limit> <quorum> <early-exec-quorum> <approval-ratio> <gov-token-id>".to_string(), color, bold)),
|
||||||
"dao import " => Some(("<name>".to_string(), color, bold)),
|
"dao import " => Some(("<name>".to_string(), color, bold)),
|
||||||
"dao list " => Some(("[name]".to_string(), color, bold)),
|
"dao list " => Some(("[name]".to_string(), color, bold)),
|
||||||
@@ -351,6 +354,7 @@ fn hints(buffer: &str) -> Option<(String, i32, bool)> {
|
|||||||
"dao proposal " => Some(("[--(export|mint-proposal)] <bulla>".to_string(), color, bold)),
|
"dao proposal " => Some(("[--(export|mint-proposal)] <bulla>".to_string(), color, bold)),
|
||||||
"dao vote " => Some(("<bulla> <vote> [vote-weight]".to_string(), color, bold)),
|
"dao vote " => Some(("<bulla> <vote> [vote-weight]".to_string(), color, bold)),
|
||||||
"dao exec " => Some(("[--early] <bulla>".to_string(), color, bold)),
|
"dao exec " => Some(("[--early] <bulla>".to_string(), color, bold)),
|
||||||
|
"dao mining-config " => Some(("<name>".to_string(), color, bold)),
|
||||||
"scan " => Some(("[--reset]".to_string(), color, bold)),
|
"scan " => Some(("[--reset]".to_string(), color, bold)),
|
||||||
"scan --reset " => Some(("<height>".to_string(), color, bold)),
|
"scan --reset " => Some(("<height>".to_string(), color, bold)),
|
||||||
"explorer " => Some(("(fetch-tx|simulate-tx|txs-history|clear-reverted|scanned-blocks)".to_string(), color, bold)),
|
"explorer " => Some(("(fetch-tx|simulate-tx|txs-history|clear-reverted|scanned-blocks)".to_string(), color, bold)),
|
||||||
@@ -774,7 +778,7 @@ async fn handle_wallet(drk: &DrkPtr, parts: &[&str], input: &[String], output: &
|
|||||||
// Check correct command structure
|
// Check correct command structure
|
||||||
if parts.len() < 2 {
|
if parts.len() < 2 {
|
||||||
output.push(String::from("Malformed `wallet` command"));
|
output.push(String::from("Malformed `wallet` command"));
|
||||||
output.push(String::from("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)"));
|
output.push(String::from("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins|mining-config)"));
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,9 +794,10 @@ async fn handle_wallet(drk: &DrkPtr, parts: &[&str], input: &[String], output: &
|
|||||||
"import-secrets" => handle_wallet_import_secrets(drk, input, output).await,
|
"import-secrets" => handle_wallet_import_secrets(drk, input, output).await,
|
||||||
"tree" => handle_wallet_tree(drk, output).await,
|
"tree" => handle_wallet_tree(drk, output).await,
|
||||||
"coins" => handle_wallet_coins(drk, output).await,
|
"coins" => handle_wallet_coins(drk, output).await,
|
||||||
|
"mining-config" => handle_wallet_mining_config(drk, parts, output).await,
|
||||||
_ => {
|
_ => {
|
||||||
output.push(format!("Unrecognized wallet subcommand: {}", parts[1]));
|
output.push(format!("Unrecognized wallet subcommand: {}", parts[1]));
|
||||||
output.push(String::from("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)"));
|
output.push(String::from("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins|mining-config)"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1064,6 +1069,76 @@ async fn handle_wallet_coins(drk: &DrkPtr, output: &mut Vec<String>) {
|
|||||||
output.push(format!("{table}"));
|
output.push(format!("{table}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Auxiliary function to define the wallet mining config subcommand handling.
|
||||||
|
async fn handle_wallet_mining_config(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
|
||||||
|
// Check correct command structure
|
||||||
|
if parts.len() < 3 || parts.len() > 5 {
|
||||||
|
output.push(String::from("Malformed `wallet mining-address` subcommand"));
|
||||||
|
output.push(String::from("Usage: wallet mining-config <index> [spend_hook] [user_data]"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse command
|
||||||
|
let mut index = 2;
|
||||||
|
let wallet_index = match usize::from_str(parts[index]) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(e) => {
|
||||||
|
output.push(format!("Invalid address id: {e}"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
index += 1;
|
||||||
|
|
||||||
|
let spend_hook = if index < parts.len() {
|
||||||
|
match FuncId::from_str(parts[index]) {
|
||||||
|
Ok(s) => Some(s),
|
||||||
|
Err(e) => {
|
||||||
|
output.push(format!("Invalid spend hook: {e}"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
index += 1;
|
||||||
|
|
||||||
|
let user_data = if index < parts.len() {
|
||||||
|
let bytes = match bs58::decode(&parts[index]).into_vec() {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
output.push(format!("Invalid user data: {e}"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes: [u8; 32] = match bytes.try_into() {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
output.push(format!("Invalid user data: {e:?}"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let elem: pallas::Base = match pallas::Base::from_repr(bytes).into() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
output.push(String::from("Invalid user data"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(elem)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) =
|
||||||
|
drk.read().await.mining_config(wallet_index, spend_hook, user_data, output).await
|
||||||
|
{
|
||||||
|
output.push(format!("Failed to generate wallet mining configuration: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Auxiliary function to define the spend command handling.
|
/// Auxiliary function to define the spend command handling.
|
||||||
async fn handle_spend(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
|
async fn handle_spend(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
|
||||||
let tx = match parse_tx_from_input(input).await {
|
let tx = match parse_tx_from_input(input).await {
|
||||||
@@ -1379,7 +1454,7 @@ async fn handle_dao(drk: &DrkPtr, parts: &[&str], input: &[String], output: &mut
|
|||||||
// Check correct command structure
|
// Check correct command structure
|
||||||
if parts.len() < 2 {
|
if parts.len() < 2 {
|
||||||
output.push(String::from("Malformed `dao` command"));
|
output.push(String::from("Malformed `dao` command"));
|
||||||
output.push(String::from("Usage: dao (create|view|import|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook)"));
|
output.push(String::from("Usage: dao (create|view|import|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook|mining-config)"));
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1399,9 +1474,10 @@ async fn handle_dao(drk: &DrkPtr, parts: &[&str], input: &[String], output: &mut
|
|||||||
"vote" => handle_dao_vote(drk, parts, output).await,
|
"vote" => handle_dao_vote(drk, parts, output).await,
|
||||||
"exec" => handle_dao_exec(drk, parts, output).await,
|
"exec" => handle_dao_exec(drk, parts, output).await,
|
||||||
"spend-hook" => handle_dao_spend_hook(parts, output).await,
|
"spend-hook" => handle_dao_spend_hook(parts, output).await,
|
||||||
|
"mining-config" => handle_dao_mining_config(drk, parts, output).await,
|
||||||
_ => {
|
_ => {
|
||||||
output.push(format!("Unrecognized DAO subcommand: {}", parts[1]));
|
output.push(format!("Unrecognized DAO subcommand: {}", parts[1]));
|
||||||
output.push(String::from("Usage: dao (create|view|import|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook)"));
|
output.push(String::from("Usage: dao (create|view|import|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook|mining-config)"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2281,6 +2357,20 @@ async fn handle_dao_spend_hook(parts: &[&str], output: &mut Vec<String>) {
|
|||||||
output.push(format!("{spend_hook}"));
|
output.push(format!("{spend_hook}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Auxiliary function to define the dao mining config subcommand handling.
|
||||||
|
async fn handle_dao_mining_config(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
|
||||||
|
// Check correct subcommand structure
|
||||||
|
if parts.len() != 3 {
|
||||||
|
output.push(String::from("Malformed `dao mining-config` subcommand"));
|
||||||
|
output.push(String::from("Usage: dao mining-config <name>"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = drk.read().await.dao_mining_config(parts[2], output).await {
|
||||||
|
output.push(format!("Failed to generate DAO mining configuration: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Auxiliary function to define the attach fee command handling.
|
/// Auxiliary function to define the attach fee command handling.
|
||||||
async fn handle_attach_fee(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
|
async fn handle_attach_fee(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
|
||||||
let mut tx = match parse_tx_from_input(input).await {
|
let mut tx = match parse_tx_from_input(input).await {
|
||||||
|
|||||||
@@ -251,6 +251,18 @@ enum WalletSubcmd {
|
|||||||
|
|
||||||
/// Print all the coins in the wallet
|
/// Print all the coins in the wallet
|
||||||
Coins,
|
Coins,
|
||||||
|
|
||||||
|
/// Print a wallet address mining configuration
|
||||||
|
MiningConfig {
|
||||||
|
/// Identifier of the address
|
||||||
|
index: usize,
|
||||||
|
|
||||||
|
/// Optional contract spend hook to use
|
||||||
|
spend_hook: Option<String>,
|
||||||
|
|
||||||
|
/// Optional user data to use
|
||||||
|
user_data: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, StructOpt)]
|
#[derive(Clone, Debug, Deserialize, StructOpt)]
|
||||||
@@ -404,6 +416,12 @@ enum DaoSubcmd {
|
|||||||
|
|
||||||
/// Print the DAO contract base64-encoded spend hook
|
/// Print the DAO contract base64-encoded spend hook
|
||||||
SpendHook,
|
SpendHook,
|
||||||
|
|
||||||
|
/// Print a DAO mining configuration
|
||||||
|
MiningConfig {
|
||||||
|
/// Name identifier for the DAO
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, StructOpt)]
|
#[derive(Clone, Debug, Deserialize, StructOpt)]
|
||||||
@@ -945,6 +963,50 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
|
|||||||
|
|
||||||
println!("{table}");
|
println!("{table}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletSubcmd::MiningConfig { index, spend_hook, user_data } => {
|
||||||
|
let spend_hook = match spend_hook {
|
||||||
|
Some(s) => match FuncId::from_str(&s) {
|
||||||
|
Ok(s) => Some(s),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Invalid spend hook: {e}");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_data = match user_data {
|
||||||
|
Some(u) => {
|
||||||
|
let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Invalid user data: {e:?}");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match pallas::Base::from_repr(bytes).into() {
|
||||||
|
Some(v) => Some(v),
|
||||||
|
None => {
|
||||||
|
eprintln!("Invalid user data");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut output = vec![];
|
||||||
|
if let Err(e) =
|
||||||
|
drk.mining_config(index, spend_hook, user_data, &mut output).await
|
||||||
|
{
|
||||||
|
print_output(&output);
|
||||||
|
eprintln!("Failed to generate wallet mining configuration: {e}");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
print_output(&output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1934,6 +1996,27 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DaoSubcmd::MiningConfig { name } => {
|
||||||
|
let drk = new_wallet(
|
||||||
|
blockchain_config.cache_path,
|
||||||
|
blockchain_config.wallet_path,
|
||||||
|
blockchain_config.wallet_pass,
|
||||||
|
None,
|
||||||
|
&ex,
|
||||||
|
args.fun,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let mut output = vec![];
|
||||||
|
if let Err(e) = drk.dao_mining_config(&name, &mut output).await {
|
||||||
|
print_output(&output);
|
||||||
|
eprintln!("Failed to generate DAO mining configuration: {e}");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
print_output(&output);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Subcmd::AttachFee => {
|
Subcmd::AttachFee => {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use rusqlite::types::Value;
|
|||||||
|
|
||||||
use darkfi::{
|
use darkfi::{
|
||||||
tx::Transaction,
|
tx::Transaction,
|
||||||
|
util::encoding::base64,
|
||||||
validator::fees::compute_fee,
|
validator::fees::compute_fee,
|
||||||
zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses, Proof},
|
zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses, Proof},
|
||||||
zkas::ZkBinary,
|
zkas::ZkBinary,
|
||||||
@@ -48,8 +49,8 @@ use darkfi_money_contract::{
|
|||||||
use darkfi_sdk::{
|
use darkfi_sdk::{
|
||||||
bridgetree::Position,
|
bridgetree::Position,
|
||||||
crypto::{
|
crypto::{
|
||||||
note::AeadEncryptedNote, BaseBlind, FuncId, Keypair, MerkleNode, MerkleTree, PublicKey,
|
note::AeadEncryptedNote, pasta_prelude::PrimeField, BaseBlind, FuncId, Keypair, MerkleNode,
|
||||||
ScalarBlind, SecretKey, MONEY_CONTRACT_ID,
|
MerkleTree, PublicKey, ScalarBlind, SecretKey, MONEY_CONTRACT_ID,
|
||||||
},
|
},
|
||||||
dark_tree::DarkLeaf,
|
dark_tree::DarkLeaf,
|
||||||
pasta::pallas,
|
pasta::pallas,
|
||||||
@@ -266,6 +267,54 @@ impl Drk {
|
|||||||
Ok(vec)
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch provided index address from the wallet and generate its
|
||||||
|
/// mining configuration.
|
||||||
|
pub async fn mining_config(
|
||||||
|
&self,
|
||||||
|
idx: usize,
|
||||||
|
spend_hook: Option<FuncId>,
|
||||||
|
user_data: Option<pallas::Base>,
|
||||||
|
output: &mut Vec<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let row = match self.wallet.query_single(
|
||||||
|
&MONEY_KEYS_TABLE,
|
||||||
|
&[MONEY_KEYS_COL_PUBLIC],
|
||||||
|
convert_named_params! {(MONEY_KEYS_COL_KEY_ID, idx)},
|
||||||
|
) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(Error::DatabaseError(format!(
|
||||||
|
"[mining_address] Address retrieval failed: {e}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Value::Blob(ref key_bytes) = row[0] else {
|
||||||
|
return Err(Error::ParseFailed("[mining_address] Key bytes parsing failed"))
|
||||||
|
};
|
||||||
|
let public_key: PublicKey = deserialize_async(key_bytes).await?;
|
||||||
|
|
||||||
|
let spend_hook = spend_hook.as_ref().map(|spend_hook| spend_hook.to_string());
|
||||||
|
|
||||||
|
let user_data =
|
||||||
|
user_data.as_ref().map(|user_data| bs58::encode(user_data.to_repr()).into_string());
|
||||||
|
|
||||||
|
output.push(String::from("DarkFi TOML configuration:"));
|
||||||
|
output.push(format!("recipient = \"{public_key}\""));
|
||||||
|
match spend_hook {
|
||||||
|
Some(ref spend_hook) => output.push(format!("spend_hook = \"{spend_hook}\"")),
|
||||||
|
None => output.push(String::from("#spend_hook = \"\"")),
|
||||||
|
}
|
||||||
|
match user_data {
|
||||||
|
Some(ref user_data) => output.push(format!("user_data = \"{user_data}\"")),
|
||||||
|
None => output.push(String::from("#user_data = \"\"")),
|
||||||
|
}
|
||||||
|
output.push(String::from("\nP2Pool wallet address to use:"));
|
||||||
|
output.push(base64::encode(&serialize(&(public_key, spend_hook, user_data))).to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch all secret keys from the wallet.
|
/// Fetch all secret keys from the wallet.
|
||||||
pub async fn get_money_secrets(&self) -> Result<Vec<SecretKey>> {
|
pub async fn get_money_secrets(&self) -> Result<Vec<SecretKey>> {
|
||||||
let rows =
|
let rows =
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ impl Drk {
|
|||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(Error::DatabaseError(format!(
|
return Err(Error::DatabaseError(format!(
|
||||||
"[get_token_mint_authority] Token mint autority retrieval failed: {e}"
|
"[get_token_mint_authority] Token mint authority retrieval failed: {e}"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -714,19 +714,34 @@ drk> dao balance DawnDAO
|
|||||||
## Mining for a DAO
|
## Mining for a DAO
|
||||||
|
|
||||||
A DAO can deploy its own mining nodes and/or other miners can choose to
|
A DAO can deploy its own mining nodes and/or other miners can choose to
|
||||||
directly give their rewards towards one. To configure a `darkfid`
|
directly give their rewards towards one. To retrieve a DAO mining
|
||||||
instance to mine for a DAO, set the corresponding fields(uncomment if
|
configuration, execute:
|
||||||
needed) as per example:
|
|
||||||
|
```shell
|
||||||
|
drk> dao mining-config {YOUR_DAO}
|
||||||
|
|
||||||
|
DarkFi TOML configuration:
|
||||||
|
recipient = "{YOUR_DAO_NOTES_PUBLIC_KEY}"
|
||||||
|
spend_hook = "{DAO_CONTRACT_SPEND_HOOK}"
|
||||||
|
user_data = "{YOUR_DAO_BULLA}"
|
||||||
|
|
||||||
|
P2Pool wallet address to use:
|
||||||
|
{YOUR_DAO_P2POOL_WALLET_ADDRESS_CONFIGURATION}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then configure a `darkfid` instance to mine for a DAO, by setting the
|
||||||
|
corresponding fields(uncomment if needed) as per retrieved
|
||||||
|
configuration:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Put your DAO notes public key here
|
# Put your DAO notes public key here
|
||||||
recipient = "YOUR_DAO_NOTES_PUBLIC_KEY_HERE"
|
recipient = "{YOUR_DAO_NOTES_PUBLIC_KEY}"
|
||||||
|
|
||||||
# Put the DAO contract spend hook from `drk dao spend-hook` here
|
# Put the DAO contract spend hook here
|
||||||
spend_hook = "6iW9nywZYvyhcM7P1iLwYkh92rvYtREDsC8hgqf2GLuT"
|
spend_hook = "{DAO_CONTRACT_SPEND_HOOK}"
|
||||||
|
|
||||||
# Put your DAO bulla here
|
# Put your DAO bulla here
|
||||||
user_data = "YOUR_DAO_BULLA_HERE"
|
user_data = "{YOUR_DAO_BULLA}"
|
||||||
```
|
```
|
||||||
|
|
||||||
After your miners have successfully mined confirmed blocks, you will
|
After your miners have successfully mined confirmed blocks, you will
|
||||||
|
|||||||
@@ -195,7 +195,19 @@ Now that everything is in order, we can use `p2pool` with merge-mining
|
|||||||
enabled in order to merge mine DarkFi. For receiving mining rewards
|
enabled in order to merge mine DarkFi. For receiving mining rewards
|
||||||
on DarkFi, we'll need a DarkFi wallet address so make sure you have
|
on DarkFi, we'll need a DarkFi wallet address so make sure you have
|
||||||
[initialized](node.md#wallet-initialization) your wallet and grab your
|
[initialized](node.md#wallet-initialization) your wallet and grab your
|
||||||
address.
|
first address configuration:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
drk> wallet mining-configuration 1
|
||||||
|
|
||||||
|
DarkFi TOML configuration:
|
||||||
|
recipient = "{YOUR_DARKFI_WALLET_ADDRESS}"
|
||||||
|
#spend_hook = ""
|
||||||
|
#user_data = ""
|
||||||
|
|
||||||
|
P2Pool wallet address to use:
|
||||||
|
{YOUR_P2POOL_WALLET_ADDRESS_CONFIGURATION}
|
||||||
|
```
|
||||||
|
|
||||||
We will also need `darkfid` running. Make sure you enable the RPC
|
We will also need `darkfid` running. Make sure you enable the RPC
|
||||||
endpoint that will be used by p2pool in darkfid's config:
|
endpoint that will be used by p2pool in darkfid's config:
|
||||||
@@ -211,7 +223,7 @@ Stop `p2pool` if it's running, and re-run it with the merge-mining
|
|||||||
parameters appended:
|
parameters appended:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ./p2pool --host 127.0.0.1 --rpc-port 28081 --zmq-port 28083 --wallet {YOUR_MONERO_WALLET_ADDRESS_HERE} --stratum 127.0.0.1:3333 --data-dir ./p2pool-data --no-igd --merge-mine 127.0.0.1:8341 {YOUR_DARKFI_WALLET_ADDRESS_HERE}
|
$ ./p2pool --host 127.0.0.1 --rpc-port 28081 --zmq-port 28083 --wallet {YOUR_MONERO_WALLET_ADDRESS_HERE} --stratum 127.0.0.1:3333 --data-dir ./p2pool-data --no-igd --merge-mine 127.0.0.1:8341 {YOUR_P2POOL_WALLET_ADDRESS_CONFIGURATION_HERE}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now `p2pool` should communicate with both `monerod` and `darkfid` in
|
Now `p2pool` should communicate with both `monerod` and `darkfid` in
|
||||||
@@ -224,6 +236,40 @@ provided to `p2pool` merge-mine parameters.
|
|||||||
|
|
||||||
Happy mining!
|
Happy mining!
|
||||||
|
|
||||||
|
## Merge mining for a DAO
|
||||||
|
|
||||||
|
To retrieve a DAO merge mining configuration, execute:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
drk> dao mining-config {YOUR_DAO}
|
||||||
|
|
||||||
|
DarkFi TOML configuration:
|
||||||
|
recipient = "{YOUR_DAO_NOTES_PUBLIC_KEY}"
|
||||||
|
spend_hook = "{DAO_CONTRACT_SPEND_HOOK}"
|
||||||
|
user_data = "{YOUR_DAO_BULLA}"
|
||||||
|
|
||||||
|
P2Pool wallet address to use:
|
||||||
|
{YOUR_DAO_P2POOL_WALLET_ADDRESS_CONFIGURATION}
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop `p2pool` if it's running, and re-run it with the merge-mining
|
||||||
|
parameters appended:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./p2pool --host 127.0.0.1 --rpc-port 28081 --zmq-port 28083 --wallet {YOUR_DAO_MONERO_WALLET_ADDRESS_HERE} --stratum 127.0.0.1:3333 --data-dir ./p2pool-data --no-igd --merge-mine 127.0.0.1:8341 {YOUR_DAO_P2POOL_WALLET_ADDRESS_CONFIGURATION}
|
||||||
|
```
|
||||||
|
|
||||||
|
After your miners have successfully mined confirmed blocks, you will
|
||||||
|
see the DAO `DRK` balance increasing:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
drk> dao balance {YOUR_DAO}
|
||||||
|
|
||||||
|
Token ID | Aliases | Balance
|
||||||
|
----------------------------------------------+---------+---------
|
||||||
|
241vANigf1Cy3ytjM1KHXiVECxgxdK4yApddL8KcLssb | DRK | 80
|
||||||
|
```
|
||||||
|
|
||||||
[1]: https://github.com/monero-project/monero?tab=readme-ov-file#dependencies
|
[1]: https://github.com/monero-project/monero?tab=readme-ov-file#dependencies
|
||||||
[2]: https://github.com/SChernykh/p2pool?tab=readme-ov-file#prerequisites
|
[2]: https://github.com/SChernykh/p2pool?tab=readme-ov-file#prerequisites
|
||||||
[3]: https://xmrig.com/docs/miner/build
|
[3]: https://xmrig.com/docs/miner/build
|
||||||
|
|||||||
Reference in New Issue
Block a user