솔리디티를 공부하면서 도움이 됐던 공부 내용들

김대익·2022년 11월 7일
0

C++, 디자인패턴등을 공부한 이후 세 달동안 솔리디티를 공부하였다.
공부를 하다보니 내가 공부해왔던 것들과 유사하거나 완전히 다른 부분들이 있었는데 그 부분들에 대해 정리해보려고한다.

C++

특징

C++은 OOP중점의 성능중심의 언어로 다른 언어들과 달리 포인터를 만질 수 있고,
기본적으로 Garbage Collector도 지원하지 않는다.
따라서 사용자가 알아서 메모리를 할당하고, 해제해야하는 언어이다.
이러한 점은 사용자의 역량에 따라 성능이 천차만별로 변할 수 있다는 뜻이다.

장점

위에서 말했듯 안전장치들이 전혀 없고 메모리단위로 접근하여 조정이 가능하기에
프로그램의 최대의 효율을 뽑아낼 수 있다.

단점

안전장치가 없기에 실수로 메모리 할당을 풀지않거나 이상한 곳에 할당을 한다면 심각한 버그를 초래하거나
프로그램 사용이 힘들정도로 메모리가 가득찰 수 있다.

블록체인에 적합한가?

빠른 속도를 가능하게 한다는 것은 모든 트랜잭션의 처리 속도가 빨라진다는 것으로,
항상 처리속도 향상을 외치는 블록체인씬에서 좋아보이지만 단점에서 말했듯 메모리 관리를 실패한다면
EVM의 정보가 c++ 메모리단으로 수정가능하다던지 메모리 해제가 되지않아
모든 노드가 마비되는 상황이 와서 안정성을 심하게 해칠 수 있어 적합하지 않다고 생각한다.

솔리디티를 공부하면서 도움이 된 점

  1. C++에서 객체에 변수를 저장할 때 처음 들어오는 타입의 크기의 배수로 데이터가 저장되어
    첫 타입보다 작은 값이 들어오면 그만큼 패딩이 들어가 순서에 따라 객체의 크기가 다르다는 점이
    솔리디티에서 storage에서 데이터를 저장하는 방식과 유사하여 도움이 되었다.
  2. C++은 코드파일을 실행파일로 만드는 과정에 있어 어셈블리 코드를 얻을 수 있는데,
    이를 통해 최적화 과정을 알아볼 수 있고,
    이는 솔리디티 컨트랙트파일에서 Yul에서 최적화한 뒤 bytecode로 변경되는 과정과 유사하다고 느꼈다.
  3. "object파일로 변환시 같은 함수명을 가졌지만 인수가 다른" 함수 오버로딩시에는
    같은 함수지만 hex값으로 된 함수명이 다른데, 이는 솔리디티에서 함수명과 인수값을 해시화하여
    인수값이 다르면 값이 달라지는 function selector와 매우 유사한 개념이여서 이해하는데 도움이 되었다.
  4. 만약 변수가 스택에 들어갈만한 크기라면 힙이 아니라 스택에 저장해서 사용하는 것이 효율적인 점은
    storage 접근을 최소화 하기위해 함수에 storage변수를 메모리로 복사해 사용하는 것과 유사하다 느껴졌다.
  5. 함수의 인수로 storage 타입의 변수와 memory 타입의 변수가 들어왔을 때,
    공부했던 call by reference와 call by value 개념이 이해에 많은 도움이 되었다.
  6. 순수가상함수를 이용하여 인터페이스 클래스를 만드는 OOP방식은
    솔리디티의 abstract contract와 유사하여 개념정리에 도움이 되었다.
  7. C++ shared pointer개념을 proxy pattern에서 이용한다면 여러 로직 컨트랙트가 프록시를 이용할 때
    로직 컨트랙트들이 upgrade하여 다른 프록시로 변경할 때 프록시에서 연결된 컨트랙트의 개수를 체크하여
    개수가 0이 되면 selfdestruct하는 방식을 생각해볼 수 있었다.

JavaScript

장점

  1. GC를 제공하고 매우 동적인 언어로 런타임에 타입이 정해지는 등 사용자가 편리하게 이용이 가능하다.
  2. I / O작업을 실행시키면 기다리지 않고 이벤트가 올 때까지 다른 작업을 진행하면서 큐에 받아놓는다.
    따라서 작업이 끝날 때까지 대기할 필요가 없다.
  3. 비동기작업을 기본적으로 많이 사용하기에 블록체인 노드와 통신하는데 있어 비동기 때문에 당황하는 일은 적다.

