๊ณต๊ฐœํ‚ค ๋น„ํŠธ์ฝ”์ธ ์ฃผ์†Œ ํ˜•์‹์— ๋Œ€ํ•ด ์•Œ์•„๋ณธ๋‹ค. ์ด๋Š” ๊ณง โ€œ์ฃผ์†Œโ€์ด๋‹ค.

๋น„ํŠธ์ฝ”์ธ ์ฃผ์†Œ WIF(Wallet Import Format)

  • ์•ž์—์„œ ๊ณต๊ฐœํ‚ค์™€ ๋น„๋ฐ€ํ‚ค, ๊ทธ๋ฆฌ๊ณ  ์„œ๋ช…, ๊ฒ€์ฆ ๋ฐฉ์‹์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ์ง๋ ฌํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๋ฐฐ์› ๋‹ค.
  • ๊ทธ๋ ‡๋‹ค๋ฉด A๊ฐ€ B์—๊ฒŒ ๋น„ํŠธ์ฝ”์ธ์„ ๋ณด๋‚ด๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?
  • ๊ณต๊ฐœํ‚ค๋ฅผ ๊ณต๊ฐœํ•˜๊ณ , ์ด์— ๋Œ€ํ•ด r, s๊ฐ’์„ ๋„˜๊ฒจ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฆ‰, ๊ณต๊ฐœํ‚ค๋Š” ์–ด๋–ค ์˜๋ฏธ๋กœ โ€œ์ฃผ์†Œโ€๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์‹ค์ œ๋กœ ๋น„ํŠธ์ฝ”์ธ์ด ๋‚˜์˜จ ์ดˆ๊ธฐ์—๋Š” ์ด๋ฅผ ์ฃผ์†Œ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ SEC ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ๊ธธ์ด๋„ ๊ธธ๊ณ  (33 or 65 byte)
  • ์ด์ง„ ํ˜•์‹์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฝ๊ธฐ๋„ ์‰ฝ์ง€ ์•Š๋‹ค. (16์ง„๋ฒ•์œผ๋กœ ๋ฐ”๊พผ๋‹คํ•ด๋„ ๊ธธ๋‹ค. - 66 or 130)
  • ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋ณ€ํ˜•ํ•ด์•ผํ•  ํ•„์š”์„ฑ์ด ์žˆ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ์ฃผ์†Œ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ WIF(Wallet Import Format)๋ผ ํ•œ๋‹ค.

๊ณ ๋ ค ์‚ฌํ•ญ

  1. ๊ฐ€๋…์„ฑ
  2. ๊ธธ์ด
  3. ๋ณด์•ˆ์„ฑ

Base58

  • ์œ„์˜ ๊ณ ๋ ค์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๊ธฐ ์œ„ํ•ด Base58์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋น„์Šทํ•œ ๊ฒƒ์œผ๋กœ URL์„ ์ถ•์•ฝํ•˜๊ธฐ ์œ„ํ•ด Base64์™€ ๊ฐ™์€ ๊ฒƒ์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.
  • Base๋ผ๋Š” ๋œป์€ โ€œ์ง„๋ฒ•โ€์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฆ‰, 64์ง„๋ฒ•, 58์ง„๋ฒ•์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ๊ทธ๋Ÿผ ๋น„ํŠธ์ฝ”์ธ์—์„œ๋Š” ์™œ ๊ตณ์ด 58์„ ์‚ฌ์šฉํ• ๊นŒ?

