Skip to content

Commit 6274b8e

Browse files
authored
api,www: add endpoint for fetching a root from a given proof (#47)
1 parent d7a10ec commit 6274b8e

File tree

6 files changed

+100
-1
lines changed

6 files changed

+100
-1
lines changed

api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func (s *Server) Handler(env, gitSha string) http.Handler {
3030
mux := http.NewServeMux()
3131
mux.HandleFunc("/api/v1/tree", s.TreeHandler)
3232
mux.HandleFunc("/api/v1/proof", s.GetProof)
33+
mux.HandleFunc("/api/v1/root", s.GetRoot)
3334
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
3435
fmt.Fprint(w, gitSha)
3536
})

api/migrations/migrations.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,10 @@ var Migrations = []migrate.Migration{
6161
ALTER TABLE merkle_trees RENAME TO trees;
6262
`,
6363
},
64+
{
65+
Name: "2022-08-22.0.add-proof-idx.sql",
66+
SQL: `
67+
CREATE INDEX on trees USING gin(proofs jsonb_path_ops);
68+
`,
69+
},
6470
}

api/root.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"net/http"
7+
"strings"
8+
9+
"github.com/ethereum/go-ethereum/common/hexutil"
10+
"github.com/jackc/pgx/v4"
11+
)
12+
13+
func proofURLToDBQuery(param string) string {
14+
type proofLookup struct {
15+
Proof []string `json:"proof"`
16+
}
17+
18+
lookup := proofLookup{
19+
Proof: strings.Split(param, ","),
20+
}
21+
22+
q, err := json.Marshal([]proofLookup{lookup})
23+
if err != nil {
24+
return ""
25+
}
26+
27+
return string(q)
28+
}
29+
30+
func (s *Server) GetRoot(w http.ResponseWriter, r *http.Request) {
31+
type rootResp struct {
32+
Root hexutil.Bytes `json:"root"`
33+
}
34+
35+
var (
36+
ctx = r.Context()
37+
proof = r.URL.Query().Get("proof")
38+
dbQuery = proofURLToDBQuery(proof)
39+
)
40+
if proof == "" || dbQuery == "" {
41+
s.sendJSONError(r, w, nil, http.StatusBadRequest, "missing list of proofs")
42+
return
43+
}
44+
45+
const q = `
46+
SELECT root
47+
FROM trees
48+
WHERE proofs @> $1
49+
LIMIT 1
50+
`
51+
52+
rr := rootResp{}
53+
err := s.db.QueryRow(ctx, q, dbQuery).Scan(
54+
&rr.Root,
55+
)
56+
if errors.Is(err, pgx.ErrNoRows) {
57+
s.sendJSONError(r, w, err, http.StatusNotFound, "root not found for proofs")
58+
return
59+
} else if err != nil {
60+
s.sendJSONError(r, w, err, http.StatusInternalServerError, "selecting root")
61+
return
62+
}
63+
64+
w.Header().Set("Cache-Control", "public, max-age=3600")
65+
s.sendJSON(r, w, rr)
66+
}

example/src/api.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ const getProofForIndexedAddress = async (
8080
return await proofRes.json()
8181
}
8282

83+
const getRootFromProof = async (proof: string[]): Promise<string> => {
84+
const rootRes = await fetch(`${baseUrl}/api/v1/root?proof=${proof.join(',')}`)
85+
const resp = await rootRes.json()
86+
return resp.root
87+
}
88+
8389
const encode = utils.defaultAbiCoder.encode.bind(utils.defaultAbiCoder)
8490
const encodePacked = utils.solidityPack
8591

@@ -236,3 +242,7 @@ checkProofEquality(
236242
utils.keccak256(encodedPackedLeafData[0]),
237243
),
238244
)
245+
246+
const root = await getRootFromProof(encodedPackedProof)
247+
console.log('root from proof', root)
248+
checkRootEquality(root, encodedPackedMerkleRoot)

www/components/Docs/codeSnippets.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,13 @@ GET https://lanyard.build/api/v1/proof?root={root}&unhashedLeaf={unhashedLeaf}
4949
"unhashedLeaf": "0x0000000000000000000000000000000000000003" // or null if not in the tree
5050
}
5151
`.trim()
52+
53+
export const rootCode = `
54+
// proof is 0x prefixed, comma separated values
55+
GET https://lanyard.build/api/v1/root?proof={proof}
56+
57+
// Response body
58+
{
59+
"root": "0x0000000000000000000000000000000000000003" // returns error if not found
60+
}
61+
`.trim()

www/components/Docs/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Link from 'next/link'
22
import { brandUnderlineClasses } from 'utils/theme'
33
import PageTitle from 'components/PageTitle'
44
import CodeBlock from 'components/CodeBlock'
5-
import { createCode, lookupCode, proofCode } from './codeSnippets'
5+
import { createCode, lookupCode, proofCode, rootCode } from './codeSnippets'
66
import Section from './Section'
77

88
export default function Docs() {
@@ -42,6 +42,12 @@ export default function Docs() {
4242
>
4343
<CodeBlock code={proofCode} language="javascript" />
4444
</Section>
45+
<Section
46+
title="Looking up a root for a given proof"
47+
description="A more advanced use but helpful if you just have a proof."
48+
>
49+
<CodeBlock code={rootCode} language="javascript" />
50+
</Section>
4551
</div>
4652
</div>
4753
)

0 commit comments

Comments
 (0)