main.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import sys
  2. from classnamespace import ClassNamespace
  3. from crypto import pallas_curve, ff_hash
  4. from tx import TransactionBuilder
  5. class State:
  6. def __init__(self):
  7. self.all_coins = set()
  8. self.nullifiers = set()
  9. def is_valid_merkle(self, all_coins):
  10. return all_coins.issubset(self.all_coins)
  11. def nullifier_exists(self, nullifier):
  12. return nullifier in self.nullifiers
  13. def apply(self, update):
  14. self.nullifiers = self.nullifiers.union(update.nullifiers)
  15. for coin, enc_note in zip(update.coins, update.enc_notes):
  16. self.all_coins.add(coin)
  17. # Try to decrypt notes here
  18. print(f"Received {enc_note.value} DRK")
  19. def state_transition(state, tx):
  20. for input in tx.clear_inputs:
  21. pk = input.signature_public
  22. # Check pk is correct
  23. for input in tx.inputs:
  24. if not state.is_valid_merkle(input.revealed.all_coins):
  25. print(f"invalid merkle root", file=sys.stderr)
  26. return None
  27. nullifier = input.revealed.nullifier
  28. if state.nullifier_exists(nullifier):
  29. print(f"duplicate nullifier found", file=sys.stderr)
  30. return None
  31. is_verify, reason = tx.verify()
  32. if not is_verify:
  33. print(f"tx verify failed: {reason}", file=sys.stderr)
  34. return None
  35. update = ClassNamespace()
  36. update.nullifiers = [input.revealed.nullifier for input in tx.inputs]
  37. update.coins = [output.revealed.coin for output in tx.outputs]
  38. update.enc_notes = [output.enc_note for output in tx.outputs]
  39. return update
  40. def main(argv):
  41. ec = pallas_curve()
  42. secret = ec.random_scalar()
  43. public = ec.multiply(secret, ec.G)
  44. initial_supply = 21000
  45. token_id = 110
  46. signature_secret = ec.random_scalar()
  47. builder = TransactionBuilder(ec)
  48. builder.add_clear_input(initial_supply, token_id, signature_secret)
  49. builder.add_output(initial_supply, token_id, public)
  50. tx = builder.build()
  51. state = State()
  52. if (update := state_transition(state, tx)) is None:
  53. return -1
  54. state.apply(update)
  55. assert len(tx.outputs) > 0
  56. note = tx.outputs[0].enc_note
  57. coin = ff_hash(
  58. ec.p,
  59. public[0],
  60. public[1],
  61. note.value,
  62. note.token_id,
  63. note.serial,
  64. note.coin_blind
  65. )
  66. assert coin == tx.outputs[0].mint_proof.get_revealed().coin
  67. all_coins = set([coin])
  68. builder = TransactionBuilder(ec)
  69. builder.add_input(all_coins, secret, note)
  70. secret2 = ec.random_scalar()
  71. public2 = ec.multiply(secret, ec.G)
  72. builder.add_output(1000, token_id, public2)
  73. # Change
  74. builder.add_output(note.value - 1000, token_id, public)
  75. tx = builder.build()
  76. if (update := state_transition(state, tx)) is None:
  77. return -1
  78. state.apply(update)
  79. return 0
  80. if __name__ == "__main__":
  81. sys.exit(main(sys.argv))