diff --git a/bin/daod/demo/classnamespace.py b/bin/daod/demo/classnamespace.py new file mode 100644 index 000000000..858a888d6 --- /dev/null +++ b/bin/daod/demo/classnamespace.py @@ -0,0 +1,46 @@ +from types import SimpleNamespace + +class ClassNamespace(SimpleNamespace): + def __init__(self, dic=None): + if dic is None: + return + # if type(dic) is dict: + for key in dic: + setattr(self, key, self.envelop(dic[key])) + # else: + # raise CatalogError("ClassNamespace AIUTO!") + + def envelop(self, elem): + if type(elem) is dict: + return ClassNamespace(elem) + elif type(elem) is list: + return [self.envelop(x) for x in elem] + else: + return elem + + # if d is not None: + # for key in d: + # if type(d[key]) is dict: + # setattr(self, key, ClassNamespace(d[key])) + # else: + # setattr(self, key, d[key]) + + def __contains__(self, x): + return x in self.__dict__ + + def __json__(self, x): + return self.__dict__ + + def copy(self): + return self.__dict__.copy() + + def classcopy(self): + dummy = ClassNamespace() + dummy.__dict__.update(self.__dict__) + return dummy + + def dictcopy(self): + return self.__dict__.copy() + + def update(self, oth): + self.__dict__.update(oth.__dict__) diff --git a/bin/daod/demo/crypto.py b/bin/daod/demo/crypto.py new file mode 100644 index 000000000..a645ccca8 --- /dev/null +++ b/bin/daod/demo/crypto.py @@ -0,0 +1,104 @@ +import random + +def ff_inv(a, p): + a %= p + + # extended euclidean algorithm + # ps + at = 1 + t = 0 + new_t = 1 + r = p + new_r = a + + while new_r != 0: + quotient = r // new_r + t, new_t = new_t, t - quotient * new_t + r, new_r = new_r, r - quotient * new_r + + assert r == 1 + if t < 0: + t += p + + return t + +class EllipticCurve: + + def __init__(self, p, A, B, order, G, H): + self.p = p + self.A = A + self.B = B + self.order = order + self.G = G + self.H = H + assert self.is_valid(G) + assert self.is_valid(H) + + def is_valid(self, P): + x, y, z = P + if z == 0: + return x != 0 or y != 0 + z_inv = ff_inv(z, self.p) + x, y = x * z_inv, y * z_inv + return y**2 % self.p == (x**3 + self.A * x + self.B) % self.p + + def add(self, p1, p2): + x1, y1, z1 = p1 + x2, y2, z2 = p2 + + if z1 == 0: + return (x2, y2, z2) + elif z2 == 0: + return (x1, y1, z1) + + if x1 == x2: + if y1 != y2: + return (0, 1, 0) + + assert y1 != 0 + m = (3 * x1**2 + self.A) * ff_inv(2*y1, self.p) + else: + m = (y2 - y1) * ff_inv(x2 - x1, self.p) + + x3 = (m**2 - x1 - x2) % self.p + y3 = (m * (x1 - x3) - y1) % self.p + return (x3, y3, 1) + + def multiply(self, m, p): + bits = f"{m:b}" + result = (0, 1, 0) + temp = p + for bit in bits[::-1]: + if bit == "1": + result = self.add(result, temp) + temp = self.add(temp, temp) + return result + + def random_point(self): + m = self.random_scalar() + return self.multiply(m, self.G) + + def random_scalar(self): + m = random.randrange(0, self.order - 1) + return m + + def random_base(self): + m = random.randrange(0, self.p - 1) + return m + +def pallas_curve(): + # Pallas + p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001 + q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 + G = (5, 5392431450607408583390510508521091931943415030464003135511088002453056875732, 1) + H = (9762257241998025279988087154025308614062019274413483967640476725944341089207, + 12058632856930756995627167820351407063813260358041446014729496773111030695755, 1) + ec = EllipticCurve(p, 0, 5, q, G, H) + A = (144931808354919915876542440378319484704499556634959420306426167479163065488, + 2699682121356767698440748624399854659825391162912545787181017961871465868196, 1) + B = (16017037670495191561606513965775243786961447026019262496667491008912834496943, + 20395164507282344548629891414360366999207473153143014512687861307997120664849, 1) + assert ec.add(A, B) == (2414658659502531855741199170408914396997834981355655923471364687102714431309, 21133344194418979683767005688724798091220515434220043854575260979109407444719, 1) + m = 26322809409216846271933211244226061368157231119725763192402071651286829040466 + assert ec.multiply(m, G) == (15862887453366837597569434439063150886012590021428640083047997467990450633825, 25887284719793568129480941070850220101898092026705204234126448799557008384178, 1) + return ec + diff --git a/bin/daod/demo/main.py b/bin/daod/demo/main.py new file mode 100644 index 000000000..a90c84934 --- /dev/null +++ b/bin/daod/demo/main.py @@ -0,0 +1,123 @@ +import sys +from collections import namedtuple +from classnamespace import ClassNamespace +from crypto import pallas_curve + +class TransactionBuilder: + + def __init__(self, ec): + self.clear_inputs = [] + self.inputs = [] + self.outputs = [] + + self.ec = ec + + def add_clear_input(self, value, token_id, signature_secret): + clear_input = ClassNamespace() + clear_input.value = value + clear_input.token_id = token_id + clear_input.signature_secret = signature_secret + self.clear_inputs.append(clear_input) + + def add_input(self, input): + self.inputs.append(input) + + def add_output(self, value, token_id, public): + output = ClassNamespace() + output.value = value + output.token_id = token_id + output.public = public + self.outputs.append(output) + + def compute_remainder_blind(self, clear_inputs, input_blinds, + output_blinds): + total = 0 + total += sum(input.value_blind for input in clear_inputs) + total += sum(input_blinds) + total -= sum(output_blinds) + return total % self.ec.order + + def build(self): + tx = Transaction() + token_blind = self.ec.random_scalar() + + for input in self.clear_inputs: + tx_clear_input = ClassNamespace() + tx_clear_input.value = input.value + tx_clear_input.token_id = input.token_id + tx_clear_input.value_blind = self.ec.random_scalar() + tx_clear_input.token_blind = input.token_blind + tx_clear_input.signature_public = self.ec.multiply( + input.signature_secret, self.ec.G) + tx.clear_inputs.append(tx_clear_input) + + input_blinds = [] + for input in self.inputs: + tx_input = ClassNamespace() + tx.inputs.append(tx_input) + + assert self.outputs + output_blinds = [] + for i, output in enumerate(self.outputs): + if i == len(self.outputs) - 1: + value_blind = self.compute_remainder_blind( + tx.clear_inputs, input_blinds, output_blinds) + else: + value_blind = self.ec.random_scalar() + output_blinds.append(value_blind) + + note = ClassNamespace() + note.serial = self.ec.random_base() + note.value = output.value + note.token_id = output.token_id + note.coin_blind = self.ec.random_base() + note.value_blind = value_blind + + tx_output = ClassNamespace() + + tx_output.mint_proof = MintProof( + note.value, note.token_id, note.value_blind, + token_blind, tx_output.serial, coin_blind, public) + tx_output.revealed = tx_output.mint_proof.get_revealed() + # Is normally encrypted + tx_output.enc_note = note + + tx.outputs.append(tx_output) + + return tx + +class MintProof: + + def __init__(self, value, token_id, value_blind, token_blind, serial, + coin_blind, public): + self.value = value + self.token_id = token_id + self.value_blind = value_blind + self.token_blind = token_blind + self.serial = serial + self.coin_blind = coin_blind + self.public = public + + def get_revealed(self): + revealed = ClassNamespace() + return revealed + + def verify(self): + pass + +class Transaction: + + def __init__(self): + self.clear_inputs = [] + self.inputs = [] + self.outputs = [] + +def main(argv): + ec = pallas_curve() + builder = TransactionBuilder(ec) + builder.add_output(44, 110, "1234") + tx = builder.build() + +if __name__ == "__main__": + sys.exit(main(sys.argv)) + diff --git a/bin/daod/src/demo.rs b/bin/daod/src/demo.rs new file mode 100644 index 000000000..4dc65a02e --- /dev/null +++ b/bin/daod/src/demo.rs @@ -0,0 +1,134 @@ +use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Tree}; +use rand::rngs::OsRng; + +use darkfi::{ + crypto::{ + coin::Coin, + keypair::{Keypair, PublicKey, SecretKey}, + merkle_node::MerkleNode, + note::{EncryptedNote, Note}, + nullifier::Nullifier, + proof::{ProvingKey, VerifyingKey}, + token_id::generate_id2, + }, + node::state::{state_transition, ProgramState, StateUpdate}, + tx, + util::NetworkName, + zk::circuit::{mint_contract::MintContract, spend_contract::SpendContract}, + Result, +}; + +struct MemoryState { + // The entire merkle tree state + tree: BridgeTree, + // List of all previous and the current merkle roots + // This is the hashed value of all the children. + merkle_roots: Vec, + // Nullifiers prevent double spending + nullifiers: Vec, + // All received coins + // NOTE: we need maybe a flag to keep track of which ones are spent + // Maybe the spend field links to a tx hash:input index + // We should also keep track of the tx hash:output index where this + // coin was received + own_coins: Vec<(Coin, Note)>, + mint_vk: VerifyingKey, + spend_vk: VerifyingKey, + + // Public key of the cashier + cashier_signature_public: PublicKey, + // List of all our secret keys + secrets: Vec, +} + +impl ProgramState for MemoryState { + fn is_valid_cashier_public_key(&self, public: &PublicKey) -> bool { + public == &self.cashier_signature_public + } + + fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool { + self.merkle_roots.iter().any(|m| m == merkle_root) + } + + fn nullifier_exists(&self, nullifier: &Nullifier) -> bool { + self.nullifiers.iter().any(|n| n == nullifier) + } + + fn mint_vk(&self) -> &VerifyingKey { + &self.mint_vk + } + + fn spend_vk(&self) -> &VerifyingKey { + &self.spend_vk + } +} + +impl MemoryState { + fn apply(&mut self, mut update: StateUpdate) { + // Extend our list of nullifiers with the ones from the update + self.nullifiers.append(&mut update.nullifiers); + + // Update merkle tree and witnesses + for (coin, enc_note) in update.coins.into_iter().zip(update.enc_notes.into_iter()) { + // Add the new coins to the merkle tree + let node = MerkleNode(coin.0); + self.tree.append(&node); + + // Keep track of all merkle roots that have existed + self.merkle_roots.push(self.tree.root()); + + if let Some((note, _secret)) = self.try_decrypt_note(enc_note) { + self.own_coins.push((coin, note)); + self.tree.witness(); + } + } + } + + fn try_decrypt_note(&self, ciphertext: EncryptedNote) -> Option<(Note, SecretKey)> { + // Loop through all our secret keys... + for secret in &self.secrets { + // ... attempt to decrypt the note ... + if let Ok(note) = ciphertext.decrypt(secret) { + // ... and return the decrypted note for this coin. + return Some((note, *secret)) + } + } + // We weren't able to decrypt the note with any of our keys. + None + } +} + +pub fn demo() -> Result<()> { + // Create the treasury token: xDRK + // - mint a new token supply using clear inputs + // Create the governance token: gDRK + // - mint a new token supply using clear inputs + // Create the DAO instance + // - create proposal auth keypair + // - mint a new bulla: + // + // DAO { + // proposal_auth_key + // gov_token_id + // treasury_token_id + // } + // + // Receive payment to DAO treasury + // - send token to a coin that has: + // - parent set to DAO bulla + // - owner set to contract:function unique address (checked by consensus) + // Create a proposal + // Proposal is signed + // Successful voting + // Proposal is executed + // - burn conditions are met + // - DAO bulla matches parent field in coins being spent + // - correct contract:function fields are set + // - burn the coins, but not the DAO + // - main dao execute: voting threshold and outcome + + let xdrk_supply = 1_000_000; + let gdrk_supply = 1_000_000; + + Ok(()) +}