[BC] Ethereum Proj

슬지로운 개발생활·2023년 2월 28일
1

blockchain

목록 보기
2/2
post-thumbnail

Intro

회사에서 Ethereum 지갑에 대해 공부하라고 작업했던 프로젝트이다.
해당 프로젝트를 확인할 수 있는 깃허브에 노션링크를 연결했는데 해당 노션 페이지를 private으로 설정해 둬 public으로 볼수 있는 벨로그에 해당 내용을 옮긴다.

해당 내용은 지갑 생성, erc20/erc721 거래에 관해 설명한다.

Project

ethers.js

ethereum 블록체인과 그 생태계와 상호작용하기 위한 라이브러리

Provider

  • 이더리움 네트워크에 대한 연결을 위한 추상화(abstraction)를 제공하는 클래스.
  • 블록체인에 대한 읽기 전용 액세스를 제공한다.
  • 개인이 노드가 될수없기 떄문에 네트워크의 정보를 대신 제공해주는 것이다.
  • 특정한 형식으로 항상 요청해야 하기 때문에, 형식으로의 변경을 도와주는 라이브러리가 Ethers.js이다.

Signer

  • 직간접적으로 private key에 대한 접근권을 가지고 있는 클래스.
  • 계정의 ether를 사용하여 트랜잭션을 수행할 수 있도록 한다.
  • signer는 이더리움 계정을 통해서 트랜잭션에 사인을 해서 이더리움 네트워크 상의 정보를 변경하는 트랜잭션을 실행할 수 있게 해준다. (가스비 발생)

Smart Contract

  • 서면으로 이루어지던 계약을 코드로 구현하고 특정 조건이 충족되었을 때 해당 계약이 이행되도록 하는 Script

Infura

  • 블록체인(네트워크에) 접속을 쉽게 도와준다.
    • provider(노트와 통신하기 위해)생성할 때 필요
    • 직접 노드를 운영하는것은 디스크 용량과 리소스를 많이 잡아먹는다.
      → infura 사용시 직접 노드 운용 필요없이 네트워크 접근 가능

MetaMask 연동

01_MetaMask API 사용

