|
1 | | -;; title: executor-state |
2 | | -;; version: 0.0.1 |
3 | | -;; summary: State contract for cross-chain executor relayer registry |
4 | | -;; description: Simple relayer address mapping for the executor system |
| 1 | +;; Title: addr32 |
| 2 | +;; Version: final (CANNOT BE UPDATED) |
5 | 3 |
|
6 | | -;;;; Constants |
| 4 | +;; This contract provides 32-byte addressing for the Stacks blockchain |
| 5 | +;; A Stacks contract principal can be longer than 32 bytes, and some protocols can't handle that |
| 6 | +;; We can generate a unique 32-byte address for any Stacks principal by hashing it |
| 7 | +;; This allows us to use existing protocols unmodified |
7 | 8 |
|
8 | | -;; State contract errors |
9 | | -(define-constant ERR_STATE_RELAYER_EXISTS (err u20001)) |
| 9 | +(define-constant ERR_INVALID_ADDRESS (err u901)) |
10 | 10 |
|
11 | | -;;;; Data maps |
12 | | - |
13 | | -;; Map to track payee addresses for payments |
14 | | -;; Universal address is keccak256(stacks-principal-as-string) |
15 | | -(define-map universal-address-to-principal |
16 | | - (buff 32) ;; Universal address (32-byte hash) |
17 | | - principal ;; Stacks principal for STX payments |
18 | | -) |
19 | | - |
20 | | -;;;; Public functions |
21 | | - |
22 | | -;; @desc Register a payee's Stacks address for their universal address |
23 | | -;; Anyone can call this to register themselves as a payee |
24 | | -(define-public (register-payee (stacks-addr principal)) |
25 | | - (let ( |
26 | | - (p-as-string (principal-to-string stacks-addr)) |
27 | | - (universal-addr (keccak256 (string-ascii-to-buff p-as-string))) |
28 | | - ) |
29 | | - ;; Check if payee already exists |
30 | | - (asserts! (is-none (universal-address-to-principal-get universal-addr)) |
31 | | - ERR_STATE_RELAYER_EXISTS |
32 | | - ) |
33 | | - |
34 | | - ;; Register the mapping |
35 | | - (map-set universal-address-to-principal universal-addr stacks-addr) |
36 | | - |
37 | | - ;; Return the universal address for confirmation |
38 | | - (ok universal-addr) |
39 | | - ) |
| 11 | +;; Registered principals |
| 12 | +(define-map registry |
| 13 | + (buff 32) ;; keccak256(principal) |
| 14 | + principal ;; Stacks principal |
40 | 15 | ) |
41 | 16 |
|
42 | | -;;;; Read-only functions |
43 | | - |
44 | | -;; Constants for principal-to-string conversion (extracted from self-listing-helper-v3) |
| 17 | +;; @desc Get or register 32-byte address |
| 18 | +(define-public (register (p principal)) |
| 19 | + (if (is-standard p) |
| 20 | + ;; Address matches network, this is expected |
| 21 | + (inner-register p) |
| 22 | + ;; Address does not match network, need to support for unit tests |
| 23 | + (let ((addr32 (hash p))) |
| 24 | + (match (lookup addr32) |
| 25 | + val (ok { |
| 26 | + created: false, |
| 27 | + addr32: addr32 |
| 28 | + }) |
| 29 | + ERR_INVALID_ADDRESS)))) |
| 30 | + |
| 31 | +;; @desc Hash a Stacks principal to generate addr32 |
| 32 | +(define-read-only (hash (p principal)) |
| 33 | + (keccak256 (string-ascii-to-buff (principal-to-string p)))) |
| 34 | + |
| 35 | +;; @desc Lookup Stacks principal for given addr32 |
| 36 | +(define-read-only (lookup (addr32 (buff 32))) |
| 37 | + (map-get? registry addr32)) |
| 38 | + |
| 39 | +;; @desc Lookup to see if Stacks principal is registered |
| 40 | +(define-read-only (reverse-lookup (p principal)) |
| 41 | + (let ((addr32 (hash p))) |
| 42 | + { |
| 43 | + registered: (is-some (map-get? registry addr32)), |
| 44 | + addr32: addr32 |
| 45 | + })) |
| 46 | + |
| 47 | +;; Constants for principal-to-string conversion |
45 | 48 | (define-constant C32 "0123456789ABCDEFGHJKMNPQRSTVWXYZ") |
46 | 49 | (define-constant LIST_15 (list 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) |
47 | 50 | (define-constant LIST_24 (list 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) |
48 | 51 | (define-constant LIST_39 (concat LIST_24 LIST_15)) |
49 | 52 |
|
50 | | -;; @desc Convert principal to string representation |
51 | | -;; Extracted from self-listing-helper-v3 to eliminate external dependencies |
52 | | -(define-read-only (principal-to-string (p principal)) |
| 53 | +;; TODO: Replace with `to-ascii?` in Clarity 4 |
| 54 | +(define-private (principal-to-string (p principal)) |
53 | 55 | (let ( |
54 | 56 | (destructed (match (principal-destruct? p) ok-value ok-value err-value err-value)) |
55 | 57 | (checksum (unwrap-panic (slice? (sha256 (sha256 (concat (get version destructed) (get hash-bytes destructed)))) u0 u4))) |
|
66 | 68 |
|
67 | 69 | (define-private (hash-bytes-to-string (data (buff 24))) |
68 | 70 | (let ( |
69 | | - ;; fixed-length: 8 * 15 / 5 = 24 |
70 | 71 | (low-part (get s (fold c32-to-string-iter LIST_24 { s: "", r: (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap-panic (slice? data u9 u24)) u16)))}))) |
71 | | - ;; fixed-length: ceil(8 * 9 / 5) = 15 |
72 | 72 | (high-part (get s (fold c32-to-string-iter LIST_15 { s: "", r: (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap-panic (slice? data u0 u9)) u16)))}))) |
73 | 73 | ) |
74 | 74 | (unwrap-panic (as-max-len? (concat high-part low-part) u39)) |
|
89 | 89 | (define-private (append-leading-0 (hash-bytes (buff 24)) (s (string-ascii 39))) |
90 | 90 | (get address (fold append-leading-0-iter LIST_24 { hash-bytes: hash-bytes, address: s }))) |
91 | 91 |
|
92 | | -;; @desc Helper function to convert string to buffer for hashing |
93 | | -;; Matches the exact implementation from Wormhole Core |
94 | | -(define-read-only (string-ascii-to-buff (s (string-ascii 256))) |
| 92 | +(define-private (string-ascii-to-buff (s (string-ascii 256))) |
95 | 93 | (let ((cb (unwrap-panic (to-consensus-buff? s)))) |
96 | | - ;; Consensus buff format for string: |
97 | | - ;; bytes[0]: Consensus Buff Type |
98 | | - ;; bytes[1..4]: String length |
99 | | - ;; bytes[5..]: String data |
100 | | - (unwrap-panic (slice? cb u5 (len cb))) |
101 | | - ) |
102 | | -) |
103 | | - |
104 | | -;;;; Map getters |
105 | | - |
106 | | -(define-read-only (universal-address-to-principal-get (universal-addr (buff 32))) |
107 | | - (map-get? universal-address-to-principal universal-addr) |
108 | | -) |
| 94 | + (unwrap-panic (slice? cb u5 (len cb))))) |
| 95 | + |
| 96 | +;; @desc Bypass checks, used in unit tests |
| 97 | +(define-private (inner-register (p principal)) |
| 98 | + (let ((addr32 (hash p))) |
| 99 | + (ok { |
| 100 | + created: (map-insert registry addr32 p), |
| 101 | + addr32: addr32 |
| 102 | + }))) |
0 commit comments