Open nelsonic opened 5 years ago
ipfs.dag.put
is used in these basic examples but "DAG" is never explained.
This is what you are looking for: https://github.com/ipfs/specs/tree/master/merkledag
that in turn points to: https://github.com/ipld/specs/
It's a bit of a "rabbit hole" ... a few ASCII diagrams but no real clarity or real-world examples. 😕
Let's get started:
Context:
Solution:
Exercise instructs learner to open Dev Tools:
Latest Google Chrome Browser. Thousands of errors. not very confidence inspiring ... 🙄
Mostly the same errors: Uncaught Error: already piped
, failed: WebSocket opening handshake was canceled
and Uncaught Error: first argument must be a buffer
I'm not "phased" by these errors, but a beginner definitely would be.
CIDs in dev tools console before making updates:
CIDs in dev tools console after adding the 'tags'
array to each blog post:
OK, this is a curiosity thing to understand how the cid
function works (before I go read the code...)
Question: does the CID depend on the order of the fields in the Object?
For example: at present the two blog posts are:
const treePostCid = await ipfs.dag.put({
content: "trees",
author: {"/": samCid.toBaseEncodedString()},
tags: ['outdoor', 'hobby']
})
const computerPostCid = await ipfs.dag.put({
content: "computers",
author: {"/": natCid.toBaseEncodedString()},
tags: ['hobby']
})
And the corresponding CIDs are:
post about trees: zdpuAri55PR9iW239ahcbnfkFU2TVyD5iLmqEFmwY634KZAJV
post about computers: zdpuAqaHPSosSZFRPe7u5q3yNqgg4JuvrLaUJxGamNPLhWivX
What if we change the order of the keys and data to:
const treePostCid = await ipfs.dag.put({
author: {"/": samCid.toBaseEncodedString()},
content: "trees",
tags: ['hobby', 'outdoor']
})
const computerPostCid = await ipfs.dag.put({
author: {"/": natCid.toBaseEncodedString()},
content: "computers",
tags: ['hobby']
})
CIDs are identical if after re-ordering the fields and the "tags" in the first blog post. ✅
post about trees: zdpuB1fDHR9vZQfNT1knNBtbcSedwTC9n4smYskwoumWcXvu1
post about computers: zdpuAqaHPSosSZFRPe7u5q3yNqgg4JuvrLaUJxGamNPLhWivX
This is a good sign because the content hasn't changed, just the order of the key:value in the Object. 👍
Context:
This time the order is a deciding factor in the resulting CID ... 🙄
Exercise:
Solution:
// Add your code here
return [
await ipfs.dag.put({
tag: "hobby",
posts: [
{'/': treePostCid.toBaseEncodedString() },
{'/': computerPostCid.toBaseEncodedString() }
]
}),
await ipfs.dag.put({
tag: "outdoor",
posts: [
{'/': treePostCid.toBaseEncodedString() }
]
})
]
Works:
The lesson in which the "DAG" acronym is finally expanded: DAG (Directed Acyclic Graph)
Exercise:
solution:
// Add your code here
const dogsPostCid = await ipfs.dag.put({
content: "dogs",
author: {"/": samCid.toBaseEncodedString()},
tags: ["funny", "hobby"]
})
const hobbyTagCid = await ipfs.dag.put({
tag: "hobby",
posts: [
{"/": treePostCid.toBaseEncodedString()},
{"/": computerPostCid.toBaseEncodedString()},
{"/": dogsPostCid.toBaseEncodedString()},
]
})
const funnyTagCid = await ipfs.dag.put({
tag: "funny",
posts: [
{"/": dogsPostCid.toBaseEncodedString()}
]
})
return dogsPostCid;
Step 5: Add the code I added proactively above ...
Solution:
const hobbyTagCid = await ipfs.dag.put({
tag: "hobby",
posts: [
{"/": treePostCid.toBaseEncodedString()},
{"/": computerPostCid.toBaseEncodedString()},
{"/": dogPostCid.toBaseEncodedString()}
]
})
// Add your new code here and modify the tags above
const funnyTagCid = await ipfs.dag.put({
tag: "funny",
posts: [
{"/": dogPostCid.toBaseEncodedString()}
]
})
return [outdoorTagCid, hobbyTagCid, funnyTagCid]; // not clear *HOW* to return the 3 CIDs ...
This exercise is pretty massive ...:
Fail: (I don't understand why the prev
should point to a post written by a different author...?)
Solution:
Hit a "brick wall" trying to implement the traversePosts
function:
Your function threw an error: TypeError: Cannot read property '/' of undefined.
This is basically Game Over.
Kept trying:
Calling it a day.
BEFORE: (trying too hard...)
// I added a second "list" parameter so I could write the function recursively ...
const traversePosts = async (cid, list = []) => {
console.log('cid', cid)
// Your code goes here
list.push(new CID(cid.multihash));
console.log(list);
if(list.length === 3) { return list };
let pcid, pCID;
try {
pcid = await ipfs.dag.get(cid);
console.log('pcid', pcid)
if(pcid && pcid.value && pcid.value.prev && pcid.value.prev['/']) {
pCID = new CID((pcid).value.prev['/'])
console.log('pCID', pCID)
traversePosts(pCID, list);
} else {
console.log(list);
console.log('return!!')
return list;
}
} catch (e) {
console.log('not a valid cid');
console.log(list);
console.log('return!!')
return list;
}
}
AFTER: using a while
loop:
(against my better judgement... but inspired by: https://github.com/ipfs-shipyard/proto.school/issues/68 )
const traversePosts = async (cid) => {
let list = [];
while (cid) {
list.push(cid)
const got = await ipfs.dag.get(cid)
const prev = got.value.prev
if (prev) {
cid = new CID(prev['/'])
} else {
return list
}
}
}
Can't say that was a very beginner friendly experience ... 😕 Overall I still feel IPFS has potential ... But I still have lots of questions!
Opened issue: https://github.com/ipfs-shipyard/proto.school/issues/96 to inform creators of difficulty.
@alanshaw, where should I go next to continue my IPFS learning quest? 🤔
This is super good feedback for the protoschool team thank you for documenting ❤️
It's worth checking out the examples:
I can't remember if I told you about https://awesome.ipfs.io/
I have some fun videos you can watch:
@alanshaw thanks for the links and encouragement. 👍 Looks like I have this weekend's learning list/objective sorted. 🤓😉 I have a few practical questions regarding using IPFS for "real apps" 🤔❓ (further to our verbal discussion the other day...) Where is the most effective place to post them? e.g: https://stackoverflow.com/questions/tagged/ipfs ?
Hope all is well in JailMake Land. ☀️ Still gutted we didn't get to see @olizilla 😞 Love to all! ❤️
We have a discussion place here https://discuss.ipfs.io/
We are on IRC #ipfs on freenode
@alanshaw thanks looks like I have a bit of reading to do ... https://discuss.ipfs.io/top 👍
In light of @terichadbourne's comment on https://github.com/ProtoSchool/protoschool.github.io/issues/96#issuecomment-445295786
I'm going to go through the https://proto.school from scratch again with "shoshin" ... 🤓
No change to home page: https://proto.school
First attempt: (faiulre)
I already knew how to link one item of content to another:
const run = async () => {
let cid = await ipfs.dag.put({test: 1})
return await ipfs.dag.put({ bar: {"/": cid } })
}
So I was able to "pass" this exercise:
But the following code:
return await ipfs.dag.put({ bar: cid })
Also works:
So we no longer need to have the {"/":cid}
to link items! 🎉
Relevant line of code to make this exercise pass:
return ipfs.dag.get(cid2, '/bar/test')
... on to the next one ...
Solution:
The way of linking to related content is much better! 🎉
Solution:
Solution:
// Add your code here
const hobbyTags = await ipfs.dag.put({
tag: "hobby",
posts: [
treePostCid,
computerPostCid
]
})
const outdoorTags = await ipfs.dag.put({
tag: "outdoor",
posts: [
treePostCid
]
})
return [hobbyTags, outdoorTags]
Solution:
const dogPostCid = await ipfs.dag.put({
content: "dogs",
author: samCid,
tags: ["funny", "hobby"]
})
Solution:
// Add your new code here and modify the tags above
const funnyTagCid = await ipfs.dag.put({
tag: "funny",
posts: [ dogPostCid ]
})
return [funnyTagCid, hobbyTagCid, outdoorTagCid]
Pretty straightforward:
// Modify the blog posts below
const treePostCid = await ipfs.dag.put({
content: "trees",
author: samCid,
tags: ["outdoor", "hobby"]
})
const computerPostCid = await ipfs.dag.put({
content: "computers",
author: natCid,
tags: ["hobby"],
prev: treePostCid
})
const dogPostCid = await ipfs.dag.put({
content: "dogs",
author: samCid,
tags: ["funny", "hobby"],
prev: computerPostCid
})
const outdoorTagCid = await ipfs.dag.put({
tag: "outdoor",
posts: [ treePostCid ]
})
const hobbyTagCid = await ipfs.dag.put({
tag: "hobby",
posts: [ treePostCid, computerPostCid, dogPostCid ]
})
const funnyTagCid = await ipfs.dag.put({
tag: "funny",
posts: [ dogPostCid ]
})
return dogPostCid
Solution:
const traversePosts = async (cid) => {
let list = [];
while (cid) {
list.push(cid);
cid = (await ipfs.dag.get(cid)).value.prev
}
return list;
}
Second time round it was much easier.
I don't know if a complete beginner would "reach for" a while
loop ...
But overall the way posts are linked is cleaner so I feel it's an improvement. 👍
@nelsonic I'm glad you liked the new link API and found the instructions more clear this time!
A couple of questions out of curiosity if you have a sec:
@terichadbourne I did not notice the while
loop copy ... 🙄 (pebkac...)
It's definitely there, I just didn't see it. Other beginners will. ✅
I don't know how many will automatically figure how to put the while(cid)
but
it's "enough" of a hint. 👍
The UI on the home page displays a "checkmark" whenever a lesson is complete:
Nice touch. 😉
The code editor seems really slick! noticed auto-saving, syntax checking and error notification. Great implementation. Is this a custom-built ("from scratch") editor? or are you using a 3rd party open source package? 💭
Thanks again!
@nelsonic Glad you noticed the code caching and the status indicator about whether you've completed a lesson. There are npm packages called monaco-editor
and vue-monaco-editor
that are used to create that code editor, but we recently built out the caching functionality ourselves by combining it with localStorage.
@terichadbourne nice work! 🎉
Note-to-self: https://microsoft.github.io/monaco-editor
https://proto.school
thanks to @alanshaw for sharing this!
It's Next on my "to learn" list. 🤓