|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import argparse |
| 4 | +import contextlib |
| 5 | +import datetime |
| 6 | +import io |
| 7 | +import json |
| 8 | +import os |
| 9 | +import sys |
| 10 | + |
| 11 | +DISABLED_OPCODES = "CAT SUBSTR LEFT RIGHT INVERT AND OR XOR 2MUL 2DIV MUL DIV MOD LSHIFT RSHIFT".split() |
| 12 | + |
| 13 | +def all_ints(*v): |
| 14 | + return all(isinstance(i, int) for i in v) |
| 15 | + |
| 16 | +@contextlib.contextmanager |
| 17 | +def ConditionalWriter(filename): |
| 18 | + f = io.StringIO() |
| 19 | + yield f |
| 20 | + data = f.getvalue() |
| 21 | + |
| 22 | + if os.path.exists(filename): |
| 23 | + old_data = open(filename, "r", encoding="utf8").read() |
| 24 | + if data == old_data: |
| 25 | + return |
| 26 | + |
| 27 | + out = open(filename, "w", encoding="utf8") |
| 28 | + out.write(data) |
| 29 | + out.close() |
| 30 | + |
| 31 | +def get_binana_info(path): |
| 32 | + data = {} |
| 33 | + if not os.path.exists(path): |
| 34 | + return data |
| 35 | + |
| 36 | + for f in os.scandir(path): |
| 37 | + if not f.is_file(): |
| 38 | + continue |
| 39 | + d = json.load(open(f, "rb")) |
| 40 | + if isinstance(d, dict) and "binana" in d: |
| 41 | + y,n,r = d["binana"] |
| 42 | + d["filename"] = f.path |
| 43 | + if all_ints(y, n, r): |
| 44 | + data[y,n,r] = d |
| 45 | + return data |
| 46 | + |
| 47 | +def gen_binana_h(data, header, depjson): |
| 48 | + script_verify_bit = 30 # count backwards; 31 is assumed unused in unit tests |
| 49 | + |
| 50 | + defines = {a: [] for a in "DEPLOYMENTS DEPLOYMENTS_SIGNET DEPLOYMENTS_REGTEST DEPLOYMENTS_GBT VERIFY_FLAGS VERIFY_FLAGS_NAMES STANDARD_VERIFY_FLAGS OPCODES OPCODE_NAMES SUCCESS_OPCODES SCRIPTERR SCRIPTERR_STRING SCRIPTERR_TEST_NAMES CONSENSUS_CHECKS POLICY_CHECKS".split()} |
| 51 | + |
| 52 | + jsoninfo = {"script_flags": [], "deployments": {}} |
| 53 | + |
| 54 | + for y,n,r in sorted(data.keys()): |
| 55 | + b = data[y,n,r] |
| 56 | + if "deployment" not in b: |
| 57 | + continue |
| 58 | + |
| 59 | + dep = b["deployment"] |
| 60 | + |
| 61 | + start = int(datetime.datetime(y,1,1,tzinfo=datetime.timezone.utc).timestamp()) |
| 62 | + timeout = int(datetime.datetime(y+10,1,1,tzinfo=datetime.timezone.utc).timestamp()) |
| 63 | + if "start" in b: |
| 64 | + start = b["start"] |
| 65 | + if "timeout" in b: |
| 66 | + timeout = b["timeout"] |
| 67 | + |
| 68 | + defines["DEPLOYMENTS"].append(f'DEPLOYMENT_{dep},') |
| 69 | + |
| 70 | + defines["DEPLOYMENTS_SIGNET"].append("consensus.vDeployments[Consensus::DEPLOYMENT_%s] = SetupDeployment{.year = %d, .number = %d, .revision = %d, .start = %d, .timeout = %d, .period=432};" % (dep, y, n, r, start, timeout)) |
| 71 | + defines["DEPLOYMENTS_REGTEST"].append("consensus.vDeployments[Consensus::DEPLOYMENT_%s] = SetupDeployment{.year = %d, .number = %d, .revision = %d, .always = true, .period=144};" % (dep, y, n, r)) |
| 72 | + |
| 73 | + jsoninfo["deployments"][dep.lower()] = { |
| 74 | + "type": "heretical", |
| 75 | + "active": True, |
| 76 | + "height": 0, |
| 77 | + "heretical": { |
| 78 | + "binana-id": "BIN-%4d-%04d-%03d" % (y,n,r), |
| 79 | + "start_time": -1, |
| 80 | + "timeout": 0x7fffffffffffffff, |
| 81 | + "period": 144, |
| 82 | + "status": "active", |
| 83 | + "status_next": "active", |
| 84 | + "since": 0 |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + defines["DEPLOYMENTS_GBT"].append('{.name="%s", .gbt_force=true},' % (dep.lower())) |
| 89 | + |
| 90 | + if b.get("scriptverify", False): |
| 91 | + jsoninfo["script_flags"].append(dep) |
| 92 | + |
| 93 | + defines["VERIFY_FLAGS"].append(f'SCRIPT_VERIFY_{dep} = (1U << {script_verify_bit}),') |
| 94 | + script_verify_bit -= 1 |
| 95 | + |
| 96 | + defines["VERIFY_FLAGS_NAMES"].append(f'{{ "{dep}", SCRIPT_VERIFY_{dep} }},') |
| 97 | + |
| 98 | + defines["STANDARD_VERIFY_FLAGS"].append(f'SCRIPT_VERIFY_{dep} |') |
| 99 | + |
| 100 | + defines["CONSENSUS_CHECKS"].append(f'if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_{dep})) flags |= SCRIPT_VERIFY_{dep};') |
| 101 | + |
| 102 | + discourage = False |
| 103 | + if b.get("scriptverify", False) and b.get("scriptverify_discourage", False): |
| 104 | + discourage = True |
| 105 | + defines["VERIFY_FLAGS"].append(f'SCRIPT_VERIFY_DISCOURAGE_{dep} = (1U << {script_verify_bit}),') |
| 106 | + script_verify_bit -= 1 |
| 107 | + |
| 108 | + defines["VERIFY_FLAGS_NAMES"].append(f'{{ "DISCOURAGE_{dep}", SCRIPT_VERIFY_DISCOURAGE_{dep} }},') |
| 109 | + |
| 110 | + defines["SCRIPTERR"].append(f'SCRIPT_ERR_DISCOURAGE_{dep},') |
| 111 | + defines["SCRIPTERR_STRING"].append(f'case SCRIPT_ERR_DISCOURAGE_{dep}: return "Reserved for {dep} soft-fork upgrade";') |
| 112 | + defines["SCRIPTERR_TEST_NAMES"].append('{ SCRIPT_ERR_DISCOURAGE_%s, "DISCOURAGE_%s" },' % (dep, dep)) |
| 113 | + defines["POLICY_CHECKS"].append('{ Consensus::DEPLOYMENT_%s, SCRIPT_VERIFY_DISCOURAGE_%s },' % (dep, dep)) |
| 114 | + |
| 115 | + if "opcodes" in b: |
| 116 | + for opcodename, opcodehexstr in b["opcodes"].items(): |
| 117 | + if opcodename not in DISABLED_OPCODES: |
| 118 | + defines["OPCODES"].append(f'OP_{opcodename} = {opcodehexstr},') |
| 119 | + defines["OPCODE_NAMES"].append(f'case OP_{opcodename}: return "OP_{opcodename}";') |
| 120 | + if discourage: |
| 121 | + defines["SUCCESS_OPCODES"].append(f'case OP_{opcodename}:') |
| 122 | + if discourage: |
| 123 | + defines["SUCCESS_OPCODES"].append(f' if (auto e = op_success_check(flags, SCRIPT_VERIFY_{dep}, SCRIPT_VERIFY_DISCOURAGE_{dep}, SCRIPT_ERR_DISCOURAGE_{dep}, serror)) return e; else break;') |
| 124 | + |
| 125 | + header.write("// Automatically generated\n") |
| 126 | + header.write("#ifndef BINANA_H\n") |
| 127 | + header.write("#define BINANA_H\n\n") |
| 128 | + for d in defines: |
| 129 | + header.write(f'#define INQ_{d} \\\n') |
| 130 | + for l in defines[d]: |
| 131 | + header.write(f' {l} \\\n') |
| 132 | + header.write("\n\n") |
| 133 | + header.write("#endif // BINANA_H\n") |
| 134 | + |
| 135 | + json.dump(jsoninfo, depjson, indent=2) |
| 136 | + |
| 137 | +def gen_binana_d(data, out): |
| 138 | + deps = sorted(data[k]["filename"] for k in data) |
| 139 | + out.write("binana.h binana.d : %s\n" % (" ".join(deps))) |
| 140 | + |
| 141 | +def main(argv): |
| 142 | + parser = argparse.ArgumentParser() |
| 143 | + parser.add_argument("defns") |
| 144 | + parser.add_argument("header") |
| 145 | + parser.add_argument("deploymentjson") |
| 146 | + args = parser.parse_args(argv) |
| 147 | + |
| 148 | + data = get_binana_info(args.defns) |
| 149 | + with ConditionalWriter(args.header) as binana_h: |
| 150 | + with ConditionalWriter(args.deploymentjson) as binana_json: |
| 151 | + gen_binana_h(data, binana_h, binana_json) |
| 152 | + |
| 153 | + #gen_binana_d(data, open("binana.d", "w", encoding="utf8")) |
| 154 | + |
| 155 | +if __name__ == "__main__": |
| 156 | + main(sys.argv[1:]) |
| 157 | + |
0 commit comments