스크립토 6기 하진원입니다.
스크립토 방학 스터디로 마스터링 이더리움 공부하고 있습니다.
솔리디티 버전 0.4.19로 작성
이더리움 네트워크 프로토콜의 일부인 EVM 컨텍스트 상에서 결정론적으로 작동하는 불변적 컴퓨터 프로그램
솔리디티로 작성 -> 바이트코드로 컴파일 -> 컨트랙트 생성 트랜잭션 통해 이더리움 플랫폼에 배포
트랜잭션으로 호출된 경우 실행. 자체적으로, 백그라운드에서 실행되지 않는다.
컨트랙트가 성공적으로 종료된 경우에만 글로벌 상태의 모든 변경사항이 기록되고 전체가 실행.
컨트랙트 코드는 변경할 수 없지만 삭제할 수 있다. SELFDESTRUCT EVM 코드를 통해 삭제 가능하지만 이를 위해서는 컨트랙트를 프로그래밍할 때 해당 코드를 내장시켜놓아야 하고 그렇지 않은 경우에는 삭제할 수 없다.
바이트코드로 프로그래밍하는 것은 힘들기 때문에 대부분 고급언어 사용.
특수한 환경에서 특수한 기능을 사용하기 때문에 스마트 컨트랙트 언어를 만듦!
스마트 컨트랙트에서 버그는 실제 비용이 발생하기 때문에 버그 없는 것이 중요.
대부분 솔리디티 사용!
바이트코드 예시
608060405260043610601f5760003560e01c80632e1a7d4d146022576020565b5b005b348015602d57600080fd5b50605760048036036020811015604257600080fd5b81019080803590602001909291905050506059565b005b6509184e72a000811115606b57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801560b0573d6000803e3d6000fd5b505056fea26469706673582212208c8365e595efb620418e18a3e1188eadd7a670c691bc208cbeb4f8a1120dd2fa64736f6c63430007040033
데이터 구조와 함수가 어떻게 기계 코드에서 사용되는지 방법 정의.
EVM에서 컨트랙트 호출을 인코딩, 트랜잭션에서 데이터를 읽는 데 사용.
ABI는 함수설명, 이벤트의 JSON 배열로 지정된다.
ABI를 통해 해당 컨트랙트에 접근하려는 어플리케이션은 올바른 인수, 인수 유형을 사용하여 트랜잭션을 생성할 수 있다.
애플리케이션이 컨트랙트와 상호작용할 때 ABI와 컨트랙트가 배포된 주소가 필요하다.
키-값 쌍에 대한 조회
Mapping(KEY_TYPE => VALUE_TYPE)
단위들을 seconds의 배수로 변환하여 사용
Wei, finney, Szabo, ether을 wei의 배수로 변환하여 사용
require(withdraw_amount <= 100000000000000000)
require(withdraw_amount <= 0.1ether)
외에도 다양한 데이터 타입이 있다.
컨트랙트 실행을 시작한 트랜잭션 혹은 메시지 호출.
트랜잭션 관련 정보에 접근하는 방법 제공
블록에 대한 정보
주소에 대한 정보
컨트랙트 내에서 EOA 트랜잭션이나 다른 컨트랙트에 의해 호출될 수 있는 함수.
function FunctionName([parameters]) {public|private|internal|external} [pure|constant|view|payable] [modifiers] [returns (return types)]
FunctionName
함수 이름.
이름 없이 정의될 수 있는 함수는 fallback 함수라고 부르고 다른 함수 이름이 없을 때 호출되며 인수가 없고 반환할 수도 없다.
parameters
인수
키워드 세트1 (public, private, internal, external) -> 호출 방법, 조건에 영향
컨트랙트 생성될 때 생성자 함수가 있는 경우 이를 실행하여 컨트랙트 상태를 초기화.
constructor 키워드를 통해서 지정
pragma ^0.4.22
contract MEContract {
constructor () {
}
}
SELFDESTRUCT EVM 코드를 통해
selfdestruct(address recipient);
pragma solidity ^0.4.22;
contract Faucet {
address owner;
constructor() {
owner = msg.sender;
}
function withdraw(uint256 withdraw_amount) public {
require(withdraw_amount <= 10000000000000);
msg.sender.transfer(withdraw_amount);
}
function() external payable {}
function destroy() public {
require(msg.sender == owner);
selfdestruct(owner);
}
}
생성자 효과 : 생성자가 woner에 저장한 동일한 주소를 호출하면 컨트랙트는 파괴되고 잔액을 owner 주소로 돌려줌
파괴자 효과 : owner 주소가 destroy 함수를 호출하면 컨트랙트 파괴
컨트랙트 내에서 함수에 적용되어야 할 조건 생성하기 위해 사용
modifier onlyOwner {
require(msg.sender == owner);
_;
}
msg.sender == owner 통해서 컨트랙트의 owner만 실행가능하게 함.
_; 부분에 수정된 코드 삽입.
사용예시
function destroy() public onlyOwner {
selfdestruct(owner);
}
컨트랙트에 기능을 추가, 확장하기 위해 상속 지원.
contract child is parent1, parent2{
~~~
}
에러로 컨트랙트가 중지될 때는 모든 상태를 원래대로 되돌리는 것이 필요하다.
assert와 require는 조건을 평가하고 조건이 거짓이면 실행을 중지시키는 방식으로 동작.
assert는 필요한 내적 조건들이 만족되는지 테스트
require는 입력값이 설정한 조건의 기댓값에 맞는지 테스트할 때 사용.
요구되는 조건 설정한 후 만족되지 않을 경우 에러를 발생시켜 나머지 부분이 실행되지 않게 한다.
require(msg.sender == owner, 'Only the contract owner can call this function')
revert는 컨트랙트의 실행을 중지하고 모든 변경 상태를 되돌린다.
출금 요청을 만족시킬 이더가 불충분한 경우와 같이 명시적으로 정의하지 않더라도 발생하는 에러가 있다.
이 경우에도 에러 메시지를 분명하게 제공하여 확인이 가능하게 하는 것이 좋다.
트랜잭션 완료될 때 발행하는 트랜잭션 영수증에 기록할 트랜잭션 실행 동안 발생했던 행위에 관한 정보를 제공하는 객체.
이벤트가 일어나는 여부를 감시할 수 있게 해주기 때문에 DApp에 유용.
이벤트 객체는 인수를 취할 수 있고 이는 indexed라는 키워드를 붙여서 검색, 필터링 가능한 인덱싱된 테이블의 값으로 만들 수 있다.
pragma solidity ^0.4.22;
contract owned {
address owner;
// 컨트랙트 생성자: woner 설정
constructor() {
owner = msg.sender;
}
}
contract mortal is owned {
//컨트랙트 소멸자
function destroy() public onlyOwner {
selfdestruct(owner);
}
}
contract Faucet is mortal {
event withdrawal(address indexed to, uint256 amount);
event Deposit(address indexed from, uint256 amount);
// Give out ether to anyone who asks for it
function withdraw(uint256 withdraw_amount) public {
// Limit withdrawl amount
require(withdraw_amount <= 0.1 ether);
require(
this.balance >= withdraw_amount,
"Insufficient balance in faucet for withdrawal request"
);
// Send amount to address that requested it
// transfer function transfers ether to the sender of msg.
msg.sender.transfer(withdraw_amount);
emit Withdrawal(msg.sender, withdraw_amount);
}
// Accept any incoming amount
// default function for tx triggered the contract that doesn't have declared function in the contract or doesn't contain data.
function() public payable {
emit Deposit(msg.sender, msg.value);
}
}
앞의 장에서 다루었듯이 트랜잭션이 가스 한계를 초과하면 복귀하고 실행하는데 사용된 가스는 반환되지 않는다. 따라서 컨트랙트에 사용되는 가스 비용을 최소화하는 것이 중요하다.
함수에서 동적 크기 배열을 통한 루프는 많은 가스를 사용한다.
다른 컨트랙트의 함수가 얼만큼의 가스를 사용할지 정확히 알 수 없다.
컨트랙트에 필요한 가스를 추정할 수 있다.
var contract = web3.eth.contract(abi).at(address);
var gasEstimate = contract.myAweSomeMethod.estimateGas(arg1, arg2, {from: account});
gasEstimate는 컨트랙트 실행에 필요한 가스의 예상치를 알려준다.