Skip to content

Instantly share code, notes, and snippets.

@michel47
Last active June 8, 2021 08:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michel47/0ec3de732041d729cd39b347fdfa1439 to your computer and use it in GitHub Desktop.
Save michel47/0ec3de732041d729cd39b347fdfa1439 to your computer and use it in GitHub Desktop.
IRQ line with IPFS
<!DOCTYPE html><html><head>
<!--script src=js/showdown.min.js><script!-->
<meta charset="utf8">
<link href="https://cdn.statically.io/gh/iglake/cssjs/master/css/style.css" rel="stylesheet" type="text/css"/>
<link href="style.css" rel="stylesheet" type="text/css"/>
</head><body>
<div id="rendered" class="first content">You need to run
<a href=http://ipfs.io/>IPFS</a> on <a href=https://webui.ipfs.io/>127.0.0.1:8080</a>
in order to view this <a href=https://www.nodesm.tk/>site</a></div>
<br>
<div id=md style="display:block; background-color: lightblue; opacity:0.7"><!-- write your page here in markdown format -->
<a class=include href=index.txt>index.txt</a>;
</div><!-- end of page -->
<br> This page is made for the humanity 4.0 with ♡, its source file is <a href=index.txt>here</a>.
<script src=https://cdn.jsdelivr.net/npm/showdown></script>
<script src=https://cdn.jsdelivr.net/gh/iglake/js@master/dist/inc-md.js></script>
<!--
<script src=http://127.0.0.1:8088/js/inc-md.js></script>
-->
# public IRQ line for updating your ledger !
With the help of 2 mutables we have have an *decentralized* interrupt line open to the public for pull requests (a la github)
* [IRQn](http://gateway.ipfs.io/ipns/QmdGrQatgrVSFWGVy121zXycKtSPNpysnZ46k1pc8miN9U) for getting info about IRQ line parameters and keys,
* [IRQx](http://gateway.ipfs.io/ipns/QmQhpmPSoM92UB6dK29F1vpoPnfAuD4F7qcbTrFZsS3Ft7) for where to place the [playload][PL]
<!--
QmTCC7JoMG69xaSXA4MbT7MNGwbC8uvcHvexhvo2vumJJK IRQ
QmfBbcn9fz3BerM4rrQP3C4jjAUB9PSfZNqHxEzgKwsyV8 IRQ1
QmYv39RU3R7xDsEToVf1hhtkFAK9JVaGT11kxnq5J53BgY IRQ2
QmdGrQatgrVSFWGVy121zXycKtSPNpysnZ46k1pc8miN9U IRQn
QmQhpmPSoM92UB6dK29F1vpoPnfAuD4F7qcbTrFZsS3Ft7 IRQx
-->
## protocol
* [PullReq IRQ](PullReq.html)
[PL]: {{DUCK}}=!g+playload+blockRing™
#
IPFS_PATH=$HOME/.ipfs
export IPFS_PATH=/media/IPFS/MUTABLES
peerid=$(ipfs config Identity.PeerID)
name=$(fullname $peerid);
envelop="to-$(echo $name | sed -e 's/\.* /-/g' | tr '[A-Z]' '[a-z]').txt"
## https://joearms.github.io/published/2015-06-19-Mutable-Value_Chains.html
n=0
while true; do
n=$(expr $n + 1 ) # increments $n
echo // IRQ$n :
if ! ipfs files stat /keystore > /dev/null 2>&1; then
ipfs files mkdir /keystore
echo "/keystore created"
fi
rm -f $IPFS_PATH/keystore/IRQx
pubkey=$(ipfs key gen -t rsa -s 512 IRQx)
bot=$(fullname $pubkey)
echo pubkey: $pubkey
cat $IPFS_PATH/keystore/IRQx | ipfs files write --create --truncate /keystore/IRQ
privkey=$(ipfs files stat /keystore/IRQ | head -1)
echo privkey: $privkey
if false; then
cat $IPFS_PATH/keystore/IRQx | ipfs add -n -Q
ipfs files read /keystore/IRQ | ipfs add -n -Q
ipfs cat /ipfs/$privkey | ipfs add -n -Q
fi
qmspot=$(spot.pl | ipfs add -Q --pin=false --hash sha1)
IRQL="secret-code+$$"
date=$(date)
cat <<EOF > PullReq.yml
--- # Pull Request Trigger
From: $bot
To: $name
date: $date
qmspot: $qmspot
message: /ipns/$pubkey/$envelop
payload: /ipns/$pubkey/data
signature: /ipns/$pubkey/data.sig
code: $IRQL
seq: $n
...
EOF
# hash code see [^1]({{DUCK}}=!g+multiformat+ipfs+hash+type:csv)
qmirq=$(ipfs add -n -Q PullReq.yml --hash keccak-224)
trigger=$(ipfs add --pin=false -Q PullReq.yml --hash sha1)
cat <<EOF > IRQline.yml
--- # next interruption line available
From: $name
To: $bot
IRQcode: $IRQL
seq: $n
publickkey: $pubkey
payload: /ipns/$pubkey/data
signature: /ipns/$pubkey/data.sig
listener: $qmirq
trigger: $trigger
spotid: $qmspot
---
privatekey: /ipfs/$privkey
sig:
...
EOF
echo "IRQ$n's listener: $qmirq"
IRQnID=$(ipfs key list -l | grep IRQn | cut -d' ' -f1)
IRQline=$(ipfs add --pin=false -Q IRQline.yml)
# publish IRQ info
ipfs name publish -Q -t=1h --ttl=300s --allow-offline --key=IRQn /ipfs/$IRQline &
echo "trigger:\nipfs cat $trigger | ipfs add --pin=false -Q --hash keccak-224"
echo "instruction found here :\n http://0.0.0.0:8050/ipns/$IRQnID"
echo " http://ipfs.io/ipns/$IRQnID"
echo " http://ipfs.io/ipfs/$IRQline"
echo " - $IRQline" >> IRQ.yml
# block on PR
echo waiting for $qmirq
eval $(ipfs cat $qmirq | eyml)
echo 
echo "received IRQ$n (code: $code)"
if [ "x$code" != "x$IRQL" ]; then
echo "Error !\n"
exit -1
fi
echo " pulling $payload"
echo " checking $signature"
echo .
done
# note this script requires
# fullname, eyml, spot.pl, ipfs
--- # next interruption line available
From: Tammy L. Clukies
To: Ladonna F. Zella
IRQcode: secret-code+4357
seq: 1
publickkey: Qmewoop8ixQLHHaX1eYxU7qDTyPDBtGGfynT6G6igBeqzt
payload: /ipns/Qmewoop8ixQLHHaX1eYxU7qDTyPDBtGGfynT6G6igBeqzt/data
signature: /ipns/Qmewoop8ixQLHHaX1eYxU7qDTyPDBtGGfynT6G6igBeqzt/data.sig
listener:
trigger:
spotid: bafkrcfgop76sy4gnf7hw2yo25oxvaffw3qkz34i
---
privatekey: /ipfs/QmaFm3nRSo5uSiFzFM8nHWRR3wiiEDWBGdT2VeAzCNX5Q1
sig:
...

Proposed poor-man's IPFS/IPNS pubsub

A rough proposal to implement the widely desired pub/sub feature on ipfs without requiring additional capabilities or message-passing behavior from the network or the clients. It could be implemented as an IPFS application right now and in fact I've got sample JS that uses the js-http-api to do so on the publish side. Hopefully building out the subscribe side this week.

I've just started diving into IPFS so this could be a super naïve approach.

The proposal's core concepts:

  • subscription is modeled as querying a non-existent block whose future multihash is known, but has not been added to the DAG. Once this block exists, its payload will use IPNS to provide a forward linked list to one or more future blocks in the publication stream.
  • publication is modeled as publishing the block with that desired multihash, and providing a payload that links forward to one or more future blocks in the publication stream.

The proposal's key properties:

  • It requires the IPNS router so it's not the fastest ride in town.
  • It requires that clients patiently wait for wantlist blocks that can't currently found on the network
    • I believe this is the current behavior of go-ipfs
  • It is loosely inspired by mutable value chains
  • It requires no additional capabilities or message-passing behavior from any actor in the network.

The proposal's minimum necessary flow (or just jump down to the example):

  • PUBLISHER creates a SUBSCRIPTION LINK json object
    • The link contains a reference to an /ipns/ address under PUBLISHER's control.
    • The reference will contain the future payload for the SUBSCRIPTION LINK.
      • henceforth <SubscriptionLink.PayloadLocation>
    • The link contains a sequence ID unique in this stream (e.g. "1")
      • In reality we will want to hash this with the private key to avoid spoofing
  • PUBLISHER calculates the SUBSCRIPTION LINK multihash, but does not add it to the DAG
    • Uses ipfs add --only-hash
    • Henceforth <SubscriptionLink.MultiHash>
  • PUBLISHER distributes <SubscriptionLink.MultiHash> widely
  • SUBSCRIBER adds <SubscriptionLink.MultiHash> to his wantlist.
  • SUBSCRIBER waits for the object to exist in the DAG.
  • PUBLISHER writes content and adds it to the DAG.
    • It has ID <Content.MultiHash>
  • PUBLISHER creates a tree/folder at <SubscriptionLink.PayloadLocation> under his IPNS root.
    • Henceforth PayloadTree
  • PUBLISHER writes <Content.Multihash> into PayloadTree/data
  • PUBLISHER calculates the hash for a NEW SUBSCRIPTION LINK using the same process as before
    • This link now has sequence ID 2
    • It should probably also contain an ipfs (not ipns) backlink to PayloadTree
    • Henceforth <NewSubscriptionLink.MultiHash>
  • PUBLISHER writes <NewSubscriptionLink.MultiHash into PayloadTree/next
  • PUBLISHER publishes the new version of his IPNS root.
    • The first subscription link's payload is now ready to consume, but nobody knows how to find it.
  • PUBLISHER adds and pins the first SUBSCRIPTION LINK json object to the DAG.
  • The subscription link (which is really more of a notification) starts percolating through the network as it satisfies subscribers wantlists.
  • SUBSCRIBER, who has patiently waited <SubscriptionLink.MultiHash> in his wantlist for ages, receives and re-pins the subscription link
    • Repinning helps other subscribers.
  • SUBSCRIBER dereferences <SubscriptionLink.PayloadLocation>, finding both the content and the next subscription link within PayloadTree/data and PayloadTree/next respectively.
  • SUBSCRIBER acquires the content using ipfs get
  • SUBSCRIBER adds <NewSubscriptionLink.Multihash> to his wantlist, and waits.

Example of basic necessary flow:

Step 1: Alice distributes a subscription link for her future blog

Publisher (Alice)
"Think I'll start a blog. Don't know what I'll write yet, but let me give the world a subscription link."
// Alice creates a subscription link: this JSON content.
// She saves it to ~/first-publication.json
{
  "payloadLocation":"/ipns/<Alice.PeerId>/ipfs-pubsub/blog/1",
  "publishedBy":["<Alice.PeerId>"],
  "sequenceId":1 // or Sign(1, PrivateKey)
}
# Alice discovers the hash of this subscription link.
# She does not add it to the DAG.
alice~> ipfs add --only-hash ~/first-publication.json
added <FirstPublication.MultiHash> ~/first-publication.json # Not true! We didn't add it, we just discovered the hash.
Publisher (Alice) Subscriber (Bob)
"Hey subscribe to my blog at <FirstPublication.MultiHash>!"
"Sure, Alice!"
bob~> ipfs object get <FirstPublication.MultiHash>
Prepares to wait the long haul. Distributes this object on his wantlist, as does every other future subscriber to Alice.

Step 2: Alice writes her first blog content, publishes it.

Publisher (Alice)
"Time for the first post!"
# Alice writes and adds her first post to the DAG.
# Nobody but her knows about the published content.
alice~> echo "I ate shit for breakfast" > post1.txt && ipfs add post1.txt
added <Post1.MultiHash> post1.txt

# Alice prepares the payload for the first publication
# She chose her blog's multihash as the payload.
alice~> PAYLOAD_DIR=$IPNS_ROOT/<Alice.PeerId>/ipfs-pubsub/blog/1
alice~> mkdir -p $PAYLOAD_DIR
alice~> echo "<Post1.MultiHash>" > $PAYLOAD_DIR/data
// Before publishing the first publication, she needs to prepare the second subscription link
// so that her followers will know when she posts again!
// She creates the following json and saves it to ~/second-publication.json
{
  "payloadLocation":"/ipns/<Alice.PeerId>/ipfs-pubsub/blog/2",
  "publishedBy":["<Alice.PeerId>"],
  "sequenceId":2 // or Sign(2, PrivateKey)
}
# Alice calculates the new subscription link's hash, and posts that hash as the "next" reference
# in the first subscription's payload
alice~> ipfs add --only-hash ~/second-publication.json
added <SecondPublication.MultiHash> ~/second-publication
alice~> echo "<SecondPublication.MultiHash>" > $PAYLOAD_DIR/next

# The message payload for the first publication is ready to go.
# Alice updates her IPNS in preparation of broadcasting her blog post
alice~> ipfs add --recursive $IPNS_ROOT
added <NewIpnsRoot.MultiHash>
alice~> ipfs name publish <NewIpnsRoot.MultiHash>

# Now she finally publishes to her subscribers, satisfying the wantlist of her many blog followers and notifying
# them that content has updated
alice~> ipfs add --pin ~/first-publication.json

Step 3: Bob grabs the new content, waits for more

Subscriber (Bob)
"Oh look, we finally received <FirstPublication.MultiHash>. I guess Alice finally posted!"
"Well let's look at the content."
# Now that Bob has FirstPublication, he can go get the payload. He does this by dereferencing
# the payloadLocation of publication event he just received, and grabbing the "data" link from that tree.
# You'll recall that the "data" link contained the ID to the actual blog content, and the "next" link
# contained the future ID for the next publication event.

bob~> ipfs cat <FirstPublication.payloadLocation>/data | ipfs cat
I ate shit for breakfast

# "Gross. I wonder what she'll say next. Let's subscribe to the next one."
bob~> ipfs get <FirstPublication.payloadLication>/next | ipfs get
# This will block until Alice writes her next blog post
#
export IPFS_PATH=/media/IPFS/MUTABLES
#IRQnID=$(ipfs key list -l | grep IRQn | cut -d' ' -f1)
IRQnID=QmdGrQatgrVSFWGVy121zXycKtSPNpysnZ46k1pc8miN9U
ipfs cat /ipns/$IRQnID
eval $(ipfs cat /ipns/$IRQnID | eyml)
echo place your payload at $payload
read ans
ipfs cat $trigger | ipfs add --pin=false -Q --hash keccak-224
--- # pub/sub endpoints
payload: /ipns/{{requester-id}}/pubsub/{{sn1}}
author: {{ownerid}}
seq: {{sn1}}
---
subscribe-here: {{sub1hash}}
...
<!DOCTYPE html>
<html>
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href=
"http://fonts.googleapis.com/css?family=Henry">
<style>
body {
/* background: yellow; */
background-image: URL(http://ipfs.io/ipfs/z8bvj1SQiwC5KD9d5Y9JxJ9F91mYgtkaX/fallback.png),
linear-gradient(to left, white,lightblue);
background-repeat: no-repeat, repeat;
background-attachment: fixed;
background-position: bottom right, center;
font-family: Verdana;
}
div {
font-family: Henry;
}
img { max-height: 720px; }
</style>
<link href="style.css" rel="stylesheet" type="text/css" media=
"all">
<link rel="stylesheet" type="text/css" href=
"http://fonts.googleapis.com/css?family=Monserrat Alternates">
<link rel="stylesheet" type="text/css" href=
"http://fonts.googleapis.com/css?family=Princess Sofia">
<title></title>
</head>
<body>
<div class="layer"></div>
<div class="content" id="first">
<h1 id=
"de-centralized-blockchain-the-problem-of-synchronizing-mutables">
De-Centralized Blockchain: the problem of synchronizing
mutables</h1>
<p>In Distributed Ledgers, updating a mutable is the core of the
problem. Indeed not matter how you look at it you have to have a
"writable" place open for abuse by any anonymous hacker in the
network.</p>
<p>We tackle the problem with a Pull-Request (a la <a href=
"http://github.com/michel47/blockRingTM">GitHub</a>)</p>
<p>This kind of "inbound" channel is very vulnerable to
<em>SPAM</em> To mitigate any attack several options are
possible</p>
<h2 id="distributed-pull-request">Distributed Pull Request</h2>
<ol style="list-style-type: decimal">
<li>increase the cost of submission</li>
<li>decrease the benefit irrelevant submission</li>
<li>shard the inbound channel to separate noise from signal</li>
<li>decrease the cost of processing spam (amplification attack vs.
division response)</li>
</ol>
<p>Time is a precious resources to spammer, so any rate limiting
tactics help ! We decide to restrict the interaction with anonymous
person to the bare minimum and having a "please introduce yourself"
to know the intent of the originator.</p>
<p>so we design a "On-time-Public-Interruption-Line" to trigger a
pull-request.</p>
</div>
<br>
<div class="content">
<h2 id="this-is-how-it-works">This is how it works:</h2>
<ol style="list-style-type: decimal">
<li>the listener create an IRQx keypair for a bot and store the
privatekey (publically) in /keystore/IRQ</li>
<li>choose a random number as a secret and create a "PullRequest"
template</li>
</ol>
<p>store the request template on 2 addr : <span class=
"citation">@QmTrigger</span> and <span class=
"citation">@QmIRQ</span></p>
<ol start="3" style="list-style-type: decimal">
<li>
<p>create a IRQline message specifying the line addresses and where
to put playload and signature and give the IRQx private key too
publish the message to IRQn address</p>
</li>
<li>
<p>Then listen on <span class="citation">@QmIRQ</span></p>
</li>
<li>
<p>once line trigger can pull the playload and check signature to
decide to process the PR.</p>
</li>
</ol>
<p>once used the IRQline is disposed an a new line is
created...</p>
<ol style="list-style-type: decimal">
<li>
<p>The requester pull the Request template from this "instruction
sheet" found at the <span class="citation">@IRQn</span> address</p>
</li>
<li>place its data at the playload location and sign it</li>
<li>
<p>then trigger the line copying the <span class=
"citation">@QmTrigger</span> data to <span class=
"citation">@QmIRQ</span></p>
</li>
</ol>
</div>
<br>
<div class="content">
<p>As a quick proof of concept I wrote bash script to simulate the
exchange between a asynchronously distributed requester</p>
<ul>
<li>"Interruptable Listener" shell script :</li>
</ul>
<p><code>sh <a href="interrupt.sh" class=
"uri">interrupt.sh</a></code></p>
<ul>
<li>"Pull Requester" shell script :</li>
</ul>
<p><code>sh <a href="PReq.sh" class="uri">PReq.sh</a></code></p>
</div>
<!-- vim: nospell
=== # ==
LIB: "${HOME}/GITrepo/Layout"
layout:
- "bground.yml"
- libraries.yml
- "${HOME}/GITrepo/Layout/fonts.yml"
- ceres.yml
...
-->
</body>
</html>

De-Centralized Blockchain: the problem of synchronizing mutables

In Distributed Ledgers, updating a mutable is the core of the problem. Indeed not matter how you look at it you have to have a "writable" place open for abuse by any anonymous hacker in the network.

We tackle the problem with a Pull-Request (a la GitHub)

This kind of "inbound" channel is very vulnerable to SPAM To mitigate any attack several options are possible

Distributed Pull Request

  1. increase the cost of submission
  2. decrease the benefit irrelevant submission
  3. shard the inbound channel to separate noise from signal
  4. decrease the cost of processing spam (amplification attack vs. division response)

Time is a precious resources to spammer, so any rate limiting tactics help ! We decide to restrict the interaction with anonymous person to the bare minimum and having a "please introduce yourself" to know the intent of the originator.

so we design a "On-time-Public-Interruption-Line" to trigger a pull-request.


This is how it works:

  1. the listener create an IRQx keypair for a bot and store the privatekey (publically) in /keystore/IRQ

  2. choose a random number as a secret and create a "PullRequest" template

    store the request template on 2 addr : @QmTrigger and @QmIRQ

  3. create a IRQline message specifying the line addresses and where to put playload and signature and give the IRQx private key too publish the message to IRQn address

  4. Then listen on @QmIRQ

  5. once line trigger can pull the playload and check signature to decide to process the PR.

    once used the IRQline is disposed an a new line is created...

  6. The requester pull the Request template from this "instruction sheet" found at the @IRQn address

  7. place its data at the playload location and sign it

  8. then trigger the line copying the @QmTrigger data to @QmIRQ


As a quick proof of concept I wrote bash script to simulate the exchange between a asynchronously distributed requester

  • "Interruptable Listener" shell script :
sh [interrupt.sh](interrupt.sh)
  • "Pull Requester" shell script :
sh [PReq.sh](PReq.sh)
--- # Pull Request Trigger
From: Ladonna F. Zella
To: Tammy L. Clukies
date: Di Aug 6 21:11:23 CEST 2019
qmspot: bafkrcfgop76sy4gnf7hw2yo25oxvaffw3qkz34i
message: /ipns/Qmewoop8ixQLHHaX1eYxU7qDTyPDBtGGfynT6G6igBeqzt/to-tammy-l-clukies.txt
payload: /ipns/Qmewoop8ixQLHHaX1eYxU7qDTyPDBtGGfynT6G6igBeqzt/data
signature: /ipns/Qmewoop8ixQLHHaX1eYxU7qDTyPDBtGGfynT6G6igBeqzt/data.sig
code: secret-code+4357
seq: 1
...
body {
background-image: URL(https://cdn.statically.io/picsum/1280/720);
background-size: cover;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
}
div.content {
width: 100%;
padding: 3em;
background: lightyellow;
background-image: linear-gradient(to bottom left, white,lightyellow);
border-radius: 4px;
box-shadow:2px 20px 10px rgba(0, 0, 0, 0.40);
}
a:link { color: grey; }
a:hover { color: hotpink; }
a:visited { color: #26B; }
a:active { color: blue; }
#first,div.first {
opacity: 0.96;
color: white; background: rgba(1,0,2,.8); opacity: 1.0;
position: sticky;
top: 8px;
}
div {
position: relative;
max-width: 740px;
margin-left: 100px;
margin: auto;
/* background-image: linear-gradient(to bottom right, white,darkgrey); */
opacity: 0.96;
padding: 2em;
border-radius: 8px;
box-shadow:2px 10px 4px rgba(0, 0, 0, 0.40);
font-size: 14px;
text-align: justify;
}
h1,h2,h3,h4,h5 { text-align: left; }
pre { overflow: auto }
img { max-width: 90%; max-height: 720px; margin: auto;}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment