Bitcoin Programming ์ฑ…์„ ์ฝ์œผ๋ฉฐ ์ดํ•ดํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•œ๋‹ค. ์ฒซ๋ฒˆ์งธ๋Š” ์œ ํ•œ์ฒด์ด๋‹ค.

ํ™˜๊ฒฝ ์„ค์น˜

// ํŒŒ์ด์ฌ 3.5 ์ด์ƒ ๋ฒ„์ „ ์„ค์น˜

// pip ์„ค์น˜
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

// ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰
python3 get-pip.py

// Git ์„ค์น˜
// https://git-scm.com/downloads

// ์ฑ… ์†Œ์Šค์ฝ”๋“œ ๋ฐ›๊ธฐ
git clone https://github.com/jimmysong/programmingbitcoin
cd programmingbitcoin

// virtualenv ์„ค์น˜
pip install virtualenv --user

// ๊ฐ€์ƒ ํ™˜๊ฒฝ ๋งŒ๋“ค๊ธฐ
python3 -m virtualenv venv

// ๊ฐ€์ƒ ํ™˜๊ฒฝ venv ์ƒ์„ฑ ํ›„, ๋‚ด๋ถ€ activate ์‹คํ–‰ (๊ฐ€์ƒ ํ™˜๊ฒฝ ๋งŒ๋“ค๋ฉด ํ•ด๋‹น ํด๋”์— ์ด๋ฆ„ ์ ์€ ํด๋”๊ฐ€ ์ƒ๊ธด๋‹ค.)
source venv/bin/activate

// ์š”๊ตฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
pip install -r requirements.txt

// jupyter notebook ์‹คํ–‰
jupyter notebook

์ฒด (Field)

์‚ฌ์น™ ์—ฐ์‚ฐ์„ ์ง‘ํ•ฉ ์•ˆ์—์„œ ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ง‘ํ•ฉ

  • ์ฆ‰, ์—ฐ์‚ฐ์„ ํ†ตํ•ด ๋‚˜์˜จ ๊ฐ’ ๋˜ํ•œ ํ•ด๋‹น ์ง‘ํ•ฉ์˜ ์›์†Œ์—ฌ์•ผ ํ•œ๋‹ค.
  • ์œ ๋ฆฌ์ˆ˜, ์‹ค์ˆ˜, ๋ณต์†Œ์ˆ˜์˜ ๊ฒฝ์šฐ ์ฒด์˜ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•œ๋‹ค.
  • ์ •์ˆ˜๋Š” ์•ˆ๋œ๋‹ค. (๋‚˜๋ˆ—์…ˆ โ†’ ์œ ๋ฆฌ์ˆ˜)
  • ์—ฌ๊ธฐ์„œ ์—„๋ฐ€ํ•œ ์ฆ๋ช…์„ ํ•˜์ž๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ˆ ์ง๊ด€์ ์œผ๋กœ ์ดํ•ดํ•˜๊ณ  ๋„˜์–ด๊ฐ€์ž.

์œ ํ•œ์ฒด (Finite field)

  • ์œ ๋ฆฌ์ˆ˜์ฒด, ์‹ค์ˆ˜์ฒด, ๋ณต์†Œ์ˆ˜์ฒด์˜ ํŠน์ง•์€ ๋ฌดํ•œ ์ง‘ํ•ฉ์ด๋‹ค.
  • ๊ทธ๋ ‡๋‹ค๋ฉด ์œ ํ•œ์ง‘ํ•ฉ์ธ ์ฒด๋„ ์žˆ๋Š”๊ฐ€?
  • ๊ทธ ๋ฐฉ๋ฒ•์ค‘ ํ•˜๋‚˜๊ฐ€ ์œ ํ•œ์ฒด์ด๋‹ค.
  • ์œ ํ•œ์ฒด์—์„œ๋Š” ์ •์ˆ˜์ฒด(Z)์—์„œ ์†Œ์ˆ˜๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ฒƒ์„ ํ†ตํ•ด ์œ ํ•œ์ง‘ํ•ฉ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค.
  • ์˜ˆ์ปจ๋ฐ, 7๊ณผ ๊ฐ™์€ ์†Œ์ˆ˜๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๊ฐ€ ๊ฐ™์€ ์›์†Œ๋“ค์€ ๋ชจ๋‘ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  • ์ฆ‰, ์†Œ์ˆ˜๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋“ค์„ ์›์†Œ๋กœ ํ•˜๋Š” ์ง‘ํ•ฉ ๋ฅผ ์ •์˜ ํ•˜์ž.
  • ์—ฌ๊ธฐ์„œ p๋ฅผ ์œ„์ˆ˜(order)๋ผ ํ•œ๋‹ค.

