(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
)
)