kee

Offline IOU signer with QR as transport
git clone git://holbrook.no/kee-gtk4.git
Info | Log | Files | Refs | README | LICENSE

testdata.py (8890B)


      1 import tempfile
      2 import logging
      3 import os
      4 import shutil
      5 import hashlib
      6 import sys
      7 import io
      8 import random
      9 import time
     10 import email.message
     11 from faker import Faker
     12 from faker.providers import lorem
     13 
     14 import lmdb
     15 import varint
     16 
     17 SIGNS_ALICE_CREDIT_DELTA_NEGATIVE = 1 << 0
     18 SIGNS_BOB_CREDIT_DELTA_NEGATIVE = 1 << 1
     19 SIGNS_ALICE_COLLATERAL_DELTA_NEGATIVE = 1 << 2
     20 SIGNS_BOB_COLLATERAL_DELTA_NEGATIVE = 1 << 3
     21 
     22 FLAGS_SIGNER_IS_BOB = 1 << 0
     23 
     24 NOBODY = b'\x00' * 64
     25 NOSIG = b'\x00' * 65
     26 PFX_LEDGER_HEAD = b'\x01'
     27 PFX_LEDGER_ENTRY = b'\x02'
     28 
     29 logging.basicConfig(level=logging.DEBUG)
     30 logg = logging.getLogger()
     31 
     32 fake = Faker()
     33 fake.add_provider(lorem)
     34 
     35 
     36 def db_init(d):
     37     d = os.path.join(d, 'testdata_mdb')
     38     logg.info('using d for db' + d)
     39 
     40     try:
     41         shutil.rmtree(d)
     42     except FileNotFoundError:
     43         pass
     44     os.makedirs(d)
     45     return d
     46 
     47 
     48 def to_absflag(v):
     49     flag = False
     50     if v < 0:
     51         flag = True
     52     return (abs(v), flag,)
     53 
     54 
     55 class LedgerContent(email.message.EmailMessage):
     56 
     57     def __init__(self):
     58         super(LedgerContent, self).__init__()
     59         self.set_default_type("text/plain")
     60         self.add_header("Subject", fake.sentence())
     61         self.set_content(fake.paragraph())
     62 
     63 
     64     def kv(self):
     65         b = self.as_bytes()
     66         h = hashlib.new("sha512")
     67         h.update(b)
     68         z = h.digest()
     69         return (z, b,)
     70 
     71 
     72 class LedgerHeadContent(LedgerContent):
     73     pass
     74 
     75 
     76 class LedgerEntryContent(LedgerContent):
     77     pass
     78 
     79 
     80 class LedgerHead:
     81 
     82     def __init__(self, alice_key=None, bob_key=None, body=NOBODY):
     83         self.uoa = "USD"
     84         self.uoa_decimals = 2
     85         if alice_key == None:
     86             alice_key = os.urandom(65)
     87         self.alice_pubkey_ref = alice_key
     88         if bob_key == None:
     89             bob_key = os.urandom(65)
     90         self.bob_pubkey_ref = bob_key
     91         self.body = LedgerHeadContent()
     92 
     93 
     94     def __serialize_add(self, b, w):
     95         c = varint.encode(len(b))
     96         w.write(c)
     97         w.write(b)
     98 
     99 
    100     def __data_add(self, data_dir, k, v):
    101         fp = os.path.join(data_dir, k.hex())
    102         f = open(fp, 'wb')
    103         logg.info("fp {}".format(fp))
    104         f.write(v)
    105         f.close()
    106 
    107 
    108     def serialize(self, data_dir, w=sys.stdout.buffer):
    109         b = self.uoa.encode('utf-8')
    110         self.__serialize_add(b, w)
    111 
    112         b = varint.encode(self.uoa_decimals)
    113         self.__serialize_add(b, w)
    114 
    115         b = self.alice_pubkey_ref
    116         self.__serialize_add(b, w)
    117 
    118         b = self.bob_pubkey_ref
    119         self.__serialize_add(b, w)
    120 
    121         (k, b) = self.body.kv()
    122         self.__data_add(data_dir, k, b)
    123         self.__serialize_add(k, w)
    124 
    125 
    126     @staticmethod
    127     def to_key(b):
    128         r = b''
    129         r += PFX_LEDGER_HEAD
    130         v = time.time_ns()
    131         b = v.to_bytes(8, byteorder='big')
    132         r += b
    133 
    134         return r
    135 
    136 
    137 class LedgerEntry:
    138 
    139     credit_delta_min = -1000
    140     credit_delta_max = 1000
    141     collateral_delta_min = 0
    142     collateral_delta_max = 0
    143 
    144     def __init__(self, head, parent=None, body=NOBODY, signer=None):
    145         random.seed(int(time.time_ns()))
    146         self.head = head
    147         self.flags = 0
    148         self.signs = 0
    149         self.parent = parent
    150         if self.parent == None:
    151             self.parent = b'\x00' * 64
    152         self.timestamp = time.time_ns()
    153 
    154         self.body = LedgerEntryContent()
    155 
    156         v = random.randint(self.credit_delta_min, self.credit_delta_max)
    157         self.flags = v % 2
    158         (v, neg) = to_absflag(v)
    159         self.credit_delta = v
    160         if neg:
    161             if self.flags:
    162                 self.signs |= SIGNS_BOB_CREDIT_DELTA_NEGATIVE
    163             else:
    164                 self.signs |= SIGNS_ALICE_CREDIT_DELTA_NEGATIVE
    165 
    166         v = random.randint(self.collateral_delta_min, self.collateral_delta_max)
    167         self.response_value = v % 2
    168         (v, neg) = to_absflag(v)
    169         self.collateral_delta = v
    170         if neg:
    171             if self.flags:
    172                 self.signs |= SIGNS_BOB_COLLATERAL_DELTA_NEGATIVE
    173             else:
    174                 self.signs |= SIGNS_ALICE_COLLATERAL_DELTA_NEGATIVE
    175 
    176         #self.request_signature = NOSIG
    177         #self.response_signature = NOSIG
    178         self.request_signature = os.urandom(65)
    179         self.response_signature = os.urandom(65)
    180         self.signer = signer
    181 
    182 
    183     def __serialize_add(self, b, w):
    184         c = varint.encode(len(b))
    185         w.write(c)
    186         w.write(b)
    187 
    188 
    189     def __data_add(self, data_dir, k, v):
    190         fp = os.path.join(data_dir, k.hex())
    191         f = open(fp, 'wb')
    192         logg.info("fp {}".format(fp))
    193         f.write(v)
    194         f.close()
    195 
    196 
    197     def serialize(self, data_dir, w=sys.stdout.buffer):
    198         b = self.flags.to_bytes(1)
    199         self.__serialize_add(b, w)
    200         
    201         b = self.parent
    202         self.__serialize_add(b, w)
    203 
    204         b = self.timestamp.to_bytes(8, byteorder='big')
    205         self.__serialize_add(b, w)
    206 
    207         b = self.signs.to_bytes(1)
    208         self.__serialize_add(b, w)
    209 
    210 #        realvalue = self.credit_delta
    211 #        if self.flags & FLAGS_SIGNER_IS_BOB:
    212 #            if (self.signs & SIGNS_BOB_CREDIT_DELTA_NEGATIVE):
    213 #                realvalue *= -1
    214 #        else:
    215 #            if (self.signs & SIGNS_ALICE_CREDIT_DELTA_NEGATIVE):
    216 #                realvalue *= -1
    217 
    218     
    219         b = varint.encode(self.credit_delta)
    220         if self.flags:
    221             self.__serialize_add(varint.encode(0), w)
    222         self.__serialize_add(b, w)
    223         if not self.flags:
    224             self.__serialize_add(varint.encode(0), w)
    225         
    226 
    227         #if self.flags:
    228         #    self.__serialize_add(b'\x00', w)
    229         #logg.debug('encode flags {} credit {} collateral {}'.format(self.flags, self.credit_delta, self.collateral_delta))
    230         b = varint.encode(self.collateral_delta)
    231         if not self.flags:
    232             self.__serialize_add(varint.encode(0), w)
    233         self.__serialize_add(b, w)
    234         if not self.flags:
    235             self.__serialize_add(varint.encode(0), w)
    236 
    237         #if self.signer != None:
    238         #    self.signature = self.signer(b)
    239 
    240         (k, b) = self.body.kv()
    241         self.__data_add(data_dir, k, b)
    242         self.__serialize_add(k, w)
    243 
    244         self.__serialize_add(self.request_signature, w)
    245 
    246         b = self.response_value.to_bytes(1)
    247         self.__serialize_add(b, w)
    248         
    249         self.__serialize_add(self.response_signature, w)
    250 
    251         return b
    252 
    253 
    254     @staticmethod
    255     def to_key(v, k):
    256         r = b''
    257         r += PFX_LEDGER_ENTRY
    258         r += k
    259         ts = v[68:68+8]
    260         logg.debug('ts {}: of {}'.format(ts.hex(), v.hex()))
    261         r += ts
    262         return r
    263   
    264 
    265 def generate_entry(data_dir, head, parent):
    266     o = LedgerEntry(head, parent=parent)
    267     w = io.BytesIO()
    268     r = o.serialize(data_dir, w=w)
    269     h = hashlib.new('sha512')
    270     b = w.getvalue()
    271     h.update(b)
    272     z = h.digest()
    273     return (z, b,)
    274 
    275 
    276 def generate_ledger(data_dir, entry_count=3):
    277     r = []
    278     o = LedgerHead()
    279     w = io.BytesIO()
    280     o.serialize(data_dir, w=w)
    281     h = hashlib.new('sha512')
    282     b = w.getvalue()
    283     h.update(b)
    284     z = h.digest()
    285     r.append((z, b,))
    286 
    287     k = z
    288     parent = None
    289     for i in range(entry_count):
    290         v = generate_entry(data_dir, k, parent=parent)
    291         # \todo generate  key value already here
    292         parent = v[0]
    293         r.append(v)
    294 
    295     return r
    296 
    297 
    298 if __name__ == '__main__':
    299     d = os.path.dirname(__file__)
    300     data_dir = os.path.join(d, 'testdata_resource')
    301     try:
    302         shutil.rmtree(data_dir)
    303     except FileNotFoundError:
    304         pass
    305     os.makedirs(data_dir)
    306 
    307     d = db_init(d)
    308 
    309     env = lmdb.open(d)
    310     dbi = env.open_db()
    311 
    312 
    313     count_ledgers = os.environ.get('COUNT', '1')
    314 
    315     with env.begin(write=True) as tx:
    316         for i in range(int(count_ledgers)):
    317             c = random.randint(1, 20)
    318             r = generate_ledger(data_dir, entry_count=c)
    319 
    320             v = r.pop(0)
    321 
    322             z = v[0]
    323             k = LedgerHead.to_key(v[0])
    324             tx.put(k, v[1])
    325 
    326             for v in r:
    327                 k = LedgerEntry.to_key(v[1], z)
    328                 tx.put(k, v[1])
    329 
    330 
    331 #pfx = b'\x00\x00\x00'
    332 #pfx_two = b'\x00\x00\x01'
    333 
    334 #keys = []
    335 #vals = [
    336 #    b"\x03foo\x03bar",
    337 #    b"\x05xyzzy\x05plugh",
    338 #    b"\x04inky\x05pinky",
    339 #    b"\x06blinky\x05clyde",
    340 #        ]
    341 #
    342 #for i in range(len(vals)):
    343 #    k = b'\x01' + i.to_bytes(16, 'big')
    344 #    #k += os.urandom(32)
    345 #    h = hashlib.sha256()
    346 #    h.update(vals[i])
    347 #    k += h.digest()
    348 #    keys.append(k)
    349 #
    350 #with env.begin(write=True) as tx:
    351 #    for i in range(len(vals)):
    352 #        v = i.to_bytes(2, byteorder='big')
    353 #        tx.put(keys[i], vals[i])
    354 #        #tx.put(keys[i], vals[i].encode('utf-8'))
    355 #        #tx.put(pfx + v, vals[i].encode('utf-8'))
    356 #    #tx.put(pfx_two + b'\x00\x00', b'xyzzy')
    357 #    #tx.put(pfx_two + b'\x00\x01', b'plugh')
    358 #
    359 #with env.begin() as tx:
    360 #    c = tx.cursor()
    361 #    #if not c.set_range(b'\x00\x00\x01'):
    362 #    #    raise ValueError("no key")
    363 #    for k, v in c:
    364 #        logg.debug('have {} {}'.format(k, v))