ソースを参照

daod python proof of concept for tx builder class

narodnik 4 年 前
コミット
9539cd61dc
4 ファイル変更407 行追加0 行削除
  1. 46 0
      bin/daod/demo/classnamespace.py
  2. 104 0
      bin/daod/demo/crypto.py
  3. 123 0
      bin/daod/demo/main.py
  4. 134 0
      bin/daod/src/demo.rs

+ 46 - 0
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__)

+ 104 - 0
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
+

+ 123 - 0
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))
+

+ 134 - 0
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<MerkleNode, 32>,
+    // List of all previous and the current merkle roots
+    // This is the hashed value of all the children.
+    merkle_roots: Vec<MerkleNode>,
+    // Nullifiers prevent double spending
+    nullifiers: Vec<Nullifier>,
+    // 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<SecretKey>,
+}
+
+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(())
+}