contract/money: Client spend-hook support.

This commit is contained in:
parazyd
2023-01-11 17:01:20 +01:00
parent ee93088dae
commit fd79349982
11 changed files with 115 additions and 18 deletions

1
Cargo.lock generated
View File

@@ -1171,6 +1171,7 @@ name = "darkfi-dao-contract"
version = "0.3.0"
dependencies = [
"async-std",
"bs58",
"chacha20poly1305",
"darkfi",
"darkfi-money-contract",

View File

@@ -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")?;

View File

@@ -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 {}",

View File

@@ -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,

View File

@@ -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))

View File

@@ -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,

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,