Skip to content

Instantly share code, notes, and snippets.

@vizanto
Last active December 12, 2018 13:03
Show Gist options
  • Save vizanto/841353e80868c736a01e61b29b9e53c8 to your computer and use it in GitHub Desktop.
Save vizanto/841353e80868c736a01e61b29b9e53c8 to your computer and use it in GitHub Desktop.
(def multi-transfer-invariant
'[$balance-sums
[:find [(sum ?balance-before) :as sum-before
(sum ?balance-after) :as sum-after
(sum ?balance-change) :as sum-of-change]
:with ?affected-entity
:in $before $after $txn
:where
;; Unify data from databases and transactions with affected-entity
[$after ?affected-entity :account/balance ?balance-after]
[$txn ?affected-entity :account/balance ?balance-change]
[(get-else $before ?affected-entity :account/balance 0) ?balance-before]
;; 1. Zero-Sum
[(+ ?balance-change ?balance-before) ?computed-balance-after]
[(= ?balance-after ?computed-balance-after)]
;; 2. Positivity
[(>= ?balance-after 0)]
;; 3. Sender spending
[$txn _ :transaction/signed-by ?sender]
(or
;; sender balance should only decrease
(and [(= ?sender ?affected-entity)]
[(> ?balance-before ?balance-after)])
;; recipient balances should only increase
(and [(not= ?sender ?affected-entity)]
[(< ?balance-before ?balance-after)]))]
$balance-check
[:find ?matches .
;-- this statement:
:within $balance-sums
;-- gets rewritten as:
:in [?sum-before ?sum-after ?sum-of-change]
;--
:where [(= ?sum-before ?sum-after)]
[(= ?sum-change 0) ?matches]]])
;;; Schema
{:db/ident :datopia.ednt/balance
:db/cardinality :db.cardinality/one
:db/valueType :db.type/bigdec
:db/invariant multi-transfer-invariant}
;;; Balance transfer Usage
(d/q multi-transfer-invariant
;; current state
@conn
;; apply transaction to current state
(dc/db-with @conn transfer-transaction)
;; empty database with only transaction applied
(dc/db-with (dc/empty-db schema) transfer-transaction)
transfer-transaction)
(def vote-results-query
'[$votes
[:find ?issue, ?proposal-name, (count ?vote) :as vote-count
:in $ ?issue
:where
[?ballot :ballot/issue ?issue]
;; Bind the proposal
[?proposal :proposal/name ?proposal-name]
;; Count only votes for this Ballot
[?vote :voter/ballot ?ballot]
;; Bind the votes for this proposal
[?vote :voter/vote-on-proposal ?proposal]]
$most-voted
[:find (max ?vote-count) :as winning-vote-count .
;-- this statement:
:within $votes
;-- gets rewritten as:
:in [[?issue ?proposal-name ?vote-count]]]
;--
$winners
[:find ?proposal-name ?vote-count
;-- this statement:
:within $votes $most-voted
;-- gets rewritten as:
:in [[?issue ?proposal-name ?vote-count]] ?winning-vote-count
;--
:where [(= ?winning-vote-count ?vote-count)]]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment