Prechádzať zdrojové kódy

daod: basic contracts structure

narodnik 4 rokov pred
rodič
commit
a736742f77
3 zmenil súbory, kde vykonal 180 pridanie a 13 odobranie
  1. 7 2
      bin/daod/demo/crypto.py
  2. 152 4
      bin/daod/demo/main.py
  3. 21 7
      bin/daod/demo/tx.py

+ 7 - 2
bin/daod/demo/crypto.py

@@ -108,16 +108,21 @@ def pedersen_encrypt(x, y, ec):
     vcr = ec.multiply(y, ec.H)
     return ec.add(vcv, vcr)
 
-def ff_hash(p, *args):
-    hasher = hashlib.sha256()
+def _add_to_hasher(hasher, args):
     for arg in args:
         match arg:
             case int() as arg:
                 hasher.update(arg.to_bytes(32, byteorder="little"))
             case bytes() as arg:
                 hasher.update(arg)
+            case list() as arg:
+                _add_to_hasher(hasher, arg)
             case _:
                 raise Exception(f"unknown hash arg '{arg}' type: {type(arg)}")
+
+def ff_hash(p, *args):
+    hasher = hashlib.sha256()
+    _add_to_hasher(hasher, args)
     value = int.from_bytes(hasher.digest(), byteorder="little")
     return value % p
 

+ 152 - 4
bin/daod/demo/main.py

@@ -50,6 +50,114 @@ def state_transition(state, tx):
     update.enc_notes = [output.enc_note for output in tx.outputs]
     return update
 
+class DaoBuilder:
+
+    def __init__(self, proposal_auth_public_key, threshold, quorum, ec):
+        self.proposal_auth_public_key = proposal_auth_public_key
+        self.threshold = threshold
+        self.quorum = quorum
+
+        self.ec = ec
+
+    def build(self):
+        mint_proof = DaoMintProof(
+            self.proposal_auth_public_key,
+            self.threshold,
+            self.quorum,
+            self.ec
+        )
+        revealed = mint_proof.get_revealed()
+
+        dao = Dao(revealed, mint_proof, self.ec)
+        return dao
+
+class Dao:
+
+    def __init__(self, revealed, mint_proof, ec):
+        self.revealed = revealed
+        self.mint_proof = mint_proof
+        self.ec = ec
+
+    def verify(self):
+        if not self.mint_proof.verify(self.revealed):
+            return False, "mint proof failed to verify"
+        return True, None
+
+# class DaoExec .etc
+
+class DaoMintProof:
+
+    def __init__(self, proposal_auth_public_key, threshold, quorum, ec):
+        self.proposal_auth_public_key = proposal_auth_public_key
+        self.threshold = threshold
+        self.quorum = quorum
+        self.ec = ec
+
+    def get_revealed(self):
+        revealed = ClassNamespace()
+
+        revealed.bulla = ff_hash(
+            self.ec.p,
+            self.proposal_auth_public_key[0],
+            self.proposal_auth_public_key[1],
+            self.threshold,
+            self.quorum
+        )
+
+        return revealed
+
+    def verify(self, public):
+        revealed = self.get_revealed()
+        return True
+
+# Shared between DaoMint and DaoExec
+class DaoState:
+
+    def __init__(self):
+        self.bullas = set()
+
+    def apply(self, update):
+        self.bullas.add(update.bulla)
+
+    def apply_exec(self, update):
+        pass
+
+# contract interface functions
+def dao_state_transition(state, tx):
+    is_verify, reason = tx.verify()
+    if not is_verify:
+        print(f"dao tx verify failed: {reason}", file=sys.stderr)
+        return None
+
+    update = ClassNamespace()
+    update.bulla = tx.revealed.bulla
+    return update
+
+###### DAO EXEC
+
+class DaoExecBuilder:
+
+    def __init__(self):
+        pass
+
+    def build(self):
+        tx = DaoExec()
+        return tx
+
+class DaoExec:
+
+    def __init__(self):
+        pass
+
+class DaoExecProof:
+
+    def __init__(self):
+        pass
+
+def dao_exec_state_transition(state, tx):
+    update = ClassNamespace()
+    return update
+
 def main(argv):
     ec = pallas_curve()
 
@@ -61,9 +169,26 @@ def main(argv):
 
     signature_secret = ec.random_scalar()
 
+    # Setup the DAO
+    proposal_auth_secret = ec.random_scalar()
+    proposal_auth_public = ec.multiply(proposal_auth_secret, ec.G)
+    threshold = 110
+    quorum = 110
+    builder = DaoBuilder(proposal_auth_public, threshold, quorum, ec)
+    dao_tx = builder.build()
+
+    # Each deployment of a contract has a unique state
+    # associated with it.
+    dao_state = DaoState()
+    if (update := dao_state_transition(dao_state, dao_tx)) is None:
+        return -1
+    dao_state.apply(update)
+
     builder = TransactionBuilder(ec)
     builder.add_clear_input(initial_supply, token_id, signature_secret)
-    builder.add_output(initial_supply, token_id, public)
+    depends = [b"0xdao_ruleset"]
+    attrs = []
+    builder.add_output(initial_supply, token_id, public, depends, attrs)
     tx = builder.build()
 
     state = State()
@@ -71,6 +196,27 @@ def main(argv):
         return -1
     state.apply(update)
 
