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)
[태그:] signature
[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]
- to가 contract address라면, 4 bytes의
- 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