// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MagicNum {
address public solver;
constructor() {}
function setSolver(address _solver) public {
solver = _solver;
}
/*
____________/\\\_______/\\\\\\\\\_____
__________/\\\\\_____/\\\///////\\\___
________/\\\/\\\____\///______\//\\\__
______/\\\/\/\\\______________/\\\/___
____/\\\/__\/\\\___________/\\\//_____
__/\\\\\\\\\\\\\\\\_____/\\\//________
_\///////////\\\//____/\\\/___________
___________\/\\\_____/\\\\\\\\\\\\\\\_
___________\///_____\///////////////__
*/
}
우리의 mission은 저어어어어엉어말 작은 코드를 만들어야 한다. 최대 10 opcode를 사용하여 만들어야 한다.
hint는 수동으로 작성하라고 나와 있다.
Magic Number는 찾기 쉽다. 주석으로 42라고 나와있다.
그럼 흐음.. 어떻게 만들어야 하나....
그전에 Contract의 초기화와 생성 과정을 알고 있으면 감이 잡힌다. 잘 모른다면 빠르게 공부해 보자!! -> 일로일로~~
[BlockChain] Contract & Assembly
안뇽 안뇽~~ 이번에는 solidity의 어셈블리에 관한 이야기를 하려고 왔어!!! ?? 아니 그냥 우리가 흔히 아는 어셈블리어랑 비슷한 거 아닌가?? 이렇게 생각할 수도 있지 그런데 제목이 뭐야 Contract & A
rhenus9911.tistory.com
결론은 우리가 Solidity로 Contract를 만들면 이 Contract는 bytecode로 바뀐다. 그리고 Initialization인 부분과 execution부분으로 나뉘는 데 이 runtime code를 작성해야 한다. 즉 bytecode를 만들라는 소리이다.
그럼 문제를 보면 setSolver(address _solver) public { solver = _solver }의 함수에서 _solver를 이용하여 42를 solver에 넣어야 한다.
Solidity Opcode
Home | ethereum.org
Ethereum is a global, decentralized platform for money and new kinds of applications. On Ethereum, you can write code that controls money, and build applications accessible anywhere in the world.
ethereum.org
opcode는 두 섹션으로 작성을 해야 한다.
EVM이 런타임 opcode를 복제하고 Storage에 저장하기 위해 Contract를 생성하는 데 사용하는 opcode
Contract의 실행 로직을 포함하는 runtime opcode 가 있다.
먼저 runtime opcode를 보면 42를 반환해야 한다.
return(p, s)를 보면 end execution, return data mem[p...(p+s)) 설명이 나와 있다,
즉 실행을 종료하고 mem [p:p+s]까지 data를 반환한다. 즉 p:시작 주소, s: 데이터 크기를 뜻한다.
먼저 42를 메모리에 저장하고 42가 저장되어 있는 위치를 반환하면 된다.
mstore(p, v)를 보면 mem[p...(p+32)) := v라고 나와 있다. 32byte를 쓰면서 메모리 위치에 v를 저장한다.
opcode랑 assembly를 같이 보자.
602A // PUSH V(2A)
6060 // PUSH P(60)
52 // MSTORE
// mstore(0x60, 2A)
bytecode가 완성되었다. 그리고 이제 메모리를 return 하면 된다.
6020 // PUSH1 s(20)
6060 // PUSH1 p(60)
F3 // RETURN
// return(0x60, 0x20);
bytecode가 완성되었다. 이 bytecode를 합치면 602A60605260206060F3으로 딱 10byte opcode가 된다.
이제 초기화 opcode를 보자
초기화 opcode에서 contract가 address랑 연결되니까 runtime opcode 앞에 와야 한다.
이러한 opcode는 runtime opcode를 메모리에 로드하고 동일한 내용을 EVM에 반환해야 한다.
assembly 중 codecopy라는 어셈이 있다.
codecopy(t, f, s)를 보면 copy s bytes from code at position f to mem at position t 설명이 나와 있다.
CODE f를 MEM t로 s bytes만큼 복사한다고 나와 있다.
600A // PUSH1 s(0A)
60?? // PUSH1 f(??)
6000 // PUSH1 t(00)
39 // CODECOPY
//codecopy
f값을 모르는 이유는 runtime opcode의 현재 위치를 뜻한다. 그래서 아직 공백으로 비워두었다.
600A // PUSH1 s(0A)
6000 // PUSH1 p(00)
F3 // RETURN
// return(0x00, 0x0a);
마지막으로 EVM에 대한 메모리값을 return 해야 한다.
총 bytecode의 결과는 600A60??600039600A6000F3이다. 그리고 그다음 바로 runtime opcode가 나온다. 초기화 opcode는 0x00부터 시작한다. 길이는 12byte이고 그다음 runtime opcode가 나오니까 f는 0x0c가 된다.
결과는 0x600A600C600039600A6000F3602A60605260206060F3 bytecode가 된다.
이 코드를 sendTransaction을 이용하여 보내면 된다.
Tx를 etherscan에서 보면 CA가 만들어진 것을 확인할 수 있다. 이 CA를 _solver의 값으로 넣으면 된다.