external call이 발생하면 (CALL, STATICCALL, DELEGATECALL) 해당 externall call에서 사용되기 위한gas가 전달되게 된다.
value를 설정하듯이 전달할 gas또한 caller가 설정할 수도 있다.
참고로
CALLopcode를 사용하게 되면 무조건21000gas는 소모하여 날아가게 된다.
머나 먼 옛날에는 CALL의 gas cost가 매우 적었고, 모든 gas를 전달할 수 있었다.
따라서 거의 무한히 call stack을 쌓을 수 있었다.
이 "stack too deep" 문제를 해결하기 위해서, stack의 깊이를 1024로 제한하게 되었다.
(현재도 이것은 유지되고 있다.)
stack의 깊이가 1024까지 도달되면 revert가 나게 되었다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.5;
// DO NOT USE!!!
contract Auction {
address highestBidder;
uint256 highestBid;
function bid() external payable {
if (msg.value < highestBid) revert();
if (highestBidder != address(0)) {
payable(highestBidder).send(highestBid); // refund previous bidder
}
highestBidder = msg.sender;
highestBid = msg.value;
}
}
공격자가 bid() 함수를 호출하기 전에, 스스로에게 recursive call을 돌려서 1023개의 call stack을 쌓아놓고,
1024번 째에 bid()를 호출한다.
그럼 send의 경우 조용히 revert가 나게 되고 코드는 마지막까지 흘러간다.
(전체 트랜잭션 롤백이 일어나지 않아 이전 highestBidder는 환불을 받지 못하게 된다.)
요약해서 말하자면, call opcode를 사용할 때 (call stack이 한 칸 늘어날 때) 현재 사용할 수 있는 gas의 63/64 만큼만 전달할 수 있다.
Gas available at Stack depth 0 = Initial gas available * (63/64)^0
Gas available at Stack depth 1 = Initial gas available * (63/64)^1
Gas available at Stack depth 2 = Initial gas available * (63/64)^2
Gas available at Stack depth 3 = Initial gas available * (63/64)^3
.
.
.
Gas available at Stack depth N = Initial gas available * (63/64)^N
이렇게 external call로 콜 스택이 하나씩 늘어날 때마다 gasleft의 (63/64)*N 만큼씩만 가스를 소모할 수 있는 것이다.
(명시적으로 gasleft를 전달해 주더라도, 1/64는 caller에게 남게 된다.)
이렇게 call stack이 늘어날 떄마다 급격하게 callee가 사용할 수 있는 gas의 양은 줄어들게 된다.

EIP-150 이후로 사실상 불가능하지만, 이론적으로 EVM에서는 여전히
1024개의 stack이 허용된다.
EIP-150은 Call Depth Attack을 막기 위해 제안되었으며, 이는 63/64 Rule을 통해서 이루어진다.
참고로 위에서 언급하였듯, call 사용 시에 명시적으로 gas left를 모두 전달해 주어도 여전히 gas left의 1/64는 Caller contract에 남아있게 된다.