Skip to content

Instantly share code, notes, and snippets.

@clarenceb
Created February 9, 2018 05:43
Show Gist options
  • Select an option

  • Save clarenceb/0cccb48827ce442c775e71c58ecf7657 to your computer and use it in GitHub Desktop.

Select an option

Save clarenceb/0cccb48827ce442c775e71c58ecf7657 to your computer and use it in GitHub Desktop.
{
"%dna": {
"header": {
"type": "%dna",
"hash": "QmbUdrQoiTzKTzZiJ8BzPeeGye7BPiVUruyQZiBqXNDruk",
"time": "2018-02-08 14:54:25.976547 +1100 AEDT",
"nextHeader": "1",
"next": "%dna: 1",
"entry": "QmSMg4wV1iuvw5V6MwWTD6ppYBeJqBSWyy4M6vm5deqCQ5"
},
"content": {
"Version": 0,
"UUID": "00000000-0000-0000-0000-000000000000",
"Name": "clutter",
"Properties": {
"description": "distributed micro-blogging (Twitter clone)",
"enableDirectoryAccess": "true",
"language": "en"
},
"PropertiesSchema": "{\n\t\"title\": \"Properties Schema\",\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"description\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n\t\t\"language\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n\t\t\"enableDirectoryAccess\": {\n\t\t\t\"type\": \"string\"\n\t\t}\n\t}\n}\n",
"AgentIdentitySchema": "",
"BasedOn": {
"H": null
},
"RequiresVersion": 18,
"DHTConfig": {
"HashType": "sha2-256",
"NeighborhoodSize": 0
},
"Progenitor": {
"Identity": "",
"PubKey": null
},
"Zomes": [
{
"Name": "clutter",
"Description": "zome that implements micro-blogging",
"Code": "// ==============================================================================\n// EXPOSED Functions: visible to the UI, can be called via localhost, web browser, or socket\n// ===============================================================================\n\nfunction getProperty(name) { // The definition of the function you intend to expose\n return property(name); // Retrieves a property of the holochain from the DNA (e.g., Name, Language\n}\n\n\nfunction appProperty(name) { // The definition of the function you intend to expose\n if (name == \"App_Agent_Hash\") {return App.Agent.Hash;}\n if (name == \"App_Agent_String\") {return App.Agent.String;}\n if (name == \"App_Key_Hash\") {return App.Key.Hash;}\n if (name == \"App_DNA_Hash\") {return App.DNA.Hash;}\n return \"Error: No App Property with name: \" + name;\n}\n\nfunction follow(userAddress) {\n // Expects a userAddress hash of the person you want to follow\n var me = getMe(); // Looks up my hash address and assign it to 'me'\n\n // Commits a new follow entry to my source chain\n // On the DHT, puts a link on their hash to my hash as a \"follower\"\n // On the DHT, puts a link on my hash to their hash as a \"following\"\n return commit(\"follow\",\n {Links:[\n {Base:userAddress,Link:me,Tag:\"followers\"},\n {Base:me,Link:userAddress,Tag:\"following\"}\n ]});\n\n\n //return User.withHash(getMe()).follow(userAddress);\n}\n\nfunction unfollow(userAddress){\n var me = getMe();\n return commit(\"unfollow\", // On my source chain, commits the unfollow entry\n {Links:[\n {Base:userAddress,Link:me,Tag:\"followers\",LinkAction:HC.LinkAction.Del},\n {Base:me,Link:userAddress,Tag:\"following\",LinkAction:HC.LinkAction.Del}\n ]});\n\n //return User.withHash(userAddress).unfollow(getMe());\n}\n\nfunction post(post) {\n var key = commit(\"post\",post); // Commits the post block to my source chain, assigns resulting hash to 'key'\n var me = getMe(); // Looks up my hash address and assign it to 'me'\n // which DHT nodes will use to request validation info from my source chain\n\n // On the DHT, puts a link on my hash to the new post\n commit(\"post_links\",{Links:[{Base:me,Link:key,Tag:\"post\"}]});\n\n debug(\"meta: \"+JSON.stringify(getLinks(me,\"post\",{Load:true})));\n debug(key);\n return key; // Returns the hash key of the new post to the calling function\n\n\n //return User.withHash(getMe()).createPost(post);\n}\n\nfunction postMod(params) {\n var hash = params.hash;\n var post = params.post;\n // TODO, update the original link too?\n return update(\"post\",post,hash);\n\n // User.withHash(getMe()).updatePost(hash, post)\n}\n\nfunction getPost(params) {\n var post, rawPost = get(params.postHash,{GetMask:HC.GetMask.All});\n if (isErr(rawPost)) {\n return rawPost;\n } else {\n post = {\n post: JSON.parse(rawPost.Entry),\n author: rawPost.Sources[0],\n H: params.postHash\n };\n return post;\n }\n\n // Post.withHash(params.postHash).get();\n}\n\n// TODO add \"last 10\" or \"since timestamp\" when query info is supported\nfunction getPostsBy(userAddresses) {\n // From the DHT, gets all \"post\" metadata entries linked from this userAddress\n var posts = [];\n for (var i=0;i\u003cuserAddresses.length;i++) {\n var author = userAddresses[i];\n var authorPosts = doGetLinkLoad(author,\"post\");\n // add in the author\n for(var j=0;j\u003cauthorPosts.length;j++) {\n var post = authorPosts[j];\n post.author = author;\n posts.push(post);\n }\n }\n return posts;\n}\n\n// get a list of all the people from the DHT a user is following or follows\nfunction getFollow(params) {\n var type = params.type;\n var base = params.from;\n var result = {};\n if ((type == \"followers\") || (type == \"following\")) {\n result[\"result\"] = doGetLink(base,type);\n }\n else {\n result[\"error\"] = \"bad type: \"+type;\n }\n return result;\n}\n\nfunction newHandle(handle){\n var me = getMe();\n var directory = getDirectory();\n var handles = doGetLink(me,\"handle\");\n var n = handles.length - 1;\n if (n \u003e= 0) {\n var oldKey = handles[n];\n var key = update(\"handle\",handle,oldKey);\n\n debug(\"new handle: \"+handle+\" is \"+key);\n debug(\"old handle: was \"+oldKey);\n commit(\"handle_links\",\n {Links:[\n {Base:me,Link:oldKey,Tag:\"handle\",LinkAction:HC.LinkAction.Del},\n {Base:me,Link:key,Tag:\"handle\"}\n ]});\n commit(\"directory_links\",\n {Links:[\n {Base:directory,Link:oldKey,Tag:\"handle\",LinkAction:HC.LinkAction.Del},\n {Base:directory,Link:key,Tag:\"handle\"}\n ]});\n return key;\n }\n return addHandle(handle);\n}\n\n// returns a map of user keys to handles\nfunction getHandles() {\n if (property(\"enableDirectoryAccess\") != \"true\") {\n return undefined;\n }\n var directory = getDirectory();\n var links = getLinks(directory, \"handle\",{Load:true});\n if (isErr(links)) {\n links = [];\n } else {\n links = links;\n }\n var handles = {};\n for (var i=0;i \u003clinks.length;i++) {\n handles[links[i].Source] = links[i].Entry;\n }\n return handles;\n}\n\n// returns the handle of an agent by looking it up on the user's DHT entry, the last handle will be the current one?\nfunction getHandle(userHash) {\n var handles = doGetLinkLoad(userHash,\"handle\");\n var n = handles.length -1;\n var h = handles[n];\n return (n \u003e= 0) ? h.handle : \"\";\n}\n\n// returns the agent associated agent by converting the handle to a hash\n// and getting that hash's source from the DHT\nfunction getAgent(handle) {\n var directory = getDirectory();\n var handleHash = makeHash(\"handle\",handle);\n var sources = get(handleHash,{GetMask:HC.GetMask.Sources});\n if (isErr(sources)) {debug(sources);sources = [];}\n if (sources != undefined) {\n var n = sources.length -1;\n return (n \u003e= 0) ? sources[n] : \"\";\n }\n return \"\";\n}\n\n// ==============================================================================\n// HELPERS: unexposed functions\n// ==============================================================================\n\n// helper function to resolve which has will be used as \"me\"\nfunction getMe() {return App.Key.Hash;}\n\n// helper function to resolve which hash will be used as the base for the directory\n// currently we just use the DNA hash as our entry for linking the directory to\n// TODO commit an anchor entry explicitly for this purpose.\nfunction getDirectory() {return App.DNA.Hash;}\n\n\n// helper function to actually commit a handle and its links on the directory\n// this function gets called at genesis time only because all other times handle gets\n// updated using newHandle\nfunction addHandle(handle) {\n // TODO confirm no collision\n var key = commit(\"handle\",handle); // On my source chain, commits a new handle entry\n var me = getMe();\n var directory = getDirectory();\n\n debug(handle+\" is \"+key);\n\n commit(\"handle_links\", {Links:[{Base:me,Link:key,Tag:\"handle\"}]});\n commit(\"directory_links\", {Links:[{Base:directory,Link:key,Tag:\"handle\"}]});\n\n return key;\n}\n\n// helper function to determine if value returned from holochain function is an error\nfunction isErr(result) {\n return ((typeof result === 'object') \u0026\u0026 result.name == \"HolochainError\");\n}\n\n// helper function to do getLinks call, handle the no-link error case, and copy the returned entry values into a nicer array\nfunction doGetLinkLoad(base, tag) {\n // get the tag from the base in the DHT\n var links = getLinks(base, tag,{Load:true});\n if (isErr(links)) {\n links = [];\n } else {\n links = links;\n }\n var links_filled = [];\n for (var i=0;i \u003clinks.length;i++) {\n var link = {H:links[i].Hash};\n link[tag] = links[i].Entry;\n links_filled.push(link);\n }\n return links_filled;\n}\n\n// helper function to call getLinks, handle the no links entry error, and build a simpler links array.\nfunction doGetLink(base,tag) {\n // get the tag from the base in the DHT\n var links = getLinks(base, tag,{Load:false});\n if (isErr(links)) {\n links = [];\n }\n else {\n links = links;\n }\n debug(\"Links:\"+JSON.stringify(links));\n var links_filled = [];\n for (var i=0;i \u003clinks.length;i++) {\n links_filled.push(links[i].Hash);\n }\n return links_filled;\n}\n\n// ==============================================================================\n// CALLBACKS: Called by back-end system, instead of front-end app or UI\n// ===============================================================================\n\n// GENESIS - Called only when your source chain is generated:'hc gen chain \u003cname\u003e'\n// ===============================================================================\nfunction genesis() { // 'hc gen chain' calls the genesis function in every zome file for the app\n\n // use the agent string (usually email) used with 'hc init' to identify myself and create a new handle\n //addHandle(App.Agent.String);\n //commit(\"anchor\",{type:\"sys\",value:\"directory\"});\n return true;\n}\n\n// ===============================================================================\n// VALIDATION functions for *EVERY* change made to DHT entry -\n// Every DHT node uses their own copy of these functions to validate\n// any and all changes requested before accepting. put / mod / del \u0026 metas\n// ===============================================================================\n\nfunction validateCommit(entry_type,entry,header,pkg,sources) {\n debug(\"validate commit: \"+entry_type);\n return validate(entry_type,entry,header,sources);\n}\n\nfunction validatePut(entry_type,entry,header,pkg,sources) {\n debug(\"validate put: \"+entry_type);\n return validate(entry_type,entry,header,sources);\n}\n\nfunction validate(entry_type,entry,header,sources) {\n if (entry_type==\"post\") {\n var l = entry.message.length;\n if (l\u003e0 \u0026\u0026 l\u003c256) {return true;}\n return false;\n }\n if (entry_type==\"handle\") {\n return true;\n }\n if (entry_type==\"follow\") {\n return true;\n }\n return true;\n}\n\n// Are there types of tags that you need special permission to add links?\n// Examples:\n// - Only Bob should be able to make Bob a \"follower\" of Alice\n// - Only Bob should be able to list Alice in his people he is \"following\"\nfunction validateLink(linkEntryType,baseHash,links,pkg,sources){\n // debug(\"validate link: \"+linkEntryType);\n if (linkEntryType==\"handle_links\") {\n var length = links.length;\n // a valid handle is when:\n\n // there should just be one or two links only\n if (length==2) {\n // if this is a modify it will have two links the first of which\n // will be the del and the second the new link.\n if (links[0].LinkAction != HC.LinkAction.Del) return false;\n if (links[1].LinkAction != HC.LinkAction.Add) return false;\n } else if (length==1) {\n // if this is a new handle, there will just be one Add link\n if (links[0].LinkAction != HC.LinkAction.Add) return false;\n } else {return false;}\n\n for (var i=0;i\u003clength;i++) {\n var link = links[i];\n // the base must be this base\n if (link.Base != baseHash) return false;\n // the base must be the source\n if (link.Base != sources[0]) return false;\n // The tag name should be \"handle\"\n if (link.Tag != \"handle\") return false;\n //TODO check something about the link, i.e. get it and check it's type?\n }\n return true;\n }\n return true;\n}\nfunction validateMod(entry_type,entry,header,replaces,pkg,sources) {\n debug(\"validate mod: \"+entry_type+\" header:\"+JSON.stringify(header)+\" replaces:\"+JSON.stringify(replaces));\n if (entry_type == \"handle\") {\n // check that the source is the same as the creator\n // TODO we could also check that the previous link in the type-chain is the replaces hash.\n var orig_sources = get(replaces,{GetMask:HC.GetMask.Sources});\n if (isErr(orig_sources) || orig_sources == undefined || orig_sources.length !=1 || orig_sources[0] != sources[0]) {return false;}\n\n } else if (entry_type == \"post\") {\n // there must actually be a message\n if (entry.message == \"\") return false;\n var orig = get(replaces,{GetMask:HC.GetMask.Sources+HC.GetMask.Entry});\n\n // check that source is same as creator\n if (orig.Sources.length !=1 || orig.Sources[0] != sources[0]) {return false;}\n\n var orig_message = JSON.parse(orig.Entry).message;\n // message must actually be different\n return orig_message != entry.message;\n }\n return true;\n}\nfunction validateDel(entry_type,hash,pkg,sources) {\n debug(\"validate del: \"+entry_type);\n return true;\n}\n\n// ===============================================================================\n// PACKAGING functions for *EVERY* validation call for DHT entry\n// What data needs to be sent for each above validation function?\n// Default: send and sign the chain entry that matches requested HASH\n// ===============================================================================\n\nfunction validatePutPkg(entry_type) {return null;}\nfunction validateModPkg(entry_type) { return null;}\nfunction validateDelPkg(entry_type) { return null;}\nfunction validateLinkPkg(entry_type) { return null;}\n",
"Entries": [
{
"Name": "handle",
"DataFormat": "string",
"Sharing": "public",
"Schema": ""
},
{
"Name": "post",
"DataFormat": "json",
"Sharing": "public",
"Schema": "{\n \"title\": \"Post Schema\",\n \"type\": \"object\",\n \"properties\": {\n\t\"message\": {\n\t \"type\": \"string\"\n\t},\n\t\"stamp\": {\n\t \"type\": \"integer\"\n\t}\n },\n \"required\": [\"message\",\"stamp\"]\n}\n"
},
{
"Name": "post_links",
"DataFormat": "links",
"Sharing": "",
"Schema": ""
},
{
"Name": "handle_links",
"DataFormat": "links",
"Sharing": "",
"Schema": ""
},
{
"Name": "directory_links",
"DataFormat": "links",
"Sharing": "",
"Schema": ""
},
{
"Name": "follow",
"DataFormat": "links",
"Sharing": "",
"Schema": ""
},
{
"Name": "unfollow",
"DataFormat": "links",
"Sharing": "",
"Schema": ""
},
{
"Name": "anchor",
"DataFormat": "json",
"Sharing": "public",
"Schema": "{\n \"title\": \"Anchor Schema\",\n \"type\": \"object\",\n \"properties\": {\n\t\"type\": {\n\t \"type\": \"string\"\n\t},\n\t\"value\": {\n\t \"type\": \"string\"\n\t}\n },\n \"required\": [\"type\",\"value\"]\n}\n"
}
],
"RibosomeType": "js",
"Functions": [
{
"Name": "getProperty",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "appProperty",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "follow",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "addPost",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "unfollow",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "post",
"CallingType": "json",
"Exposure": "public"
},
{
"Name": "getPost",
"CallingType": "json",
"Exposure": "public"
},
{
"Name": "postMod",
"CallingType": "json",
"Exposure": "public"
},
{
"Name": "getPostsBy",
"CallingType": "json",
"Exposure": "public"
},
{
"Name": "newHandle",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "getHandle",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "getAgent",
"CallingType": "string",
"Exposure": "public"
},
{
"Name": "getFollow",
"CallingType": "json",
"Exposure": "public"
},
{
"Name": "getHandles",
"CallingType": "json",
"Exposure": "public"
}
],
"BridgeFuncs": null,
"BridgeTo": {
"H": null
}
}
]
}
},
"%agent": {
"header": {
"type": "%agent",
"hash": "QmTStgJhG2YWj3EhhKLpSbpgayE8nXstZG2mCx53LTDXzs",
"time": "2018-02-08 14:54:25.977046 +1100 AEDT",
"nextHeader": "QmbUdrQoiTzKTzZiJ8BzPeeGye7BPiVUruyQZiBqXNDruk",
"next": "%agent: 1",
"entry": "QmbGsfXz3D67kdWjj6qkrVNfNwCrimakWDabnVwgzPT6WJ"
},
"content": {
"Identity": "lucy",
"Revocation": null,
"PublicKey": "CAESIC1t5p0wcEB992lfdZMSEI5R0jJ7dMlVqiN7gBA40oDy"
}
},
"entries": [
{
"header": {
"type": "handle",
"hash": "QmVWwEK2TCn2KcfeiUov1Q2zRTKJkRiktKMsg4ivNXLto2",
"time": "2018-02-08 14:55:29.515272 +1100 AEDT",
"nextHeader": "QmTStgJhG2YWj3EhhKLpSbpgayE8nXstZG2mCx53LTDXzs",
"next": "handle: 1",
"entry": "QmW4jaz9N97n7Siu9eHg99Kkcx3siv3tALA3uHnShDFgYk"
},
"content": "phil"
},
{
"header": {
"type": "handle_links",
"hash": "Qmag7x4erofR3hVSYKcesazq8oYqRXbvisbRHUn5TX1WB8",
"time": "2018-02-08 14:55:29.527874 +1100 AEDT",
"nextHeader": "QmVWwEK2TCn2KcfeiUov1Q2zRTKJkRiktKMsg4ivNXLto2",
"next": "handle_links: 1",
"entry": "QmQvobj1NS87gH3bypVL5SLubQV4kuT8pRiL4yTxzkLCih"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmW4jaz9N97n7Siu9eHg99Kkcx3siv3tALA3uHnShDFgYk\",\"Tag\":\"handle\"}]}"
},
{
"header": {
"type": "directory_links",
"hash": "QmW8WPQXpZeajoitSJxXnWEXXNxDdXjYx3rL7kTXXK1trP",
"time": "2018-02-08 14:55:29.536298 +1100 AEDT",
"nextHeader": "Qmag7x4erofR3hVSYKcesazq8oYqRXbvisbRHUn5TX1WB8",
"next": "directory_links: 1",
"entry": "QmcPVsvsh2Y7GdrV8GjMGft4iGGS5SrNNpRJ6WJpENRyzj"
},
"content": "{\"Links\":[{\"Base\":\"QmSMg4wV1iuvw5V6MwWTD6ppYBeJqBSWyy4M6vm5deqCQ5\",\"Link\":\"QmW4jaz9N97n7Siu9eHg99Kkcx3siv3tALA3uHnShDFgYk\",\"Tag\":\"handle\"}]}"
},
{
"header": {
"type": "post",
"hash": "QmRMmXZ2mXBBDoyL3dXisFUUKp7HS4mBkSihu51EWaSp43",
"time": "2018-02-08 14:56:26.068295 +1100 AEDT",
"nextHeader": "QmW8WPQXpZeajoitSJxXnWEXXNxDdXjYx3rL7kTXXK1trP",
"next": "post: 1",
"entry": "QmWox6XvQ1w928NJZu7w5AwwoWnfVDskduB2zSn4nHDymN"
},
"content": "{\"message\":\"hi people\",\"stamp\":1518062186056}"
},
{
"header": {
"type": "post_links",
"hash": "QmendZgUoNekZrvbWSkLgCFQBQ3iM9Uu1m13LUgUoQy4hj",
"time": "2018-02-08 14:56:26.078456 +1100 AEDT",
"nextHeader": "QmRMmXZ2mXBBDoyL3dXisFUUKp7HS4mBkSihu51EWaSp43",
"next": "post_links: 1",
"entry": "QmbGrasgYfrPn6kswg8byYDPbPjSdytgiyrckMCTvb7wQW"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmWox6XvQ1w928NJZu7w5AwwoWnfVDskduB2zSn4nHDymN\",\"Tag\":\"post\"}]}"
},
{
"header": {
"type": "post",
"hash": "QmQhevUqqpp87S9i4yVmxrXW4qd3zyDGDvTAy6EfjAjwPX",
"time": "2018-02-08 14:59:59.228857 +1100 AEDT",
"nextHeader": "QmendZgUoNekZrvbWSkLgCFQBQ3iM9Uu1m13LUgUoQy4hj",
"next": "post: QmRMmXZ2mXBBDoyL3dXisFUUKp7HS4mBkSihu51EWaSp43",
"entry": "QmNWkcnK5ahNAj9tvYAux59PzQbj6v7bGQUCr7iiEaexTt"
},
"content": "{\"message\":\"yo man\",\"stamp\":1518062399218}"
},
{
"header": {
"type": "post_links",
"hash": "QmQLWZxAK37TyiuxonrRkY2mMYTt7PckTUeYC2y7m5A2Xa",
"time": "2018-02-08 14:59:59.239983 +1100 AEDT",
"nextHeader": "QmQhevUqqpp87S9i4yVmxrXW4qd3zyDGDvTAy6EfjAjwPX",
"next": "post_links: QmendZgUoNekZrvbWSkLgCFQBQ3iM9Uu1m13LUgUoQy4hj",
"entry": "QmT5kJrqyvawCYGxfszmAtX8tvxAtn9VhapuwXNr9WWPdx"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmNWkcnK5ahNAj9tvYAux59PzQbj6v7bGQUCr7iiEaexTt\",\"Tag\":\"post\"}]}"
},
{
"header": {
"type": "post",
"hash": "QmTXexcc99GjmvpsdY5d7Q2nLk1C7vSohzkuL4wTsuCpHx",
"time": "2018-02-08 15:37:31.733243 +1100 AEDT",
"nextHeader": "QmQLWZxAK37TyiuxonrRkY2mMYTt7PckTUeYC2y7m5A2Xa",
"next": "post: QmQhevUqqpp87S9i4yVmxrXW4qd3zyDGDvTAy6EfjAjwPX",
"entry": "QmaxsSsEi2PNvbC51Ar9HPMaZkEhDQBQ55GiieTEb63m2f"
},
"content": "{\"message\":\"wsasd\",\"stamp\":1518064651722}"
},
{
"header": {
"type": "post_links",
"hash": "QmdRfs12tUBHUyYUafiVab7JAGi5bnrAf4VZLzYwgoq4kT",
"time": "2018-02-08 15:37:31.744272 +1100 AEDT",
"nextHeader": "QmTXexcc99GjmvpsdY5d7Q2nLk1C7vSohzkuL4wTsuCpHx",
"next": "post_links: QmQLWZxAK37TyiuxonrRkY2mMYTt7PckTUeYC2y7m5A2Xa",
"entry": "QmXAxz1uME51qfJTrKSWYqGegTuStPsW5cgnNM4rRfkCcx"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmaxsSsEi2PNvbC51Ar9HPMaZkEhDQBQ55GiieTEb63m2f\",\"Tag\":\"post\"}]}"
},
{
"header": {
"type": "post",
"hash": "QmQkkrYUTHjxxboBZJJbPJAzo6nMre6axdhbvvFSnU5xAx",
"time": "2018-02-08 15:38:06.431889 +1100 AEDT",
"nextHeader": "QmdRfs12tUBHUyYUafiVab7JAGi5bnrAf4VZLzYwgoq4kT",
"next": "post: QmTXexcc99GjmvpsdY5d7Q2nLk1C7vSohzkuL4wTsuCpHx",
"entry": "QmW858tpKRmrxmdbTKiNGsd6hZ6vX4XaSw5Y6aYkuK3sxe"
},
"content": "{\"message\":\"asdadd\",\"stamp\":1518064686424}"
},
{
"header": {
"type": "post_links",
"hash": "QmTRDb4EWpNtnuuiQD4mE1rM5SphTyrjSqV8suo9TCMNvR",
"time": "2018-02-08 15:38:06.442667 +1100 AEDT",
"nextHeader": "QmQkkrYUTHjxxboBZJJbPJAzo6nMre6axdhbvvFSnU5xAx",
"next": "post_links: QmdRfs12tUBHUyYUafiVab7JAGi5bnrAf4VZLzYwgoq4kT",
"entry": "QmQXSAk7yXk2o6sCKUEeBV2aESQJtxtoq4cM8S54BmLogH"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmW858tpKRmrxmdbTKiNGsd6hZ6vX4XaSw5Y6aYkuK3sxe\",\"Tag\":\"post\"}]}"
},
{
"header": {
"type": "post",
"hash": "Qmb9GDCwAiXoyDCSHmCprfUtBcHj7bRMZf5p322R29mhwN",
"time": "2018-02-08 15:38:33.24818 +1100 AEDT",
"nextHeader": "QmTRDb4EWpNtnuuiQD4mE1rM5SphTyrjSqV8suo9TCMNvR",
"next": "post: QmQkkrYUTHjxxboBZJJbPJAzo6nMre6axdhbvvFSnU5xAx",
"entry": "QmRDD9kWBh2rUqe5EEwskE4xphfhbrzV3TdxcQCKvzgXsG"
},
"content": "{\"message\":\"asdd\",\"stamp\":1518064713235}"
},
{
"header": {
"type": "post_links",
"hash": "QmSUmYuqckqMQdpvQjQCf9ReQyYJjxrpzCr8dTDaVnmzdS",
"time": "2018-02-08 15:38:33.256121 +1100 AEDT",
"nextHeader": "Qmb9GDCwAiXoyDCSHmCprfUtBcHj7bRMZf5p322R29mhwN",
"next": "post_links: QmTRDb4EWpNtnuuiQD4mE1rM5SphTyrjSqV8suo9TCMNvR",
"entry": "QmcVVU8aVSnRDYPHbnw17KQJ41WBv7fLjaKzvjf1QdbmYh"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmRDD9kWBh2rUqe5EEwskE4xphfhbrzV3TdxcQCKvzgXsG\",\"Tag\":\"post\"}]}"
},
{
"header": {
"type": "post",
"hash": "QmV3TFhMYfiR9eBRKwWQUegktRsnafjB3HgbVpzyVXDWbA",
"time": "2018-02-08 15:39:04.626188 +1100 AEDT",
"nextHeader": "QmSUmYuqckqMQdpvQjQCf9ReQyYJjxrpzCr8dTDaVnmzdS",
"next": "post: Qmb9GDCwAiXoyDCSHmCprfUtBcHj7bRMZf5p322R29mhwN",
"entry": "QmWSwp4i76tYsQCSMQw2Ls4XwBNzATxVEsBZ6KUdm5tMqS"
},
"content": "{\"message\":\"asdasd\",\"stamp\":1518064744618}"
},
{
"header": {
"type": "post_links",
"hash": "QmYHNPuKVGMfCWv5zDggvwBNeYSfkSfVfLD7sKeoGsN1R2",
"time": "2018-02-08 15:39:04.632202 +1100 AEDT",
"nextHeader": "QmV3TFhMYfiR9eBRKwWQUegktRsnafjB3HgbVpzyVXDWbA",
"next": "post_links: QmSUmYuqckqMQdpvQjQCf9ReQyYJjxrpzCr8dTDaVnmzdS",
"entry": "QmYBH8ANmVqZZyPAeerc6ui8VWUDToJ6tYyycx2ySGNB3M"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmWSwp4i76tYsQCSMQw2Ls4XwBNzATxVEsBZ6KUdm5tMqS\",\"Tag\":\"post\"}]}"
},
{
"header": {
"type": "post",
"hash": "QmXc33VLmgo3Ty2bDzLyKsiauxAjB1FdA9dkjsdAqHYbZD",
"time": "2018-02-08 15:39:18.191326 +1100 AEDT",
"nextHeader": "QmYHNPuKVGMfCWv5zDggvwBNeYSfkSfVfLD7sKeoGsN1R2",
"next": "post: QmV3TFhMYfiR9eBRKwWQUegktRsnafjB3HgbVpzyVXDWbA",
"entry": "QmcCrrqFNRrcrRkyHnAhCpp1iUXzfY49AjzQVkCFUPQUuN"
},
"content": "{\"message\":\"asdasd\",\"stamp\":1518064758181}"
},
{
"header": {
"type": "post_links",
"hash": "QmVLUrexC9xtWEfx5svktCb6zM16vFvU8pJ8vcmCoU1nUe",
"time": "2018-02-08 15:39:18.19923 +1100 AEDT",
"nextHeader": "QmXc33VLmgo3Ty2bDzLyKsiauxAjB1FdA9dkjsdAqHYbZD",
"next": "post_links: QmYHNPuKVGMfCWv5zDggvwBNeYSfkSfVfLD7sKeoGsN1R2",
"entry": "QmYiakjcFzLQuyp6WP8Hn6wqXjMk55KxJ7mC8ak3A3daGX"
},
"content": "{\"Links\":[{\"Base\":\"QmWQVaqEayZJWnvxLtsKr1iyeTDp3s7m7TTE36HhAUTiTK\",\"Link\":\"QmcCrrqFNRrcrRkyHnAhCpp1iUXzfY49AjzQVkCFUPQUuN\",\"Tag\":\"post\"}]}"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment