mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
daod python proof of concept for tx builder class
This commit is contained in:
46
bin/daod/demo/classnamespace.py
Normal file
46
bin/daod/demo/classnamespace.py
Normal 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
104
bin/daod/demo/crypto.py
Normal 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
123
bin/daod/demo/main.py
Normal 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
134
bin/daod/src/demo.rs
Normal 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(())
|
||||
}
|
||||
Reference in New Issue
Block a user