+    # Now the depends field specifies the function DaoExec
+    # so the tx above must also be combined with a DaoExec tx
+    for input in tx.inputs:
+        assert input.revealed.depends == [b"0xdao_ruleset"]
+    builder = DaoExecBuilder()
+    dao_tx = builder.build()
+    if (update := dao_exec_state_transition(dao_state, dao_tx)) is None:
+        return -1
+    dao_state.apply_exec(update)
+
+    # State
+    # functions that can be called on state with params
+    # functions return an update
+    # optional encrypted values that can be read by wallets
+    # --> (do this outside??)
+    # --> penalized if fail
+    # apply update to state
+
+    # payment state transition in coin specifies dependency
+    # the tx exists and ruleset is applied
+
     assert len(tx.outputs) > 0
     note = tx.outputs[0].enc_note
     coin = ff_hash(
@@ -80,7 +226,9 @@ def main(argv):
         note.value,
         note.token_id,
         note.serial,
-        note.coin_blind
+        note.coin_blind,
+        depends,
+        attrs
     )
     assert coin == tx.outputs[0].mint_proof.get_revealed().coin
     all_coins = set([coin])
@@ -91,9 +239,9 @@ def main(argv):
     secret2 = ec.random_scalar()
     public2 = ec.multiply(secret, ec.G)
 
-    builder.add_output(1000, token_id, public2)
+    builder.add_output(1000, token_id, public2, depends=[b"0x0000"], attrs=[])
     # Change
-    builder.add_output(note.value - 1000, token_id, public)
+    builder.add_output(note.value - 1000, token_id, public, depends, attrs)
 
     tx = builder.build()
 

+ 21 - 7
bin/daod/demo/tx.py

@@ -24,11 +24,13 @@ class TransactionBuilder:
         input.note = note
         self.inputs.append(input)
 
-    def add_output(self, value, token_id, public):
+    def add_output(self, value, token_id, public, depends, attrs):
         output = ClassNamespace()
         output.value = value
         output.token_id = token_id
         output.public = public
+        output.depends = depends
+        output.attrs = attrs
         self.outputs.append(output)
 
     def compute_remainder_blind(self, clear_inputs, input_blinds,
@@ -68,7 +70,8 @@ class TransactionBuilder:
             tx_input.burn_proof = BurnProof(
                 input.note.value, input.note.token_id, input.note.value_blind,
                 token_blind, input.note.serial, input.note.coin_blind,
-                input.secret, input.all_coins, signature_secret, self.ec)
+                input.secret, input.note.depends, input.note.attrs,
+                input.all_coins, signature_secret, self.ec)
             tx_input.revealed = tx_input.burn_proof.get_revealed()
             tx.inputs.append(tx_input)
 
@@ -89,6 +92,8 @@ class TransactionBuilder:
             note.coin_blind = self.ec.random_base()
             note.value_blind = value_blind
             note.token_blind = token_blind
+            note.depends = output.depends
+            note.attrs = output.attrs
 
             tx_output = ClassNamespace()
             tx_output.__name__ = "TransactionOutput"
@@ -96,7 +101,7 @@ class TransactionBuilder:
             tx_output.mint_proof = MintProof(
                 note.value, note.token_id, note.value_blind,
                 note.token_blind, note.serial, note.coin_blind,
-                output.public, self.ec)
+                output.public, output.depends, output.attrs, self.ec)
             tx_output.revealed = tx_output.mint_proof.get_revealed()
             assert tx_output.mint_proof.verify(tx_output.revealed)
 
@@ -196,7 +201,8 @@ class Transaction:
 class BurnProof:
 
     def __init__(self, value, token_id, value_blind, token_blind, serial,
-                 coin_blind, secret, all_coins, signature_secret, ec):
+                 coin_blind, secret, depends, attrs, all_coins,
+                 signature_secret, ec):
         self.value = value
         self.token_id = token_id
         self.value_blind = value_blind
@@ -204,6 +210,8 @@ class BurnProof:
         self.serial = serial
         self.coin_blind = coin_blind
         self.secret = secret
+        self.depends = depends
+        self.attrs = attrs
         self.all_coins = all_coins
         self.signature_secret = signature_secret
 
@@ -238,7 +246,9 @@ class BurnProof:
             self.value,
             self.token_id,
             self.serial,
-            self.coin_blind
+            self.coin_blind,
+            self.depends,
+            self.attrs,
         )
         # Merkle root check
         if coin not in self.all_coins:
@@ -255,7 +265,7 @@ class BurnProof:
 class MintProof:
 
     def __init__(self, value, token_id, value_blind, token_blind, serial,
-                 coin_blind, public, ec):
+                 coin_blind, public, depends, attrs, ec):
         self.value = value
         self.token_id = token_id
         self.value_blind = value_blind
@@ -263,6 +273,8 @@ class MintProof:
         self.serial = serial
         self.coin_blind = coin_blind
         self.public = public
+        self.depends = depends
+        self.attrs = attrs
 
         self.ec = ec
 
@@ -275,7 +287,9 @@ class MintProof:
             self.value,
             self.token_id,
             self.serial,
-            self.coin_blind
+            self.coin_blind,
+            self.depends,
+            self.attrs
         )
 
         revealed.value_commit = pedersen_encrypt(