단점

  1. var의 호이스팅으로 인해 컨텍스트 관리가 헷갈리는 경우가 생긴다
  2. 이벤트 연산이 아니라 자바스크립트에서 직접 연산을 해야하는 경우,
    그 연산이 오래걸린다면 큐에서 대기 중인 이벤트 결과들은 연산이 끝날 때까지 무작정 기다려야한다.

블록체인에 적합한가?

JS는 너무 동적인 나머지 const로 변수를 고정시켜도 reference만 변경이 불가능할 뿐 속성은 변경이 가능하다.
이렇게 쉽게 변하는 속성은 안정성이 최우선인 블록체인에는 적합하지 않다고 생각된다.

솔리디티를 공부하면서 도움이 된 점

ethernaut을 푸는 과정에서 web3.js를 이용해 프론트엔드 콘솔에서 노드와 통신을 하면서
abi가 어떻게 보이는지 보거나, storage index 내부를 확인해보거나,
getter함수를 실행시키면서 storage변수의 public, private 선언의 중요성을 배웠다.


Golang

장점

  1. 자바의 경우 bytecode를 실행시 machine코드로 변환한다, 이와 달리 Golang은 빌드과정에서 이미 machine코드로 변환했기 때문에 바로 동작하며, 초기 C++에 실용성을 추가한 언어기에 빌드하는 속도도 C++과 거의 비슷할 정도로 빠르다.
  2. gorutine을 이용하면 자바스크립트처럼 비동기처리가 매우 간편하다.

단점

  1. JSON을 읽는데 있어 번거로운 부분이 있다.
  2. 쉬운 언어를 위해 OOP, 함수형프로그래밍등을 포기했다.

블록체인에 적합한가?

빠른 속도와 쉬운 비동기처리, 엄격한 정적타입으로 블록체인에 매우 적합하여
많은 블록체인 메인 코드들이 Golang으로 쓰여져있다.
그 덕에 많은 레퍼런스가 있어 공부하기에도 좋다

솔리디티를 공부하면서 도움이 된 점

EVM 메인 코드와 하이퍼레저 패브릭 코드를 읽기 위해 공부했지만 아직 읽어보진 못했다.
여러개의 리턴값을 가지는 방식은 솔리디티의 call과 delegatecall의 리턴방식을 닮아 이해하는데 도움이 되었다.


디자인패턴

