안뇽 안뇽~~ 이번에는 solidity의 어셈블리에 관한 이야기를 하려고 왔어!!!
?? 아니 그냥 우리가 흔히 아는 어셈블리어랑 비슷한 거 아닌가?? 이렇게 생각할 수도 있지
그런데 제목이 뭐야 Contract & Assembly잖아 ㅎㅎ assembly를 설명하는 데 contract까지 연관이 되어 있어서 가져왔어!!
귀찮아도 한번 봐줘 ㅠㅠ
모든 설명은 Ethernaut 19 MagicNumber 출제자 포스팅이랑 Yellow Paper 기반으로 한다는 것을 알아줭!!
일단 먼저 Contract에 대해서 살펴볼까??
Contract Initialization
contract는 어떻게 만들어 질까??
먼저 Contract를 생성하기 위한 트랜잭션이 Ethereum Network로 전송이 돼. 이 트랜잭션에는 그림과 같은 변수들이 포함되어 있어
symbol |
Variable |
Mean |
s |
Sender |
새로운 컨트랙트를 생성하고자 하는 지갑의 주소 |
o |
Original Transactor |
원래의 외부 사용자, 사용자가 많은 Contract를 생성한 경우 o!=s이다. |
g |
Available gas |
Contract 생성을 위해 할당된 총 가스 |
p |
Gas Price |
거래 비용을 ETH로 환산한 가스 단위의 시장 가 |
v |
Endowment |
임의 길이 바이트 배열 |
i |
Initialization EVM code |
초기화 EVM 코드 |
e |
Stack |
call/contract 생성 스택의 현재 깊이 |
ζ |
salt |
새 계정의 주소에 대한 보증금 default = 0 |
w |
state |
상태를 수정할 수 있는 권한 |
그리고 입력된 데이터를 기반으로 새로운 컨트랙트 주소가 미리 계산이 되어!! 이 단계에서 입력 상태 값이 수정되지만 여전히 새로운 Contract의 상태는 비어있는 상태야.
그럼 EVM에서 초기화 코드가 실행되고 실제 Contract가 생성되는 거야~~~ 이 단계에서는 상태 변수가 변경되고, 데이터가 저장되며 가스가 소비돼
Contract가 초기화되면 Contract는 자체 코드를 미리 계산된 주소와 연결하여 저장해
마지막으로 소비하고 남은 가스와 성공/실패 여부를 비동기적으로 S에게 보내
Contract 초기화 과정은 거의 6단계에 걸쳐서 진행이 돼
글로 한 내용을 대략적으로 그림으로 나타내면 이런 흐름으로 나타나
어때 이해가 조금 되는 거 같아??
Contract Creation
자! 우리는 Contract가 만들어지기 까지의 과정을 살펴보았어
그럼 Contract를 초기화시키는 동안 어떻게 Contract가 만들어질까??
출제자의 포스팅 그림을 그대로 가지고 왔당.
첫 번째로 우리가 이더리움 네트워크에 트랜잭션을 보내, data는 포함되어 있지만 수신자는 없겠지??
그러면 두 번째로 EVM에서 우리가 작성한 solidity의 언어를 bytecode로 컴파일해 이때 바이트 코드는 순차적으로 Initialization code와 runtime code를 연결해
Contract가 생성 돼는 동안 EVM은 스택의 첫 번째 STOP 또는 RETURN 명령에 도달 할 때까지만 실행해. 이 단계에서는 컨트랙트의 constructor() 함수가 실행되고 컨트랙트는 주소를 가지게 되
Initialization code가 실행된 후 runtime code만 스택에 남게 되겠지?? 그럼 남은 opcode가 메모리에 복사되어 EVM으로 반환이 돼
EVM은 리턴된 남은 코드를 새로운 컨트랙트 주소와 관련하여 state storage에 저장하고 이것들은 새로운 컨트랙트에 대한 모든 호출에서 스택에 의해 실행 될 runtime code인 거야~~
어때 그림이랑 같이 이해하니까 할만하지??
Contract는 이렇게 만들어지는 거야 ㅎㅎ
그럼 우리 다 같이 어셈블리로 코드를 한번 만들어 볼까??
// SPDX-Lisence-Identifier: MIT
pragma solidity >= 0.7.0 < 0.9.0;
library AssemblyTest {
function plus(uint x, uint y) public pure returns (uint result) {
assembly {
result := add(x, y)
}
}
function sub(uint x, uint y) public pure returns (uint result) {
assembly {
result := sub(x, y)
}
}
function mul(uint x, uint y) public pure returns (uint result) {
assembly {
result := mul(x, y)
}
}
function div(uint x, uint y) public pure returns (uint result) {
assembly {
result := div(x, y)
}
}
}
어셈블리를 하려면 library를 이용하여 contract를 만들고 안에 assembly라는 키워드를 사용하면 돼~~
bytecode
Assembly
여기 Assembly 명령어랑 bytecode가 있으니까 직접 한번 assembly를 만들어봥~~
이번 시간은 컨트랙트가 초기화되고 만들어지는 과정을 알아봤는 데 이 부분에서 많은 시간을 투자한 거 같다.
아직까지 지식이 많이 부족한 데 직접 코드도 짜보고 실습을 해보면서 발전할 예정이야!!
우리 다 함께 같이 공부해보쟈 긴 글 읽느라 수고했어!!