์ฒด์˜ ์ฆ๋ช…

  • ์ฒด ์ธ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ„๋‹จํžˆ ์ƒ๊ฐํ–ˆ์„ ๋•Œ ์‚ฌ์น™ ์—ฐ์‚ฐ์— ๋Œ€ํ•ด ๋‹ซํ˜€์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.
  • ๋ง์…ˆ, ๊ณฑ์…ˆ, ๋บ„์…ˆ์— ๋Œ€ํ•ด์„œ๋Š” ์ž๋ช…ํ•˜๋‹ค.
  • ๋‚˜๋ˆ—์…ˆ์ด ๋ฌธ์ œ๋‹ค.

๋‚˜๋ˆ—์…ˆ

  • ์œ ํ•œ์ฒด ์›์†Œ๋“ค ๊ฐ„์˜ ๋‚˜๋ˆ—์…ˆ ๊ฒฐ๊ณผ๋Š” ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? ์‰ฝ๊ฒŒ ์˜ˆ์ƒํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ณฑ์…ˆ์œผ๋กœ ๋ถ€ํ„ฐ ๋‚˜๋ˆ—์…ˆ์˜ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ธกํ•ด๋ณด๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋œ๋‹ค.
  • ์— ์†ํ•œ ์›์†Œ(0~18)์— ๋Œ€ํ•ด ํ™•์ธํ•ด๋ณด์ž.
  • , ์ฆ‰
  • ๊ทธ๋Ÿผ ๋‚˜๋ˆ—์…ˆ์„ ํ•  ๋•Œ๋งˆ๋‹ค ์ด๋ ‡๊ฒŒ ๊ณ„์‚ฐํ•ด์•ผ ํ• ๊นŒ?
  • ์—ฌ๊ธฐ์„œ ํŽ˜๋ฅด๋งˆ์˜ ์†Œ์ •๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฆ๋ช…

  • ์—ฌ๊ธฐ์„œ ์‚ฌ์น™ ์—ฐ์‚ฐ์€ ๋ชจ๋“ˆ๋Ÿฌ ์—ฐ์‚ฐ์„ ๋งํ•จ

๊ทธ๋Ÿฐ๋ฐ,

์ฆ‰,

  • ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋‚˜๋ˆ„๊ธฐ๋ฅผ ๊ฑฐ๋“ญ์ œ๊ณฑ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์„œ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์ด๋œ๋‹ค.
  • ์—ฌ๊ธฐ์„œ ์ง€์ˆ˜๊ฐ€ ์ปค์ง€๊ฒŒ ๋˜๋ฉด ๊ณ„์‚ฐ์‹œ๊ฐ„์€ ๋” ๊ธธ์–ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค.
  • python์—์„œ pow ์—ฐ์‚ฐ์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋” ๋น ๋ฅธ ์—ฐ์‚ฐ์„ ์ง€์›ํ•œ๋‹ค.
    • pow(7, 17): ์ผ๋ฐ˜ ์†๋„ (7**17)
    • pow(7, 17, 19): ๋น ๋ฅธ ์†๋„ (7**17%19)
      • ๋ชจ๋“ˆ๋Ÿฌ ์—ฐ์‚ฐ ํŠน์ง•์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ๊ณฑ์…ˆ๋งˆ๋‹ค ๊ฐ’์˜ ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ์„œ ๊ณ„์‚ฐ
      • ๋ชจ๋“ˆ๋Ÿฌ๋Š” ์Œ์ˆ˜์— ์ ์šฉํ–ˆ์„ ์‹œ ๋ณด์ˆ˜์˜ ๋ชจ๋“ˆ๋Ÿฌ๊ฐ€ ์ ์šฉ๋˜์–ด ์–‘์ˆ˜๋กœ ๋–จ์–ด์ง„๋‹ค.

