Workspace Index › Math › Day 3
Propositional Logic, Sets, Functions, and Relations TODO
Math · Day 3 / 52 · July — Discrete Math & Logic (Day 3-10)
Concept
Propositional logic consists of propositions with truth values combined with connectives (∧, ∨, ¬, →, ↔); the key points are that the implication p→q is vacuously true whenever p is false, and its contrapositive ¬q→¬p is always logically equivalent. Sets are collections of elements using notation like ∈, ⊆, ∪, ∩, complement, and Cartesian product; the quantifiers ∀ and ∃ swap when negated, with the inner proposition also negated (¬∀x P(x) ≡ ∃x ¬P(x)). A relation is defined as a subset of the Cartesian product A×B; if it has all three of reflexivity, symmetry, and transitivity, it's an equivalence relation that partitions the set into disjoint equivalence classes, and if it has reflexivity, antisymmetry, and transitivity, it's a partial order. A function is a special relation that maps each element of the domain to exactly one value, and the distinctions between injective, surjective, and bijective are the basis for comparing sizes and for the existence of an inverse function. This notation isn't content in itself so much as the language for reading every definition and proof that follows, so the goal is to practice translating symbols back into sentences.
Papers, specs, and formal verification documents are all written in this notation, so failing to read the symbols leads to misreading an algorithm's preconditions and the scope of its guarantees.
Code & Formula
# 명제논리·집합·함수·관계 — 함의(→)의 대우(¬q→¬p)가 항상 동치임을 진리표로 확인.
# 곱집합의 부분집합으로서의 "관계"와, 정의역 원소마다 값이 유일한 "함수"도 함께 점검.
def implies(p, q):
return (not p) or q
def contrapositive(p, q):
return implies(not q, not p)
# p -> q 와 그 대우 ¬q -> ¬p 가 모든 진리값 조합에서 같은지 확인 (진리표 4행)
rows = [(p, q) for p in (False, True) for q in (False, True)]
same = all(implies(p, q) == contrapositive(p, q) for p, q in rows)
print("p→q ≡ ¬q→¬p (모든 행 일치)?", same)
for p, q in rows:
print(f" p={p!s:5} q={q!s:5} p→q={implies(p,q)!s:5} ¬q→¬p={contrapositive(p,q)}")
# 관계 R ⊆ A×B: A×B 의 부분집합이면 뭐든 관계다.
A = {1, 2, 3}
B = {"a", "b"}
R = {(1, "a"), (2, "b"), (2, "a")}
print("\nR ⊆ A×B ?", R.issubset({(a, b) for a in A for b in B}))
# 함수는 "정의역의 각 원소가 정확히 하나의 값"을 갖는 특수한 관계.
def is_function(rel, domain):
seen = {}
for a, b in rel:
if a in seen and seen[a] != b:
return False
seen[a] = b
return set(seen.keys()) == domain
f_ok = {(1, "a"), (2, "b"), (3, "a")} # 1->a, 2->b, 3->a : 함수
f_bad = {(1, "a"), (1, "b"), (2, "a")} # 1이 두 값을 가짐: 함수 아님
print("f_ok 는 함수?", is_function(f_ok, A))
print("f_bad 는 함수?", is_function(f_bad, A))
docs/code/math/math-3.py
Exercise
Formalize five sentences like "verification passes for every valid signature" using quantifiers and logical connectives, then write out both the negation and the contrapositive of each, in symbols and in English.
Practical Connection
If you write a smart contract invariant (e.g., "the sum of position tokens issued across all markets equals the collateral balance") precisely in ∀ form, you can carry it directly into a test property or a formal verification spec.
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/math-50-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.
한국어
명제논리·집합·함수·관계 TODO
Math · Day 3 / 52 · 7월 — 이산수학·논리 (Day 3–10)
표기법만 읽으면 끝
개념
명제논리는 참·거짓 값을 갖는 명제와 결합자(∧, ∨, ¬, →, ↔)로 이루어지며, 특히 함의 p→q는 p가 거짓이면 무조건 참이라는 점과 그 대우 ¬q→¬p가 항상 동치라는 점이 핵심이다. 집합은 원소의 모임으로 ∈, ⊆, ∪, ∩, 여집합, 곱집합 같은 표기를 쓰고, 한정기호 ∀와 ∃는 부정할 때 서로 뒤바뀌며 안쪽 명제가 부정된다(¬∀x P(x) ≡ ∃x ¬P(x)). 관계는 곱집합 A×B의 부분집합으로 정의되며, 반사·대칭·추이 세 성질을 모두 가지면 동치관계가 되어 집합을 서로소인 동치류로 분할하고, 반사·반대칭·추이를 가지면 부분순서가 된다. 함수는 정의역의 각 원소에 정확히 하나의 값을 대응시키는 특수한 관계이며, 단사(injective)·전사(surjective)·전단사(bijective)의 구분이 크기 비교와 역함수 존재의 기준이 된다. 이 표기법들은 그 자체가 내용이라기보다 이후의 모든 정의와 증명을 읽는 언어이므로, 기호를 문장으로 바꿔 읽는 훈련이 목표다.
논문·명세·정형 검증 문서는 전부 이 표기로 쓰여 있어서, 기호를 못 읽으면 알고리즘의 전제 조건과 보장 범위를 오해하게 된다.
코드 · 수식
# 명제논리·집합·함수·관계 — 함의(→)의 대우(¬q→¬p)가 항상 동치임을 진리표로 확인.
# 곱집합의 부분집합으로서의 "관계"와, 정의역 원소마다 값이 유일한 "함수"도 함께 점검.
def implies(p, q):
return (not p) or q
def contrapositive(p, q):
return implies(not q, not p)
# p -> q 와 그 대우 ¬q -> ¬p 가 모든 진리값 조합에서 같은지 확인 (진리표 4행)
rows = [(p, q) for p in (False, True) for q in (False, True)]
same = all(implies(p, q) == contrapositive(p, q) for p, q in rows)
print("p→q ≡ ¬q→¬p (모든 행 일치)?", same)
for p, q in rows:
print(f" p={p!s:5} q={q!s:5} p→q={implies(p,q)!s:5} ¬q→¬p={contrapositive(p,q)}")
# 관계 R ⊆ A×B: A×B 의 부분집합이면 뭐든 관계다.
A = {1, 2, 3}
B = {"a", "b"}
R = {(1, "a"), (2, "b"), (2, "a")}
print("\nR ⊆ A×B ?", R.issubset({(a, b) for a in A for b in B}))
# 함수는 "정의역의 각 원소가 정확히 하나의 값"을 갖는 특수한 관계.
def is_function(rel, domain):
seen = {}
for a, b in rel:
if a in seen and seen[a] != b:
return False
seen[a] = b
return set(seen.keys()) == domain
f_ok = {(1, "a"), (2, "b"), (3, "a")} # 1->a, 2->b, 3->a : 함수
f_bad = {(1, "a"), (1, "b"), (2, "a")} # 1이 두 값을 가짐: 함수 아님
print("f_ok 는 함수?", is_function(f_ok, A))
print("f_bad 는 함수?", is_function(f_bad, A))
docs/code/math/math-3.py
연습
'모든 유효한 서명에 대해 검증이 통과한다' 같은 문장 다섯 개를 한정기호와 논리 결합자로 형식화한 뒤, 각각의 부정과 대우를 기호와 한국어 문장으로 함께 써 보라.
실무 · Verex 연결
스마트 컨트랙트 불변식(예: 모든 마켓에서 발행된 포지션 토큰의 합은 담보 잔액과 같다)을 ∀ 형태로 정확히 적어 두면 그대로 테스트 속성이나 정형 검증 명세로 옮길 수 있다.
공부한 날 원본 커리큘럼(docs/knowledge/math-50-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.
Session note · 2026-08-13
Making the notation earn its keep — the Coreum bridge bug as a logic formula
Day 3 is a vocabulary day, which is dull read on its own. It stops being dull the moment you see that a real $600k exploit is one missing conjunct in an antecedent.
Hand-written note, appended below the generated study sections. The curriculum entry links here directly.
1. The bug, written as a formula
On 2026-08-09 the Coreum XRPL bridge lost 199,916 XRP in 97 minutes. The relayer credited deposits based on a transaction’s memo field without ever checking that the funds actually went to the bridge address. In logic:
implemented:
∀tx. (Payment(tx) ∧ Validated(tx) ∧ HasMemo(tx)) → Credit(memo(tx).addr, amount(tx))
intended:
∀tx. (Payment(tx) ∧ Validated(tx) ∧ HasMemo(tx) ∧ Dest(tx) = BridgeAddr) → Credit(...)
The difference is a single conjunct in the antecedent. Drop a conjunct and the set of transactions satisfying the antecedent grows:
S_impl = { tx : Payment ∧ Validated ∧ HasMemo }
S_correct = { tx : Payment ∧ Validated ∧ HasMemo ∧ Dest = Bridge }
S_correct ⊊ S_impl
the exploit lives exactly in S_impl \ S_correct
This is the formal statement of “a weak antecedent makes the implication true in cases you never intended.” The attacker sent XRP to their own wallet with a well-formed memo — a perfectly valid XRPL transaction — and the relayer credited them. XRPL was never compromised; the entire fault was in how an off-chain program interpreted a legitimate transaction.
2. Negate the invariant and you get the attack
The safety property you actually want:
∀tx. Credited(tx) → PaidBridge(tx)
Negate it, applying ¬∀x P(x) ≡ ∃x ¬P(x) and ¬(p → q) ≡ p ∧ ¬q:
¬∀tx. (Credited(tx) → PaidBridge(tx))
≡ ∃tx. (Credited(tx) ∧ ¬PaidBridge(tx))
That is the definition of the exploit — “there exists a transaction that got credited without paying the bridge.”
Practical technique. The quantifier-negation rule taught on Day 3 has a direct audit use: negating an invariant mechanically produces the specification of its attack. Write the invariant, negate it, and ask “can I construct a witness for this ∃?”
3. The contrapositive tells you which tests you never wrote
original: ∀tx. Credited(tx) → PaidBridge(tx)
contrapositive: ∀tx. ¬PaidBridge(tx) → ¬Credited(tx)
Logically equivalent — but they suggest completely different test suites:
| Form | The test it naturally suggests |
| Original | Take credited transactions → check they paid the bridge |
| Contrapositive | Construct transactions that did not pay → check they are not credited |
Coreum never wrote the second one. Almost nobody does — people test with valid inputs and rarely build a systematic suite for “invalid inputs are rejected.”
⚠ The classic confusion — converse and inverse are not equivalent.
p → q "paid the bridge → gets credited"
q → p converse: "got credited → paid the bridge" ← a DIFFERENT claim
¬p → ¬q inverse: "did not pay → not credited" ← a DIFFERENT claim
¬q → ¬p contrapositive: "not credited → did not pay" ← equivalent ✅
Only the contrapositive is equivalent. Implementing “paid → credited” and then believing “credited → paid” is exactly the Coreum mistake.
4. Vacuous truth — the fuzzing hazard
p → q is false in only one row of the truth table:
| p | q | p→q |
| T | T | T |
| T | F | F ← the only false row |
| F | T | T |
| F | F | T |
A false antecedent makes the whole implication true regardless of the consequent. “If I were president I would give you a billion” is true, because I am not president. No lie was told.
This abstraction causes real damage in property-based testing:
function testInvariant(uint amount) public {
vm.assume(amount > 0 && amount < balance); // ← the antecedent
// ... assert
}
If the assumption filters out nearly every generated input, the property passes on the handful that survive while verifying essentially nothing — green, and vacuously true. This is exactly why Foundry caps vm.assume rejection rates and Hypothesis warns about heavy filtering.
Habit: when a property test passes, also check how many inputs actually satisfied the antecedent.
5. Glossary — every term on this page
Propositional logic
| Symbol | Read as | Meaning |
p, q | proposition | a sentence with a definite truth value |
∧ | and | true only if both are true |
∨ | or | true if at least one is true |
¬ | not | flips the value |
→ | implies | false only when p is true and q is false |
↔ | if and only if | both sides always share a truth value |
≡ | logically equivalent | the two formulas agree in every case |
antecedent = the p in p → q; consequent = the q. A conjunct is one piece joined by ∧ — in A ∧ B ∧ C, each of A, B, C.
Sets
| Symbol | Read as | Meaning |
a ∈ A | a is an element of A | membership |
A ⊆ B | A is a subset of B | every element of A is in B (equality allowed) |
A ⊊ B | proper subset | subset and not equal |
A ∪ B | union | in either one |
A ∩ B | intersection | in both |
Aᶜ | complement | everything outside A |
A \ B | difference | in A but not in B |
A × B | Cartesian product | the set of all ordered pairs (a,b) |
A = {1,2}, B = {x,y}
A × B = { (1,x), (1,y), (2,x), (2,y) } ← 2×2 = 4 pairs, "every combination"
Quantifiers
| Symbol | Read as | Mnemonic |
∀x | for all x | an upside-down All |
∃x | there exists an x such that | a mirrored Exists |
¬∀x P(x) ≡ ∃x ¬P(x) "not everything is P" = "something isn't P"
¬∃x P(x) ≡ ∀x ¬P(x) "nothing is P" = "everything isn't P"
Relations
Definition: a relation R is a subset of the Cartesian product, R ⊆ A × B. It sounds grand but means only “out of every possible pairing, the list of ones that actually hold.” (a,b) ∈ R reads “a is related to b.”
| Property | Formula | In words | Example |
| reflexive | ∀a. aRa | related to itself | =, ≤ |
| symmetric | aRb → bRa | direction does not matter | =, “is a sibling of” |
| transitive | aRb ∧ bRc → aRc | chains through | ≤, “is an ancestor of” |
| antisymmetric | aRb ∧ bRa → a=b | both directions ⇒ same thing | ≤, ⊆ |
⚠ Antisymmetric does not mean “not symmetric.” It means that if the relation holds both ways, the two elements were identical to begin with. ≤ is the model: a≤b and b≤a forces a=b.
Equivalence relation = reflexive + symmetric + transitive. It partitions the set into disjoint equivalence classes — clumps of mutually related elements.
Example: “is in the same block as.” Reflexive (a tx is in its own block), symmetric, transitive. Result: every transaction is cleanly sorted into exactly one block, no overlaps. That is a partition.
Partial order = reflexive + antisymmetric + transitive. An ordering where not every pair is comparable.
Example: Git’s ancestor relation. If A is an ancestor of B there is an order, but two commits on different branches are incomparable — so it is partial, not total. Dependency graphs and happens-before have the same shape.
Functions
Definition: a special relation where each element of the domain maps to exactly one value.
a relation but not a function: {(1,a), (1,b)} ← 1 has two values ❌
a function: {(1,a), (2,a)} ← distinct inputs sharing a value is fine ✅
| Term | Meaning |
| domain | the set of inputs |
| codomain | the set outputs are declared to live in (the declared type) |
| range / image | the values that actually come out (a subset of the codomain) |
| Kind | Formula | In words |
| injective (one-to-one) | f(a)=f(b) → a=b | distinct inputs give distinct outputs — no collisions |
| surjective (onto) | ∀y ∈ codomain. ∃x. f(x)=y | covers the codomain — range = codomain |
| bijective | injective ∧ surjective | perfect pairing — an inverse exists |
hash function: must NOT be injective — the domain is far larger, so collisions
are unavoidable (pigeonhole). An injective hash would be invertible.
encoding: must be bijective (base58, RLP, ABI) — decoding is the inverse,
so the inverse has to exist.
address derivation: we want injectivity (two keys sharing an address is a disaster)
but only get it probabilistically.
“An inverse exists ⟺ the function is bijective” is the whole requirement spec for an encoding scheme in one line.
A trap worth knowing: quantifier placement
∀t. ( Σ_{s ≤ t} spent(s) ≤ cap ) ← the running total is under the cap at EVERY moment
Σ_{all t} spent(t) ≤ cap ← the grand total is under the cap
The first has ∀ on the outside, so it is checked at every point in time; the second is a single equation. A session-key spending limit needs the first. Implement the second and you only check at settlement time, missing any overrun in between.
6. If you keep only one thing
Once you implement p → q, check that you have also tested the contrapositive ¬q → ¬p.
That single line is why Coreum lost 199,916 XRP. Everything else on this page can be met again later without loss — this notation is not content so much as the language for reading every definition and proof that follows, and Days 4–10 will keep supplying repetitions.
And most of these concepts are already familiar under other names:
| Mathematical term | What you already do |
| contrapositive | “write the failure-case tests” |
| injective | “hash collisions must not happen” |
| bijective | “an encoding must be decodable” |
| partial order | Git branches are mutually incomparable |
| equivalence classes / partition | grouping transactions by block |
| vacuous truth | a fuzz run where vm.assume filtered everything out |
It is less learning new ideas than attaching labels to ones you have.
세션 노트 · 2026-08-13
표기법이 밥값을 하는 순간 — Coreum 브릿지 버그를 논리식으로
Day 3은 용어를 익히는 날이라 그 자체로는 지루하다. 그런데 실제로 20만 XRP가 나간 사고가 전건에서 연언지 하나가 빠진 것으로 정확히 표현된다는 걸 보면 얘기가 달라진다.
생성된 학습 섹션 아래에 덧붙인 손으로 쓴 노트. 커리큘럼 항목이 이 페이지를 직접 가리킨다.
1. 버그를 논리식으로 써보면
2026-08-09, Coreum XRPL 브릿지가 97분 만에 199,916 XRP를 잃었다. 릴레이어가 트랜잭션의 메모 필드만 보고 입금을 인정했고, 돈이 실제로 브릿지 주소로 갔는지는 한 번도 확인하지 않았다. 논리식으로 쓰면:
구현된 규칙:
∀tx. (Payment(tx) ∧ Validated(tx) ∧ HasMemo(tx)) → Credit(memo(tx).addr, amount(tx))
의도한 규칙:
∀tx. (Payment(tx) ∧ Validated(tx) ∧ HasMemo(tx) ∧ Dest(tx) = BridgeAddr) → Credit(...)
차이는 전건의 연언지 하나뿐이다. 연언지를 하나 빼면 전건을 만족하는 tx의 집합이 커진다:
S_구현 = { tx : Payment ∧ Validated ∧ HasMemo }
S_의도 = { tx : Payment ∧ Validated ∧ HasMemo ∧ Dest = Bridge }
S_의도 ⊊ S_구현
공격은 정확히 S_구현 \ S_의도 에 산다
이것이 “전건이 약하면 함의가 의도보다 넓게 참이 된다”의 형식적 서술이다. 공격자는 자기 지갑으로 XRP를 보내면서 메모만 올바른 형식으로 채웠다 — XRPL 상에서 완벽하게 유효한 트랜잭션이었고, 릴레이어가 크레딧을 줬다. XRPL은 뚫린 적이 없다. 버그는 100% 오프체인 프로그램이 정상 트랜잭션을 어떻게 해석했느냐에 있었다.
2. 불변식을 부정하면 공격이 나온다
지키고 싶은 안전 속성:
∀tx. Credited(tx) → PaidBridge(tx)
¬∀x P(x) ≡ ∃x ¬P(x) 와 ¬(p → q) ≡ p ∧ ¬q 를 적용해 부정하면:
¬∀tx. (Credited(tx) → PaidBridge(tx))
≡ ∃tx. (Credited(tx) ∧ ¬PaidBridge(tx))
이게 공격의 정의 그 자체다 — “크레딧은 받았는데 브릿지에 안 낸 tx가 존재한다.”
실전 기법. Day 3에서 배우는 한정기호 부정 규칙에는 감사에 바로 쓰는 용법이 있다: 불변식을 부정하면 그 공격의 명세가 기계적으로 나온다. 불변식을 쓰고, 부정하고, “이 ∃의 증인을 만들 수 있나?”를 묻는 절차.
3. 대우는 안 쓴 테스트를 알려준다
원래: ∀tx. Credited(tx) → PaidBridge(tx)
대우: ∀tx. ¬PaidBridge(tx) → ¬Credited(tx)
논리적으로 동치인데 시사하는 테스트가 완전히 다르다:
| 형태 | 자연스럽게 떠오르는 테스트 |
| 원래 | 크레딧된 tx들을 가져와서 → 브릿지에 냈는지 확인 |
| 대우 | 브릿지에 안 낸 tx를 만들어서 → 크레딧 안 되는지 확인 |
Coreum이 안 짠 건 두 번째다. 그리고 대부분이 안 짜는 것도 두 번째다 — 정상 입력으로는 테스트하지만, “잘못된 입력이 거부되는지”를 체계적으로 짜는 경우는 드물다.
⚠ 고전적 혼동 — 역과 이는 동치가 아니다.
p → q "브릿지에 냈으면 크레딧을 준다"
q → p 역(converse): "크레딧을 줬으면 낸 것이다" ← 다른 명제
¬p → ¬q 이(inverse): "안 냈으면 크레딧을 안 준다" ← 다른 명제
¬q → ¬p 대우: "크레딧을 안 줬으면 안 낸 것이다" ← 동치 ✅
대우만 동치다. “냈으면 준다”를 구현해놓고 “줬으면 낸 것이다”라고 믿는 것이 정확히 Coreum의 착각이었다.
4. 공허한 참 — 퍼징의 함정
p → q는 진리표에서 딱 한 줄만 거짓이다:
| p | q | p→q |
| 참 | 참 | 참 |
| 참 | 거짓 | 거짓 ← 유일하게 거짓 |
| 거짓 | 참 | 참 |
| 거짓 | 거짓 | 참 |
전건이 거짓이면 후건이 뭐든 전체가 참이다. “내가 대통령이면 너에게 100억을 준다” — 나는 대통령이 아니니 이 문장은 참이다. 거짓말을 한 게 아니다.
이 추상적인 성질이 property-based 테스트에서 실제 사고를 낸다:
function testInvariant(uint amount) public {
vm.assume(amount > 0 && amount < balance); // ← 전건
// ... assert
}
가정이 너무 빡세서 생성된 입력이 거의 다 걸러지면, 살아남은 몇 개로 통과하지만 사실상 아무것도 검증하지 않는다 — 초록불인데 공허하게 참인 상태. Foundry가 vm.assume 거부율에 상한을 두고 Hypothesis가 필터링 경고를 띄우는 이유가 정확히 이것이다.
습관: 속성 테스트가 통과하면 실제로 전건을 만족한 입력이 몇 개였는지도 함께 본다.
5. 용어 사전 — 이 페이지의 모든 기호
명제논리
| 기호 | 읽는 법 | 뜻 |
p, q | 명제 | 참/거짓이 정해지는 문장. “서명이 유효하다” ✅ / “오늘 날씨” ❌ |
∧ | 그리고 (AND) | 둘 다 참일 때만 참 |
∨ | 또는 (OR) | 하나라도 참이면 참 |
¬ | 아니다 (NOT) | 뒤집기 |
→ | ~이면 ~이다 (함의) | p가 참이고 q가 거짓일 때만 거짓 |
↔ | ~일 때만 (동치) | 양쪽 진리값이 항상 같음 |
≡ | 논리적 동치 | 두 식이 모든 경우에 같은 값 |
전건(antecedent) = p → q의 p, 후건(consequent) = q. 연언지(conjunct)는 ∧로 이어붙인 각 조각 — A ∧ B ∧ C에서 A, B, C 각각.
집합
| 기호 | 읽는 법 | 뜻 |
a ∈ A | a는 A의 원소 | 소속 |
A ⊆ B | A는 B의 부분집합 | A의 모든 원소가 B에 있음 (같아도 됨) |
A ⊊ B | 진부분집합 | 부분집합이면서 같지는 않음 |
A ∪ B | 합집합 | 둘 중 하나에라도 속함 |
A ∩ B | 교집합 | 둘 다에 속함 |
Aᶜ | 여집합 | 전체집합에서 A를 뺀 것 |
A \ B | 차집합 | A에는 있고 B에는 없는 것 |
A × B | 곱집합 | 모든 순서쌍 (a,b)의 집합 |
A = {1,2}, B = {x,y}
A × B = { (1,x), (1,y), (2,x), (2,y) } ← 2×2 = 4개, "가능한 모든 조합"
한정기호
| 기호 | 읽는 법 | 기억법 |
∀x | 모든 x에 대해 | for All 의 A를 뒤집은 모양 |
∃x | 어떤 x가 존재해서 | there Exists 의 E를 뒤집은 모양 |
¬∀x P(x) ≡ ∃x ¬P(x) "모든 게 P다"의 부정 = "P가 아닌 게 하나라도 있다"
¬∃x P(x) ≡ ∀x ¬P(x) "P인 게 있다"의 부정 = "전부 P가 아니다"
관계
정의: 관계 R은 곱집합의 부분집합이다 (R ⊆ A × B). 거창해 보이지만 뜻은 “가능한 모든 조합 중에서 실제로 성립하는 것만 골라낸 목록”이다. (a,b) ∈ R이면 “a와 b는 관계가 있다”로 읽는다.
| 성질 | 식 | 말로 | 예 |
| 반사(reflexive) | ∀a. aRa | 자기 자신과 관계 있음 | =, ≤ |
| 대칭(symmetric) | aRb → bRa | 방향이 상관없음 | =, “형제다” |
| 추이(transitive) | aRb ∧ bRc → aRc | 타고 넘어감 | ≤, “조상이다” |
| 반대칭(antisymmetric) | aRb ∧ bRa → a=b | 양방향이면 같은 것 | ≤, ⊆ |
⚠ 반대칭은 “대칭이 아니다”가 아니다. “양쪽으로 다 성립하면 애초에 같은 놈이었다”는 뜻이다. ≤가 대표적 — a≤b이고 b≤a면 a=b다.
동치관계 = 반사 + 대칭 + 추이. 집합을 서로소인 동치류로 분할한다 — 동치류는 서로 관계 있는 것들끼리 뭉친 덩어리.
예: “같은 블록에 포함된 트랜잭션이다.” 자기 자신과 같은 블록(반사), 방향 무관(대칭), 타고 넘어감(추이). 결과: 모든 tx가 블록 단위로 겹치지 않게 깔끔히 쪼개진다. 이게 분할이다.
부분순서 = 반사 + 반대칭 + 추이. 순서는 있지만 모든 쌍을 비교할 수는 없는 관계.
예: Git 커밋의 조상 관계. A가 B의 조상이면 순서가 있지만, 다른 브랜치의 두 커밋은 비교 불가다 — 그래서 전순서가 아니라 부분순서. 의존성 그래프, happens-before도 같은 구조.
함수
정의: 정의역의 각 원소가 정확히 하나의 값에 대응되는 특수한 관계.
관계인데 함수 아님: {(1,a), (1,b)} ← 1이 두 값을 가짐 ❌
함수: {(1,a), (2,a)} ← 서로 다른 게 같은 값 가는 건 OK ✅
| 용어 | 뜻 |
| 정의역(domain) | 입력이 되는 집합 |
| 공역(codomain) | 출력이 놓일 수 있다고 선언된 집합 (타입 선언) |
| 치역(range/image) | 출력이 실제로 나오는 값들 (공역의 부분집합) |
| 종류 | 식 | 말로 |
| 단사(injective, 1:1) | f(a)=f(b) → a=b | 서로 다른 입력은 서로 다른 출력 — 충돌 없음 |
| 전사(surjective, onto) | ∀y ∈ 공역. ∃x. f(x)=y | 공역을 빠짐없이 덮음 — 치역 = 공역 |
| 전단사(bijective) | 단사 ∧ 전사 | 완벽한 1:1 대응 — 역함수가 존재 |
해시 함수: 단사가 아니어야 정상 — 정의역이 훨씬 크니 충돌은 불가피(비둘기집 원리).
단사면 원본 복원이 가능해져 오히려 위험하다.
인코딩: 전단사여야 한다(base58, RLP, ABI) — 디코딩이 곧 역함수라 존재해야 하니까.
주소 파생: 단사이길 바라지만(다른 키가 같은 주소면 재앙) 확률적으로만 보장된다.
“역함수가 존재한다 ⟺ 전단사다” — 이 한 줄이 인코딩 설계의 요구사항 전부다.
알아둘 함정: 한정기호의 위치
∀t. ( Σ_{s ≤ t} spent(s) ≤ cap ) ← 모든 시점에서 누적합이 한도 이하
Σ_{모든 t} spent(t) ≤ cap ← 전체 총합이 한도 이하
위는 ∀가 밖에 있어 매 시점 확인하고, 아래는 하나의 등식이다. 세션 키 한도는 위가 맞다. 아래로 구현하면 정산 시점에만 확인하게 되어 중간의 초과를 못 잡는다.
6. 하나만 가져간다면
p → q를 구현했으면, 대우 ¬q → ¬p도 테스트했는지 확인한다.
Coreum이 199,916 XRP를 잃은 이유가 이 한 줄이다. 나머지는 나중에 다시 만나도 손해가 없다 — 이 표기법은 내용이라기보다 이후의 모든 정의와 증명을 읽는 언어이고, Day 4–10 내내 반복 노출이 걸려 있다.
그리고 이 개념들 대부분은 이미 다른 이름으로 알고 있는 것들이다:
| 수학 용어 | 이미 하고 있는 것 |
| 함의의 대우 | “실패 케이스 테스트를 짜야 한다” |
| 단사(injective) | “해시 충돌이 나면 안 된다” |
| 전단사(bijective) | “인코딩은 디코딩이 가능해야 한다” |
| 부분순서 | Git 브랜치는 서로 비교 불가 |
| 동치류 분할 | 트랜잭션을 블록 단위로 묶기 |
| 공허한 참 | 퍼징에서 vm.assume이 다 걸러버린 상태 |
새로 배우는 게 아니라 이름표를 붙이는 작업에 가깝다.