메타마스크는 ethereum.request(args) method 사용해 RPC API를 래핑한다.

  • ethereum은 window객체에 포함 되어 있다. (metamask extension이 설치되어있는 경우 존재)
  • api 통신시 params의 값들은 hexString으로 보내야한다.
  • 지갑 연결 및 전송
    1. MetaMask Extension에서 Account를 가져온다.
      • { method: 'eth_requestAccounts’ }를 argument로 넣어 보낸다.
    2. send Transaction
      • 최소한의 TxParams property 구성
        • from: 연동한 acct의 address
        • to: value를 보낼 계정의 address
        • value(optional): 없는 경우 0으로 생성
        • 그 외
          • nonce(ignore): 난스 증가는 민감한 문제로, 사용자가 custom하게 하지 않고, 메타마스크에서 관리한다.
          • Gas Price, Gas Limit (optional)
          • Data(semi-optional)
          • chainId(currently ignored): 해당 부분에 메타마스크와 다른 네트워크를 넣어도 무시한다.
      • txParams를 eth_sendTransaction 메소드를 사용해 tx를 params에 값을 줘 api통신한다.
        • 예시 코드
          const gasPrice = await ethereum.request({ method: 'eth_gasPrice' });
          const weiVal = ethers.utils.parseUnits(value); // ether를 wei로 변경해주는 메소드
          
          // txParams 생성
          const txParams = {
            from: metaMaskAddr,
            to: recipient,
            value: ethers.utils.hexlify(weiVal), // hexStr값으로 변경
            // gasPrice: gasPrice,
          };
          
          try {
            // tx send
            const sendTx = await ethereum.request({
              method: 'eth_sendTransaction',
              params: [txParams],
            });
          } catch (err) {
            console.log(err)
          }
  • 그 밖의 메소드 및 이벤트
    • 현재 metamask의 체인(네트워크)를 표시하고 싶을 때
      • method: ‘eth_chainId’ 사용
    • balance를 가져오고 싶을때
      • { method: 'eth_getBalance’, params: [address, QUANTITY|TAG] }
        • QUANTITY|TAG: blockNum(eth_blockNumber메소드 사용)이나 "latest""earliest" or "pending" 사용
    • 메타마스크 extension에서 변경 했을 때 화면에 적용되게 하고 싶을 때
      • ethereum.on(eventName, function)을 사용한다.
        • event 사용후 지우는 것도 잊지 말자 (ethereum.removeListener(eventName, function) 사용)
        • 예시 코드
          // metamask extension에서 chain(network)을 변경했을때,
          // handleChainChanged함수 실행
          ethereum.on('chainChanged', handleChainChanged);
          // metamask extension에서 account를 변경했을때,
          // handleAccountsChanged함수 실행
          ethereum.on('accountsChanged', handleAccountsChanged);
          
          ethereum.removeListener('chainChanged', handleChainChanged);
          ethereum.removeListener('accountsChanged', handleAccountsChanged);

02_Ethers.js 사용

  • 지갑 연결 및 전송
    1. JsonRpcProvider생성(new ethers.providers.Web3Provider(window.ethereum))
    2. provider에서 주소 추출
    3. provider에서 JsonRpcSigner(signer) 생성
      • provider.getSigner(): metamask로 생성한 JsonRpcProvider는 signer 생성 가능, infura로 생성한 JsonRpcProvider는 signer가 연결되어있지(?) 않아 생성할 수 없다.
    4. tx 생성
      • to(recipient): to만 넣어도 전송 가능
      • value(optional): 없는 경우 0으로 생성
    5. signer를 통해 transaction 전송
      • signer.sendTransaction(tx)를 사용해도 메타마스크 extension이 뜨고 메타마스크에서 확인을 눌러야 거래가 끝난다.
      • 메타마스크 연결시 메타마스크 안키고 따로 작동하는 방법은 아직 모름

Steps to Generate EOA

( EOA 계정 생성 과정 )

1. To create Private Key

  1. ethers/lib/utils에서 randomBytes를 불러와 32byte생성 → Unit8Array값으로 리턴
  2. buffer.from을 이용해 32바이트 값을 새로운 버퍼 인스턴스를 생성
  3. .toString(’hex’)로 인코딩해 64자리의 스트링값 생성 → private key 완성

2. 2 ways to create Address

  1. To Create Wallet Instance

  2. privateKey → publicKey → address

    // ! 방법 1
    ethers.utils.computeAddress(privateKey)
    // computeAddress 메소드가 publicKey를 계산하고 publicKey에서 address를 생성
    // ! 방법 2: publicKey를 구한 후 address를 구한다.
    const publicKey = ethers.utils.computePublicKey(privateKey);
    const addrFromPublic = ethers.utils.computeAddress(publicKey);

Transaction

1. get / create Provider and Signer

  • 메타마스크를 통해 Provider를 가져와 Signer 생성

2. generating raw Transaction

3. signing Transaction with privateKey

  • signer.signTransactiontransactionRequest ) ⇒ Promise< string< DataHexString > >

4. sending Transaction

  • provider로 전송하는 방법
  • signer로 전송하는 방법: signer.sendTransactiontransactionRequest ) ⇒ Promise< TransactionResponse >
    • signer에 provider를 connect후 진행할 수 있다.
    • transaction에 sign하지 않고 사용

ERC-20 Token

  • Smart Contract를 통해 생성된다.

1. create and deploy ERC-20 with OpenZeppelin & Remix

  1. OpenZeppelin에서 Contract Wizard를 통해 ERC-20 token 코드를 만든다.
  2. 리믹스에서 열어서 Contract를 compile한다(Compile contract 파란 버튼 클릭~!)
  3. 버튼 클릭후 생성되는 Contract part에서 select box에서 생성자가 만든 contract를 선택후 ABI를 복사한다.
  4. deploy 메뉴에 들어가서 environment, contract 등을 설정 후 Deploy한다.
  5. 배포 후 생성된 CA(Contract Address)를 복사한다.

2. connecting Contract

  • *new ethers.Contractaddress , abi , providerOrSigner* )
    • 새로운 인스턴스 Contract를 생성해 address, abi, provider/signer로 기존 계약과 연결
      • abi: class method를 채우는데 쓰인다.
      • provider를 제공할시 읽기 전용 권한만 가지고, signer를 인자값으로 제공하면 read-write가 가능하다.
    • Signer를 인자값으로 넣어야 할 때, Signer는 provider와 연결 되어있어야 한다.
      → JsonRpcProvider(metaMask) 경우 provider에서 signer를 get하기 때문에 signer.connect()을 하지 않아도 provider와 연결되어있다.
      → Provider가 연결되어있지 않다면 method를 제대로 쓸 수가 없다.

