No Such lAbs releases V for Victory.
The idea behind this is discussed in an older post to the Bitcoin Foundation's mailing list :
The V-genesis patch is a most important milestone in the evolution of computer programming, from its troglodyte state prior to the involvement of The Most Serene Republic, towards its civilised state as you enjoy it today.
For the first time in the century or so of history of this particular human endeavour, text was deliberately structured with due consideration given not only to its meaning, but also to its source, and to its context. Prior attempts at structuring software, at first consisting of a naive approach focused on meaning only, over time added a half-hearted consideration of context, very unequally and rather haphazardly. The change the V-genesis introduced is exactly like the move from understanding and controlling movement in terms of mass and somewhat velocity, such as it occurs in the mind of a monkey throwing rocks, to understanding and controlling movement in terms of mass, impulse and energy, such as it occurs in the launching of satellites. The proposition that there is still room for improvement in the endless march of human thought, on this topic as on any other, is absolutely indubitable. The proposition that the sum total of what came before can be compared with this particular advance is strictly ridiculous.
Used together with specialised scripts, V-genesis allows an agent to reconstruct a complete Bitcoin tree, verify its correctness, and manage his investment of trust at all junctures so that he is never required to implicitly trust either an unknown code author, or a code snippet of unknown provenance.
There remains after today no alternative manner to deploy Bitcoin software, or indeed any software that is not a toy intended to be used by children playing, outside of this paradigm. May the switchover be bloody and painful in all the right places.
The engine of the entire thing is quoted below, for your viewing pleasure :
#!/usr/bin/python
##############################################################################
# Quick Intro:
# 1) Create '.wot' in your home directory. Fill it with public keys from 'wot'.
# 2) Create '.seals' in your home directory. Place all signatures there from
'sigs'.
# 3) Create a 'patches' directory somewhere where 'v' can find it. Or use this
one.
# 4) ./v.py patches command
# e.g.,
# ./v.py patches w
# ^^ displays WoT
# ./v.py patches p patches/asciilifeform_add_verifyall_option.vpatch
asciis_bleedingedge
# ^^ this 'presses' (creates the actual tree)
# ^^ approximately like a 'checkout' in your vanilla flavoured
shithub.
##############################################################################
import os, sys, shutil, argparse, re, tempfile, gnupg
##############################################################################
vver = 100 # This program's Kelvin versioni.
## HOW YOU CAN HELP: ##
# * TESTS plox, ty!
#
# Report findings in #bitcoin-assets on Freenode.
##############################################################################
prolog = '''\
(C) 2015 NoSuchlAbs.
You do not have, nor can you ever acquire the right to use, copy or distribute
this software ;
Should you use this software for any purpose, or copy and distribute it to
anyone or in any manner, you are breaking the laws of whatever soi-disant
jurisdiction, and you promise to continue doing so for the indefinite future.
In any case, please always : read and understand any software ; verify any PGP
signatures that you use - for any purpose.
'''
intro = "V (ver. {0}K)\n".format(vver)
##############################################################################
def toposort(unsorted):
sorted = []
unsorted = dict(unsorted)
while unsorted:
acyclic = False
for node, edges in unsorted.items():
for edge in edges:
if edge in unsorted:
break
else:
acyclic = True
del unsorted[node]
sorted.append((node, edges))
if not acyclic:
fatal("Cyclic graph!")
return sorted
##############################################################################
verbose = False
def fatal(msg):
sys.stderr.write(msg + "\n")
exit(1)
def spew(msg):
if verbose:
print msg
# List of files in a directory, in lexical order.
def dir_files(dir):
return sorted([os.path.join(dir, fn) for fn in next(os.walk(dir))[2]])
# GPG is retarded and insists on 'keychain.'
# This will be a temp dir, because we don't do any crypto.
gpgtmp = tempfile.mkdtemp()
gpg = gnupg.GPG(gnupghome=gpgtmp)
gpg.encoding = 'utf-8'
# Known WoT public keys.
pubkeys = {}
# The subset of vpatches that are considered valid.
patches = []
# Banners (i.e. vpatches mapped to their guarantors)
banners = {}
# Roots (i.e. vpatches parented by thin air)
roots = []
# Table mapping file hash to originating vpatch
desc = {}
desc['false'] = 'false'
# Grep for diff magics, and memoize
def vpdata(path, exp, cache):
l = cache.get(path)
if not l:
l = []
patch = open(path, 'r').read()
for m in re.findall(exp, patch, re.MULTILINE):
l += [{'p':m[0], 'h':m[1]}]
cache[path] = l
return l
# Get parents of a vpatch
pcache = {}
def parents(vpatch):
parents = vpdata(vpatch, r'^--- (\S+) (\S+)$', pcache)
if not parents:
fatal("{0} is INVALID, check whether it IS a vpatch!".format(vpatch))
return parents
# Get children of a vpatch
ccache = {}
def children(vpatch):
children = vpdata(vpatch, r'^\+\+\+ (\S+) (\S+)$', ccache)
if not children:
fatal("{0} is INVALID, check whether it IS a vpatch!".format(vpatch))
# Record descendents:
for child in children:
h = child['h']
if h != 'false':
desc[h] = vpatch
return children
# It is entirely possible to have more than one root!
# ... exactly how, is left as an exercise for readers.
def find_roots(patchset):
rset = []
# Walk, find roots
for p in patchset:
if all(p['h'] == 'false' for p in parents(p)):
rset += [p]
spew("Found a Root: '{0}'".format(p))
return rset
# Get antecedents.
def get_ante(vpatch):
ante = {}
for p in parents(vpatch):
pp = desc.get(p['h']) # Patch where this appears
if not ante.get(pp):
ante[pp] = []
ante[pp] += [p['p']]
return ante
# Get descendants.
def get_desc(vpatch):
des = {}
for p in patches:
ante = get_ante(p)
if vpatch in ante.keys():
des[p] = ante[vpatch]
return des
##############################################################################
# Print name of patch and its guarantors, or 'WILD' if none known.
def disp_vp(vpatch):
seals = ', '.join(map(str, banners[vpatch]))
if seals == '':
seals = 'WILD'
return "{0} ({1})".format(vpatch, seals)
##############################################################################
# Command: WoT
def c_wot(args):
for k in pubkeys.values():
print "{0}:{1} ({2})".format(k['handle'], k['fp'], k['id'])
# Command: Flow
def c_flow(args):
for p in patches:
print disp_vp(p)
# Command: Roots.
def c_roots(args):
for r in roots:
print "Root: " + disp_vp(r)
# Command: Antecedents.
def c_ante(args):
ante = get_ante(args.query)
for p in ante.keys():
if p != 'false':
print "{0} [{1}]".format(disp_vp(p), '; '.join(map(str, ante[p])))
# Command: Descendants
def c_desc(args):
des = get_desc(args.query)
for d in des.keys():
print "Descendant: {0} [{1}]".format(disp_vp(d), '; '.join(map(str,
des[d])))
# Command: Press.
def c_press(args):
print "Pressing using head: {0} to path: '{1}'".format(args.head, args.dest)
headpos = patches.index(args.head)
seq = patches[:headpos + 1]
os.mkdir(args.dest)
for p in seq:
print "Using: {0}".format(disp_vp(p))
os.system("patch -E --dir {0} -p1 < {1}".format(args.dest, p))
print "Completed Pressing using head: {0} to path: '{1}'".format(args.head,
args.dest)
# Command: Origin.
def c_origin(args):
o = desc.get(args.query)
if o:
print disp_vp(o)
else:
print "No origin known."
##############################################################################
##############################################################################
# Command line parameter processor.
parser = argparse.ArgumentParser(description=intro, epilog=prolog)
# Print paths, etc
parser.add_argument('-v', dest='verbose', default=False,
action="store_true", help='Verbose.')
# Permit the use of patches no one has yet sealed. Use this ONLY for own dev
work!
parser.add_argument('-wild', dest='wild', default=False,
action="store_true", help='Permit wild (UNSEALED!)
vpatches.')
# Glom keyid (short fingerprint) onto every WoT handle.
parser.add_argument('-fingers', dest='fingers', default=False,
action="store_true", help='Prefix keyid to all WoT
handles.')
# Default path of WoT public keys is /home/yourusername/.wot
# This dir must exist. Alternatively, you may specify another.
parser.add_argument('--wot', dest='wot',
default=os.path.join(os.path.expanduser('~'), '.wot'),
action="store", help='Use WoT in given directory. (Default:
~/.wot)')
# Default path of the seals (PGP signatures) is /home/yourusername/.seals
# This dir must exist. Alternatively, you may specify another.
parser.add_argument('--seals', dest='seals',
default=os.path.join(os.path.expanduser('~'), '.seals'),
action="store", help='Use Seals in given directory.
(Default: ~/.seals)')
# REQUIRED: Path of directory with vpatches.
parser.add_argument('vpatches', help='Vpatch directory to operate on.
[REQUIRED]')
# REQUIRED: Command.
subparsers = parser.add_subparsers(help='Command [REQUIRED]')
parser_w = subparsers.add_parser('w', help='Display WoT.')
parser_w.set_defaults(f=c_wot)
parser_r = subparsers.add_parser('r', help='Display Roots.')
parser_r.set_defaults(f=c_roots)
parser_a = subparsers.add_parser('a', help='Display Antecedents [PATCH]')
parser_a.set_defaults(f=c_ante)
parser_a.add_argument('query', action="store", help='Patch.')
parser_d = subparsers.add_parser('d', help='Display Descendants [PATCH]')
parser_d.set_defaults(f=c_desc)
parser_d.add_argument('query', action="store", help='Patch.')
parser_l = subparsers.add_parser('f', help='Compute Flow.')
parser_l.set_defaults(f=c_flow)
parser_p = subparsers.add_parser('p', help='Press [HEADPATCH AND DESTINATION]')
parser_p.set_defaults(f=c_press)
parser_p.add_argument('head', action="store", help='Head patch.')
parser_p.add_argument('dest', action="store", help='Destionation directory.')
parser_o = subparsers.add_parser('o', help='Find Origin [SHA512]')
parser_o.set_defaults(f=c_origin)
parser_o.add_argument('query', action="store", help='SHA512 to search for.')
##############################################################################
# V cannot operate without vpatches, WoT, and Seals datasets.
def reqdir(path):
if (not (os.path.isdir(path))):
fatal("Directory '{0}' does not exist!".format(path))
return path
def main():
global verbose, pubkeys, patches, roots, banners
args = parser.parse_args()
verbose = args.verbose
# Patch and Sigs dirs
pdir = reqdir(args.vpatches)
sdir = reqdir(args.seals)
wdir = reqdir(args.wot)
spew("Using patches from:" + pdir)
spew("Using signatures from:" + sdir)
spew("Using wot from:" + wdir)
pfiles = dir_files(pdir)
sfiles = dir_files(sdir)
wfiles = dir_files(wdir)
# Build WoT from pubkeys
handle = {}
for w in wfiles:
pubkey = open(w, 'r').read()
impkey = gpg.import_keys(pubkey)
for fp in impkey.fingerprints:
handle[fp] = os.path.splitext(os.path.basename(w))[0]
for k in gpg.list_keys():
name = handle[k['fingerprint']]
if args.fingers:
name += '-' + k['keyid']
pubkeys[k['keyid']] = {'fp':k['fingerprint'],
'id':', '.join(map(str, k['uids'])),
'handle':name}
# Validate seals
for p in pfiles:
pt = os.path.basename(p)
banners[p] = []
for s in sfiles:
sig = os.path.basename(s)
# All seals must take the form patchtitle.vpatch.yourname.sig
if sig.find(pt) == 0: # substring of sig filename up through
'.vpatch'
v = gpg.verify_file(open(s, 'r'), data_filename=p)
if v.valid:
banners[p] += [pubkeys[v.key_id]['handle']]
else:
fatal("---------------------------------------------------------------------\n"
+
"WARNING: {0} is an INVALID seal for {1}
!\n".format(sig, pt) +
"Check that this user is in your WoT, and that this
key has not expired.\n" +
"Otherwise remove the invalid seal from your SEALS
directory.\n" +
"---------------------------------------------------------------------")
# Select the subset of vpatches currently in use.
for p in pfiles:
if banners.get(p) or args.wild:
patches += [p]
children(p) # Memoize.
parents(p) # Memoize.
roots = find_roots(patches)
if not roots:
fatal('No roots found!')
# Topological ordering of flow graph
l = []
for p in patches:
l += [(p, get_desc(p).keys())]
s = map(lambda x:x[0], toposort(l))
patches = s[::-1]
# Run command
args.f(args)
# Remove temporary keychain
shutil.rmtree(gpgtmp)
##############################################################################
if __name__ == '__main__' :
main()
##############################################################################
The whole shebang is available either from the aforementioned mailing list, or directly hereii. Congrats to Stan for his tireless effectuality, and always remember :
PS. The only thing this needs in terms of packages is 'python-gnupg'.iii
———- Quoth Knuth (TUGboat, Volume 11/1990, No. 4) :
The current version number for TEX is 3.1, and for METAFONT it is 2.7. If corrections are necessary, the next versions of TEX will be 3.14, then 3.141. then 3.1415. . . . , converging to the ratio of a circle's circumference to its diameter; for METAFONT the sequence will be 2.71. 2.718, . . . , converging to the base of natural logarithms. I intend to be fully responsible for all changes to these systems for the rest of my life. I will periodically study reports of apparent bugs, and I will decide whether changes need to be made. Rewards will be paid to the first finders of any true bugs, at my discretion. but I can no longer afford to double the size of the reward each year. Whenever I have created a new version, I will put it in the official master T archive which currently resides at Stanford University. At the time of my death, it is my intention that the then-current versions of 7&X and METAFONT be forever left unchanged, except that the final version numbers to be reported in the "banner" lines of the programs should become TeX, Version $\pi$ and METAFONT, Version $e$ respectively. From that moment on, all "bugs" will be permanent "features."
The man knew what he was doing. Unlike virtually anyone "involved" in computing then, or hence. [↩] - Since Automattic is a pile of imbeciles, should you wish to use this you'll have to rename the file to .tar.gz. [↩]
- Notably NOT 'gnupg'. If you have that one installed it will croak. [↩]