JIT 증거 복원
구현 저장소: BackwardLabs/daejang-jit-engine @ 8cb725d
요청된 subject에 필요한 거래 증거만 복원해 Observation을 만든다.
왜 필요한 거래만 복원하는가
전체 체인의 상세 실행 데이터를 계속 저장하면 trace와 state 수집 비용이 너무 크다. 반대로 block number, transaction index, match bits 같은 최소 역색인만으로는 다음 내용을 확정할 수 없다.
- 그 위치가 index snapshot이 가리킨 동일 canonical block인지
- 실제 transaction hash와 inclusion이 무엇인지
- coarse match가 실제 account 참여인지 false positive인지
- 관련된 정확한
logIndex,traceAddress,authorizationIndex가 무엇인지 - 실행이 성공, revert, reorg 중 어느 상태인지
- account 관점의 자산 수량 부호와 Observation origin이 무엇인지
- 결과가 불완전한 이유가 증거 누락인지 schema 표현 한계인지
그래서 역색인과 canonical Observation 사이에 replay 계층을 둔다. 재현 가능하고, 멱등적이며, 실패를 숨기지 않는다.
역색인 hit 자체는 결코 관련성의 증거로 인정되지 않는다. 엔진은 provider에서 block과 transaction을 다시 해석하고, 관측한 block hash를 기록한다.
JIT 처리 흐름 한눈에 보기
이 그림은 JIT의 핵심 흐름만 보여준다. 전체 체인을 다시 훑지 않고 역색인으로 범위를 좁힌 뒤, 각 후보를 canonical 실행 증거와 대조해 Observation을 만든다.
| 단계 | 맡는 일 |
|---|---|
| 지갑 주소 조회 | 요청한 지갑 주소를 기준으로 account 범위를 정한다 |
| 역색인 조회 | 해당 지갑과 관련될 가능성이 있는 transaction으로 범위를 좁힌다 |
| 체인 증거 확인 | 관련 transaction, receipt, log, trace와 state evidence를 복원하고 검증한다 |
| Observation | 검증된 실행 결과에서 확인한 account 범위 사실만 기록한다 |
이 흐름 뒤에서는 schema 검증과 저장도 수행하며, unsupported 또는 불완전한 evidence에는 명시적인 outcome을 남긴다. JIT 경로는 의도적으로 Observation에서 멈춘다. Swap, bridge, self-transfer, valuation, 취득가액, 세무 의미는 이후 계층이 담당한다.
입력 계약
interface StartJitRun {
generationId: string;
subjectId: string;
indexSnapshotId: string;
evidenceProfile: 'EVM_ACCOUNT_FULL';
accounts: Array<{
accountId: string;
chainId: string;
address: string;
ownershipFrom: string;
ownershipTo?: string;
}>;
candidates: Array<{
accountId: string;
chainId: string;
blockNumber: string;
transactionIndex?: number;
blockEventIndex?: number;
matchBits: number;
}>;
}
accounts와 candidates는 모두 비어 있을 수 없고, 모든 candidate는 같은 요청 안의 account를 참조해야 한다.
정규화와 검증
| 필드 | 규칙 |
|---|---|
| ID | letter, digit, ., _, :, - |
chainId | CAIP-2 EVM 형식 eip155:<10진수 chain id>. candidate의 chain은 account의 chain과 같아야 한다 |
address | 20-byte 0x EVM 주소, 소문자로 정규화. EIP-55 checksum은 요구하지 않는다 |
| ownership window | RFC 3339 timestamp를 [ownershipFrom, ownershipTo)로 해석. 끝은 선택이며 시작보다 앞설 수 없다 |
blockNumber | 선행 0 없는 음이 아닌 canonical 10진 문자열. 단 0은 예외 |
| candidate 좌표 | transactionIndex 또는 blockEventIndex 중 정확히 하나 |
matchBits | 0x01..0x80 범위의 알려진 bit들의 0이 아닌 bitwise OR |
| evidence profile | EVM_ACCOUNT_FULL로 고정. 요청별 축소 profile은 받지 않는다 |
| 중복 candidate | 한 snapshot 안에서 동일 locator의 첫 번째가 canonical이고, 이후는 DUPLICATE로 종결 |
| 알 수 없는 필드 | 정규화 과정에서 제거 |
Match bits
| Bit | 의미 |
|---|---|
0x01 | TX_FROM |
0x02 | TX_TO |
0x04 | ASSET_FROM |
0x08 | ASSET_TO |
0x10 | ERC4337_ACCOUNT |
0x20 | EIP7702_AUTHORITY_CANDIDATE |
0x40 | NATIVE_INTERNAL_FROM |
0x80 | NATIVE_INTERNAL_TO |
match bit는 발견 힌트다. exact-hit 재구성은 여전히 NOT_RELEVANT를 반환할 수 있다. internal call, log, authorization 좌표는 실행 증거에서 재구성하며, 외부 candidate 좌표로 받지 않는다.
구현된 기능
- 불변 selection snapshot과 selection digest
- 후보 거래의 exact hit 및 bounded closure 선택
- provider retry·failover, 체인 identity 확인, provider divergence 감지
- canonical transaction, receipt, log, trace 기반 증거 수집
- ERC-20, ERC-721, ERC-1155 transfer Observation
- ERC-4337
UserOperationEvent, EIP-7702 authorization, withdrawal 관측 - reorg 및 inclusion revision 처리
- 실행 결과 cache, singleflight, bounded concurrency
- CUE schema 검증
CandidateQueryService와JitEngineService를 제공하는 Go gRPC daemonjitd- Unix peer UID 또는 mTLS subject 기반 접근 제어
- artifact staging과 terminal commit을 포함한 저장 adapter
- graceful shutdown, health endpoint, secret redaction
지갑·프로토콜 가정
- 입력 주소를 EOA로 가정하지 않는다. EOA, contract wallet, ERC-4337 smart account 주소를 모두
WALLETaccount로 만든다. - EIP-7702 candidate는 복원한 authorization과 적용 전후 account state로 검증한다. candidate bit만으로 Observation을 만들지 않는다.
- ERC-4337
userOpHash와userOpIndex는 native 실행 증거로 보존한다. 고정된 schema에 일급USER_OPERATION좌표가 없으므로, 해당 작업은 임의의 transaction 좌표로 매핑되지 않고 partial 또는 unsupported로 종결된다. - 현재
ERC4337_ACCOUNTexact match는 user operation의sender다. paymaster와 실제 gas payer 역할은 wallet hit가 되기 전에 별도의 discovery semantics가 필요하다. - transaction 없는 block credit과 withdrawal은
blockEventIndex를 사용한다. 고정된 schema에 transaction 없는 Observation origin이 없으므로, 현재UNSUPPORTED로 종결하며 native 좌표를 보존한다.
하지 않는 일
다음 기능은 JIT 엔진의 책임이 아니다.
- swap, bridge, self-transfer 같은 경제적 event 판정
- Posting, KRW valuation, 취득가액, 세무 정책 적용
- 최종 report 생성 또는 GIWA commitment 제출
- ERC-4337 paymaster나 실제 gas payer를 wallet 소유 관계로 추론
- schema에 없는 좌표를 임의로 생성
- 이미 발행한 generation을 제자리에서 수정
범위에 대한 두 가지 추가 사항:
- 출력 discovery 범위는 account별로 해석된 candidate block의 최소·최대에서 유도한다. 엔진이 그 구간의 모든 block을 독립적으로 스캔했다는 주장이 아니다.
- artifact 보존, eviction,
EVICTEDlifecycle은 durable production store가 필요하며 in-memory 참조 구현에는 없다.
고정 schema 계약
| 항목 | 값 |
|---|---|
| 저장소 | BackwardLabs/schema |
| 모듈 digest | d08257dbf7f22a381540cc1331ca6047a53bf0863eaa42d28d8db65bde371303 |
검증
기본 브랜치에서 go test ./..., go vet ./..., go build ./...가 통과한다.
이 테스트는 fixture, mock provider, transport, storage adapter 경계를 확인한다. 상용 RPC provider나 전체 제품 경로에 대한 동작은 증명하지 않는다.