Source Code

(impl-trait .messenger-trait.messenger-trait)
(use-trait gas-oracle-trait .gas-oracle-trait.gas-oracle-trait)

(define-constant err-unauthorized (err u3000))
(define-constant err-message-not-found (err u3001))
(define-constant err-invalid-chain-id (err u3002))
(define-constant err-already-sent (err u3003))
(define-constant err-invalid-destination (err u3004))
(define-constant err-invalid-primary (err u3005))
(define-constant err-invalid-secondary (err u3006))
(define-constant err-wrong-gas-oracle (err u3007))
(define-constant err-already-initialized (err u3008))
(define-constant err-wrong-buffer-length (err u3009))
(define-constant err-gas-usage-not-set (err u3010))
(define-constant err-not-initialized (err u3011))
(define-constant err-wrong-msg-length (err u3012))

(define-data-var owner principal contract-caller)
(define-data-var gas-oracle-principal (optional principal) none)
(define-data-var this-chain-id (optional uint) none)
(define-data-var other-chain-ids (buff 32) 0x0000000000000000000000000000000000000000000000000000000000000000)
(define-map sent-messages-block
  (buff 32)
  uint
)
(define-map received-messages
  (buff 32)
  bool
)
(define-data-var primary-validator (buff 33) 0x000000000000000000000000000000000000000000000000000000000000000000)
(define-map secondary-validators
  (buff 33)
  bool
)

(define-map gas-usage-map
  uint
  uint
)

(define-public (init
    (this-chain-id_ uint)
    (gas-oracle principal)
  )
  (begin
    (asserts! (is-none (var-get this-chain-id)) err-already-initialized)
    (var-set this-chain-id (some this-chain-id_))
    (var-set gas-oracle-principal (some gas-oracle))
    (only-owner)
  )
)

(define-public (send-message
    (gas-oracle-ref <gas-oracle-trait>)
    (message (buff 32))
  )
  (let (
      (src-id (try! (buffer-byte-uint message u0)))
      (dst-id (try! (buffer-byte-uint message u1)))
      (tx-cost (try! (get-transaction-cost gas-oracle-ref dst-id)))
      (caller-bytes (principal-hash contract-caller))
      (msg-hash (try! (hash-with-sender-address message caller-bytes)))
      (this-chain-id_ (unwrap! (var-get this-chain-id) err-not-initialized))
    )
    (try! (assert-gas-oracle gas-oracle-ref))
    (asserts! (is-eq src-id this-chain-id_) err-invalid-chain-id)
    (asserts! (is-eq (buffer-byte (var-get other-chain-ids) dst-id) (ok 0x01))
      err-invalid-destination
    )
    (asserts! (map-insert sent-messages-block msg-hash stacks-block-height)
      err-already-sent
    )
    (try! (stx-transfer? tx-cost contract-caller (as-contract contract-caller)))
    (print {
      event: "MessageSent",
      message: msg-hash,
    })
    (ok tx-cost)
  )
)

(define-public (receive-message
    (message (buff 32))
    (signature1 (buff 65))
    (signature2 (buff 65))
  )
  (let (
      (dst-id (try! (buffer-byte-uint message u1)))
      (message-hash (keccak256 message))
      (signer1 (try! (secp256k1-recover? message-hash signature1)))
      (signer2 (try! (secp256k1-recover? message-hash signature2)))
      (this-chain-id_ (unwrap! (var-get this-chain-id) err-not-initialized))
    )
    (asserts! (is-eq dst-id this-chain-id_) err-invalid-chain-id)
    (asserts! (is-eq signer1 (var-get primary-validator)) err-invalid-primary)
    (asserts! (is-eq (map-get? secondary-validators signer2) (some true))
      err-invalid-secondary
    )
    (map-set received-messages message true)
    (print {
      event: "MessageReceived",
      message: message,
    })
    (ok true)
  )
)

(define-public (withdraw-gas-tokens (amount uint))
  (let ((caller contract-caller))
    (try! (as-contract (stx-transfer? amount tx-sender caller)))
    (only-owner)
  )
)

(define-public (set-primary-validator (val (buff 33)))
  (begin
    (asserts! (is-eq (len val) u33) err-wrong-buffer-length)
    (var-set primary-validator val)
    (only-owner)
  )
)

(define-public (add-secondary-validator (val (buff 33)))
  (begin
    (asserts! (is-eq (len val) u33) err-wrong-buffer-length)
    (map-set secondary-validators val true)
    (only-owner)
  )
)

(define-public (remove-secondary-validator (val (buff 33)))
  (begin
    (asserts! (is-eq (len val) u33) err-wrong-buffer-length)
    (map-delete secondary-validators val)
    (only-owner)
  )
)

(define-public (set-other-chain-ids (value (buff 32)))
  (begin
    (asserts! (is-eq (len value) u32) err-wrong-buffer-length)
    (var-set other-chain-ids value)
    (only-owner)
  )
)

;; ============================ VIEW =============================
(define-read-only (get-this-chain-id)
  (ok (unwrap! (var-get this-chain-id) err-not-initialized))
)

(define-read-only (get-other-chain-ids)
  (ok (var-get other-chain-ids))
)

(define-read-only (get-gas-oracle)
  (ok (unwrap! (var-get gas-oracle-principal) err-not-initialized))
)

(define-read-only (is-message-received (message (buff 32)))
  (ok (is-eq (map-get? received-messages message) (some true)))
)

(define-read-only (get-sent-message-block (message (buff 32)))
  (ok (unwrap! (map-get? sent-messages-block message) err-message-not-found))
)

;; ========================== GAS USAGE ==========================

(define-public (set-gas-usage
    (chain-id_ uint)
    (gas uint)
  )
  (begin
    (map-set gas-usage-map chain-id_ gas)
    (only-owner)
  )
)

(define-read-only (get-gas-usage (chain-id_ uint))
  (match (map-get? gas-usage-map chain-id_)
    gas-usage (ok gas-usage)
    err-gas-usage-not-set
  )
)

(define-public (get-transaction-cost
    (gas-oracle-ref <gas-oracle-trait>)
    (chain-id_ uint)
  )
  (begin
    (try! (assert-gas-oracle gas-oracle-ref))
    (let (
        (gas-usage (try! (get-gas-usage chain-id_)))
        (tx-const (try! (contract-call? gas-oracle-ref get-transaction-gas-cost-in-native-token
          chain-id_ gas-usage
        )))
      )
      (ok (if (is-eq tx-const u0)
        u1
        tx-const
      ))
    )
  )
)

(define-private (assert-gas-oracle (gas-oracle-ref <gas-oracle-trait>))
  (let (
      (registered-gas-oracle (unwrap! (var-get gas-oracle-principal) err-not-initialized))
      (gas-oracle-address (contract-of gas-oracle-ref))
    )
    (asserts! (is-eq registered-gas-oracle gas-oracle-address)
      err-wrong-gas-oracle
    )
    (ok true)
  )
)

;; ========================= HELPERS ==========================

(define-read-only (hash-with-sender-address
    (message (buff 32))
    (sender (buff 32))
  )
  (let (
      (first-2-bytes (unwrap! (slice? message u0 u2) err-wrong-buffer-length))
      (combined (concat message sender))
      (hash (keccak256 combined))
      (last-30-bytes (unwrap! (slice? hash u2 u32) err-wrong-buffer-length))
    )
    (asserts! (is-eq (len combined) u64) err-wrong-msg-length)
    (ok (unwrap! (as-max-len? (concat first-2-bytes last-30-bytes) u32)
      err-wrong-msg-length
    ))
  )
)

(define-read-only (principal-hash (value principal))
  (let (
      (dest (unwrap-panic (principal-destruct? value)))
      (hash-bytes (get hash-bytes dest))
      (name (get name dest))
      (name-bytes (match name
        v (string-to-buff v)
        0x
      ))
    )
    (keccak256 (concat hash-bytes name-bytes))
  )
)

;; ========================= PRIVATE HELPERS ==========================

(define-private (buffer-byte-uint
    (b (buff 32))
    (i uint)
  )
  (ok (buff-to-uint-be (unwrap! (element-at? b i) err-wrong-buffer-length)))
)

(define-private (buffer-byte
    (b (buff 32))
    (i uint)
  )
  (ok (unwrap! (element-at? b i) err-wrong-buffer-length))
)

(define-private (string-to-buff (str (string-ascii 40)))
  (let (
      (str-bytes (unwrap-panic (to-consensus-buff? str)))
      (length (len str-bytes))
    )
    (unwrap-panic (slice? str-bytes u5 length))
  )
)

;; ========================= OWNABLE ==========================

(define-public (set-owner (new-owner principal))
  (begin
    (try! (only-owner))
    (var-set owner new-owner)
    (ok true)
  )
)

(define-private (only-owner)
  (if (is-eq contract-caller (var-get owner))
    (ok true)
    err-unauthorized
  )
)

Functions (21)

FunctionAccessArgs
send-messagepublicgas-oracle-ref: <gas-oracle-trait>, message: (buff 32
receive-messagepublicmessage: (buff 32
withdraw-gas-tokenspublicamount: uint
set-primary-validatorpublicval: (buff 33
add-secondary-validatorpublicval: (buff 33
remove-secondary-validatorpublicval: (buff 33
set-other-chain-idspublicvalue: (buff 32
get-this-chain-idread-only
get-other-chain-idsread-only
get-gas-oracleread-only
is-message-receivedread-onlymessage: (buff 32
get-sent-message-blockread-onlymessage: (buff 32
get-gas-usageread-onlychain-id_: uint
assert-gas-oracleprivategas-oracle-ref: <gas-oracle-trait>
hash-with-sender-addressread-onlymessage: (buff 32
principal-hashread-onlyvalue: principal
buffer-byte-uintprivateb: (buff 32
buffer-byteprivateb: (buff 32
string-to-buffprivatestr: (string-ascii 40
set-ownerpublicnew-owner: principal
only-ownerprivate