Skip to content

Latest commit

 

History

History
741 lines (518 loc) · 18.3 KB

wrapper-migrator.md

File metadata and controls

741 lines (518 loc) · 18.3 KB

wrapper-migrator

wrapper-migrator-v1.clar

The wrapper migrator contract provides a way for users to upgrade a BNS legacy name to BNSx.

The high-level flow for using this migrator is:

  • Deploy a wrapper contract (see .name-wrapper)
  • Verify the wrapper contract
  • Finalize the migration

Because Stacks contracts don't have a way to verify the source code of another contract, each wrapper contract must be verified by requesting a signature off-chain. This prevents malicious users from deploying "fact" wrapper contracts without the same guarantees.

For more detail on how each wrapper is verified, see verify-wrapper

Authorization for valid wrapper verifiers is only allowed through extensions with the "mig-signer" role. By default, the contract deployer is a valid signer.

During migration, the legacy name is transferred to the wrapper contract. Then, this contract interfaces with the .bnsx-registry contract to mint a new BNSx name.

Public functions:

Read-only functions:

Private functions:

Maps

Variables

Constants

Functions

is-dao-or-extension

View in file

(define-public (is-dao-or-extension () (response bool uint))

Authorization check - only extensions with the role "mig-signer" can add/remove wrapper verifiers.

Source code:
(define-public (is-dao-or-extension)
  (ok (asserts! (or (is-eq tx-sender .bnsx-extensions) (contract-call? .bnsx-extensions has-role-or-extension contract-caller ROLE)) ERR_UNAUTHORIZED))
)

set-signers-iter

View in file

(define-private (set-signers-iter ((item (tuple (enabled bool) (signer (buff 20))))) (buff 20))

#[allow(unchecked_data)]

Source code:
(define-private (set-signers-iter (item { signer: (buff 20), enabled: bool }))
  (let
    (
      (pubkey (get signer item))
    )
    (print pubkey)
    (map-set migrator-signers-map pubkey (get enabled item))
    pubkey
  )
)

Parameters:

Name Type Description
item (tuple (enabled bool) (signer (buff 20)))

set-signers

View in file

(define-public (set-signers ((signers (list 50 (tuple (enabled bool) (signer (buff 20)))))) (response (list 50 (buff 20)) uint))

Set valid wrapper verifiers

Source code:
(define-public (set-signers (signers (list 50 { signer: (buff 20), enabled: bool })))
  (begin
    (try! (is-dao-or-extension))
    (ok (map set-signers-iter signers))
  )
)

Parameters:

Name Type Description
signers (list 50 (tuple (enabled bool) (signer (buff 20)))) a list of { signer: principal, enabled: bool } tuples. Existing verifiers can be removed by setting enabled to false.

migrate

View in file

(define-public (migrate ((wrapper principal) (signature (buff 65)) (recipient principal)) (response (tuple (id uint) (lease-ending-at (optional uint)) (lease-started-at uint) (name (buff 48)) (namespace (buff 20)) (owner principal) (zonefile-hash (buff 20))) uint))

Upgrade a name to BNSx

This function has three main steps:

(define-public (migrate (wrapper principal) (signature (buff 65)) (recipient principal))
  (let
    (
      ;; #[filter(wrapper)]
      (wrapper-ok (try! (verify-wrapper wrapper signature)))
      (properties (try! (resolve-and-transfer wrapper)))
      (name (get name properties))
      (namespace (get namespace properties))
      (id (try! (contract-call? .bnsx-registry register
        {
          name: name,
          namespace: namespace,
        }
        recipient
      )))
      (meta (merge { id: id } properties))
    )
    (print {
      topic: "migrate",
      wrapper: wrapper,
      id: id,
    })
    (asserts! (map-insert name-wrapper-map id wrapper) ERR_WRAPPER_USED)
    (asserts! (map-insert wrapper-name-map wrapper id) ERR_WRAPPER_USED)

    (ok meta)
  )
)

Parameters:

Name Type Description
wrapper principal the principal of the wrapper contract that will be used
signature (buff 65) a signature attesting to the validity of the wrapper contract
recipient principal a principal that will receive the BNSx name. Useful for consolidating names into one wallet.

register-wrapper

View in file

(define-public (register-wrapper ((wrapper principal)) (response uint uint))

Register a wrapper contract

This is necessary to establish an integer ID for each wrapper principal. This ID can then be used to validate signatures

Source code:
(define-public (register-wrapper (wrapper principal))
  (let
    (
      (id (get-next-wrapper-id))
    )
    (asserts! (map-insert wrapper-id-map wrapper id) ERR_WRAPPER_ALREADY_REGISTERED)
    (map-insert id-wrapper-map id wrapper)
    (ok id)
  )
)

Parameters:

Name Type Description
wrapper principal

verify-wrapper

View in file

(define-read-only (verify-wrapper ((wrapper principal) (signature (buff 65))) (response bool uint))

Verify a wrapper principal.

The message being signed is the Clarity-serialized representation of the wrapper principal.

The pubkey is recovered from the signature. The hash160 of this pubkey is then checked to ensure that pubkey hash is stored as a valid signer.

@throws if the signature is invalid (cannot be recovered)

@throws if the pubkey is not a valid verifier

Source code:
(define-read-only (verify-wrapper (wrapper principal) (signature (buff 65)))
  (let
    (
      (id (unwrap! (map-get? wrapper-id-map wrapper) ERR_WRAPPER_NOT_REGISTERED))
      (msg (sha256 id))
      (pubkey (unwrap! (secp256k1-recover? msg signature) ERR_RECOVER))
      (pubkey-hash (hash160 pubkey))
    )
    ;; (ok pubkey-hash)
    (asserts! (default-to false (map-get? migrator-signers-map pubkey-hash)) ERR_UNAUTHORIZED)
    (ok true)
  )
)

Parameters:

Name Type Description
wrapper principal
signature (buff 65)

hash-id

View in file

(define-read-only (hash-id ((id uint)) (buff 32))

Source code:
(define-read-only (hash-id (id uint))
  (sha256 id)
)

Parameters:

Name Type Description
id uint

debug-signature

View in file

(define-read-only (debug-signature ((wrapper principal) (signature (buff 65))) (response (tuple (pubkey-hash (buff 20)) (valid-signer bool)) uint))

Source code:
(define-read-only (debug-signature (wrapper principal) (signature (buff 65)))
  (let
    (
      (pubkey-hash (try! (recover-pubkey-hash wrapper signature)))
    )
    (ok {
      pubkey-hash: pubkey-hash,
      valid-signer: (default-to false (map-get? migrator-signers-map pubkey-hash)),
    })
  )
)

Parameters:

Name Type Description
wrapper principal
signature (buff 65)

recover-pubkey-hash

View in file

(define-read-only (recover-pubkey-hash ((wrapper principal) (signature (buff 65))) (response (buff 20) uint))

Source code:
(define-read-only (recover-pubkey-hash (wrapper principal) (signature (buff 65)))
  (let
    (
      (id (unwrap! (map-get? wrapper-id-map wrapper) ERR_WRAPPER_NOT_REGISTERED))
      (msg (sha256 id))
      (pubkey (unwrap! (secp256k1-recover? msg signature) ERR_RECOVER))
    )
    (ok (hash160 pubkey))
  )
)

Parameters:

Name Type Description
wrapper principal
signature (buff 65)

is-valid-signer

View in file

(define-read-only (is-valid-signer ((pubkey (buff 20))) bool)

Helper method to check if a given principal is a valid verifier

Source code:
(define-read-only (is-valid-signer (pubkey (buff 20)))
  (default-to false (map-get? migrator-signers-map pubkey))
)

Parameters:

Name Type Description
pubkey (buff 20)

get-legacy-name

View in file

(define-read-only (get-legacy-name ((account principal)) (response (tuple (lease-ending-at (optional uint)) (lease-started-at uint) (name (buff 48)) (namespace (buff 20)) (owner principal) (zonefile-hash (buff 20))) uint))

Fetch the BNS legacy name and name properties owned by a given account.

@throws if the account does not own a valid name

@throws if the name owned by the account is expired

Source code:
(define-read-only (get-legacy-name (account principal))
  (match (contract-call? 'SP000000000000000000002Q6VF78.bns resolve-principal account)
    name (let
      (
        (properties (unwrap-panic (contract-call? 'SP000000000000000000002Q6VF78.bns name-resolve (get namespace name) (get name name))))
      )
      (ok (merge name properties))
    )
    e ERR_NO_NAME
  )
)

Parameters:

Name Type Description
account principal

resolve-and-transfer

View in file

(define-private (resolve-and-transfer ((wrapper principal)) (response (tuple (lease-ending-at (optional uint)) (lease-started-at uint) (name (buff 48)) (namespace (buff 20)) (owner principal) (zonefile-hash (buff 20))) uint))

Transfer an account's BNS legacy name to a wrapper contract. #[allow(unchecked_data)]

Source code:
(define-private (resolve-and-transfer (wrapper principal))
  (let
    (
      (name (try! (get-legacy-name tx-sender)))
    )
    (match (contract-call? 'SP000000000000000000002Q6VF78.bns name-transfer (get namespace name) (get name name) wrapper (some (get zonefile-hash name)))
      success (begin
        (print (merge name {
          topic: "v1-name-transfer",
          wrapper: wrapper,
        }))
        (ok name)
      )
      err-code (begin
        (print {
          topic: "name-transfer-error",
          bns-error: err-code,
          sender: tx-sender,
          name: name,
        })
        ERR_NAME_TRANSFER
      )
    )
  )
)

Parameters:

Name Type Description
wrapper principal

get-next-wrapper-id

View in file

(define-private (get-next-wrapper-id () uint)

Source code:
(define-private (get-next-wrapper-id)
  (let
    (
      (id (var-get next-wrapper-id-var))
    )
    (var-set next-wrapper-id-var (+ id u1))
    id
  )
)

get-wrapper-name

View in file

(define-read-only (get-wrapper-name ((wrapper principal)) (optional uint))

Helper method to fetch the BNS legacy name that was previously transferred to a given wrapper contract.

Source code:
(define-read-only (get-wrapper-name (wrapper principal)) (map-get? wrapper-name-map wrapper))

Parameters:

Name Type Description
wrapper principal

get-name-wrapper

View in file

(define-read-only (get-name-wrapper ((name uint)) (optional principal))

Helper method to fetch the wrapper contract that was used during migration of a given name

Source code:
(define-read-only (get-name-wrapper (name uint)) (map-get? name-wrapper-map name))

Parameters:

Name Type Description
name uint the name ID of a BNSx name

get-id-from-wrapper

View in file

(define-read-only (get-id-from-wrapper ((wrapper principal)) (optional uint))

Source code:
(define-read-only (get-id-from-wrapper (wrapper principal))
  (map-get? wrapper-id-map wrapper)
)

Parameters:

Name Type Description
wrapper principal

get-wrapper-from-id

View in file

(define-read-only (get-wrapper-from-id ((id uint)) (optional principal))

Source code:
(define-read-only (get-wrapper-from-id (id uint))
  (map-get? id-wrapper-map id)
)

Parameters:

Name Type Description
id uint

Maps

migrator-signers-map

(define-map migrator-signers-map (buff 20) bool)

View in file

name-wrapper-map

(define-map name-wrapper-map uint principal)

View in file

wrapper-name-map

(define-map wrapper-name-map principal uint)

View in file

wrapper-id-map

(define-map wrapper-id-map principal uint)

View in file

id-wrapper-map

(define-map id-wrapper-map uint principal)

View in file

Variables

next-wrapper-id-var

uint

(define-data-var next-wrapper-id-var uint u0)

View in file

Constants

ROLE

(define-constant ROLE "mig-signer")

View in file

ERR_NO_NAME

(define-constant ERR_NO_NAME (err u6000))

View in file

ERR_UNAUTHORIZED

(define-constant ERR_UNAUTHORIZED (err u6001))

View in file

ERR_RECOVER

(define-constant ERR_RECOVER (err u6002))

View in file

ERR_INVALID_CONTRACT_NAME

(define-constant ERR_INVALID_CONTRACT_NAME (err u6003))

View in file

ERR_NAME_TRANSFER

(define-constant ERR_NAME_TRANSFER (err u6004))

View in file

ERR_WRAPPER_USED

(define-constant ERR_WRAPPER_USED (err u6005))

View in file

ERR_WRAPPER_NOT_REGISTERED

(define-constant ERR_WRAPPER_NOT_REGISTERED (err u6006))

View in file

ERR_WRAPPER_ALREADY_REGISTERED

(define-constant ERR_WRAPPER_ALREADY_REGISTERED (err u6007))

View in file