zcash-test-vectors/zcash_test_vectors/output.py

253 lines
7.0 KiB
Python

#!/usr/bin/env python3
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
import argparse
from binascii import hexlify
import json
def chunk(h):
hstr = str(h, 'utf-8')
hstr = ', 0x'.join([hstr[i:i+2] for i in range(0, len(hstr), 2)])
return '0x' + hstr if hstr else ''
class Some(object):
def __init__(self, thing):
self.thing = thing
def option(x):
return Some(x) if x else None
#
# JSON (with string comments)
# If bitcoin_flavoured == True, 32-byte values are reversed
#
def tv_value_json(value, bitcoin_flavoured):
if isinstance(value, Some):
value = value.thing
def bitcoinify(value):
if type(value) == list:
return [bitcoinify(v) for v in value]
if type(value) == str:
return value
if type(value) == bytes:
if bitcoin_flavoured and len(value) == 32:
value = value[::-1]
value = hexlify(value).decode()
return value
return bitcoinify(value)
def tv_json(filename, parts, vectors, bitcoin_flavoured):
if type(vectors) == type({}):
vectors = [vectors]
print('''[
["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/%s.py"],
["%s"],''' % (
filename,
', '.join([p[0] for p in parts])
))
print(' ' + ',\n '.join([
json.dumps([tv_value_json(v[p[0]], p[1].get('bitcoin_flavoured', bitcoin_flavoured)) for p in parts]) for v in vectors
]))
print(']')
#
# Rust
#
def tv_bytes_rust(name, value, pad):
print('''%s%s: [
%s%s
%s],''' % (
pad,
name,
pad,
chunk(hexlify(value)),
pad,
))
def tv_vec_bytes_rust(name, value, pad):
print('''%s%s: vec![
%s%s
%s],''' % (
pad,
name,
pad,
chunk(hexlify(value)),
pad,
))
def tv_vec_bool_rust(name, value, pad):
print('''%s%s: vec![
%s%s
%s],''' % (
pad,
name,
pad,
', '.join(['true' if x else 'false' for x in value]),
pad,
))
def tv_str_rust(name, value, pad):
print('''%s%s: "%s",''' % (
pad,
name,
value,
))
def tv_option_bytes_rust(name, value, pad):
if value:
print('''%s%s: Some([
%s%s
%s]),''' % (
pad,
name,
pad,
chunk(hexlify(value.thing)),
pad,
))
else:
print('%s%s: None,' % (pad, name))
def tv_option_vec_bytes_rust(name, value, pad):
if value:
print('''%s%s: Some(vec![
%s%s
%s]),''' % (
pad,
name,
pad,
chunk(hexlify(value.thing)),
pad,
))
else:
print('%s%s: None,' % (pad, name))
def tv_int_rust(name, value, pad):
print('%s%s: %d,' % (pad, name, value))
def tv_option_int_rust(name, value, pad):
if value:
print('%s%s: Some(%d),' % (pad, name, value.thing))
else:
print('%s%s: None,' % (pad, name))
def tv_part_rust(name, value, config, indent=3):
if 'rust_fmt' in config:
value = config['rust_fmt'](value)
elif config['rust_type'].startswith('Option<') and not (value is None or isinstance(value, Some)):
value = Some(value)
pad = ' ' * indent
if config['rust_type'] == 'Option<Vec<u8>>':
tv_option_vec_bytes_rust(name, value, pad)
elif config['rust_type'] == 'Vec<u8>':
tv_vec_bytes_rust(name, value, pad)
elif config['rust_type'] == 'Vec<bool>':
tv_vec_bool_rust(name, value, pad)
elif config['rust_type'] == '&\'static str':
tv_str_rust(name, value, pad)
elif config['rust_type'].startswith('Option<['):
tv_option_bytes_rust(name, value, pad)
elif type(value) == bytes:
tv_bytes_rust(name, value, pad)
elif config['rust_type'].startswith('Option<'):
tv_option_int_rust(name, value, pad)
elif type(value) == int:
tv_int_rust(name, value, pad)
elif type(value) == list:
print('''%s%s: %s[''' % (
pad,
name,
'vec!' if config['rust_type'].startswith('Vec<') else '',
))
for item in value:
if 'Vec<u8>' in config['rust_type']:
print('''%svec![
%s%s
%s],''' % (
' ' * (indent + 1),
' ' * (indent + 1),
chunk(hexlify(item)),
' ' * (indent + 1),
))
elif type(item) == bytes:
print('''%s[%s],''' % (
' ' * (indent + 1),
chunk(hexlify(item)),
))
elif type(item) == int:
print('%s%d,' % (' ' * (indent + 1), item))
elif type(item) == list:
print('''%s[''' % (
' ' * (indent + 1)
))
for subitem in item:
if type(subitem) == bytes:
print('''%s[%s],''' % (
' ' * (indent + 2),
chunk(hexlify(subitem)),
))
else:
raise ValueError('Invalid sublist type(%s): %s' % (name, type(subitem)))
print('''%s],''' % (
' ' * (indent + 1)
))
else:
raise ValueError('Invalid list type(%s): %s' % (name, type(item)))
print('''%s],''' % (
pad,
))
else:
raise ValueError('Invalid type(%s): %s' % (name, type(value)))
def tv_rust(filename, parts, vectors):
print(' struct TestVector {')
for p in parts: print(' %s: %s,' % (p[0], p[1]['rust_type']))
print(''' };
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/%s.py''' % (
filename,
))
if type(vectors) == type({}):
print(' let test_vector = TestVector {')
for p in parts: tv_part_rust(p[0], vectors[p[0]], p[1])
print(' };')
elif type(vectors) == type([]):
print(' let test_vectors = vec![')
for vector in vectors:
print(' TestVector {')
for p in parts: tv_part_rust(p[0], vector[p[0]], p[1], 4)
print(' },')
print(' ];')
else:
raise ValueError('Invalid type(vectors)')
#
# Rendering functions
#
def render_args():
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', choices=['zcash', 'json', 'rust'], default='rust')
return parser.parse_args()
def render_tv(args, filename, parts, vectors):
# Convert older format
parts = [(p[0], p[1] if type(p[1]) == type({}) else {'rust_type': p[1]}) for p in parts]
if args.target == 'rust':
tv_rust(filename, parts, vectors)
elif args.target == 'zcash':
tv_json(filename, parts, vectors, True)
elif args.target == 'json':
tv_json(filename, parts, vectors, False)