Feature 3: Sign and Verify Posts
Last updated
Was this helpful?
Last updated
Was this helpful?
In this feature, we have introduced a couple new lnd
endpoints, and . These can be useful in cases where one user wants to be certain that some content has not been altered by a third-party. Following our mantra of “Can’t Be Evil”, we want to ensure our users that we have not tampered with the posts they author. So when a user creates a post, we will sign the message using their node and store the signature
. If another user wants to verify that the post has not been modified, they will verify the signature using their own node.
In addition to verifying signatures, we took this opportunity to remove the user-specified usernames for each post. Instead we use the node’s alias
for this field.
Let’s go to the feat-3a
branch to see what’s changed.
To use the alias instead of a user-specified username, we needed to fetch the alias
when the post is submitted to the backend and store it in the username field of the post. Then we need to remove the username
field from the frontend flow that creates a post. As usual, we’ll start by updating the backend first then work our way up to the UI.
source: /backend/routes.ts
We updated the createPost()
route handler to get the alias using lnd
’s getInfo()
endpoint. Then we pass the alias to the db.createPost()
function instead of the username received from the client. Notice that we no longer take the username
from req.body
as we do not require the client to send this value anymore. Instead, this handler expects to receive the node’s token
, which will be automatically provided after the user connects their node.
source: /src/lib/api.ts
On the frontend, we removed the username
parameter in the API wrapper’s createPost()
function since the backend no longer needs this info.
source: /src/store/store.ts
In the mobx store, we removed the username
parameter as well.
source: /src/pages/CreatepPost.tsx
Finally, in the CreatePost
component, we removed the username
state variable and associated field from the form.
Now when you visit the Create a Post screen in the browser, you will only see form fields to collect the Post’s title and content.
When you click submit, the new post will appear on the main screen with the subtitle “Posted by alice”. It set the username of the post to the alias of the node correctly.
Checkout the feat-3b
branch to see what’s changed.
source: /src/shared/types.ts
We’ll need to add a couple more fields to our Post
model to store the signature
and the node’s pubkey
. These values are needed when another user wants to confirm that the signature is valid.
source: /backend/posts-db.ts
The createPost()
function in the PostsDb
class needed to be updated to add parameters for signature
and pubkey
, which are saved directly in the post.
Note: since we are modifying the schema of the Post model, you should delete the /db.json file now. Any posts that were created without these new fields will cause the app to throw errors when we run it the next time.
source: /backend/routes.ts
source: /db.json
Now if you create a new post, then open the /db.json
file in your editor, you should see the new post listed with the pubkey and signature.
With these changes, the backend now supports signing all new posts when they are created. Our last step in this feature is to allow users to verify the signature using their node.
Checkout the feat-3c
branch to see what’s changed.
source: /src/shared/types.ts
We had to update the Post
type to include a verified
flag which will be false
by default. We will switch it to true
when another user verifies the content.
source: /backend/posts-db.ts
Next, we’ve added a new verifyPost()
function to the PostsDb
class. This function just sets the verified
flag to true
for a specified post.
source: /backend/index.ts
source: /backend/routes.ts
In the routes.ts
file we’ve added the verifyPost()
route handler and mapped it to the url in index.ts
. This is where the bulk of the logic for verification is. There is a bunch going on here so let’s walk through it step by step.
Get the id
of the post from the url and the verifying user’s token
from the request body
Find the post in the DB using the id
Find the node record using the token
of the user performing the verification
We do not want to allow a user to verify their own posts, so we compare the pubkey
of the node that signed this post with the pubkey
of the verifying user’s node. If they are the same, we throw an error
The verifyMessage endpoint will return two values:
valid
is a boolean flag indicating if the signature is valid
pubkey
is the pubkey of the node that signed the message. It is recovered from the signature
To confirm the signature is valid, we must ensure that the valid
flag is true and also that the response pubkey
matches the pubkey of the node that created the post. It is possible that another node can sign this message and produce a valid signature, so we need to check both of these conditions.
Finally, if all of the verification checks pass, we call verifyPost()
in the PostsDb
class to set the post’s verified
flag to true and return the updated post to the client
This completes all of the changes we need to make on the backend. Now let’s move to the frontend to implement verifying posts from the UI.
source: /src/lib/api.ts
In our API wrapper module, we added the verifyPost()
function to make the http request to the backend.
source: /src/store/store.ts
In the mobx store, we added a new verifyPost()
function which calls the API wrapper to make the http request, then updates the app state with the modified post record that is returned.
source: /src/components/PostCard.tsx
In the PostCard
component, we’ve made a few updates for post verification:
In the post’s subtitle, we display that the post was signed by the user if there is a signature
present.
Also in the subtitle, it displays a badge with the text “verified” if the post’s verified
flag is true.
Next to the “Upvote” button we render a “Verify Signature” button. This is a new component that we’ll explain below.
source: /src/components/VerifyButton.tsx
We created a new VerifyButton
component which is pretty simple. If the post is not verified, it renders a button on the screen. When the button is clicked, it calls the store.verifyPost()
function to initiate the signature verification.
Refresh the page in your browser and create a couple posts. You will now see the updates we’ve made. The post subtitles display that the posts are signed and there is a “Verify Signature” button. If you click on the button, you will receive an error message stating “You cannot verify your own posts!”. This is the intended behavior as we do not want to allow users to verify their own posts.
To test the Verify Signature feature, you’ll need to disconnect and reconnect using a different node, such as carol. Then you’ll be able to click on the “Verify Signature” button and see the “verified” badge now displayed.
We have now completed this feature. Next, we are going to take on our biggest improvement. We’ll implement making payments over Lightning in order to upvote a post.
Using lnd
to sign a message is pretty straightforward. We just need to call the endpoint with the message content in a specific format.
The final step on the backend is to update the createPost()
route handler to sign the message using lnd’s
endpoint, then pass the signature
and pubkey
to the database to store for later.
Verifying a Post
’s signature is very similar to signing a message. We’ll just need to make use of lnd
’s endpoint, giving it the post’s content and signature. It will return a valid
flag set to true
as well as the pubkey
of the signing node which we can compare to the pubkey
stored with the post record to confirm the signature is valid.
With the basic validation done, now we can get the RPC connection of the verifying node and call the endpoint, passing it the base64 encoded message and the signature stored in the post record