diff --git a/directory_hashes.js b/directory_hashes.js index 47fa8c4..6217c8f 100644 --- a/directory_hashes.js +++ b/directory_hashes.js @@ -1 +1 @@ -var ipfs_directory_hashes={"vanity_text":"soy","vanity_number":1891,"tree":{"Links":[{"Name":".ipfsignore","Hash":"QmWMeSQQxuSYMddVwJGrnn12zrX1TTW7ueZbhZdaZ64Wr1","Size":14},{"Name":"directory_hashes.js","Hash":"","Size":0},{"Name":"index.html","Hash":"QmSGG1iXXNkpLWeKmmZaqnEaUD9Thpkq3ysvzg7Y5VJVBN","Size":853},{"Name":"ipfs.js","Hash":"QmUyp8avhb7JJRuvCMccocta99vfpphp3sWRHD5JpiJVBS","Size":10701},{"Name":"ipfs_self_hash.js","Hash":"QmQNg8yy2PKKKrMbNDAfTALxrU9U1Lv7M6CX9tb5sWQFfL","Size":3099},{"Name":"lzw.js","Hash":"QmP2CsTDmyR9h5UrXJgXGswyMhiSHBZkQKLszdgh9Eb8Rk","Size":2034},{"Name":"quine.js","Hash":"QmcHWTtgH53KkaSsWEvTxKDuuTNxipC3iJyebS3BBuF21B","Size":3099},{"Name":"s","Hash":"QmZ1aMjVWAowuu7jYXU2uYLTwDiKSnh5yCeHTenP3ctFd6","Size":949},{"Name":"sha256.js","Hash":"QmciGVnQsvn5TFczbrte6JkZ81r9s1MXgFQW1kv2ZXErpM","Size":8477},{"Name":"update_directory_hashes.sh","Hash":"QmePjEEJnG6N569H3Fn9DayRHBHrkhvdWwZaoJtnKkf9Bt","Size":1113},{"Name":"update_quine.sh","Hash":"QmVZY9bqdiopA4BaxNRwJr5AWLbE8ikb19dAJoKCNENNez","Size":3029}],"Data":"\b\u0001"}}; +var ipfs_directory_hashes={"vanity_text":"soy","vanity_number":10448,"tree":{"Links":[{"Name":".ipfsignore","Hash":"QmWMeSQQxuSYMddVwJGrnn12zrX1TTW7ueZbhZdaZ64Wr1","Size":14},{"Name":"directory_hashes.js","Hash":"","Size":0},{"Name":"find_vanity.js","Hash":"QmYcaVuBSCY3CbuXut23kQYpKHN6HuDG6F8mAHeRifdmXZ","Size":229},{"Name":"index.html","Hash":"QmZaXPFa47VxJNsNcaejw7R9GNPGCF5FjkSCDrvrmBAeP9","Size":553},{"Name":"lzw.js","Hash":"QmP2CsTDmyR9h5UrXJgXGswyMhiSHBZkQKLszdgh9Eb8Rk","Size":2034},{"Name":"micro_ipfs.js","Hash":"Qmefnv6QiZqcqAEh8G1gsVQi4hAoXxwkXjWxL3jxjx1Kk7","Size":15370},{"Name":"quine.js","Hash":"QmcHWTtgH53KkaSsWEvTxKDuuTNxipC3iJyebS3BBuF21B","Size":3099},{"Name":"sha256.js","Hash":"QmRhgx5Fq4JqfCgsPcMxNSYwt8M9WRBkec9omPWzJ7gdwL","Size":8553},{"Name":"update_directory_hashes.sh","Hash":"QmYmJcAQpxWb4TbLw2UwM92w6sVfrQMCKitgn8Qg62T8zU","Size":1473},{"Name":"update_quine.sh","Hash":"QmVZY9bqdiopA4BaxNRwJr5AWLbE8ikb19dAJoKCNENNez","Size":3029}],"Data":"\b\u0001"}}; if (typeof module != 'undefined') { module.exports = { ipfs_directory_hashes: ipfs_directory_hashes }; } diff --git a/find_vanity.js b/find_vanity.js new file mode 100644 index 0000000..04bc106 --- /dev/null +++ b/find_vanity.js @@ -0,0 +1,3 @@ +const directory_hashes = require('./directory_hashes.js'); +const micro_ipfs = require('./micro_ipfs.js'); +console.log(micro_ipfs.ipfs_self_hash.find_vanity_node(null, 'soy', 0, directory_hashes.ipfs_directory_hashes)); \ No newline at end of file diff --git a/index.html b/index.html index e279547..029c23a 100644 --- a/index.html +++ b/index.html @@ -8,17 +8,12 @@ - - + diff --git a/ipfs_self_hash.js b/ipfs_self_hash.js deleted file mode 100644 index 1b10de1..0000000 --- a/ipfs_self_hash.js +++ /dev/null @@ -1,73 +0,0 @@ -var ipfs_self_hash = (function() { - var ipfs = micro_ipfs; - var get_root_with_vanity = function(vanity_attempt) { - var find_link_entry = function() { - for (var i = 0; i < ipfs_directory_hashes.tree.Links.length; i++) { - if (ipfs_directory_hashes.tree.Links[i].Name == 'directory_hashes.js') { - return i; - } - } - } - var foo_link_entry = find_link_entry(); - ipfs_directory_hashes.tree.Links[foo_link_entry].Hash = ""; - ipfs_directory_hashes.tree.Links[foo_link_entry].Size = 0; - ipfs_directory_hashes.vanity_number = vanity_attempt; - - // TODO: using JSON.stringify to recreate the file is more brittle, better store the stringified version as a hex string, and then decode it? - var file_directory_hashes = 'var ipfs_directory_hashes=' + JSON.stringify(ipfs_directory_hashes) + ';\n'; - var foo = ipfs.hashWithLinks(16, { - "Links": [], - "isFile": true, - "File": ipfs.utf8StringToHex(file_directory_hashes) - }); - - ipfs_directory_hashes.tree.Links[foo_link_entry].Hash = foo.hash; - ipfs_directory_hashes.tree.Links[foo_link_entry].Size = foo.block.length; - - root = ipfs.hashWithLinks(32, ipfs_directory_hashes.tree); - return root; - } - - var expected_vanity_attempt = 32*32*32; - var max_vanity_attempt = expected_vanity_attempt*10; - function find_vanity(old_root, vanity_text, vanity_attempt, callback) { - var root = get_root_with_vanity(vanity_attempt); - console.log(root.hash, vanity_attempt, vanity_text); - if (vanity_attempt > max_vanity_attempt) { - // give up: - root = get_root_with_vanity(ipfs_directory_hashes.vanity_number) - callback(root, 'timeout'); - } else { - if (root.hash[root.hash.length-1] == vanity_text[2]) { - callback(old_root, '… ' + vanity_attempt + ' (' + Math.floor(100*vanity_attempt/expected_vanity_attempt) + '%)'); - if (root.hash[root.hash.length-2] == vanity_text[1] && root.hash[root.hash.length-3] == vanity_text[0]) { - callback(root, vanity_attempt); - } else { - window.setTimeout(function() { find_vanity(old_root, vanity_text, vanity_attempt + 1, callback); }, 0); - } - } else { - window.setTimeout(function() { find_vanity(old_root, vanity_text, vanity_attempt + 1, callback); }, 0); - } - } - } - - function main(show_link) { - console.log('ipfs_self_hash a'); - var root = get_root_with_vanity(ipfs_directory_hashes.vanity_number); - var vanity_text = ipfs_directory_hashes.vanity_text; - - console.log('ipfs_self_hash b'); - if (root.hash[root.hash.length-1] == vanity_text[2] && root.hash[root.hash.length-2] == vanity_text[1] && root.hash[root.hash.length-3] == vanity_text[0]) { - // vanity check is ok - console.log('ipfs_self_hash c'); - show_link(root, ipfs_directory_hashes.vanity_number); - } else { - // Brute-force to try to find a number that gives the desired last 3 characters - console.log('ipfs_self_hash d'); - show_link(root, '…'); - find_vanity(root, vanity_text, 0, show_link); - } - } - - return main; -})(); \ No newline at end of file diff --git a/ipfs.js b/micro_ipfs.js similarity index 69% rename from ipfs.js rename to micro_ipfs.js index 4fedd34..60d0a35 100644 --- a/ipfs.js +++ b/micro_ipfs.js @@ -1,3 +1,6 @@ +// for nodejs +if (typeof sha256 == 'undefined' && typeof require != 'undefined') { try { sha256 = require('./sha256.js').sha256; } catch (e) {console.log(e);} } + var micro_ipfs = (function() { var hexVarintToInteger = function(str) { var s = String(str); @@ -16,7 +19,7 @@ var micro_ipfs = (function() { offset *= Math.pow(2,7); } return total; - } + }; var hexStringToIntegerList = function(str) { var s = String(str); @@ -25,7 +28,7 @@ var micro_ipfs = (function() { result[i/2] = parseInt(s.substring(i, i+2), 16); } return result; - } + }; var hexStringToString = function(str) { var s = String(str); @@ -34,7 +37,7 @@ var micro_ipfs = (function() { result += String.fromCharCode(parseInt(s.substring(i, i+2), 16)); } return result; - } + }; var sha256IntegerListToMultihash = function(base, lst) { // 0x20 is the length of the hash. @@ -51,7 +54,7 @@ var micro_ipfs = (function() { result[j+i] = lst[j]; } return result; - } + }; var integerListToLowercaseBase16Multibase = function(lst) { var result = ''; @@ -61,7 +64,7 @@ var micro_ipfs = (function() { result += hex; } return 'f' + result; - } + }; var int8ListToBitList = function(lst) { var result = []; @@ -76,7 +79,7 @@ var micro_ipfs = (function() { result[i*8+7] = (lst[i] & 1) ? 1 : 0; } return result; - } + }; var base32StringToBitList = function(str) { var baseChars = 'abcdefghijklmnopqrstuvwxyz234567'; @@ -94,7 +97,7 @@ var micro_ipfs = (function() { result[i*5+4] = (part & 1) ? 1 : 0; } return result; - } + }; // https://gist.github.com/diafygi/90a3e80ca1c2793220e5/, license: wtfpl var from_b58 = function(S,A){var d=[],b=[],i,j,c,n;for(i in S){j=0,c=A.indexOf(S[i]);if(c<0)return undefined;c||b.length^i?i:b.push(0);while(j in d||c){n=d[j];n=n?n*58+c:c;c=n>>8;d[j]=n%256;j++}}while(j--)b.push(d[j]);return new Uint8Array(b)}; @@ -109,7 +112,7 @@ var micro_ipfs = (function() { result += hex; } return result; - } + }; var integerListToLowercaseBase32Multibase = function(lst) { var baseChars = 'abcdefghijklmnopqrstuvwxyz234567'; @@ -122,7 +125,7 @@ var micro_ipfs = (function() { result += baseChars[part]; } return 'b' + result; - } + }; var base32StringToBase16LowercaseMultibase = function(str) { var baseChars = '0123456789abcdef'; @@ -135,7 +138,7 @@ var micro_ipfs = (function() { result += baseChars[part]; } return 'f' + result; - } + }; var integerToHexVarint = function(i) { // This function takes a JavaScript integer and returns a hexadecimal string representing that integer encoded as a protobuf varint according to the rules explained at @@ -171,7 +174,7 @@ var micro_ipfs = (function() { result += hex; } return result; - } + }; var ipfsBlockWithLinks = function(object) { // object should be an { "Links": links, "Data": hex-encoded string } object @@ -277,7 +280,7 @@ var micro_ipfs = (function() { result += encodedData; return result; - } + }; var ipfsHashWithLinks = function(base, object) { var block = hexStringToIntegerList(ipfsBlockWithLinks(object)); @@ -290,10 +293,104 @@ var micro_ipfs = (function() { } else { return { "hash" : integerListToLowercaseBase32Multibase(hash), "block" : block }; } - } + }; return { utf8StringToHex: utf8StringToHex, hashWithLinks: ipfsHashWithLinks - } -})(); \ No newline at end of file + }; +})(); + +var ipfs_self_hash = (function() { + var ipfs = micro_ipfs; + var get_root_with_vanity = function(vanity_attempt, ipfs_directory_hashes) { + var find_link_entry = function() { + for (var i = 0; i < ipfs_directory_hashes.tree.Links.length; i++) { + if (ipfs_directory_hashes.tree.Links[i].Name == 'directory_hashes.js') { + return i; + } + } + } + var foo_link_entry = find_link_entry(); + ipfs_directory_hashes.tree.Links[foo_link_entry].Hash = ""; + ipfs_directory_hashes.tree.Links[foo_link_entry].Size = 0; + ipfs_directory_hashes.vanity_number = vanity_attempt; + + // TODO: using JSON.stringify to recreate the file is more brittle, better store the stringified version as a hex string, and then decode it? + var file_directory_hashes = 'var ipfs_directory_hashes=' + JSON.stringify(ipfs_directory_hashes) + '; if (typeof module != \'undefined\') { module.exports = { ipfs_directory_hashes: ipfs_directory_hashes }; }\n'; + var foo = ipfs.hashWithLinks(16, { + "Links": [], + "isFile": true, + "File": ipfs.utf8StringToHex(file_directory_hashes) + }); + + ipfs_directory_hashes.tree.Links[foo_link_entry].Hash = foo.hash; + ipfs_directory_hashes.tree.Links[foo_link_entry].Size = foo.block.length; + + root = ipfs.hashWithLinks(32, ipfs_directory_hashes.tree); + return root; + }; + + var expected_vanity_attempt = 32*32*32; + var max_vanity_attempt = expected_vanity_attempt*10; + function find_vanity_node(old_root, vanity_text, vanity_attempt, ipfs_directory_hashes) { + while (true) { + if (vanity_attempt > max_vanity_attempt) { + // give up: + return null; + } else { + var root = get_root_with_vanity(vanity_attempt, ipfs_directory_hashes); + if (root.hash[root.hash.length-1] == vanity_text[2] && root.hash[root.hash.length-2] == vanity_text[1]) { + console.error(vanity_attempt + ' (' + Math.floor(100*vanity_attempt/expected_vanity_attempt) + '%)'); + if (root.hash[root.hash.length-3] == vanity_text[0]) { + return vanity_attempt; + } + } + vanity_attempt++; + } + } + }; + + function find_vanity_browser(old_root, vanity_text, vanity_attempt, callback, ipfs_directory_hashes) { + var root = get_root_with_vanity(vanity_attempt, ipfs_directory_hashes); + if (vanity_attempt > max_vanity_attempt) { + // give up: + root = get_root_with_vanity(ipfs_directory_hashes.vanity_number, ipfs_directory_hashes) + callback(root, 'timeout', false); + } else { + if (root.hash[root.hash.length-1] == vanity_text[2]) { + callback(old_root, '… ' + vanity_attempt + ' (' + Math.floor(100*vanity_attempt/expected_vanity_attempt) + '%)', false); + if (root.hash[root.hash.length-2] == vanity_text[1] && root.hash[root.hash.length-3] == vanity_text[0]) { + callback(root, vanity_attempt, true); + } else { + window.setTimeout(function() { find_vanity_browser(old_root, vanity_text, vanity_attempt + 1, callback, ipfs_directory_hashes); }, 0); + } + } else { + window.setTimeout(function() { find_vanity_browser(old_root, vanity_text, vanity_attempt + 1, callback, ipfs_directory_hashes); }, 0); + } + } + }; + + var debug = function(show_link) { + var root = get_root_with_vanity(ipfs_directory_hashes.vanity_number, ipfs_directory_hashes); + var vanity_text = ipfs_directory_hashes.vanity_text; + + if (root.hash[root.hash.length-1] == vanity_text[2] && root.hash[root.hash.length-2] == vanity_text[1] && root.hash[root.hash.length-3] == vanity_text[0]) { + // vanity check is ok + show_link(root, ipfs_directory_hashes.vanity_number, true); + } else { + // Brute-force to try to find a number that gives the desired last 3 characters + show_link(root, '…', false); + find_vanity_browser(root, vanity_text, 0, show_link, ipfs_directory_hashes); + } + }; + + var get_link = function get_link() { + var root = get_root_with_vanity(ipfs_directory_hashes.vanity_number, ipfs_directory_hashes); + return root.hash; + }; + + return { get_link: get_link, find_vanity_browser: find_vanity_browser, find_vanity_node: find_vanity_node }; + })(); + + if (typeof module != 'undefined') { module.exports = { micro_ipfs : micro_ipfs, ipfs_self_hash : ipfs_self_hash }; } \ No newline at end of file diff --git a/sha256.js b/sha256.js index e69b47f..2775598 100644 --- a/sha256.js +++ b/sha256.js @@ -200,3 +200,5 @@ var sha256 = (function() { } return hash; })(); + +if (typeof module != 'undefined') { module.exports = { sha256 : sha256 }; } \ No newline at end of file diff --git a/update_directory_hashes.sh b/update_directory_hashes.sh index d49eb10..79fd661 100755 --- a/update_directory_hashes.sh +++ b/update_directory_hashes.sh @@ -3,28 +3,31 @@ set -euET -o pipefail vanity_text="${1:-xyz}" -vanity_number="${2:-0}" -directory="${3:-.}" +directory="${2:-.}" temp_file="$(mktemp)" hexdump="$(mktemp)" -if test -z "$directory" -o "$directory" = "-h" -o "$directory" = "--help"; then - echo 'Usage: ./update-hashes.sh vanity-text vanity-number [path/to/directory]' +if test -z "$vanity_text" -o "$vanity_text" = "-h" -o "$vanity_text" = "--help"; then + echo 'Usage: ./update-hashes.sh vanity-text [path/to/directory]' echo 'The given directory should contain a file named meta.js, which will be overwritten.' + echo 'The vanity text should be three letters, which will appear at the end of your website'\''s hash' exit 1 fi -printf %s 'var ipfs_directory_hashes=' >> "$temp_file" - # TODO: use ipfs dag get instead of ipfs object get partial_hash="$(ipfs add --ignore-rules-path .ipfsignore --pin=false --hidden -Qr "$directory")" -ipfs object get "$partial_hash" \ -| jq '.Links |= map(if .Name == "directory_hashes.js" then { "Name": .Name, "Hash": "", "Size": 0 } else . end)' \ -| jq -r '{vanity_text:"'"$vanity_text"'", vanity_number:'$vanity_number',tree:.} | tostring' >> "$temp_file" -sed -i -e 's/$/;/' "$temp_file" +foo="$(ipfs object get "$partial_hash" | jq '.Links |= map(if .Name == "directory_hashes.js" then { "Name": .Name, "Hash": "", "Size": 0 } else . end)' )" -mv "$temp_file" "$directory"/directory_hashes.js +write_directory_hashes() { + contents="$(printf %s "$foo" | jq -r '{vanity_text:"'"$vanity_text"'", vanity_number:'$1',tree:.} | tostring')" + printf 'var ipfs_directory_hashes=%s; if (typeof module != '\''undefined'\'') { module.exports = { ipfs_directory_hashes: ipfs_directory_hashes }; }\n' "$contents" > "$directory"/directory_hashes.js +} + +write_directory_hashes "0" +vanity_number="$(node find_vanity.js)" +printf 'Found vanity number: %s\n' $vanity_number +write_directory_hashes "$vanity_number" echo "The hash given by the page should be:" -ipfs cid base32 "$(ipfs add --ignore-rules-path .ipfsignore --hidden -Qr "$directory")" +printf 'ipfs://%s\n' "$(ipfs cid base32 "$(ipfs add --ignore-rules-path .ipfsignore --hidden -Qr "$directory")")"