├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin ├── register.js ├── scan.js └── serve.js ├── lib └── index.js ├── notary.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | bin/db/ 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Dependency directory 25 | # Commenting this out is preferred by some people, see 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | # Users Environment Variables 30 | .lock-wscript 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 William Cotton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blockname - A blockchain-backed DNS resolver 2 | 3 | This is a simple bitcoin and telehash based DNS resolver, using the blockchain as a backup cache for normal DNS resolution as well as to resolve alternative domains and custom DHT-based TLDs (completely distributed, no registrars, root servers, or central authorities). 4 | 5 | Simply publish your own hostname as a valid `OP_RETURN` output on *any* transaction with the text format `*!host.name.com11223344` (valid lower case text hostname followed by a fixed 8-char hex IP address), these are called `hint` transactions and the first byte is always the star character (`*`). The hints can be registered with any bitcoin wallet software that can include an `OP_RETURN` output on a transaction. 6 | 7 | The blockname resolver is a traditional DNS cache and recursive resolver, it will attempt to resolve all queries via regular DNS first and only when they fail will it use any names that come from the blockchain-based hints. In this mode blockname will always act as a backup for any existing valid DNS names and only provides additional resolution for unregistered domains or unsupported TLDs. 8 | 9 | In the background the resolver will continuously index all newly broadcast transactions that have a valid hints (any `OP_RETURN` starting with a `*`), storing only the unique hints that have the largest values associated with them. The value of the hint's own output (the "burned" value in satoshis) must be larger for the new hint to replace a previous one of the same name. 10 | 11 | A custom TLD is formed by designated public blockname resolvers advertising their existence to each other and building a distributed hashtable (DHT) index for a TLD from those advertisements. The DHT index is then used to dynamically resolve any names with that TLD, allowing for ephemeral and alternative uses on a custom TLD that do not require a transaction per name or traditional DNS registration. 12 | 13 | ## Status 14 | 15 | This project is at an early stage of development yet and actively evolving. 16 | 17 | It is [currently working](http://testnet.coinsecrets.org/?to=322562.000001) on testnet ([domain hint](https://www.blocktrail.com/tBTC/tx/d1bb941d7efc1fc33920a9ac48dc1e46bd1be0ebaadb29768d28aeda1736c1a3) and [host hint](https://www.blocktrail.com/tBTC/tx/3cf995487dacff844ff3c000f1d57032731e8aa6d5fb2de98b90ee14c60197b9)), and being tested/developed for the [main blockchain](https://www.blocktrail.com/BTC/tx/823d02d2689bdb1430faddc4a6c57fc0b7be23e1e56ee686c92f300d67e51390#tx_messages). 18 | 19 | These commands are working but expect them to change: 20 | 21 | ``` 22 | git clone https://github.com/quartzjer/blockname.git 23 | cd blockname 24 | npm install 25 | ``` 26 | 27 | Start a local DNS resolver (defaults to port `8053`) 28 | 29 | ``` 30 | node bin/serve.js 31 | ``` 32 | 33 | Start a process to sync and monitor the transactions on the (testnet) blockchain: 34 | 35 | ``` 36 | node bin/scan.js 37 | ``` 38 | 39 | Register your own hint on the blockchain, passing the hostname and an IP to resolve any `A` queries, or a domain and IP:port of a nameserver that will resolve that whole domain. Uses a testnet faucet service by default currently, will generate a temporary address to send funds to (run command w/ no args to see options) 40 | 41 | ``` 42 | node bin/register.js "somename.tld" 12.34.56.78 43 | ``` 44 | 45 | Now do a test resolution to the local cache server, it will check normal DNS first, then fallback to any indexed hints from the blockchain: 46 | 47 | ``` 48 | dig somename.tld @127.0.0.1 -p 8053 49 | ``` 50 | 51 | ## Plans 52 | 53 | After some more testing and docs, this will default to mainnet and become a `blocknamed` DNS resolver service and `blockname` registration command that anyone can `npm install -g blockname`. 54 | 55 | There will be a list of public blockname resolvers that can be used by anyone and a web-based registration tool and a chart of top hints in the blockchain. 56 | 57 | Explore using types of secondary "confirmation" hints to enable self-forming distributed organizations a way to help reduce potential abuse of the simple value based priority? 58 | 59 | # Hint Types 60 | 61 | ## Host Hints `*!` 62 | 63 | A host hint is a direct mapping of an exact hostname to an IP address, an answer is returned immediately to any query with the given IP. 64 | 65 | Any matching domain or TLD hints are authorative and checked first, hostname hints are only used when there is no other answer. 66 | 67 | * Any OP_RETURN starting with `*!` 68 | * followed by up to 30 valid domain name characters with any number of labels 69 | * followed by a required 8 characters of the IPv4 address in hex 70 | 71 | Examples: 72 | 73 | * `test.domain.tld` A `192.168.0.1` => hint `*!test.domain.tldc0a80001` 74 | * `test.name` A `1.2.3.4` => hint `*!test.name01020304` 75 | * `some.host.jeremie.com` A `208.68.163.251` => hint `*!some.host.jeremie.comd044a3fb` 76 | 77 | ## Domain Hints `*.` 78 | 79 | Domain hints are used to match one or more given queries to a name server IP and port. Any DNS query including or matching the suffix/domain will be forwarded to the hint's IP:port and any answers returned verbatim and cached. 80 | 81 | A domain hint is only matched if there is no TLD answer. 82 | 83 | * any OP_RETURN starting with `*.` 84 | * followed by up to 26 valid [domain name](http://en.wikipedia.org/wiki/Domain_name) characters that must include two labels (name.tld) 85 | * followed by a required 8 characters that are always the IPv4 address octets hex encoded, this address is used as the dns server to forward the query to 86 | * followed by a required 4 characters that are the port of the DNS server in hex (network byte order uint16_t) 87 | 88 | Examples: 89 | 90 | * `domain.tld` NS `192.168.0.1:53` => hint `*.domain.tldc0a800010035` 91 | * `test.name` NS `1.2.3.4:1286` => hint `*.test.name010203040506` 92 | * `jeremie.com` NS `208.68.163.251:53` => hint `*.jeremie.comd044a3fb0035` 93 | 94 | 95 | ## Name Authority Hints `*+` 96 | 97 | > work-in-progress, very rough draft 98 | 99 | A Name Authority is broadcast in a TX: 100 | 101 | * Any OP_RETURN starting with `*+` 102 | * followed by the hostname of the NA 103 | * followed by 8 hex characters, the new [Notary ID](notary.md) 104 | 105 | The NA then also Stamps this transaction, which must be published/verified out of band by the hostname (via TLS, DNS-SEC, or NA-chaining). Once the NA hint Stamp is verified, any subsequent Stamps are trusted from this NA. 106 | 107 | ## TLD Hints `*#` 108 | 109 | A TLD hint will match any query with the given root label and send a query to the DHT for that label. The hints start with a `*#` followed by the TLD label characters, then separated with a `.` from one or more hex characters of the node's location in the DHT and its IP:port. 110 | 111 | TLDs are always checked first after there is no traditional DNS answer, before checking for any domain or host hints. When creating a new blockname based TLD, care should be taken not to create conflicts with any [existing or proposed](http://en.wikipedia.org/wiki/List_of_Internet_top-level_domains) names. 112 | 113 | * `*#tld.dht010203040506` 114 | * DHT is the hex prefix of hashname at the IP:port 115 | * by default include as much hex as possible, only designated would elect shorter 116 | * TLD caretakers must monitor advertised hints for abuse 117 | * DHT participating resolvers must be customized to mesh (know how to answer queries) 118 | 119 | All blockname resolvers will process TLD hints and attempt to keep a connection open to at least eight of the shortest DHT prefix hints for each unique TLD. When any query comes in matching that TLD, the name will be hashed and sent to the closest two connected hashnames which will internally process/route the query and return an answer if any. 120 | 121 | A resolver that is helping maintain a TLD must process all of its matching hints, attempting to keep connections open to a minimum number of peers in each bucket closest to its own hashname. The DHT is always seeded only by peers with valid hints, and each bucket is prioritzed by the value of the hints in that bucket. 122 | 123 | Participating resolvers may have custom internal query processing and routing logic per TLD, these customizations can only be validated by other peers in that TLD ensuring that the highest priority hints are behaving correctly. 124 | 125 | > WIP, merging [dotPublic](https://github.com/telehash/dotPublic) into here 126 | 127 | ### Well-known blockname TLDs 128 | 129 | #### `.hashname` 130 | 131 | This TLD is dedicated to resolving [hashnames](https://github.com/telehash/telehash.org/tree/master/v3/hashname) for any [telehash](http://telehash.org) based service to associate itself publicly with a known stable network location. They should only be used for services that are intended to be public (web servers, etc), private devices should never be published in an anonymous DHT. 132 | 133 | * `4w0fh69ad6d1xhncwwd1020tqnhqm4y5zbdmtqdk7d3v36qk6wbg.hashname` 134 | 135 | All hashname lookups are internally verified against the returned IP and port with a handshake to ensure authenticity before returning their information to any queries. 136 | 137 | #### `.btc` 138 | 139 | Any bitcoin address that is a public key (starts with `1`) can be resolved and verified under the `.btc` TLD. 140 | 141 | While the base58 string encoding of a bitcoin address is regularly used and would be optimal for mapping to a special TLD, normal DNS is case-insensitive and some DNS tools may not support the case-sensitive base58 encoding. In practice most DNS resolution libraries will just pass the queried hostname verbatim through to the resolver/server and the address will be preserved, so using the base58check address often works when mapping from normal DNS. When possible, the address may be converted from base58 to base32 before sending to a blockname resolver via DNS. 142 | 143 | * `16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM.btc` 144 | 145 | #### Others 146 | 147 | > TODO: decide/document on support for .onion, .bit, etc alternative TLDs, write a guide for creating a new custom TLD 148 | 149 | ## Thanks 150 | 151 | Thanks to help from [William Cotton](https://github.com/williamcotton/blockcast). 152 | -------------------------------------------------------------------------------- /bin/register.js: -------------------------------------------------------------------------------- 1 | var bitcoin = require('bitcoinjs-lib'); 2 | var opret = require('raw-op-return'); 3 | var ip = require('ip'); 4 | var level = require('level-party'); 5 | 6 | var yargs = require('yargs') 7 | .describe('db','hint storage db directory') 8 | .describe('key','WIF format secret key to use for the registration transaction') 9 | .boolean('test').describe('test','use a testnet faucet to fund the registration').default('test',true) 10 | .usage('Usage: $0 "domain" 1.2.3.4:5678 [satoshis] [refundto]') 11 | .demand(2); 12 | var argv = yargs.argv; 13 | 14 | var domain = argv._[0]; 15 | var ipp = argv._[1].split(':'); 16 | try { var server = ip.toBuffer(ipp[0]); }catch(E){} 17 | if(!server || server.length != 4) return console.error('bad ip address',argv._[1]); 18 | 19 | // check for optional port to register a NS hint 20 | if(ipp.length > 1) 21 | { 22 | var port = parseInt(ipp[1]) || 53; 23 | var ns = new Buffer(2); 24 | ns.writeUInt16BE(port,0); 25 | var hint = '*.'+domain+server.toString('hex')+ns.toString('hex'); 26 | }else{ 27 | var hint = '*!'+domain+server.toString('hex'); 28 | } 29 | 30 | if(hint.length > 40) return console.error('hint too large, name must be <32 chars:',hint); 31 | 32 | var network = argv.test?bitcoin.networks.testnet:bitcoin.networks.bitcoin; 33 | var key = argv.key ? bitcoin.ECKey.fromWIF(argv.key) : bitcoin.ECKey.makeRandom(); 34 | var address = key.pub.getAddress(network).toString(); 35 | var dbdir = argv.db || (__dirname + '/db'); 36 | var db = level(dbdir, { encoding: 'json' }); 37 | var helloblock = require('helloblock-js')({network:argv.test?'testnet':'mainnet'}); 38 | 39 | // optional value to spend on registration 40 | var value = parseInt(argv._[2]) || 1000; 41 | var refund = argv._[3] || address; 42 | 43 | console.log('using private key (WIF)',key.toWIF(),argv.test); 44 | console.log('public address requiring funds',address); 45 | console.log('registering hint `%s` to %s (OP_RETURN `%s`)', domain, ip.toString(server), hint); 46 | console.log('spending %d and sending balance to %s',value,refund); 47 | 48 | 49 | // test mode, we use a faucet to get funds first 50 | if(argv.test) 51 | { 52 | helloblock.faucet.withdraw(address, 10000, function(err, res, ret) { 53 | if(err) return console.error('faucet withdrawl failed',err); 54 | unspent(); 55 | }); 56 | 57 | }else{ 58 | // live mode, start looking for unspents 59 | unspent(); 60 | } 61 | 62 | 63 | // loop until we find enough unspents for the given address 64 | function unspent(){ 65 | helloblock.addresses.getUnspents(address, function(err, res, unspents) { 66 | if(err) return console.error('fetching unspent failed',err); 67 | var total = 0; 68 | unspents.forEach(function(utx){ 69 | total += utx.value; 70 | }); 71 | if(total < value) 72 | { 73 | console.log('not enough funds found yet, waiting...',total,value); 74 | return setTimeout(unspent,10*1000); 75 | } 76 | register(key,refund,unspents,hint); 77 | }); 78 | 79 | } 80 | 81 | 82 | // actually do the registration transaction 83 | function register(from, to, sources, hint){ 84 | console.log('performing registration'); 85 | 86 | var signTransaction = function(tx, callback) { 87 | tx.sign(0, from); 88 | callback(false, tx); 89 | }; 90 | 91 | var propagateTransaction = function(tx, callback) { 92 | helloblock.transactions.propagate(tx, function(err, res, body) { 93 | callback(err, res); 94 | }); 95 | }; 96 | 97 | // use the OP_RETURN module 98 | opret.post({ 99 | stringData: hint, 100 | address: to, 101 | fee: network.estimateFee, 102 | unspentOutputs: sources, 103 | propagateTransaction: propagateTransaction, 104 | signTransaction: signTransaction 105 | }, function(error, postedTx) { 106 | if(error) return console.error('registration error',error); 107 | console.log('registered hint', postedTx.txHash); 108 | db.put(domain,{ip:ip.toString(server),v:postedTx.totalInputsValue},function(){ 109 | process.exit(); 110 | }); 111 | }); 112 | 113 | }; 114 | 115 | -------------------------------------------------------------------------------- /bin/scan.js: -------------------------------------------------------------------------------- 1 | var argv = require('yargs') 2 | .boolean('test').describe('test', 'use testnet').default('test',true) 3 | .describe('block','starting block') 4 | .describe('db','hint storage db directory') 5 | .boolean('list').describe('list','print a list of all hints in the local db') 6 | .argv; 7 | 8 | // start from a default recent block on the right network 9 | var id = argv.block; 10 | if(!id) id = argv.test?322560:342656; 11 | var network = argv.test?'testnet':'mainnet'; 12 | var dbdir = argv.db || (__dirname + '/db'); 13 | 14 | 15 | var helloblock = require('helloblock-js')({network:network}); 16 | var opret = require('raw-op-return'); 17 | var ip = require('ip'); 18 | var level = require('level-party'); 19 | var db = level(dbdir, { encoding: 'json' }); 20 | 21 | // print out all hints 22 | if(argv.list) 23 | { 24 | var count = 0; 25 | db.createReadStream() 26 | .on('data', function (data) { 27 | count++; 28 | console.log(data.key, data.value) 29 | }) 30 | .on('end', function () { 31 | console.log(count+' hints in '+dbdir); 32 | process.exit(); 33 | }); 34 | return; 35 | } 36 | 37 | console.log('starting to scan for hints from',network,'at block',id,'into',dbdir); 38 | 39 | function setHint(name, hint) 40 | { 41 | console.log('checking hint',name); 42 | 43 | db.get(name, function(err, existing){ 44 | if(existing) 45 | { 46 | if(hint.ip == existing.ip && hint.v == existing.v) return; // ignore duplicate 47 | if(hint.v < existing.v) return console.log('existing better hint',name,existing); 48 | } 49 | console.log('saving new hint', name, hint); 50 | db.put(name, hint); 51 | }); 52 | 53 | } 54 | 55 | function getBlock() 56 | { 57 | helloblock.blocks.getTransactions(id , {limit: 1000}, function(err, res, transactions){ 58 | if(!Array.isArray(transactions) || transactions.length == 0) 59 | { 60 | console.log('waiting for block',id); 61 | setTimeout(getBlock,10*1000); 62 | return; 63 | } 64 | console.log(id,transactions.length); 65 | id++; 66 | setTimeout(getBlock,100); 67 | 68 | transactions.forEach(function(tx){ 69 | opret.scan(tx, function(err, dtx) { 70 | if(!dtx || !dtx.data) return; 71 | var opreturn = dtx.data; 72 | var value = dtx.output.value; 73 | 74 | // first character '*' 75 | if(opreturn.length < 10 || opreturn[0] != 42) return; 76 | 77 | var type = opreturn[1]; 78 | 79 | // second character '.' is domain hint ip+port 80 | if(type == 46) 81 | { 82 | // include the . in the name for the cache 83 | var domain = opreturn.slice(1,opreturn.length-12).toString(); 84 | var iphex = opreturn.slice(opreturn.length-12, opreturn.length-4).toString(); 85 | var ipbuf = new Buffer(iphex,'hex'); 86 | if(ipbuf.length != 4) return console.log('invalid ip hex'); 87 | var server = ip.toString(ipbuf); 88 | var porthex = opreturn.slice(opreturn.length-4).toString(); 89 | var portbuf = new Buffer(porthex,'hex'); 90 | if(portbuf.length != 2) return console.log('invalid port hex'); 91 | var port = portbuf.readUInt16BE(0); 92 | if(!port) return console.log('invalid port 0'); 93 | 94 | var hint = {ip:server, port:port, v:value}; 95 | return setHint(domain, hint); 96 | } 97 | 98 | // second character is a '!' hostname hint, ip only 99 | if(type == 33) 100 | { 101 | var host = opreturn.slice(2,opreturn.length-8).toString(); 102 | var iphex = opreturn.slice(opreturn.length-8).toString(); 103 | var ipbuf = new Buffer(iphex,'hex'); 104 | if(ipbuf.length != 4) return console.log('invalid ip hex'); 105 | var server = ip.toString(ipbuf); 106 | 107 | var hint = {ip:server, v:value}; 108 | return setHint(host, hint); 109 | } 110 | 111 | console.log('found unknown hint',type,opreturn.slice(2).toString('hex')); 112 | 113 | }); 114 | }) 115 | }); 116 | } 117 | 118 | getBlock() -------------------------------------------------------------------------------- /bin/serve.js: -------------------------------------------------------------------------------- 1 | var argv = require('yargs') 2 | .describe('port','listen port').default('port',8053) 3 | .describe('db','hint storage db directory') 4 | .describe('ignore','ip address to ignore any results to') 5 | .argv; 6 | 7 | var dbdir = argv.db || (__dirname + '/db'); 8 | var ignore = {}; 9 | if(argv.ignore) ignore[argv.ignore] = true; 10 | 11 | var dns = require('native-dns'); 12 | var server = dns.createServer(); 13 | var level = require('level-party'); 14 | var db = level(dbdir, { encoding: 'json' }); 15 | 16 | console.log('resolving dns requests at',argv.port,'with hints from',dbdir); 17 | 18 | // check for domain match first 19 | function getDomain(domain, cbHint) 20 | { 21 | db.get('.'+domain, function(err, hint){ 22 | cbHint(err, hint && hint.port); 23 | }); 24 | } 25 | 26 | // find any hint 27 | function getHint(name, cbHint) 28 | { 29 | // paranoid sanity 30 | name = name.toLowerCase(); 31 | if(name.substr(name.length-1) == '.') name = name.substr(0,name.length-1); 32 | 33 | // first try to get any domain matching one 34 | var labels = name.split('.'); 35 | var domain = labels.join(labels.slice(labels.length-2),'.'); 36 | getDomain(domain, function(err, hint){ 37 | if(hint) return cbHint(false, hint); 38 | 39 | // fallback get any host match 40 | db.get(name, cbHint); 41 | }); 42 | 43 | } 44 | 45 | server.on('request', function (request, response) { 46 | if(!Array.isArray(request.question) || request.question.length == 0) return; 47 | if(!dns.platform.name_servers.length) return console.log('no local name servers?'); 48 | var q = request.question[0]; 49 | 50 | // first try to resolve it normally 51 | var req = dns.Request({ 52 | question: q, 53 | server: dns.platform.name_servers[0], 54 | timeout: 2000 55 | }); 56 | 57 | var ok = false; 58 | req.on('message', function (err, answer) { 59 | if(err || answer.answer.length == 0) return; 60 | answer.answer.forEach(function (a) { 61 | if(ignore[a.address]) return; 62 | response.answer.push(a); 63 | ok = true; 64 | }); 65 | if(ok) response.send(); 66 | }); 67 | 68 | req.on('end', function () { 69 | if(ok) return console.log('ok\t',q.name); 70 | 71 | // now check for a hint to use as the nameserver 72 | getHint(q.name, function(err, hint){ 73 | if(err || !hint || !hint.ip) 74 | { 75 | console.log('n/a\t',q.name); 76 | return response.send(); 77 | } 78 | 79 | 80 | // any host hints don't have a port 81 | if(!hint.port) 82 | { 83 | console.log('host\t',q.name,hint); 84 | response.answer.push(dns.A({name: q.name, address: hint.ip, ttl: 60})); 85 | response.send(); 86 | return; 87 | } 88 | 89 | console.log('domain\t',q.name,hint); 90 | var req = dns.Request({ 91 | question: q, 92 | server: { address: hint.ip, port: hint.port, type: 'udp' }, 93 | timeout: 2000 94 | }); 95 | 96 | req.on('message', function (err, answer) { 97 | if(err || answer.answer.length == 0) return; 98 | answer.answer.forEach(function (a) { 99 | response.answer.push(a); 100 | }); 101 | }); 102 | 103 | req.on('end', function () { 104 | response.send(); 105 | }); 106 | 107 | req.send(); 108 | }); 109 | 110 | }); 111 | 112 | req.send(); 113 | }); 114 | 115 | server.on('error', function (err, buff, req, res) { 116 | console.log(err.stack); 117 | }); 118 | 119 | server.serve(argv.port); 120 | 121 | // check if the local resolver responsds to any domain already and ignore it 122 | dns.resolve('globcheck.tld',function(err,addresses){ 123 | if(Array.isArray(addresses)) addresses.forEach(function(ip){ 124 | if(ignore[ip]) return; 125 | console.log('ignoring a catch-all IP',ip); 126 | ignore[ip] = true; 127 | }); 128 | }); -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // TODO, migrate code from ../bin/*.js to a library here -------------------------------------------------------------------------------- /notary.md: -------------------------------------------------------------------------------- 1 | # Bitcoin Notary - Anonymous, Distributed, & Simple 2 | 3 | > work in progress 4 | 5 | The goal is to provide a very small and simple technique for any (anonymous) entity to "notarize" any bitcoin transaction, independent of the actual transaction itself and without requiring any public key technology. 6 | 7 | Terminology: 8 | 9 | * Notary - a sequential source of assertions / revocations of any txid (Stamps) 10 | * Stamp - a single notarization published in any transaction as an OP_RETURN 11 | 12 | A Notary is formed by always keeping/generating a new secret token that is used to encrypt the last Stamp, when a new Stamp is published it is encrpyed with a new secret token for the next one. 13 | 14 | Any other entity can establish trust starting from any Stamp and verify subsequent Stamps in the blockchain created from it by the Notary. Stamps can only be validated in order since the token links the validation "forward", the last one is always dangling and can't be verified/decrypted until the next one is known. 15 | 16 | A Stamp is a 40-byte binary OP_RETURN containing: 17 | 18 | * 4 byte Notary ID 19 | * 16 byte token 20 | * 16 byte ciphertext 21 | * 4 byte MAC 22 | 23 | The ciphertext is generated using ChaCha20, the cipher key is the SHA-256 digest of the *next* Stamp's token, and the nonce is the 4 byte Notary ID + 4 byte MAC. The encrypted 16 bytes are the first half of an existing bitcoin transaction id. 24 | 25 | The MAC is the SipHash digest output of the Notary ID, key, and the full bitcoin transaction id (52 bytes) using the same cipher key. 26 | 27 | Positive values output to the OP_RETURN are an `assertion`, 0 values are a `revocation`. 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockname", 3 | "version": "0.0.1", 4 | "description": "A simple bitcoin-based DNS cache", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/quartzjer/blockname.git" 11 | }, 12 | "keywords": [ 13 | "bitcoin", 14 | "blockchain", 15 | "dns", 16 | "decentralized", 17 | "crypto", 18 | "cryptocurrency" 19 | ], 20 | "author": "Jeremie Miller", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/quartzjer/blockname/issues" 24 | }, 25 | "homepage": "https://github.com/quartzjer/blockname", 26 | "devDependencies": { 27 | }, 28 | "dependencies": { 29 | "bitcoinjs-lib": "^1.2.0", 30 | "helloblock-js": "^0.2.5", 31 | "ip": "^0.3.2", 32 | "level-party": "^1.0.1", 33 | "native-dns": "^0.7.0", 34 | "raw-op-return": "git+https://github.com/quartzjer/raw-op-return.git", 35 | "yargs": "^1.3.3" 36 | } 37 | } 38 | --------------------------------------------------------------------------------