티스토리 뷰

EIP-7702에서 등장한 새로운 트랜잭션 타입 0x04에 추가된 authorization_list의 튜플의 서명을 복원하는 과정을 geth 코드로 살펴보겠다. 클라이언트 노드는 서명을 복원함으로써 authority(EOA)를 얻고 해당 계정에 코드 슬롯에 위임 지시자(deligation indicator)를 기록한다. 사실 EIP-7702 표준 하나를 이해하는데 여기까지 볼 필요는 없지만, 궁금하기도하고 geth 분석도 할겸 겸사겸사 살펴보겠다. 버전은 geth v1.17.5를 기준으로 한다

 

0. 튜플이란? (튜플 서명 과정)

트랜잭션 SetCodeTx.AuthList에 들어있는 튜플이다. 다음과 같이 구성되어 있다.

core/types/tx_setcode.go#L72

 

서명은 SigHash()로 keccak256(MAGIC(0x05) || rlp([chain_id, address, nonce]))을 수행하고, SignSetCode() 함수를 호출해 완성한다.

core/types/tx_setcode.go#L91

1. type 4 tx에서 복원되는 서명 종류

auth_list를 추가한 type 4 tx의 경우 복원해야하는 서명 종류가 크게 2가지로 나뉜다.

 
바깥 tx SetCodeAuthorization 튜플
필드
SetCodeTx의 V, R, S
 SetCodeAuthorization의 V, R, S (yParity)
복원 함수
types.Sender
SetCodeAuthorization.Authority
결과
msg.From (tx.origin)
지시자를 받을 authority
실패
트랜잭션 전체 무효
그 튜플만 skip

 

2. 튜플로부터 authority 값 복원

EVM이 아니라 executecall안의 Authority()가 ecrecover로 authority(EOA)주소를 확보한다. 트랜잭션이 노드에 들어오고 검증되는 과정은 생략하고, 전체적인 콜스택은 다음과 같다. 순서대로 설명해보겠다.

 execute(트랜잭션 진입점)
 -> executeCall
 	-> applyAuthorizations
            -> applyAuthorization // 튜플마다 시행
                -> validateAuthorization
                    -> auth.Authority()	// EOA 주소 복원
                -> SetNonce / SetCode
            -> evm.Call

 

먼저 복사해둔 auth_list를 인자로 넘겨 applyAuthorizations()를 호출한다. 

core/state_transition.go#L849

 

applyAuthorizations()는 auths를 돌면서 applyAuthorization()을 호출한다. 서명 실패·체인 ID 불일치·nonce 불일치는 그 튜플만 건너뛰고 다음으로 진행한다. 전체 tx가 revert되지 않는다.

core/state_transition.go#L1144

 

applyAuthorization()의 첫 줄은 validateAuthorization()를 호출하는데, 이안에서 chaind_id, nonce값 확인과 서명 복원이 이루어진다.

core/state_transition.go#L1067

 

validateAuthorization()의 전체 과정은 다음과 같다.

core/state_transition.go#L1027

 

3번 auth.Authority()로 authority 주소를 확보하는 과정은 다음과 같다.

core/state_transition.go#L121

 

3. Deligation indicator(위임 지시자) 기록

다시 applyAuthorization()으로 돌아와서, authority 주소를 확보했으니 위임 지시자를 authority 코들 슬롯에 기록할 차례다.

core/state_transition.go#L1067

 

3. TestEIP7702

setCode 트랜잭션이 EOA에 위임 지시자를 쓰고, 위임 컨트랙트를 따라 call이 실제로 실행되는지를 검증하는 테스트를 TestEIP7702를 실행해보자. 테스트 전체코드는 요기에

 

테스트에서는 다음 세가지를 확인한다. 

  • addr1(EOA)가 컨트랙트 aa 위임
  • addr2(EOA)가 컨트랙트 bb 위임
  • addr2 storage slot 0x42에 값 0x42 기록 확인 (위임된 bb 코드가 addr2 context에서 실행되었음을 확인)

테스트에서 진행하는 tx는 addr1이 자신을 call 하는 형태이고 다음과 같이 진행된다.

  1. 트랜잭션이 addr1을 CALL
  2. addr1은 aa로 위임 → aa 코드 실행 (CALL addr2, 1 wei)
  3. addr2는 bb로 위임 → bb 코드 실행 (SSTORE(0x42, 0x42))
  4. storage는 addr2에 기록됨 (실행 주체가 addr2이기 때문)

먼저 각 EOA에 1ether씩 주고, 컨트랙트에는 코드를 삽입한다.

core/blockchain_test.go#L4102

 

각 EOA들이 컨트랙트를에 위임하기 위해 2개의 setCodeAuthorization을 만든다. 

 

이후에 SetCodeTx를 만들고 블록에 추가한다.

tx가 정상적으로 실행되었는지 아래 코드를 통해 확인한다.

 

테스트를 돌려보면 pass한 것을 볼 수 있다.

go test ./core -run '^TestEIP7702$' -count=1 -v