์œ„์ˆ˜๊ฐ€ ์†Œ์ˆ˜์ธ ์œ ํ•œ์ฒด๊ฐ€ ์œ ์šฉํ•œ ์ด์œ 

  • ์œ„์ˆ˜๊ฐ€ ์†Œ์ˆ˜์ธ ์œ ํ•œ์ฒด์˜ ๊ฒฝ์šฐ, ์œ ํ•œ์ฒด์— 0์ด ์•„๋‹Œ ์›์†Œ k๋กœ ์ „์ฒด ์ง‘ํ•ฉ์„ ๊ณฑํ•˜๋ฉด ๊ทธ ๊ฒฐ๊ณผ๋Š” ๋‹ค์‹œ ์›๋ž˜ ์ง‘ํ•ฉ์ด ๋œ๋‹ค.
    • ์›์†Œ์˜ ๋ฐฐ์—ด ์ˆœ์„œ๋Š” ๋‹ฌ๋ผ์ง€๋‚˜, ์ง‘ํ•ฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์„œ๋Š” ์ƒ๊ด€ ์—†๋‹ค.
  • ๋งŒ์•ฝ ์œ„์ˆ˜๊ฐ€ ํ•ฉ์„ฑ์ˆ˜๋ผ๋ฉด, ๊ณฑํ•˜๋Š” ์ˆ˜์— ๋”ฐ๋ผ ์ง‘ํ•ฉ์ด ๋‹ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.
    • ๋” ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ ธ ์ค‘๋ณต๋˜๋Š” ์›์†Œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ
    • ๊ฐœ์ˆ˜๊ฐ€ ๋” ์ž‘์•„์ง„๋‹ค.
  • ์†Œ์ˆ˜์˜ ํŠน์ง•๋•Œ๋ฌธ์— ๋‚˜ํƒ€๋‚˜๋Š” ์„ฑ์งˆ์ด๋‹ค.
prime = 19
candidates = [1, 3, 7, 13, 18, 2985]
for cand in candidates:
    result = []
    for i in range(0, 19):
        result.append((i*k)%prime)
    result.sort()
    print(result)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

ํŽ˜๋ฅด๋งˆ์˜ ์†Œ์ •๋ฆฌ

p๊ฐ€ ์†Œ์ˆ˜์ด๋ฉด, ๋ชจ๋“  ์ •์ˆ˜ ์— ๋Œ€ํ•ด ์ด๋‹ค.

ํ˜น์€ ๊ฐ€ ์†Œ์ˆ˜์ด๊ณ  ๊ฐ€ ์˜ ๋ฐฐ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฉด, ์ด๋‹ค.

prime = 43
result = []
for p in range(0, prime):
    element = FieldElement(p, prime)**(prime-1)
    result.append(element.num)
result.sort()
print(result)
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

FieldElement Class

class FieldElement:
 
    def __init__(self, num, prime):
        if num >= prime or num < 0: 
            error = 'Num {} not in field range 0 to {}'.format(
                num, prime - 1)
            raise ValueError(error)
        self.num = num 
        self.prime = prime
 
    def __repr__(self): # ๊ฐœ์ฒด ์„ค๋ช…์šฉ
        return 'FieldElement_{}({})'.format(self.prime, self.num)
 
    def __eq__(self, other): # ==
        if other is None:
            return False
        return self.num == other.num and self.prime == other.prime 
 
    def __ne__(self, other): # !=
        return not (self == other)
 
    def __add__(self, other): # +
        if self.prime != other.prime: 
            raise TypeError('Cannot add two numbers in different Fields')
        result = (self.num + other.num) % self.prime  
        return self.__class__(result, self.prime)  
 
    def __sub__(self, other): # -
        if self.prime != other.prime:
            raise TypeError('Cannot subtract two numbers in different Fields')
        result = (self.num - other.num) % self.prime
        return self.__class__(result, self.prime)
 
    def __mul__(self, other): # *
        if self.prime != other.prime:
            raise TypeError('Cannot multiply two numbers in different Fields')
        result = (self.num * other.num) % self.prime
        return self.__class__(result, self.prime)
 
    def __pow__(self, exponent): # **
        n = exponent % (self.prime - 1)
        result = pow(self.num, n, self.prime) # ํŒŒ์ด์ฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋น ๋ฅธ ๊ฑฐ๋“ญ์ œ๊ณฑ ๋ชจ๋“ˆ๋Ÿฌ ์—ฐ์‚ฐ
        return self.__class__(result, self.prime)
 
    def __truediv__(self, other): # ์‹ค์ˆ˜ ๋‚˜๋ˆ—์…ˆ ์—ฐ์‚ฐ (/) - ์ •์ˆ˜์˜ ๊ฒฝ์šฐ __floordiv__(//)๋ฅผ ํ†ตํ•ด ํ•  ์ˆ˜ ์žˆ๋‹ค.
        if self.prime != other.prime:
            raise TypeError('Cannot divide two numbers in different Fields')
        other_num = other.num**self.prime-2
        result = (self.num * other_num) % self.prime
        return self.__class__(result, self.prime)

Reference