블록체인 계정 주소 checksum

2025. 2. 3. 21:09Blockchain/Base

728x90
반응형

이더리움의 경우, 만들어진 초기에는 계정 주소(account address)가 오타가 있는지 없는지 검증하는 기능이 들어가 있지 않았다. 아래의 케이스를 보면 주소가 다르다는 것을 눈으로 확인하는 것은 굉장히 어렵다.

  • 0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359
  • 0xfb6916095ca1df60bb79ee92ce3ea74c37c5d359

20-byte hex 값으로 구성되어 있는 주소는 한개의 단어라도 오타가 발생하면 착오송금이 이루어지는 큰 문제가 발생하기 때문에 주소의 유효성을 검증하는 필요성이 부각되어서 EIP 55에서는 계정 주소가 올바른지 판단하는 표준을 제안한다.

Mixed-case checksum address encoding

운영되고 있는 이더리움 네트워크에서 갑자기 주소의 형태를 바꾸어 버리면 기존에 연결되어 있던 시스템을 모조리 변경해야 하는 문제점이 존재하여서 기존 주소 시스템과 호환되는 방법을 고안했는데, 16진수에서 character로 존재하는 digit을 (e.g. abcdef) 대소문자로 구분하는 방식을 택했다.

"0x1234abcd"라는 주소가 존재한다 가정하자. 이 hex address를 0x를 제외하고 keccak256 해시 함수를 적용한 값이 "1a2b3c4d5e.."라는 값이 도출된다고 했을 때, 각 hex address의 character값과 동일한 index에 할당된 해시값이 8보다 크면 대문자로 설정한다. Hex address의 "abcd" index에 대응되는 해시값은 "3c4d"이다. 따라서, 기존 hex address는 checksum encoding을 적용하면 "0x1234aBcD" 가 된다.

EIP 55에서 확인 가능한 specification code는 아래와 같다.


import eth_utils


def checksum_encode(addr): # Takes a 20-byte binary address as input
    hex_addr = addr.hex()
    checksummed_buffer = ""

    # Treat the hex address as ascii/utf-8 for keccak256 hashing
    hashed_address = eth_utils.keccak(text=hex_addr).hex()

    # Iterate over each character in the hex address
    for nibble_index, character in enumerate(hex_addr):

        if character in "0123456789":
            # We can't upper-case the decimal digits
            checksummed_buffer += character
        elif character in "abcdef":
            # Check if the corresponding hex digit (nibble) in the hash is 8 or higher
            hashed_address_nibble = int(hashed_address[nibble_index], 16)
            if hashed_address_nibble > 7:
                checksummed_buffer += character.upper()
            else:
                checksummed_buffer += character
        else:
            raise eth_utils.ValidationError(
                f"Unrecognized hex character {character!r} at position {nibble_index}"
            )

    return "0x" + checksummed_buffer


def test(addr_str):
    addr_bytes = eth_utils.to_bytes(hexstr=addr_str)
    checksum_encoded = checksum_encode(addr_bytes)
    assert checksum_encoded == addr_str, f"{checksum_encoded} != expected {addr_str}"


test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")
test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")
test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")
test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")

주소의 대소문자 확인만으로 생성된 주소의 유효성을 확인할 수 있다. 시스템에 "0x1234aBcD"가 아닌 "0x1234AbCd"가 들어오면 잘못된 주소라고 판단할 수 있게된다.

Bech32 checksum

주소 표현의 다른 대표적인 예시인 Bech32의 checksum도 함께 확인해본다. 이전 글에서 Bech32 형식의 주소는 HRP(Human Readable Part), seperator, payload, checksum으로 나뉜다고 언급했었다. Hex 형식의 주소와는 달리 checksum 문자가 끝에 붙는 형태를 가진다.

먼저 BIP 173에서 확인 가능한 checksum을 구현하는 코드를 첨부한다.


def bech32_polymod(values):
  GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
  chk = 1
  for v in values:
    b = (chk >> 25)
    chk = (chk & 0x1ffffff) << 5 ^ v
    for i in range(5):
      chk ^= GEN[i] if ((b >> i) & 1) else 0
  return chk

def bech32_hrp_expand(s):
  return [ord(x) >> 5 for x in s] + [0] + [ord(x) & 31 for x in s]

def bech32_verify_checksum(hrp, data):
  return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1

Bech32의 checksum은 5-bit 단위로 데이터를 저장하는 Base32 변환을 사용하기 때문에 encoding 데이터는 5-bit 단위로 변환된다.

Checksum 계산은 제네레이터 다항식(Generator Polynomial)을 기반으로 동작한다. GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]는 오류를 검출하는 성능을 높이기 위해 설계된 BCH code 기반의 다항식이다. 입력되는 데이터 (bech32_hrp_expand(hrp) + data)를 좌측부터 하나씩 처리하며, 현재 다항식의 값을 5-bit 씩 shift left 처리 한다. 새로운 비트를 추가하고 제네레이터 다항식을 XOR처리를 하여 PolyMod를 사용한 나머지 값이 checksum이 된다.

Checksum 생성에 대한 상세한 과정은 해당 글에서 조금 더 자세하게 확인 가능하다.

최종적으로 checksum 값은 6개의 5비트 값으로 변환되어 Bech32 문자열의 마지막에 추가된다.

Conclusion

착오 송금이 일어났을 때 중앙에서 컨트롤 할 수 없는 블록체인 네트워크이기 때문에 송금 시 수령자의 주소 확인이 무엇보다 중요하다. 통신에서 사용하는 IP header의 checksum처럼 데이터의 무결성을 간단하게 검증하는 방법을 사용함으로써 주소의 유효성을 검사하여 보다 안전한 블록체인 송금 환경을 만들 수 있다.

참고
https://eips.ethereum.org/EIPS/eip-55
https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#reference-implementations
https://medium.com/@meshcollider/some-of-the-math-behind-bech32-addresses-cf03c7496285

728x90
반응형