for { // 점프 테이블에서 연산을 가져오고 스택의 유효성을 검사하여 // 연산을 수행할 수 있는 충분한 스택 항목이 있는지 확인 op = contract.GetOp(pc) operation := in.table[op] cost = operation.constantGas // 추적을 위함 // 스택 유효성 검사 if sLen := stack.len(); sLen < operation.minStack { return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } if !contract.UseGas(cost) { return nil, ErrOutOfGas } if operation.dynamicGas != nil { // 동적 메모리 사용량이 있는 모든 작업에는 동적 가스 비용도 있다. var memorySize uint64 // 새 메모리 크기를 계산하고 // 연산에 맞게 메모리를 확장한다. // 동적 가스 부분을 평가하기 전에 메모리 검사를 수행해야 // 계산 오버플로를 감지할 수 있다. if operation.memorySize != nil { memSize, overflow := operation.memorySize(stack) if overflow { return nil, ErrGasUintOverflow } // 메모리는 32바이트 단위로 확장 // 가스도 동일 if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { return nil, ErrGasUintOverflow } } // 가스를 소비하고 사용 가능한 가스가 충분하지 않으면 오류를 반환한다. // 캡처 상태 지연 메서드가 적절한 비용을 얻을 수 있도록 비용이 명시적으로 설정된다. var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // 추적을 위함 if err != nil || !contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } if memorySize > 0 { mem.Resize(memorySize) } } else if debug { in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } // execute the operation res, err = operation.execute(&pc, in, callContext) if err != nil { break } pc++ }