58์˜ ์‚ฌ์šฉ์ด์œ 

  • Base64๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, 6๊ฐœ์˜ ๋น„ํŠธ๋ฅผ ํ•˜๋‚˜์˜ ๋ฌธ์ž๋กœ ์ถ•์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ Base64์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ธ€์ž์™€ ์ˆซ์ž๋Š” ํ˜ผ๋™ํ•˜๊ธฐ ์‰ฝ๋‹ค.
    • 0๊ณผ O, 1๊ณผ I, -๊ณผ _ ๋“ฑ
  • ์ด๋Ÿฌํ•œ ๊ธ€์ž๋ฅผ ์ œ์™ธํ•˜์—ฌ ๊ฐ€๋…์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚จ๋‹ค.
  • ๋ช‡๊ฐœ์˜ ๋ฌธ์ž๋งŒ ์ œ์™ธ๋˜๋ฏ€๋กœ ๊ธธ์ด๋„ ์ค„์–ด๋“ ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋น„ํŠธ์˜ ๊ฐœ์ˆ˜์™€ ๋ฌธ์ž์™€ 1:1 ๋Œ€์‘์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.
    • 5.86๋น„ํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๋ฌธ์ž์— ๋Œ€์‘๋œ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€์ง€ ์•Š๋Š” ๋‚˜๋จธ์ง€ ๋น„ํŠธ์˜ ๊ฒฝ์šฐ checksum์„ ๋ถ™์—ฌ ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
  • ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ณด์•ˆ์„ฑ๊นŒ์ง€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

Code

BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
 
def encode_base58(s):
    count = 0
    for c in s:  # ์ „์ฒ˜๋ฆฌ์ด๋‹ค. 0๋ฐ”์ดํŠธ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ •ํ•œ๋‹ค.
        if c == 0:
            count += 1
        else:
            break
    num = int.from_bytes(s, 'big') # ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆซ์ž๋กœ ๋””์ฝ”๋”ฉํ•œ๋‹ค.
    prefix = '1' * count # 0์— ๋Œ€์‘๋˜๋Š” ๊ฐ’์ธ "1"์„ 0๋ฐ”์ดํŠธ ๊ฐœ์ˆ˜๋งŒํผ prefix๋กœ ๋‹ฌ์•„๋‘”๋‹ค. ์ด ๋ถ€๋ถ„์€ p2pkh์—์„œ ํ•„์š”ํ•˜๋‹ค.
    result = ''
    while num > 0:  
        num, mod = divmod(num, 58)
        result = BASE58_ALPHABET[mod] + result # 58๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ฌธ์ž๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.
    return prefix + result  # ๊ฒฐ๊ณผ๋ฅผ ๋”ํ•œ๋‹ค.
 
def decode_base58(s):
    num = 0
    for c in s:
        num *= 58
        num += BASE58_ALPHABET.index(c)
    combined = num.to_bytes(25, byteorder='big')
    checksum = combined[-4:]
    if hash256(combined[:-4])[:4] != checksum:
        raise ValueError('bad address: {} {}'.format(checksum, hash256(combined[:-4])[:4]))
    return combined[1:-4]

์‚ฌ์žฅ๋˜๋Š” Base58

  • ๋”ฑ ๋ด๋„ ์ง๊ด€์ ์ด์ง€๋Š” ์•Š๋‹ค.
  • 58๊ฐœ๋„ ์—ฌ์ „ํžˆ ๋งŽ๋‹ค.
  • ์—ฌ์ „ํžˆ ๊ธธ๋‹ค.
  • Bech32 ํ‘œ์ค€์ด BIP0173์œผ๋กœ ์ œ์•ˆ๋˜์—ˆ๋‹ค.
  • 32๊ฐœ์˜ ๊ธ€์ž๋กœ๋งŒ ๊ตฌ์„ฑํ•œ ์ธ์ฝ”๋”ฉ ๋ฐฉ์‹์ด๋‹ค.
  • Segwit์—์„œ๋งŒ ํ˜„์žฌ๋กœ๋Š” ์‚ฌ์šฉ๋œ๋‹ค.

