Branch data Line data Source code
1 : : ;; Title: BME021 Market Gating
2 : : ;; Synopsis:
3 : : ;; Efficient access control using merkle proofs.
4 : : ;; Description:
5 : : ;; Provides gating functionality based on account (can-access-by-account) and
6 : : ;; ownership (can-access-by-ownership). The map of keys / roots are DAO managed.
7 : : ;; Keys can by any data hash or a specific contract id hash. For ownership the user
8 : : ;; must pass either an NFT or FT token and a merkle proof of ownership. For access
9 : : ;; by account the account principal is passed along with the proof.
10 : :
11 : : ;; Define the SIP-009 and SIP-010 traits
12 : : (use-trait nft-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
13 : : (use-trait ft-trait 'SP2AKWJYC7BNY18W1XXKPGP0YVEK63QJG4793Z2D4.sip-010-trait-ft-standard.sip-010-trait)
14 : : (impl-trait 'SP3JP0N1ZXGASRJ0F7QAHWFPGTVK9T2XNXDB908Z.extension-trait.extension-trait)
15 : :
16 : : (define-constant err-unauthorised (err u2200))
17 : : (define-constant err-either-sip9-or-sip10-required (err u2201))
18 : : (define-constant err-token-contract-invalid (err u2202))
19 : : (define-constant err-token-ownership-invalid (err u2203))
20 : : (define-constant err-expecting-nft-contract (err u2204))
21 : : (define-constant err-expecting-ft-contract (err u2205))
22 : : (define-constant err-expecting-token-id (err u2206))
23 : : (define-constant err-not-nft-owner (err u2207))
24 : : (define-constant err-not-ft-owner (err u2208))
25 : : (define-constant err-expecting-nft-buffer (err u2209))
26 : : (define-constant err-expecting-ft-buffer (err u2210))
27 : : (define-constant err-expecting-valid-merkle-proof (err u2211))
28 : : (define-constant err-expecting-merkle-root-for-poll (err u2212))
29 : : (define-constant err-expecting-an-owner (err u2213))
30 : : (define-constant err-account-proof-invalid (err u2214))
31 : : (define-constant err-ownership-proof-invalid (err u2215))
32 : :
33 : : ;; Merkle roots for gated data
34 : : (define-map merkle-roots
35 : : (buff 32)
36 : : {
37 : : merkle-root: (buff 32)
38 : : }
39 : : )
40 : :
41 : 421 : (define-public (is-dao-or-extension)
42 [ + ][ + + ]: 1294 : (ok (asserts! (or (is-eq tx-sender .bigmarket-dao) (contract-call? .bigmarket-dao is-extension contract-caller)) err-unauthorised))
43 : : )
44 : :
45 : 15 : (define-public (set-merkle-root (hashed-id (buff 32)) (root (buff 32)))
46 : 15 : (begin
47 : : ;; Ensure only dao can set the root
48 : 15 : (try! (is-dao-or-extension))
49 : 14 : (map-set merkle-roots hashed-id { merkle-root: root})
50 : 14 : (print {event: "merkle-root", hashed-id: hashed-id, merkle-root: (map-get? merkle-roots hashed-id)})
51 : 14 : (ok true)
52 : : )
53 : : )
54 : :
55 : 421 : (define-public (set-merkle-root-by-principal (contract-id principal) (root (buff 32)))
56 : 1279 : (let
57 : : (
58 : : ;; construct the key from the contract-id
59 : 1279 : (principal-contract (unwrap! (principal-destruct? contract-id) (err u1001)))
60 : 1279 : (contract-bytes (get hash-bytes principal-contract))
61 : : ;; panics if not contract principal
62 : 1279 : (contract-name (unwrap! (to-consensus-buff? (unwrap! (get name principal-contract) err-expecting-an-owner)) err-expecting-an-owner))
63 : 1279 : (contract-key (sha256 (concat contract-bytes contract-name )))
64 : : )
65 : : ;; Ensure only dao can set the root
66 : 1279 : (try! (is-dao-or-extension))
67 : 1279 : (map-set merkle-roots contract-key { merkle-root: root})
68 : 1279 : (print {event: "set-merkle-root-by-principal", contract-id: contract-id, contract-name: contract-name, contract-key: contract-key, merkle-root: (map-get? merkle-roots contract-key)})
69 : 1279 : (ok true)
70 : : ))
71 : :
72 : 3 : (define-read-only (get-merkle-root (hashed-id (buff 32)))
73 : 3 : (map-get? merkle-roots hashed-id)
74 : : )
75 : :
76 : : ;; Verify a Merkle proof
77 : 12 : (define-private (calculate-hash (hash1 (buff 32)) (hash2 (buff 32)) (position bool))
78 : 36 : (if position
79 [ + ]: 12 : (sha256 (concat hash2 hash1))
80 [ + ]: 24 : (sha256 (concat hash1 hash2))
81 : : )
82 : : )
83 : :
84 : 12 : (define-private (process-proof-step (proof-step (tuple (position bool) (hash (buff 32)))) (current (buff 32)))
85 : 36 : (let ((position (get position proof-step))
86 : 36 : (hash (get hash proof-step)))
87 : 36 : (calculate-hash current hash position)
88 : : )
89 : : )
90 : :
91 : 306 : (define-private (verify-merkle-proof
92 : : (leaf (buff 32)) ;; The leaf hash (token hash)
93 : : (proof (list 10 (tuple (position bool) (hash (buff 32)))))
94 : : (root (buff 32))
95 : : )
96 : 439 : (let
97 : : (
98 : 0 : (calculated-root
99 : 439 : (fold process-proof-step proof leaf)
100 : : )
101 : : )
102 : 439 : (ok (is-eq calculated-root root))
103 : : )
104 : : )
105 : :
106 : :
107 : 3 : (define-private (verify-nft-ownership
108 : : (nft-contract <nft-trait>) ;; NFT contract
109 : : (voter principal) ;; Voter's principal
110 : : (token-id uint) ;; Token ID
111 : : )
112 : 3 : (let
113 : : (
114 : 3 : (owner (unwrap! (contract-call? nft-contract get-owner token-id) err-not-nft-owner))
115 : : )
116 : 3 : (ok (is-eq (unwrap! owner err-expecting-an-owner) voter))
117 : : ))
118 : :
119 : 3 : (define-private (verify-ft-balance
120 : : (ft-contract <ft-trait>) ;; FT contract
121 : : (voter principal) ;; Voter's principal
122 : : (quantity uint) ;; Required token quantity
123 : : )
124 : 3 : (let
125 : : (
126 : 3 : (balance (unwrap! (contract-call? ft-contract get-balance voter) err-not-ft-owner))
127 : : )
128 : 3 : (ok (>= balance quantity))
129 : : ))
130 : :
131 : :
132 : : ;; Validate proof of access
133 : 8 : (define-public (can-access-by-ownership
134 : : (market-data-hash (buff 32)) ;; The hashed ID
135 : : (nft-contract (optional <nft-trait>)) ;; Optional NFT contract
136 : : (ft-contract (optional <ft-trait>)) ;; Optional FT contract
137 : : (token-id (optional uint)) ;; Token ID for NFTs
138 : : (proof (list 10 (tuple (position bool) (hash (buff 32))))) ;; The Merkle proof
139 : : (quantity uint) ;; Required token quantity
140 : : )
141 : 8 : (let
142 : : (
143 : : ;; Determine if this is an NFT or FT contract
144 : 8 : (is-nft-contract (is-some nft-contract))
145 : :
146 : : ;; Fetch the Merkle root for the poll
147 : 8 : (root (unwrap! (map-get? merkle-roots market-data-hash) err-expecting-merkle-root-for-poll))
148 : :
149 : : ;; Compute the Merkle proof leaf
150 : 6 : (contract-id (if is-nft-contract
151 [ + ]: 3 : (unwrap! (to-consensus-buff? (as-contract (unwrap! nft-contract err-expecting-nft-contract))) err-expecting-nft-buffer)
152 [ + ]: 3 : (unwrap! (to-consensus-buff? (as-contract (unwrap! ft-contract err-expecting-ft-contract))) err-expecting-ft-buffer)))
153 : 6 : (leaf (sha256 contract-id))
154 : :
155 : : ;; Verify the Merkle proof
156 : 6 : (proof-valid (unwrap! (verify-merkle-proof leaf proof (get merkle-root root)) err-expecting-valid-merkle-proof))
157 : :
158 : : ;; Verify ownership or balance
159 : 0 : (ownership-valid
160 : 6 : (if is-nft-contract
161 [ + ]: 3 : (unwrap! (verify-nft-ownership (unwrap! nft-contract err-expecting-nft-contract) tx-sender (unwrap! token-id err-expecting-token-id)) err-not-nft-owner)
162 [ + ]: 3 : (unwrap! (verify-ft-balance (unwrap! ft-contract err-expecting-ft-contract) tx-sender quantity) err-not-ft-owner)))
163 : : )
164 : : ;; Ensure both conditions are satisfied
165 [ + ]: 5 : (asserts! proof-valid err-ownership-proof-invalid)
166 [ - ]: 0 : (asserts! ownership-valid err-token-ownership-invalid)
167 : 0 : (ok true)
168 : : ))
169 : :
170 : 300 : (define-public (can-access-by-account
171 : : (sender principal)
172 : : (proof (list 10 (tuple (position bool) (hash (buff 32))))) ;; The Merkle proof
173 : : )
174 : 433 : (let
175 : : (
176 : : ;; Fetch the Merkle root for the poll
177 : 433 : (principal-contract (unwrap! (principal-destruct? tx-sender) (err u1001)))
178 : 433 : (contract-bytes (get hash-bytes principal-contract))
179 : 433 : (contract-name (unwrap! (to-consensus-buff? (unwrap! (get name principal-contract) err-expecting-an-owner)) err-expecting-an-owner))
180 : 433 : (contract-key (sha256 (concat contract-bytes contract-name )))
181 : 433 : (root (unwrap! (map-get? merkle-roots contract-key) err-expecting-merkle-root-for-poll))
182 : :
183 : : ;; Compute the Merkle proof leaf
184 : 433 : (principal-data (unwrap! (principal-destruct? sender) (err u1001)))
185 : 433 : (leaf (sha256 (get hash-bytes principal-data)))
186 : :
187 : : ;; Verify the Merkle proof
188 : 433 : (proof-valid (unwrap! (verify-merkle-proof leaf proof (get merkle-root root)) err-expecting-valid-merkle-proof))
189 : : )
190 : : ;; Ensure both conditions are satisfied
191 [ + ]: 433 : (asserts! proof-valid err-account-proof-invalid)
192 : 430 : (print {event: "can-access-by-account", contract-key: contract-key, contract-name: contract-name, root: root, leaf: leaf, sender: sender, txsender: tx-sender, proof-valid: proof-valid})
193 : 430 : (ok true)
194 : : ))
195 : :
196 : :
197 : : ;; --- Extension callback
198 : 1 : (define-public (callback (sender principal) (memo (buff 34)))
199 : 1 : (ok true)
200 : : )
201 : :
|