내가 생각한 디자인패턴을 솔리디티에 적용한 경우

  1. 팩토리 / 팩토리 메서드 패턴

    OOP에서 객체는 솔리디티의 컨트랙트에 대입할 수 있다
    팩토리 패턴은 원하는 조건을 입력에 넣으면 조건에 맞는 객체를 리턴해주는 패턴으로
    UniSwapFactoryV2에서 토큰 주소를 넣어 Pair 컨트랙트를 생성하는 것이 이와 동일하고
    팩토리 메서드 패턴은 생성하는 메서드 뿐만 아니라 객체를 관리하는 메서드를 추가하는 패턴으로
    getPair를 통해 컨트랙트의 주소를 확인할 수 있는 부분은 팩토리 메서드 패턴에 해당한다고 생각한다.
    https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol

  2. 빌더 패턴

    constructor에서 속성들을 인수로 넣어 객체를 생성하는데, 객체가 너무 많아
    기본값으로 객체를 생성한 뒤, setter를 만들어 속성별로 따로 설정할 수 있게 하는 패턴이다.
    솔리디티에서는 아래와 같이 쓸 수 있다

    ````````
    string public greeting;
    //
    function greeter(string memory _greeting) public {
    	greeting = _greeting;
    }
    ```````````
  3. 싱글턴 패턴

    하나의 인스턴스만 생성하여 같은 인스턴스를 생성하려고하면 만들어진 인스턴스를 가리키도록 만드는 메서드이다.
    이는 create2와 유사하다고 생각한다.
    create2를 이용하면 항상 같은 주소값을 리턴하므로
    한번만 생성될 뿐만 아니라 항상 같은 컨트랙트를 가리키기 때문이다.
    https://github.com/ishinu/CREATE2-Opcode-/blob/main/contracts/Create2Logic.sol

  4. Facade 패턴

    로직을 이용하려는 사람은 로직을 알 필요없이 컨트롤하는 방법만 알면 된다는 패턴
    V2-periphery UniSwapRouter가 이 패턴에 해당한다고 생각한다.
    유니스왑을 사용하고 싶은 사람은 로직을 알 필요없이 라우터를 이용하는 방법만 알면 된다.
    https://github.com/Uniswap/v2-periphery/blob/master/contracts/UniswapV2Router01.sol

  5. Adapter 패턴

    어떤 컨트랙트 A가 run(address CA, bool ready)메서드가 있는 B컨트랙트를 상속 받고 싶은데
    start(address CA) 메서드만 있는 경우
    Adapter컨트랙트를 생성하고 내용에

    ```
    function run(address CA, bool ready) public {
    	A(A주소).start(CA);
    }
    ```

    를 추가하여 상속이 가능하도록 맞춰주는 패턴

  6. Fly Weight 패턴

    어떤 객체들이 공통적으로 사용하는 거대한 변수가 있을 때,
    모든 객체가 이 거대한 변수를 가지고 있는 것이 아니라
    하나의 static한 변수를 두어 모두 그 변수를 참조하는 패턴
    솔리디티에서 적용한다면 어떤 컨트랙트들이 거대한 bytes값을 가진다면
    이를 라이브러리에 따로 저장해놓은 뒤 컨트랙트들은 getter함수로 이를 읽기만 하는 방식처럼 보인다

  7. template method 패턴

    사용가능한 메서드를 제공하는 인터페이스를 받아 필요한 경우 오버라이딩하여 사용하는 패턴
    Open zeppelin ERC20 코드가 이에 부합한다고 생각한다
    https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol

  8. observer 패턴

    notify() 메서드가 실행되면 알람을 받고싶은 객체들의 update()들이 실행되도록 만드는 패턴
    솔리디티에서 예를 들면

    ```````
    function notify() public {
    	for (uint i = 0; i < contracts.length; i++ {
        	IObserver(contracts[i]).update();
        }
    }
    ``````
  9. 메멘토 패턴

    현재 설정된 변수들을 백업하는 메서드를 만들어 구조체등으로 저장해둔 뒤 필요할 때 다시 읽어오는 메서드를 만들어 변수들을 다시 사용하는 패턴
    솔리디티에서 예를 들면

    ```
    contract Memento {
    	bool genderM;
        address EOAM;
        uint IdNumberM;
    	struct StudentId{
        	bool gender;
        	address EOA;
        	uint IdNumber;
        }
        mapping(uint256=>StudentId) public StudentIdMapping;
        function setGender(bool _gender) public {
        	genderM = _gender;
        }
        function getEOA(address _EOA) public {
        	EOAM = _EOA;
        }
        function setIdNumber(uint _IdNumber) public {
        	IdNumberM = _IdNumber;
        }
        function setStudentId(uint i) public {
        	StudentIdMapping[i] = StudentId(genderM, EOAM, IdNumberM);
        }
        function readStudentId(uint index) public pure {
        	StudentId memory temp = StudentIdMapping[i];
            genderM = temp.gender;
            EOAM = temp.EOA;
            IdNumberM = temp.IdNumber;
    	}
    }
    ```
  10. 브릿지 패턴

    여러 객체가 서로 참조하거나 메서드를 사용해야할 때 dependency관리가 어렵기에 모든 객체를 불러오는 객체를 만들어
    그 객체에서만 참조를 하는 패턴
    솔리디티에서는

    ```
    import A;
    import B;
    import C;
    contract Bridge {
    	function executeA() public {
        	A(address).testA();
        }
        function executeB() public {
        	B(address).testB();
        }
        function executeC() public {
        	C(address).testC();
        }
    }
    ```
  11. Visitor 패턴

    객체를 필요할때 마다 갈아끼워 사용하는 패턴
    솔리디티의 upgradeable proxy와 유사하다
    https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/transparent/TransparentUpgradeableProxy.sol

  12. Proxy 패턴

    인터넷 제공자들이 프록시서버를 두어 자주 사용하는 데이터들을 리턴하듯 객체 앞에 프록시 객체를 두어 결과를 미리 예측하는 메서드를 사용하거나, 결과값들을 저장하는 메서드들을 사용하는 패턴
    솔리디티에서는 forta modifier를 이용해 실제 로직함수가 실행되기 전 메서드가 인수로 받은 data를 forta 컨트랙트에 보내 data가 정상적인지 검사하는 방식이 이와 유사하다
    https://forta.org/

  13. state 패턴

    state 변수에 객체를 저장해두고 그 객체의 setState()메서드를 이용하면 state 변수에 다른 객체가 들어가 다른 객체에 접근하는 패턴
    솔리디티로 예를 들면 아래와 같다

contract State {
	address state;

	function setState(address _state) {
    	state = _state;
    }
}

contract On {
	function setState() public {
		State(State주소).setState(Off주소);
    }
}

contract Off {
	function setState() public {
		State(State주소).setState(On주소);
    }
}

0개의 댓글