Author

Topic: Transaction naming protocol (Read 2492 times)

sr. member
Activity: 461
Merit: 251
October 27, 2013, 06:44:25 AM
#1
Transaction naming protocol
I'd like to propose an extension to the identity protocol that would give a secure unique mapping from your MPK (master public key) to a short, pronounceable, memorable name.  It's simply an encoding of a transactions coordinates in the blockchain into a list of phonemes.  For example (code below), in block 264192:

tx              name
1               ~dus
2               ~mirlep
50             ~harbyr-salmev
8000         ~larryt-norbel
1000000   ~nisneb-neb-rondef

(Last two don't exist in the blockchain, but if they did, these would be their names.)

Motivation:
A very convenient key fingerprint to help with key verification.

For those weird enough to make these their commonly used name (hopefully not in meatspace), key verification is automatic.  These names are sufficiently dissimilar that problems associated with using strings as identities don't exist (I want to do some measurements still).  Using them this way leverages our monkey brain's natural ability to conceptualize social networks and reputation using memorable names.  Of course all of this assumes the monkeys can communicate over multiple independent channels so that a single operator can't replace all the names that are passed around with ones whose keys he holds.

The intention is for certain groups of weirdos to avoid needing a PKI.

Draft proposed standard:
(I'll do a nice job of writing this up if there's any interest, or after I receive some feedback.  Should it go on the wiki?)

Basic rules:
- Transactions take 100 blocks to mature to ensure their blockchain coordinates are unmalleable.
- Names expire after 262144 blocks (~5 years) because it's a good idea to periodically retire keys.
- Names are recycled after 524288 blocks (~10 years) because dead names must be freed up for future use.  Notice the 5 year safety window between name expiry and reuse.
- 8 bit names created every 2048 blocks (~once every 2 weeks) as the first transaction after the coinbase.
- 16 bit names created every 8 blocks as the second transaction after the coinbase.
- 24 bit names associated with the first 32 transactions after the coinbase (but are trumped by the shorter names).
- 32 bit names associated with the first 8192 transactions after the 32nd.
- 40 bit names associated with the first 2097152 transactions after the 8224th.
- The shorter names might create a kind of valuable "blockchain real estate" that miners could earn extra revenue from.
- Different phoneme sets (and alphabets) can simultaneously be used as long as they respect backwards-compatibility to avoid collisions.
- I used phonemes from Urbit:

    Onset phonemes:
      doz mar bin wan sam lit sig hid fid lis sog dir wac sab wis sib
      rig sol dop mod fog lid hop dar dor lor hod fol rin tog sil mir
      hol pas lac rov liv dal sat lib tab han tic pid tor bol fos dot
      los dil for pil ram tir win tad bic dif roc wid bis das mid lop
      ril nar dap mol san loc nov sit nid tip sic rop wit nat pan min
      rit pod mot tam tol sav pos nap nop som fin fon ban por wor sip
      ron nor bot wic soc wat dol mag pic dav bid bal tim tas mal lig
      siv tag pad sal div dac tan sid fab tar mon ran nis wol mis pal
      las dis map rab tob rol lat lon nod nav fig nom nib pag sop ral
      bil had doc rid moc pac rav rip fal tod til tin hap mic fan pat
      tac lab mog sim son pin lom ric tap fir has bos bat poc hac tid
      hav sap lin dib hos dab bit bar rac par lod dos bor toc hil mac
      tom dig fil fas mit hob har mig hin rad mas hal rag lag fad top
      mop hab nil nos mil fop fam dat nol din hat nac ris fot rib hoc
      nim lar fit wal rap sar nal mos lan don dan lad dov riv bac pol
      lap tal pit nam bon ros ton fod pon sov noc sor lav mat mip fap

    Coda phonemes:
      zod nec bud wes sev per sut let ful pen syt dur wep ser wyl sun
      ryp syx dyr nup heb peg lup dep dys put lug hec ryt tyv syd nex
      lun mep lut sep pes del sul ped tem led tul met wen byn hex feb
      pyl dul het mev rut tyl wyd tep bes dex sef wyc bur der nep pur
      rys reb den nut sub pet rul syn reg tyd sup sem wyn rec meg net
      sec mul nym tev web sum mut nyx rex teb fus hep ben mus wyx sym
      sel ruc dec wex syr wet dyl myn mes det bet bel tux tug myr pel
      syp ter meb set dut deg tex sur fel tud nux rux ren wyt nub med
      lyt dus neb rum tyn seg lyx pun res red fun rev ref mec ted rus
      bex leb dux ryn num pyx ryg ryx fep tyr tus tyc leg nem fer mer
      ten lus nus syl tec mex pub rym tuc fyl lep deb ber mug hut tun
      byl sud pem dev lur def bus bep run mel pex dyt byt typ lev myl
      wed duc fur fex nul luc len ner lex rup ned lec ryd lyd fen wel
      nyd hus rel rud nes hes fet des ret dun ler nyr seb hul ryl lud
      rem lys fyn wer ryc sug nys nyl lyn dyn dem lux fed sed bec mun
      lyr tes mud nyt byr sen weg fyr mur tel rep teg pec nel nev fes
      
In more detail (Rough draft!  Please chime in with any advice!):

Code:
import sys
import binascii
import hashlib

# (un)hexlify to/from unicode, needed for Python3
unhexlify = binascii.unhexlify
hexlify = binascii.hexlify
if sys.version > '3':
    unhexlify = lambda h: binascii.unhexlify(h.encode('utf8'))
    hexlify = lambda b: binascii.hexlify(b).decode('utf8')

class Swizzler(object):
    ''' Balanced Feistel network.  Operates on bit strings of even length.  Key expansion and round function based on SHA256. '''
    def __init__(self, key=None, rounds=20):
        key = unhexlify(''.zfill(64)) if key is None else key
        K = [hashlib.sha256(key).digest()]
        for i in xrange(rounds):
            K.append(hashlib.sha256((K[-1])).digest())
        self._K = K
        self.r = rounds

    def _F(self, i, x, m):
        x = self._K[i] + unhexlify(hex(x)[2:].zfill(512))
        x = int(hashlib.sha256(x).hexdigest(), 16)
        return int(x & m)

    def E(self, b):
        assert len(b) & 1 == 0
        n = len(b)/2
        m = (1 << n) - 1
        x = int(b, 2)
        R = x & m
        L = x >> n
        for i in xrange(self.r):
            tmp = R
            R = self._F(i, R, m) ^ L
            L = tmp
        return bin((R << n) + L)[2:].zfill(2*n)
         
    def D(self, b):
        assert len(b) & 1 == 0
        n = len(b)/2
        m = (1 << n) - 1
        x = int(b, 2)
        L = x & m
        R = x >> n
        for i in reversed(xrange(self.r)):
            tmp = L
            L = self._F(i, L, m) ^ R
            R = tmp
        return bin((L << n) + R)[2:].zfill(2*n)

class BlockchainNamespace(object):
    ''' Martian namespace for transactions in the Bitcoin blockchain. '''
    def __init__(self):
        self.swizzler = Swizzler()
        self.sis = ['doz', 'mar', 'bin', 'wan', 'sam', 'lit', 'sig', 'hid', 'fid', 'lis', 'sog', 'dir', 'wac', 'sab', 'wis', 'sib', 'rig', 'sol', 'dop', 'mod', 'fog', 'lid', 'hop', 'dar', 'dor', 'lor', 'hod', 'fol', 'rin', 'tog', 'sil', 'mir', 'hol', 'pas', 'lac', 'rov', 'liv', 'dal', 'sat', 'lib', 'tab', 'han', 'tic', 'pid', 'tor', 'bol', 'fos', 'dot', 'los', 'dil', 'for', 'pil', 'ram', 'tir', 'win', 'tad', 'bic', 'dif', 'roc', 'wid', 'bis', 'das', 'mid', 'lop', 'ril', 'nar', 'dap', 'mol', 'san', 'loc', 'nov', 'sit', 'nid', 'tip', 'sic', 'rop', 'wit', 'nat', 'pan', 'min', 'rit', 'pod', 'mot', 'tam', 'tol', 'sav', 'pos', 'nap', 'nop', 'som', 'fin', 'fon', 'ban', 'por', 'wor', 'sip', 'ron', 'nor', 'bot', 'wic', 'soc', 'wat', 'dol', 'mag', 'pic', 'dav', 'bid', 'bal', 'tim', 'tas', 'mal', 'lig', 'siv', 'tag', 'pad', 'sal', 'div', 'dac', 'tan', 'sid', 'fab', 'tar', 'mon', 'ran', 'nis', 'wol', 'mis', 'pal', 'las', 'dis', 'map', 'rab', 'tob', 'rol', 'lat', 'lon', 'nod', 'nav', 'fig', 'nom', 'nib', 'pag', 'sop', 'ral', 'bil', 'had', 'doc', 'rid', 'moc', 'pac', 'rav', 'rip', 'fal', 'tod', 'til', 'tin', 'hap', 'mic', 'fan', 'pat', 'tac', 'lab', 'mog', 'sim', 'son', 'pin', 'lom', 'ric', 'tap', 'fir', 'has', 'bos', 'bat', 'poc', 'hac', 'tid', 'hav', 'sap', 'lin', 'dib', 'hos', 'dab', 'bit', 'bar', 'rac', 'par', 'lod', 'dos', 'bor', 'toc', 'hil', 'mac', 'tom', 'dig', 'fil', 'fas', 'mit', 'hob', 'har', 'mig', 'hin', 'rad', 'mas', 'hal', 'rag', 'lag', 'fad', 'top', 'mop', 'hab', 'nil', 'nos', 'mil', 'fop', 'fam', 'dat', 'nol', 'din', 'hat', 'nac', 'ris', 'fot', 'rib', 'hoc', 'nim', 'lar', 'fit', 'wal', 'rap', 'sar', 'nal', 'mos', 'lan', 'don', 'dan', 'lad', 'dov', 'riv', 'bac', 'pol', 'lap', 'tal', 'pit', 'nam', 'bon', 'ros', 'ton', 'fod', 'pon', 'sov', 'noc', 'sor', 'lav', 'mat', 'mip', 'fap']
        self.dex = ['zod', 'nec', 'bud', 'wes', 'sev', 'per', 'sut', 'let', 'ful', 'pen', 'syt', 'dur', 'wep', 'ser', 'wyl', 'sun', 'ryp', 'syx', 'dyr', 'nup', 'heb', 'peg', 'lup', 'dep', 'dys', 'put', 'lug', 'hec', 'ryt', 'tyv', 'syd', 'nex', 'lun', 'mep', 'lut', 'sep', 'pes', 'del', 'sul', 'ped', 'tem', 'led', 'tul', 'met', 'wen', 'byn', 'hex', 'feb', 'pyl', 'dul', 'het', 'mev', 'rut', 'tyl', 'wyd', 'tep', 'bes', 'dex', 'sef', 'wyc', 'bur', 'der', 'nep', 'pur', 'rys', 'reb', 'den', 'nut', 'sub', 'pet', 'rul', 'syn', 'reg', 'tyd', 'sup', 'sem', 'wyn', 'rec', 'meg', 'net', 'sec', 'mul', 'nym', 'tev', 'web', 'sum', 'mut', 'nyx', 'rex', 'teb', 'fus', 'hep', 'ben', 'mus', 'wyx', 'sym', 'sel', 'ruc', 'dec', 'wex', 'syr', 'wet', 'dyl', 'myn', 'mes', 'det', 'bet', 'bel', 'tux', 'tug', 'myr', 'pel', 'syp', 'ter', 'meb', 'set', 'dut', 'deg', 'tex', 'sur', 'fel', 'tud', 'nux', 'rux', 'ren', 'wyt', 'nub', 'med', 'lyt', 'dus', 'neb', 'rum', 'tyn', 'seg', 'lyx', 'pun', 'res', 'red', 'fun', 'rev', 'ref', 'mec', 'ted', 'rus', 'bex', 'leb', 'dux', 'ryn', 'num', 'pyx', 'ryg', 'ryx', 'fep', 'tyr', 'tus', 'tyc', 'leg', 'nem', 'fer', 'mer', 'ten', 'lus', 'nus', 'syl', 'tec', 'mex', 'pub', 'rym', 'tuc', 'fyl', 'lep', 'deb', 'ber', 'mug', 'hut', 'tun', 'byl', 'sud', 'pem', 'dev', 'lur', 'def', 'bus', 'bep', 'run', 'mel', 'pex', 'dyt', 'byt', 'typ', 'lev', 'myl', 'wed', 'duc', 'fur', 'fex', 'nul', 'luc', 'len', 'ner', 'lex', 'rup', 'ned', 'lec', 'ryd', 'lyd', 'fen', 'wel', 'nyd', 'hus', 'rel', 'rud', 'nes', 'hes', 'fet', 'des', 'ret', 'dun', 'ler', 'nyr', 'seb', 'hul', 'ryl', 'lud', 'rem', 'lys', 'fyn', 'wer', 'ryc', 'sug', 'nys', 'nyl', 'lyn', 'dyn', 'dem', 'lux', 'fed', 'sed', 'bec', 'mun', 'lyr', 'tes', 'mud', 'nyt', 'byr', 'sen', 'weg', 'fyr', 'mur', 'tel', 'rep', 'teg', 'pec', 'nel', 'nev', 'fes']
        self.sis_index = {self.sis[i]:i for i in range(len(self.sis))}       
        self.dex_index = {self.dex[i]:i for i in range(len(self.dex))}
       
    def coords_to_vint16(self, tx_height, block_height, block_count):
        if block_count < block_height + 100:
            raise Exception("Transaction hasn't matured yet!")
        # Names expire after 262144 blocks (~5 years).
        if (block_count - block_height) >> 18 > 0:
            raise Exception("Transaction's name has expired!")
        if tx_height <= 0 or tx_height > 2097152:
            raise Exception('Transaction out of range!')   
        h = block_height
        t = tx_height
        # 8 bit names created every 2048 blocks (~once every 2 weeks).
        if h & 2047 == 0 and t == 1:
            # Names are recycled after 524288 blocks (~10 years).
            b = bin((h & 524287) >> 11)[2:].zfill(8)
        # 16 bit names created every 8 blocks.
        elif h & 7 == 0 and t == 2:
            b = bin((h & 524287) >> 3)[2:].zfill(16) 
        # 24 bit names created 32/block.
        elif (t-1) >> 5 == 0:
            b = bin(((t-1) << 19) + (h & 524287))[2:].zfill(24)
        # 32 bit names created 8192/block.
        elif (t-33) >> 13 == 0:
            b = bin(((t-33) << 19) + (h & 524287))[2:].zfill(32)
        # 40 bit names created 2097152/block.
        elif (t-8225) >> 21 == 0:
            b = bin(((t-8225) << 19) + (h & 524287))[2:].zfill(40)
        else:
            raise Exception
        # Randomized 1-1 mapping to minimize similarities between consecutive names.
        b = self.swizzler.E(b)
        return [int(b[8*i:8*(i+1)], 2) for i in range(len(b)/8)]
       
    def vint16_to_coords(self, vint16, block_count):
        b = ''.join([bin(n)[2:].zfill(8) for n in vint16])
        b = self.swizzler.D(b)
        n = int(b, 2)
        if len(b) == 8:
            t = 1
            h = n << 11
        elif len(b) == 16:
            t = 2
            h = n << 3
        elif len(b) == 24:
            t = (n >> 19) + 1
            h = n & 524287
        elif len(b) == 32:
            t = (n >> 19) + 33
            h = n & 524287
        elif len(b) == 40:
            t = (n >> 19) + 8225
            h = n & 524287
        assert block_count >= h + 100
        while (block_count - h) / 524288 > 0:
            h += 524288
        return (t, h)

    def encode(self, tx_height, block_height, block_count):
        v = self.coords_to_vint16(tx_height, block_height, block_count)
        name = []
        for i in range(0,len(v)-1,2):
            name.append(self.sis[v[i]] + self.dex[v[i+1]])
        if len(v) & 1 == 1:
            name.insert(abs(len(v)/2-1), self.dex[v[-1]])
        return '~' + '-'.join(name)

    def decode(self, name, block_count):
        v = []
        end = None
        for w in name.lstrip('~').split('-'):
            if len(w) == 3:
                end = w
            else:
                v.append(self.sis_index[w[:3]])
                v.append(self.dex_index[w[3:]])
        if end:
            v.append(self.dex_index[end])
        return self.vint16_to_coords(v, block_count)
Jump to: