[golang] go-ethereum으로 이벤트 구독

	go func(client *ethclient.Client) {
		contractAddresses := []common.Address{}
		for _, pair := range Pair {
			contractAddresses = append(contractAddresses, common.HexToAddress(pair.PairAddress))
		}

		SyncEventSig := []byte("Sync(uint112,uint112)")
		hash := sha3.NewLegacyKeccak256()
		hash.Write(SyncEventSig)
		SyncEventHashBytes := hash.Sum(nil)
		signature := common.BytesToHash(SyncEventHashBytes) // 0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1
		topic := []common.Hash{signature}
		topics := [][]common.Hash{topic}
		query := ethereum.FilterQuery{
			Addresses: contractAddresses,
			Topics:    topics,
		}

		logs := make(chan types.Log)
		sub := event.Resubscribe(2*time.Second, func(ctx context.Context) (event.Subscription, error) {
			return client.SubscribeFilterLogs(context.Background(), query, logs)
		})
		defer sub.Unsubscribe()
		for {
			select {
			case err := <-sub.Err():
				log.Fatal("Error on select:", err)
			case vLog := <-logs:
				fmt.Println("Log block number:", vLog.BlockNumber)
				// 여기서 하고 싶은 일 하기.
			}
		}
	}(client)

[ethers.js] Contract 시그니처와 hash 확인

const ca = await ethers.getContractFactory("DasomToken");
const cabi = ca.interface;
cabi.format() // Fragments 확인
cabi.forEachFunction((func, index) => { console.log(func.format()); }); // 각 함수의 시그니처 확인
cabi.forEachFunction((func, index) => { console.log(func.format(), ethers.keccak256(ethers.toUtf8Bytes(func.format()))); }); // 시그니처와 해시 값 확인.

[Ethereum] 트랜잭션과 서명

트랜잭션

트랜잭션 T는 다음 data를 포함한다.

T = { nonce, gasPrice, gasLimit, to, value, data, v, r, s }
  • nonce: 보내는 EOA(External Owned Account) 의 counter.
    • 중간에 생략되면 생략된 nonce가 와서 유효하게 처리되기 전까지 이후의 nonce를 가지는 트랜잭션은 mempool에 저장되었다가 처리 후 유효하게 된다.
  • gasPrice: 발신자가 보내는 가스의 가격. 단위는 wei.
    • 0도 가능. 높을 수록 해당 트랜잭션이 빨리 처리됨.
  • gasLimit: 이 트랜잭션을 위해 구입할 가스의 최대량
    • 21000 gas. 이더 소비량 = 21000 * gasPrice
  • to: 수신 이더리움 address (20 bytes)
  • value: 수신처에 보낼 이더의 양
  • data: 가변 길이 data payload
    • to가 contract address라면, 4 bytes의 function selector와 그 이후의 function argument를 serialize한 data이다.
      • function selector = (keccak-256(function prototype))[0:4]
  • v, r, s: EOA의 ECDSA 디지털 서명의 구성 요소

서명

서명 시 사용되는 트랜잭션 T는 9개 필드로 다음과 같다. 이 중 맨 끝의 3개 { chainID, 0, 0 }은 EIP-155에 의해 추가된다. EIP-155는 Simple Replay Attack Protection으로 chainID를 포함하여 다른 네트워크 체인에서 해당 트랜잭션이 replay될 수 없도록 한다.

T = { nonce, gasPrice, gasLimit, to, value, data, chainID, 0, 0 }

Sig는 서명으로 서명 알고리즘, F sig() 로 (r, s) 두 값이 output으로 만들어진다. Transaction T와 private key k를 사용한다. RLP는 Recursive Length Prefix (RLP) encoding scheme 을 말한다.

Sig = F sig(keccak256(RLP(T)), k) = (r, s)

서명 시에는 임시 private key q, 그리고 q로부터 생성되는 임시 public key Q 를 사용한다.

q = rand() % 2**256
Q = q * K = (x, y)

여기서 r = Q의 x 좌표이다. s는 다음으로 계산된다.

s ≡ q**-1 (Keccak-256(RLP(T)) + r * k) mod p

서명 검증

r, s 그리고 sender의 public key K를 사용해서 Q를 계산한다. Q의 x 좌표와 r이 같으면 서명이 유효하다.

w = s**-1 mod p
u1 = Keccak-256(RLP(T)) * w mod p
u2 = r * w mod p
Q ≡ u1 * G + u2 * K     (mod p)

참고: https://github.com/ethereumbook/ethereumbook/blob/develop/06transactions.asciidoc