=== RUN   TestEIP7702
--- PASS: TestEIP7702 (0.00s)
PASS
ok      github.com/ethereum/go-ethereum/core    0.052s

 

지금까지 geth를 통해 프로토콜 단에서 EIP-7702를 어떻게 적용하는지 살펴봤다. 이제는 테스트넷에서 EOA가 eth-infinitism에서 작성한 EIP7702구현체 Simple7702Account를 위임하는 트랜잭션을 제출하고, 최종상태와 트랜잭션을 살펴보겠다.

 

먼저 sepolia testnet을 로컬로 fork 한 후에 테스트를 진행하겠다.

anvil --fork-url https://ethereum-sepolia-rpc.publicnode.com

 

스크립트는 다음과 같이 작성했다. 요청 JSON은 직접 작성하지 않고 viem 라이브러리를 사용했다.

import {
  createWalletClient,
  createPublicClient,
  http,
  parseGwei,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";

// address : 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
const PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
// sepolia 테스트넷 로컬 fork
const RPC_URL = "http://127.0.0.1:8545";
// https://github.com/eth-infinitism/account-abstraction/blob/v0.8.0/deployments/ethereum/Simple7702Account.json
const IMPL = "0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9";

// 개인키를 계정 객체로 변환
const eoa = privateKeyToAccount(PRIVATE_KEY);
const transport = http(RPC_URL);
// 체인 조회용 클라이언트
const publicClient = createPublicClient({ chain: sepolia, transport });
// 지갑 객체 생성
const walletClient = createWalletClient({
    account : eoa, // 서명 주체
    chain : sepolia,
    transport : transport,
});

// EOA nonce값 읽어오기
const nonce = await publicClient.getTransactionCount({
    address : eoa.address,
});

// 위임 튜플 서명(setCodeAuthorization 서명)
const authorization = await walletClient.signAuthorization({
    account : eoa,
    // 위임할 코드 주소(Simple7702Account)
    contractAddress : IMPL,
    chainId : sepolia.id,
    // tx sender와 authority가 같은 상황이므로 
    // 바깥 tx에서 nonce가 먼저 1증가한 것을 고려해야 함
    nonce : nonce + 1,
});

console.log("authorization:", authorization);

// type 4 tx 전송
const hash = await walletClient.sendTransaction({
    account : eoa,
    to : eoa.address,
    authorizationList: [authorization],
});

console.log("tx hash:", hash);

// eth_getTransactionByHash 요청
const transaction = await publicClient.getTransaction({ 
    hash: hash,
});

console.log("\n=== transaction info ===");
console.log("transaction:", transaction);

// EOA code 읽어오기
const bytecode = await publicClient.getCode({
    address: eoa.address,
});
console.log("\nEOA code:", bytecode);

 

EOA privatekey는 anvil 테스트 계정을 사용했고, 배포된 eip7702 관련 구현체 주소는 Simple7702Account에서 확인할 수 있다.

스크립트를 돌려본 결과를 같이 살펴보자

signAuthorization 반환값

트랜잭션 정보도 조회한 결과를 살펴보자. 트랜잭션 타입이 0x04임이 표시되어 있다.

=== transaction info ===
transaction: {
  type: 'eip7702',
  chainId: 11155111,
  nonce: 47818,
  gas: 46046n,
  maxFeePerGas: 2178393150n,
  maxPriorityFeePerGas: 1000000000n,
  to: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  value: 0n,
  accessList: [],
  authorizationList: [
    {
      address: '0x4cd241e8d1510e30b2076397afc7508ae59c66c9',
      chainId: 11155111,
      nonce: 47819,
      r: '0xbb7455bc8f3185a0efb3e526d55cede18afb6036404064c42a329f0044f65bce',
      s: '0x6668d049e1dba23aa7bbdeae1faa55098abad54cf6f2d3b761d1019a4fba165f',
      yParity: 1
    }
  ],
  input: '0x',
  r: '0xb7eca5282a473e3c818782db092830e5c7ee7e6bd6e891c4bdc27e851b10f1f0',
  s: '0x43d8596bed9f8cac24b0771c4c81d775acafecdeec4f48e80581573378886a0e',
  yParity: 1,
  v: 1n,
  hash: '0x2a8b25f3af3f33c004b669e6f130d5298b2de33b3ad44f29d6c2612b7b1fb59a',
  blockHash: '0x26e6919bc5ea06cac91563cb6d9fb6a320e190a78a3f915dddde06530ff9eae5',
  blockNumber: 11583623n,
  transactionIndex: 0,
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  gasPrice: 2104705239n,
  maxFeePerBlobGas: undefined,
  typeHex: '0x4'
}

 

EOA의 코드 슬롯 조회 결과에서 위임지시자 형식을 갖춘 것을 볼 수 있다.

0xef0100 ❘❘ 구현체 주소


References

ethereum/go-ethereum at v1.17.5

 

GitHub - ethereum/go-ethereum at v1.17.5

Go implementation of the Ethereum protocol. Contribute to ethereum/go-ethereum development by creating an account on GitHub.

github.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/09   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함