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))