Workspace IndexMath › 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))

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

연습

'모든 유효한 서명에 대해 검증이 통과한다' 같은 문장 다섯 개를 한정기호와 논리 결합자로 형식화한 뒤, 각각의 부정과 대우를 기호와 한국어 문장으로 함께 써 보라.

실무 · 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:

FormThe test it naturally suggests
OriginalTake credited transactions → check they paid the bridge
ContrapositiveConstruct 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:

pqp→q
TTT
TFF ← the only false row
FTT
FFT

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

SymbolRead asMeaning
p, qpropositiona sentence with a definite truth value
andtrue only if both are true
ortrue if at least one is true
¬notflips the value
impliesfalse only when p is true and q is false
if and only ifboth sides always share a truth value
logically equivalentthe 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

SymbolRead asMeaning
a ∈ Aa is an element of Amembership
A ⊆ BA is a subset of Bevery element of A is in B (equality allowed)
A ⊊ Bproper subsetsubset and not equal
A ∪ Bunionin either one
A ∩ Bintersectionin both
Aᶜcomplementeverything outside A
A \ Bdifferencein A but not in B
A × BCartesian productthe 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

SymbolRead asMnemonic
∀xfor all xan upside-down All
∃xthere exists an x such thata 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.”

PropertyFormulaIn wordsExample
reflexive∀a. aRarelated to itself=,
symmetricaRb → bRadirection does not matter=, “is a sibling of”
transitiveaRb ∧ bRc → aRcchains through, “is an ancestor of”
antisymmetricaRb ∧ bRa → a=bboth 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 ✅
TermMeaning
domainthe set of inputs
codomainthe set outputs are declared to live in (the declared type)
range / imagethe values that actually come out (a subset of the codomain)
KindFormulaIn words
injective (one-to-one)f(a)=f(b) → a=bdistinct inputs give distinct outputs — no collisions
surjective (onto)∀y ∈ codomain. ∃x. f(x)=ycovers the codomain — range = codomain
bijectiveinjective ∧ surjectiveperfect 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 termWhat you already do
contrapositive“write the failure-case tests”
injective“hash collisions must not happen”
bijective“an encoding must be decodable”
partial orderGit branches are mutually incomparable
equivalence classes / partitiongrouping transactions by block
vacuous trutha 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는 진리표에서 딱 한 줄만 거짓이다:

pqp→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 → qp, 후건(consequent) = q. 연언지(conjunct)로 이어붙인 각 조각 — A ∧ B ∧ C에서 A, B, C 각각.

집합

기호읽는 법
a ∈ Aa는 A의 원소소속
A ⊆ BA는 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≤aa=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이 다 걸러버린 상태

새로 배우는 게 아니라 이름표를 붙이는 작업에 가깝다.

← 2. 공급·수요·효용·시장균형4. 귀납법/구조적 재귀 →