daod python proof of concept for tx builder class

This commit is contained in:
narodnik
2022-05-05 10:24:12 +02:00
parent aa659f7318
commit 9539cd61dc
4 changed files with 407 additions and 0 deletions

View File

@@ -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
bin/daod/demo/crypto.py Normal file
View File

@@ -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
bin/daod/demo/main.py Normal file
View File

@@ -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
bin/daod/src/demo.rs Normal file
View File

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