Hash160

  • SEC ๋ฐฉ์‹์„ ํ†ตํ•ด ๊ณต๊ฐœํ‚ค๋ฅผ ์ง๋ ฌํ™” ํ–ˆ๋‹ค.
  • ์ด ๊ธธ์ด๊ฐ€ ๊ธธ์–ด ์••์ถ• SEC ๋ฐฉ์‹์„ ๋„์ž…ํ–ˆ๋‹ค. (33byte * 4 = 132bit)
  • ์ตœ์ข…์ ์œผ๋กœ ์ด ์••์ถ• SEC์—์„œ ๋งŒ๋“  bit๋ฅผ Base58๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—์„œ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.
  • ํ•˜์ง€๋งŒ, ์ด ์ค‘๊ฐ„๋‹จ๊ณ„์—์„œ ๋” ์ค„์ผ ์ˆ˜๋Š” ์—†์„๊นŒ?
  • ์ด ๊ณผ์ •์—์„œ Base58๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ์— ์•ž์„œ ํ•œ๋‹จ๊ณ„ ์ถ”๊ฐ€์ž‘์—…์„ ํ•  ๊ฒƒ์ด๋‹ค.
    • ๋ณด์•ˆ์ ์ธ ๋ฌธ์ œ๋„ ์žˆ๋‹ค.
  • ์ด ๊ณผ์ •์—์„œ Base58์ธ์ฝ”๋”ฉ ์ „์— Hash160์„ ํ†ตํ•ด ๊ธธ์ด๋ฅผ ์ค„์ด๊ณ , ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•œ๋‹ค.
  • sha256๊ณผ ripemd160 ํ•ด์‹œ๋ฅผ ์—ฐ์†์œผ๋กœ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค.

์ตœ์ข… ์ •๋ฆฌ

  • ๋ถ€๋ถ„ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์„ค๋ช…์€ ํ–ˆ์ง€๋งŒ, ์ „์ฒด์ ์ธ ๊ทธ๋ฆผ์„ ์ดํ•ดํ•˜๊ธฐ๋Š” ์‰ฝ์ง€ ์•Š์•˜๋‹ค.
  • ๊ทธ๋ž˜์„œ ์„ ํ˜•์ ์œผ๋กœ ๋น„ํŠธ์ฝ”์ธ ์ฃผ์†Œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ ๋ชจ์‹๋„์™€ ์ฝ”๋“œ๋กœ ๋‚˜ํƒ€๋ƒˆ๋‹ค.

 
class S256Point(Point):
...
    def address(self, compressed=True, testnet=False):
        '''Returns the address string'''
        h160 = self.hash160(compressed)
        if testnet:
            prefix = b'\x6f'
        else:
            prefix = b'\x00'
        return encode_base58_checksum(prefix + h160)
 
    def hash160(self, compressed=True):
        return hash160(self.sec(compressed))
 
 
def hash160(s):
    '''sha256 followed by ripemd160'''
    return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest()
 
def encode_base58_checksum(b):
    return encode_base58(b + hash256(b)[:4])
 
def hash256(s):
    '''two rounds of sha256'''
    return hashlib.sha256(hashlib.sha256(s).digest()).digest()
 
def encode_base58(s):
    count = 0
    for c in s:  # <1>
        if c == 0:
            count += 1
        else:
            break
    num = int.from_bytes(s, 'big')
    prefix = '1' * count
    result = ''
    while num > 0:  # <2>
        num, mod = divmod(num, 58)
        result = BASE58_ALPHABET[mod] + result
    return prefix + result  # <3>
  • ์ฝ”๋“œ๊ฐ€ ์•„๋ฌด๋ž˜๋„ ์„ ํ˜•์ ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค๋ณด๋‹ˆ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.
  • ํ•˜์ง€๋งŒ ๋ชจ์‹๋„๋ฅผ ๋ณด๋ฉด ๊ธˆ๋ฐฉ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ๋„ท์€ ๋น„ํŠธ์ฝ”์ธ ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์— ํ™œ์šฉ๋˜๋Š” ๋น„ํŠธ์ฝ”์ธ ๋„คํŠธ์›Œํฌ์ด๋‹ค.

Reference