mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 15:17:57 -05:00
contract/money: Client spend-hook support.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1171,6 +1171,7 @@ name = "darkfi-dao-contract"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"bs58",
|
||||
"chacha20poly1305",
|
||||
"darkfi",
|
||||
"darkfi-money-contract",
|
||||
|
||||
@@ -161,6 +161,12 @@ enum Subcmd {
|
||||
|
||||
/// Recipient address
|
||||
recipient: String,
|
||||
|
||||
/// Mark if this is being sent to a DAO
|
||||
dao: bool,
|
||||
|
||||
/// DAO bulla, if the tokens are being sent to a DAO
|
||||
dao_bulla: Option<String>,
|
||||
},
|
||||
|
||||
/// OTC atomic swap
|
||||
@@ -534,7 +540,7 @@ async fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Subcmd::Transfer { amount, token, recipient } => {
|
||||
Subcmd::Transfer { amount, token, recipient, dao, dao_bulla } => {
|
||||
let _ = f64::from_str(&amount).with_context(|| "Invalid amount")?;
|
||||
let token_id = TokenId::try_from(token.as_str()).with_context(|| "Invalid Token ID")?;
|
||||
let rcpt = PublicKey::from_str(&recipient).with_context(|| "Invalid recipient")?;
|
||||
@@ -546,7 +552,7 @@ async fn main() -> Result<()> {
|
||||
let drk = Drk { rpc_client };
|
||||
|
||||
let tx = drk
|
||||
.transfer(&amount, token_id, rcpt)
|
||||
.transfer(&amount, token_id, rcpt, dao, dao_bulla)
|
||||
.await
|
||||
.with_context(|| "Failed to create payment transaction")?;
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ impl Drk {
|
||||
let mut owncoins = self.wallet_coins(false).await?;
|
||||
// Then we see if we have one that we can send.
|
||||
owncoins.retain(|x| (x.0.note.value == value_send && x.0.note.token_id == token_send));
|
||||
owncoins.retain(|x| (x.0.note.spend_hook == pallas::Base::zero()));
|
||||
if owncoins.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"Did not find any unspent coins of value {} and token_id {}",
|
||||
|
||||
@@ -20,15 +20,20 @@ use anyhow::{anyhow, Result};
|
||||
use darkfi::{
|
||||
tx::Transaction,
|
||||
util::parse::{decode_base10, encode_base10},
|
||||
zk::{proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
|
||||
zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
|
||||
zkas::ZkBinary,
|
||||
};
|
||||
use darkfi_dao_contract::dao_model::DaoBulla;
|
||||
use darkfi_money_contract::{
|
||||
client::{build_transfer_tx, OwnCoin},
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, Keypair, PublicKey, TokenId},
|
||||
crypto::{
|
||||
contract_id::{DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
|
||||
Keypair, PublicKey, TokenId,
|
||||
},
|
||||
pasta::pallas,
|
||||
tx::ContractCall,
|
||||
};
|
||||
use darkfi_serial::Encodable;
|
||||
@@ -43,13 +48,33 @@ impl Drk {
|
||||
amount: &str,
|
||||
token_id: TokenId,
|
||||
recipient: PublicKey,
|
||||
dao: bool,
|
||||
dao_bulla: Option<String>,
|
||||
) -> Result<Transaction> {
|
||||
let dao_bulla: Option<DaoBulla> = if dao {
|
||||
let Some(dao_bulla) = dao_bulla else {
|
||||
return Err(anyhow!("Missing DAO bulla in parameters"))
|
||||
};
|
||||
|
||||
Some(DaoBulla::try_from(dao_bulla.as_str())?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (spend_hook, user_data, user_data_blind) = if dao {
|
||||
(DAO_CONTRACT_ID.inner(), dao_bulla.unwrap().inner(), pallas::Base::random(&mut OsRng))
|
||||
} else {
|
||||
(pallas::Base::zero(), pallas::Base::zero(), pallas::Base::random(&mut OsRng))
|
||||
};
|
||||
|
||||
// First get all unspent OwnCoins to see what our balance is.
|
||||
eprintln!("Fetching OwnCoins");
|
||||
let owncoins = self.wallet_coins(false).await?;
|
||||
let mut owncoins: Vec<OwnCoin> = owncoins.iter().map(|x| x.0.clone()).collect();
|
||||
// We're only interested in the ones for the token_id we're sending
|
||||
// And the ones not owned by some protocol (meaning spend-hook should be 0)
|
||||
owncoins.retain(|x| x.note.token_id == token_id);
|
||||
owncoins.retain(|x| x.note.spend_hook == pallas::Base::zero());
|
||||
if owncoins.is_empty() {
|
||||
return Err(anyhow!("Did not find any coins with token ID: {}", token_id))
|
||||
}
|
||||
@@ -109,6 +134,9 @@ impl Drk {
|
||||
&recipient,
|
||||
amount,
|
||||
token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&owncoins,
|
||||
&tree,
|
||||
&mint_zkbin,
|
||||
|
||||
@@ -350,6 +350,7 @@ impl Drk {
|
||||
let mut balmap: HashMap<String, u64> = HashMap::new();
|
||||
|
||||
// Let's scan through the rows and see if we got anything.
|
||||
// TODO: Separate tokens with spend-hook != 0
|
||||
for row in rows {
|
||||
let Some(row) = row.as_array() else {
|
||||
return Err(anyhow!("Unexpected response from darkfid: {}", rep))
|
||||
|
||||
@@ -23,7 +23,7 @@ use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use darkfi::{
|
||||
tx::Transaction,
|
||||
zk::{proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
|
||||
zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
|
||||
zkas::ZkBinary,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
@@ -40,7 +40,7 @@ use darkfi_sdk::{
|
||||
},
|
||||
db::SMART_CONTRACT_ZKAS_DB_NAME,
|
||||
incrementalmerkletree::bridgetree::BridgeTree,
|
||||
pasta::group::ff::PrimeField,
|
||||
pasta::{group::ff::PrimeField, pallas},
|
||||
tx::ContractCall,
|
||||
};
|
||||
use darkfi_serial::{deserialize, serialize, Encodable};
|
||||
@@ -436,6 +436,9 @@ impl Faucetd {
|
||||
&pubkey,
|
||||
amount,
|
||||
token_id,
|
||||
pallas::Base::zero(),
|
||||
pallas::Base::zero(),
|
||||
pallas::Base::random(&mut OsRng),
|
||||
&[], // <-- The faucet doesn't really have to pass OwnCoins I think
|
||||
&self.merkle_tree,
|
||||
&mint_zkbin,
|
||||
|
||||
@@ -12,6 +12,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
darkfi-sdk = { path = "../../sdk" }
|
||||
darkfi-serial = { path = "../../serial", features = ["derive", "crypto"] }
|
||||
darkfi-money-contract = { path = "../money", features = ["no-entrypoint"] }
|
||||
bs58 = "0.4.0"
|
||||
|
||||
# The following dependencies are used for the client API and
|
||||
# probably shouldn't be in WASM
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use darkfi_sdk::crypto::{pallas, pasta_prelude::*, MerkleNode, Nullifier, PublicKey};
|
||||
use darkfi_sdk::{
|
||||
crypto::{pallas, pasta_prelude::*, MerkleNode, Nullifier, PublicKey},
|
||||
error::ContractError,
|
||||
};
|
||||
use darkfi_serial::{SerialDecodable, SerialEncodable};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
|
||||
@@ -34,6 +37,35 @@ impl From<pallas::Base> for DaoBulla {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for DaoBulla {
|
||||
type Error = ContractError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
let bytes: [u8; 32] = match bs58::decode(s).into_vec() {
|
||||
Ok(v) => {
|
||||
if v.len() != 32 {
|
||||
return Err(ContractError::IoError(
|
||||
"Decoded bs58 string for DaoBulla is not 32 bytes long".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
v.try_into().unwrap()
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ContractError::IoError(format!(
|
||||
"Failed to decode bs58 for DaoBulla: {}",
|
||||
e
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
match pallas::Base::from_repr(bytes).into() {
|
||||
Some(v) => Ok(Self(v)),
|
||||
None => Err(ContractError::IoError("Bytes for DaoBulla are noncanonical".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SerialEncodable, SerialDecodable)]
|
||||
pub struct DaoMintParams {
|
||||
pub dao_bulla: DaoBulla,
|
||||
|
||||
@@ -827,6 +827,9 @@ pub fn build_half_swap_tx(
|
||||
/// * `pubkey` - Public key of the recipient
|
||||
/// * `value` - Value of the transfer
|
||||
/// * `token_id` - Token ID to transfer
|
||||
/// * `spend_hook` - Spend hook
|
||||
/// * `user_data` - User data
|
||||
/// * `user_data_blind` - Blinding for user data
|
||||
/// * `coins` - Set of coins we're able to spend
|
||||
/// * `tree` - Current Merkle tree of coins
|
||||
/// * `mint_zkbin` - ZkBinary of the mint circuit
|
||||
@@ -841,6 +844,9 @@ pub fn build_transfer_tx(
|
||||
pubkey: &PublicKey,
|
||||
value: u64,
|
||||
token_id: TokenId,
|
||||
spend_hook: pallas::Base,
|
||||
user_data: pallas::Base,
|
||||
user_data_blind: pallas::Base,
|
||||
coins: &[OwnCoin],
|
||||
tree: &MerkleTree,
|
||||
mint_zkbin: &ZkBinary,
|
||||
@@ -948,11 +954,6 @@ pub fn build_transfer_tx(
|
||||
let signature_secret = SecretKey::random(&mut OsRng);
|
||||
signature_secrets.push(signature_secret);
|
||||
|
||||
// Disable composability for this old obsolete API
|
||||
let spend_hook = pallas::Base::zero();
|
||||
let user_data = pallas::Base::zero();
|
||||
let user_data_blind = pallas::Base::random(&mut OsRng);
|
||||
|
||||
info!(target: "money", "Creating transfer burn proof for input {}", i);
|
||||
let (proof, revealed) = create_transfer_burn_proof(
|
||||
burn_zkbin,
|
||||
@@ -1000,10 +1001,6 @@ pub fn build_transfer_tx(
|
||||
let serial = pallas::Base::random(&mut OsRng);
|
||||
let coin_blind = pallas::Base::random(&mut OsRng);
|
||||
|
||||
// Disable composability for this old obsolete API
|
||||
let spend_hook = pallas::Base::zero();
|
||||
let user_data = pallas::Base::zero();
|
||||
|
||||
info!(target: "money", "Creating transfer mint proof for output {}", i);
|
||||
let (proof, revealed) = create_transfer_mint_proof(
|
||||
mint_zkbin,
|
||||
@@ -1026,8 +1023,8 @@ pub fn build_transfer_tx(
|
||||
serial,
|
||||
value: output.value,
|
||||
token_id: output.token_id,
|
||||
spend_hook: pallas::Base::zero(),
|
||||
user_data: pallas::Base::zero(),
|
||||
spend_hook,
|
||||
user_data,
|
||||
coin_blind,
|
||||
value_blind,
|
||||
token_blind,
|
||||
|
||||
@@ -68,6 +68,11 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
let alice_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
|
||||
let bob_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
|
||||
|
||||
// We're just going to be using a zero spend-hook and user-data
|
||||
let spend_hook = pallas::Base::zero();
|
||||
let user_data = pallas::Base::zero();
|
||||
let user_data_blind = pallas::Base::random(&mut OsRng);
|
||||
|
||||
let mut alice_owncoins = vec![];
|
||||
let mut bob_owncoins = vec![];
|
||||
|
||||
@@ -79,6 +84,9 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
&th.alice_kp.public,
|
||||
ALICE_INITIAL,
|
||||
alice_token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&[],
|
||||
&th.faucet_merkle_tree,
|
||||
&th.mint_zkbin,
|
||||
@@ -96,6 +104,9 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
&th.bob_kp.public,
|
||||
BOB_INITIAL,
|
||||
bob_token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&[],
|
||||
&th.faucet_merkle_tree,
|
||||
&th.mint_zkbin,
|
||||
@@ -207,6 +218,9 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
&th.bob_kp.public,
|
||||
ALICE_FIRST_SEND,
|
||||
alice_token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&alice_owncoins,
|
||||
&th.alice_merkle_tree,
|
||||
&th.mint_zkbin,
|
||||
@@ -302,6 +316,9 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
&th.alice_kp.public,
|
||||
BOB_FIRST_SEND,
|
||||
bob_token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&bob_owncoins_tmp,
|
||||
&th.bob_merkle_tree,
|
||||
&th.mint_zkbin,
|
||||
@@ -558,6 +575,9 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
&th.alice_kp.public,
|
||||
ALICE_INITIAL,
|
||||
alice_token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&alice_owncoins,
|
||||
&th.alice_merkle_tree,
|
||||
&th.mint_zkbin,
|
||||
@@ -635,6 +655,9 @@ async fn money_contract_transfer() -> Result<()> {
|
||||
&th.bob_kp.public,
|
||||
BOB_INITIAL,
|
||||
bob_token_id,
|
||||
spend_hook,
|
||||
user_data,
|
||||
user_data_blind,
|
||||
&bob_owncoins,
|
||||
&th.bob_merkle_tree,
|
||||
&th.mint_zkbin,
|
||||
|
||||
@@ -33,6 +33,7 @@ use darkfi_sdk::{
|
||||
pasta_prelude::*, ContractId, Keypair, MerkleTree, PublicKey, TokenId, MONEY_CONTRACT_ID,
|
||||
},
|
||||
db::SMART_CONTRACT_ZKAS_DB_NAME,
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::{serialize, Encodable};
|
||||
@@ -218,6 +219,9 @@ impl MoneyTestHarness {
|
||||
rcpt,
|
||||
amount,
|
||||
token_id,
|
||||
pallas::Base::zero(),
|
||||
pallas::Base::zero(),
|
||||
pallas::Base::random(&mut OsRng),
|
||||
&[],
|
||||
&self.faucet_merkle_tree,
|
||||
&self.mint_zkbin,
|
||||
|
||||
Reference in New Issue
Block a user