3. Transferring ERC20 Token

  • erc20_rw.transfertarget , amount [ , overrides ] ) ⇒ Promise< TransactionResponse >
    • tx생성은 아마 transfer하기 전에 생성하는 거 같다…?

ERC-721 Token ( NFT )

1. ERC-20를 만들때 처럼 Contract를 배포하고, 새로운 인스턴트 생성

2. IPFS for data of Tx ( Feat. Pinata )

  • image를 먼저 ipfs를 사용해 upload한다. → hash값
  • name, desc, image hash를 json형태로 만들어 ipfs를 사용해 올린 후 tokenURI값을 받는다. → tokenURI를 민팅할때 인자값으로 보낼때 ipfs://를 붙이지 않고, 되돌려받은 CID값만 인자값으로 보낸다.(그래야 메타마스크에서 이미지 확인 가능)
    • CID(Calling Identification Display): 발신자 번호표시

3. Contract를 통해 Minting

  • contract.safeMint( address, uri )

4. Minting된 NFT 전송

  • await contract.safeTransferFrom(fromAddr, toAddr, tokenId)
  • await contract['safeTransferFrom(address,address,uint256)']( fromAddr, toAddr, tokenId );


용어 정리 / 리서치

해당 프로젝트를 진행하며 이해 안간부분이나 모르는 부분을 정리했다.

Account

  • 이더리움 상태는 어카운트라고 하는 오브젝트(Object)들로 구성되어있고,
    어카운트 오브젝트는 두가지로 분류된다.
    - 외부 소유 어카운트(EOA: Externall Owned Accounts)
    - 일반적으로 알려져있는 지갑과 같다.
    - 개인키 사용
    - 코드를 담고 있지 않다.
    - 프라이빗 키에 의해 통제되는 계정 정보
    - EOA 생성 방법
    1. Private Key: 랜덤한 256bit(32byte)데이터를 64자리의 Hex열로 인코딩
    2. Public Key: 개인키에서 ECDSA(타원곡선전자서명 알고리즘)을 이용해 공개키 생성
    3. Address:
    1. 공개키를 Kecak256 Hash값으로 변환해 256bit의 바이너리 데이터를 생성
    2. 생성된 바이너리 데이터의 앞쪽 96bit(12byte) 데이터 제거 후 남아있는 160bit binary data를 Hex열로 인코딩한 결과값이 Address다.
    3. 160bit를 16진수로 변환한 20byte의 데이터이며, 16진수로는 40개의 문자열을 표현할 수 있다.
    - 자체적으로 트랜잭션 생성 가능, 다른 외부 소유 어카운트에 이더 전송 가능, 메세지를 통해 컨트랙트 계정 실행 가능
    - 컨트랙트 어카운트(CA: Contract Account)
    - 이더리움 코드를 담고 있는 계정
    - EVM 코드를 담고 있다.
    - 컨트랙트 코드에 의해 통제되는 계정 정보
    - 개인키(private key) 정보를 가지고 있지 않다.
    - 오직 외부 소유 어카운트(EOA)에 의해 실행 가능, 자체적으로 트랜잭션 생성 불가능, 조건에 따라 또 다른 컨트랙트 계정을 참조하기 위해 Internal Tx를 생성 가능하다.
  • 어카운트 오브젝트는 20바이트의 주소와 상태변화를 가지고 있다.
  • Account는 총 4가지(Nonce, Balance, storageRoot, codeHash) 정보로 구성되어있다.

    EOA

    • Nonce : Account에서 전송된 트랜잭션의 수

    • Balance : Account가 소유한 잔고 정보로써, wei 단위로 표시

    • storageRoot : Merkle Paticia Tree의 Root Hash

    • codeHash : 빈 문자열의 Hash 정보

      CA

    • Nonce : Account에서 생성된 Contract 수

    • Balance : Account가 소유한 잔고 정보로써, wei 단위로 표시

    • storageRoot : Merkle Paticia Tree의 Root Hash

    • codeHash : Account에 포함된 이더리움 버츄얼 머신(EVM) code의 Hash


Transaction

  • 발신자는 transaction 발송할 때, 개인 키로 서명해 암호화.
    수신자는 발신자의 공개키로 transaction 내용을 복호화 한다.

Transaction 구조

  • Nonce : replay attack을 막기 위한 용도로, 하나의 account가 있을 때 몇 개의 transaction을 전송했는지 의미함.
    • 하나의 Acct에서 몇 개의 tx를 전송했는지 알고, 성공된 tx 개수만 카운트된다.(wallet에서 관리)
    • nonce 이슈 : 하나의 account를 ~
      1. 하나의 wallet에서 관리 시: 짧은 시간에 여러개의 tx가 생성될때 nonce 2에서 문제가 발생되면 정상적인 tx 3, 4, 5도 보류 상태가 된다.
      2. 여러 개의 wallet에서 관리 시(주로 거래소 wallet 케이스): 동시에 tx가 만들어지다 보면 중복되거나 차이가 발생하는 nonce가 발생할 수 있는데 하루 단위로 nonce를 리밸런싱하는 간헐적 방식을 취한다.
  • Gas price : 가스 하나의 가격(wei 단위)
  • Gas limit : 최대 가스 소모량
  • Recipient : target account / receiving address
    • EOA인 경우: value 송금
    • CA인 경우: contract code 실행
  • Value : 이더 송금 금액(in Wei)
  • Data : contract 코드의 실행을 위한 필드
  • v, r, s : 전자서명 서명 값(ECDSA 알고리즘)

서명 전 필요 값

  • nonce, gas price, gas Limit, recipient, value, data, v, r, s
  • from address는 EOA 공개키를 v, r, s 구성 요소로부터 알아낼 수 있어 존재하지 않는다.
  • v값에는 chainId값이 포함된다.

ERC-20

  • Ethereum Request for Comment 20의 약자이고, 20은 리퀘스트 숫자다.

ERC-721

  • NFT(Non Fungible Token: 대체불가토큰)
  • 대체불가능하다
    • ERC-20은 단순히 어카운트 별로 잔액을 추적하는데 중점을 둔다.
    • ERC-721은 토큰 아이디 별로 소유권자가 누구인지 추적한다.
      • 디지털 소유권 인증서 개념
      • 소유권의 이전 가능 but 다른 토큰으로 대체 불가하다(토큰 간에 대체 불가).
      • 해당 토큰 아이디 별로 이미지 등이 저장되어있다.
  • 이더리움 플렛폼에 이미지를 바로 올릴 수 있지만 엄청난 가스 비용으로 이슈가 발생하므로, IPFS를 사용한다.
  • 숫자와 문자열만 존재, 가격X: Protocol(Wyvern, Ox)을 통해 NFT를 주고판 걸 묶는다

Contract

  • contract를 배포하면 contract생성 → CA생성
  • 블록체인에 배포된 코드의 추상화이다.
  • 스마트 컨트랙트가 블록체인에 포함된다는 의미는 블록체인 네트워크 상 모든 노드들이 동일한 스마트 컨트랙트(코드)를 가지고 있는 것이다.
  • 만드는 순서
    1. 스마트 컨트랙트 구현: 구현하고자 하는 내용을 솔리디티(대표적)나 다른 언어로 코딩
    2. 구현한 코드 컴파일: 컴파일 결과 EVM바이트 코드 생성
    3. 스마트 컨트랙트 배포
      • 컴파일된 EVM코드를 하나의 트랜잭션처럼 블록에 추가시켜 블록체인에 등록시키는 작업이다.
      • 마이너가 해당 블록을 채굴하게 되면 블록체인에 포함된다.
      • 소스 컴파일 → EVM 바이트 코드 → 구체적인 작업은 ABI취득해 작업 → ABI로부터 contract 객체 생성 → transaction생성해 블록에 추가
  • Smart Contract에 쓰는 것은 transaction을 발생시키지만, 값을 읽어오는 것은 transaction발생 X

IPFS(Inter-Planetary File System)

  • 블록체인에 직접 저장하는 경우 수수료가 많이 부담되므로, 외부 저장매체로 쓰임
  • 분산형 파일 시스템에 데이터를 저장하고 인터넷으로 공유하기 위한 프로토콜
    • 원본 데이터의 내용을 변환한 해시값을 이용, 전 세계 여러 컴퓨터에 분산 저장된 콘텐츠를 찾아 데이터를 조각 나눠 빠른속도로 가져온 후 하나로 합쳐 보여주는 방식으로 작동
  • 특징: 탈중앙화(분산화),

ABI

  • Application Binary Interface
  • 컨트랙트의 함수와 매개변수들을 JSON 형식으로 나타낸 리스트

JSON RPC

  • RPC 를 Json 포맷으로 표현한 것
    • RPC(Remote Procedure Call: 원격 프로시져 호출)
      • 언어나 환경 등에 종속되지 않고 서비스 간 프로시저를 호출하도록 한 개념
  • 2.0부터 Request-Response 뿐만 아니라 응답없는 Notification 지원한다.
  • 4계층 TCP 에서 동작하여 Http 프로토콜을 얹을 필요없이, Json 만 전달하여 동작 가능하다.
    • 가볍고 빠르다.
// 예시
const response = await fetch(URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: "update",
      params: [1,2,3,4,5],
      id: 1
    }
  })

  await response.json() // {"jsonrpc": "2.0", "method": "foobar"}

Event

  • 컨트랙트 내에 있는 함수들이 호출 될 때 블록에 기록을 남김으로써, 나중에 보기 편하게 기록을 찾아 볼 수 있도록 삽입하는 것
  • Frontend가 Smart Contract 코드 함수에서 실행되는 것에 대해 소통을 하기 위해 이용되는 것이 바로 Event이다.
    • Smart Contract가 Event를 send하면 Frontend code에서 Listen하는 식으로 동작한다.
  • 트랜잭션 내에서 호출될 수 있는 리턴값이 없는 함수이다.
  • 이벤트를 호출하면 그 호출한 기록이 Transaction Receipt에 저장된다.
    → 일종의 Log이다.

LOGS

  • Smart Contract에서 발생되는 event에서 만들어진다.
  • 로그들을 사용해 블룸필터를 만들고 그것을 통해 빠르게 로그들을 검색할 수 있다.
  • 이벤트를 설명하는데 사용된다.
  • 주로 topic[0]은 event signature(a keccak256 hash)이다.
    • 예외사항은 익명함수를 발생할때 이 서명이 첫 번째(0번째) 항목으로 포함되지 않는다.

    • topic은 이벤트 인자에 따라 변경될 수 있다.

      Transfer(address,address,uint256) // *event signature*


Understanding event logs on the Ethereum blockchain 이미지를 참조했습니다.

Topics [usually]

  1. ERC-20
    • 1st indexed topic is the sender address
    • 2nd indexed topic is the recipient address
    • 3rd indexed topic is the value
  2. ERC-721
    • 1st indexed topic is the sender address
    • 2nd indexed topic is the recipient address
    • 3rd indexed topic is the token ID

Wallet

  • 암호 화폐 지갑은 암호 화폐를 저장할 수 있는 계정을 의미한다.

Multi-Sig Wallet(멀티시그 월렛)

  • Smart Contract를 기반으로 구현됐다.
    보안이 일반지갑에 비해 우수하지만, 편이성이 부족하고, 많은 주의성 요구 및 사용이 쉽지 않아 종류 또한 많지 않다.
  • 인출 혹은 송금 등을 처리할 때, 다수의 서명을 필요로 하는 지갑
    • M-of-N거래: 하나의 주소에 n개의 개인키가 설정되어있고, 인출할 시 n개의 개인키 중 m개의 서명이 있어야 가능하다.
  • 목적
    • 자금의 공동관리: 주된 창업자 한 명이 모든 금액을 가지고 잠적하는 등의 이슈 사전 방지
    • 보안 이슈 해소: 거래소의 비밀키가 해킹 당해도 추가 서명이 없으면 큰 거래가 이뤄지지 못하게 가능
  • 종류
    • Consensys
      • 꾸준한 유지 관리 X → Gnosis 멀티시그 월렛에서 계속 개발이 되고 있다.
    • Gnosis
      • ConsenSys 멀티시그 컨트랙트를 기반으로 지속적 개발 이루어짐 → 많은 사람들 사용
    • Argent
      • Guardians기능으로 다른 Argent 사용자를 선택해 모든 출금에 그 사용자의 승인이 필요하게 한다.
    • BitGo
      • End User가 Off-chain에서 서명하고 이 데이터를 또 다른 Signer가 서명해 트랜잭션을 발생시켜 2 of 3을 만족해야 출금 되는 방식

비결정적 지갑(nondeterministic wallet)

  • 개인정보 보호를 위해 암호화폐 주소는 재사용하지 않는 것이 바람직하다.
  • 무작위로 선택된 Private 키가 저장되어있는 지갑입니다.
    • 여러개의 키 사이에 규칙이나 연속성이 없이 무작위로 생성이 되는 것.
  • 정기적인 백업 필요 → 백업 전 데이터를 잃어버리면 해당 계좌에 있는 자금 및 smart contract 접근 불가

결정적 지갑(deterministic wallet)

  • 모든 키가 Seed라고 알려진 하나의 마스터 키에서 유도되어 나온다.
    • 모든 키가 서로 연관되어 있으면 원본 시드가 있는 경우 복구 가능.
    • Seed는 개인키를 만들기 위해 랜덤하게 생성된 숫자로, 인덱스 번호 또는 체인 코드와 같은 다른 데이터와 결합돼 개인 키 유도.
  • Seed 보안은 매우 중요하다.
    • Seed는 모든 파생된 키 복구 가능.
    • 시드 하나만 있다면 전체 지갑에 접근 가능하다.(지갑 내보내기 가져오기에 활용 가능해 다른 지갑 간에 모든 키를 쉽게 이동 시킬 수 있다.)
    • 보안 노력을 단일 데이터에 집중할 수 있다는 장점이 있다.

계층적 결정 지갑(HD Wallet: hierarchical deterministic wallet)

  • 단일 시드에서 많은 키를 쉽게 유도하기 위해 만들어졌다.
  • 부모키가 연속된 자식키를, 각각의 자식키는 손자키를 유도할 수 있는 구조인 트리 구조로 파생된 키를 포함한다.
    • 부모키가 자식키를, 다시 자식키가 손자키의 시퀀스를 유도할 수 있다.
  • 장점
    • 특정 서브 키의 특정 분기는 입금을 위해 사용, 다른 브랜치는 출금의 잔돈을 받기 위해 사용가능
      • 기업 설정과 같은 구조적인 의미를 표현하는 데도 사용할 수 있다.
    • 사용자가 개인키에 접근하지 않고, 연속된 공개키 생성 가능
      • 보안상 안전하지 않은 서버, 보기전용, 수신 전용의 용도로 사용할 수 있는데, 이때 지갑에 개인키(자금을 움직이는)가 들어있지 않게 만들 수 있다.

Hot Wallet

  • 인터넷 연결 O (온라인)
  • 실시간으로 편리하게 이용 가능하지만, 개인키를 온라인에 연결해서 입력하기 때문에 해킹 등 보안 문제에 취약하다.
  • 유형
    • 모바일 지갑: 편의성 때문에 많이 사용하나, 스마트 폰은 거의 항상 인터넷에 연결되어있기 때문에 보안 문제가 있다.
    • 데스크탑 및 브라우저 지갑: 바이러스에 취약하고, 휴대 장치의 보안 시스템에 의해 안전성이 좌우된다.

Cold Wallet

  • 인터넷 연결 X (오프라인)
  • 암호화폐 금고라고 생각하면 된다.
    • 고액투자자나 코인을 안전하게 보관하기 위한 목적으로 쓰인다.
  • 유형
    • 하드웨어 지갑:
      • USB케이블 또는 블루투스를 통해 모바일, 컴퓨터 등에 연결해 사용하는 물리적 장치.
      • 장치에 연결할때만 핫 월렛이 되기 때문에 보안성이 더 좋다.
    • 종이 지갑:
      • 온라인 상의 공격이 모두 방지 된다.
        → 지갑 주소와 개인 키가 프린트 된(보통 QR코드 형식) 한 장의 종이이다.

최근 규제 지침은 수탁형(예, 거래소 지갑)지갑과 비수탁형(예, 메타마스크 지갑)지갑을 구별하는것에 초점을 두고 있다.


참조

0개의 댓글