> ## Documentation Index
> Fetch the complete documentation index at: https://docs.optimism.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 프리컴파일 추가하기

> 현재 지원되지 않는 연산을 가속화하기 위해 OP Stack 체인 운영에서 새 프리컴파일이 포함된 EVM을 실행하는 방법을 알아봅니다.

> [!NOTE]
>
> OP Stack Hacks는 OP Stack으로 할 수 있지만 현재 프로덕션 사용을 위한 것이 *아님*이 명시된 작업들입니다.
>
> OP Stack Hacks는 가벼운 마음으로 시도할 수 있는 것이 아닙니다. OP Stack Hacks에 대해서는 충분한 개발자 지원을 받을 수 없습니다. 직접 손을 더럽히고 지원 없이 작업할 각오를 하십시오.

OP Stack의 활용 방법 중 하나는 현재 지원되지 않는 연산을 가속화하기 위해 새 프리컴파일(precompile)이 포함된 EVM을 실행하는 것입니다. 이 튜토리얼에서는 4바이트 이하로 호출되면 상수 값을 반환하고, 그보다 많은 바이트로 호출되면 오류를 반환하는 간단한 프리컴파일을 만듭니다.

새 프리컴파일을 생성하려면 [`op-geth/core/vm/contracts.go`](https://github.com/ethereum-optimism/op-geth/blob/optimism-history/core/vm/contracts.go) 파일을 수정해야 합니다.

**1. 82번째 줄의 `PrecompiledContractsBerlin`(또는 프리컴파일 목록이 다시 변경된 경우 이후 포크)에 추가합니다**

* 새 프리컴파일의 이름을 딴 구조체를 추가하고,
* 표준 프리컴파일과 충돌할 가능성이 낮고 [EIP-7587](https://eips.ethereum.org/EIPS/eip-7587) 예약 범위를 피하는 주소(예: 0x1337)를 사용합니다.

  ```go theme={null}
  common.BytesToAddress([]byte{0x13,0x37}): &retConstant{},
  ```

**2. 프리컴파일 코드를 추가합니다**

```go theme={null}
  type retConstant struct{}

  func (c *retConstant) RequiredGas(input []byte) uint64 {
      return uint64(1024)
  }

  var (
      errConstInvalidInputLength = errors.New("invalid input length")
  )

  func (c *retConstant) Run(input []byte) ([]byte, error) {
      // Only allow input up to four bytes (function signature)
      if len(input) > 4 {
          return nil, errConstInvalidInputLength
      }

      output := make([]byte, 6)
      for i := 0; i < 6; i++ {
          output[i] = byte(64+i)
      }
      return output, nil
  }
```

**3. `op-geth`를 중지하고 다시 컴파일합니다.**

```bash theme={null}
  cd ~/op-geth
  make geth
```

**4. `op-geth`를 재시작합니다**

**5. 다음 명령을 실행하여 프리컴파일 호출이 성공한 결과와 오류가 발생한 결과를 확인합니다.**

```bash theme={null}
  cast call 0x0000000000000000000000000000000000001337 "whatever()"
  cast call 0x0000000000000000000000000000000000001337 "whatever(string)" "fail"
```

## 작동 방식

다음은 프리컴파일 인터페이스 정의입니다.

```go theme={null}
type PrecompiledContract interface {
	RequiredGas(input []byte) uint64  // RequiredPrice calculates the contract gas use
	Run(input []byte) ([]byte, error) // Run runs the precompiled contract
}
```

즉, 모든 프리컴파일에는 두 개의 함수가 필요합니다.

* `RequiredGas`는 호출에 드는 gas 비용을 반환합니다. 이 함수는 바이트 배열을 입력으로 받아 gas 비용이라는 단일 값을 반환합니다.
* `Run`은 실제 프리컴파일을 실행합니다. 이 함수도 바이트 배열을 입력으로 받지만, 호출 출력(바이트 배열)과 오류라는 두 개의 값을 반환합니다.

프리컴파일을 변경하는 각 포크마다 주소를 `PrecompiledContract` 정의에 매핑하는 [`map`](https://www.w3schools.com/go/go_maps.php)이 있습니다.

```go theme={null}
// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
  common.BytesToAddress([]byte{1}): &ecrecover{},
  .
  .
  .
  common.BytesToAddress([]byte{9}): &blake2F{},
  common.BytesToAddress([]byte{0x13,0x37}): &retConstant{},
}
```

map의 키는 주소입니다. 주소는 `common.BytesToAddress([]byte{<bytes to convert to address go here>})`를 사용하여 바이트로부터 생성합니다. 이 경우에는 `0x13`과 `0x37` 두 바이트가 있으며, 이를 합치면 `0x0…1337` 주소가 됩니다.

프리컴파일된 컨트랙트 인터페이스의 구문은 `&<variable name>{}`입니다.

다음 단계는 프리컴파일된 컨트랙트 자체를 정의하는 것입니다.

```go theme={null}
type retConstant struct{}
```

먼저 프리컴파일을 위한 구조체를 생성합니다.

```go theme={null}
func (c *retConstant) RequiredGas(input []byte) uint64 {
    return uint64(1024)
}
```

그런 다음 해당 구조체의 일부로 함수를 정의합니다. 여기에서는 일정한 양의 gas만 요구하지만, 물론 계산을 훨씬 더 정교하게 할 수도 있습니다.

```go theme={null}
var (
    errConstInvalidInputLength = errors.New("invalid input length")
)

```

다음으로 오류를 위한 변수를 정의합니다.

```go theme={null}
func (c *retConstant) Run(input []byte) ([]byte, error) {
```

이것이 실제로 프리컴파일을 실행하는 함수입니다.

```go theme={null}

    // Only allow input up to four bytes (function signature)
    if len(input) > 4 {
        return nil, errConstInvalidInputLength
    }
```

필요한 경우 오류를 반환합니다. 이 프리컴파일이 최대 4바이트의 입력을 허용하는 이유는 모든 표준 호출(예: `cast` 사용)에는 함수 시그니처를 위한 최소 4바이트가 포함되기 때문입니다.

`return a, b`는 Go에서 함수가 두 개의 값을 반환하는 방식입니다. 여기에서는 오류를 반환하므로 일반 출력은 아무것도 없는 `nil`입니다.

```go theme={null}
    output := make([]byte, 6)
    for i := 0; i < 6; i++ {
        output[i] = byte(64+i)
    }
    return output, nil
}
```

마지막으로 출력 버퍼를 생성하고 값을 채운 다음 반환합니다.

## 결론

추가 프리컴파일이 포함된 OP Stack 체인은 유용하게 활용할 수 있습니다. 예를 들어 암호화 연산을 인터프리트되는 EVM 코드에서 컴파일된 Go 코드로 옮겨 필요한 연산량을 더욱 줄일 수 있습니다.
