LLVM을 사용하여 ETC 스마트 컨트랙트 언어 만들기 - 스마트 컨트랙트 기능 추가하기


  • 각 EVM 계약은 바이트 코드 섹션의 시작 부분에서 실행됩니다. 처음에는 메모리와 스택이 비어있습니다. 따라서 컴파일러는 시스템을 부팅하기 위해 적절한 초기화 코드 조각을 생성합니다. 따라서 스마트 컨트랙트는 여기에서 "함수 분배 자"라고하는 메타 기능이 필요합니다. EVM-LLVM에는 올바른 스마트 계약이 생성되도록 특정 기능 레이아웃이 있습니다. 이름은 "main"이어야한다. 반환 유형은 무효어야한다. 연결이 없어야한다. 즉, 다른 함수는 엔트리 루션을 호출해서는 안된다. LLVM IR 모듈의 첫 번째 기능이 어야한다. 개발자와 사용자에게는 보이지 않는다.

엔트리 함수의 정의를 식별하기 위해 특수 이름“main”이 주어지며 컴파일러는 엔트리 함수에 대한 리턴 서브 루틴 코드를 생성하지 않습니다 (입력 함수가 항상 리턴되기 때문에 항상 RETURN 또는 REVER 명령어로 종료 됨). 우리의 스마트 컨트랙트 컴파일러에서 엔트리 함수를 생성하는 함수를 생각해 냈습니다.


Function *GenerateWrapperFunction(Function* calleeF) {
    FunctionType* FT = FunctionType::get(Type::getVoidTy(TheContext),
                                         inputs, false);
     Function *F = Function::Create(FT, Function::ExternalLinkage, 
                                    "main",
                                    TheModule.get());     BasicBlock *BB = BasicBlock::Create(TheContext, "entry", F);
     Builder.SetInsertPoint(BB);...
}

GenerateWrapperFunction은 칼리 함수("포함" 함수)를 인수로 사용합니다. callee 기능은 실제로 실행하고자 하는 기능입니다. 이 예에서는 스마트 계약 주장을 추출하기 위해 코드를 생성해야 하기 때문에 칼리 기능만 사용하려고 합니다.


프레임 포인터 초기화 스마트 컨트랙트를 체결 할 수있는 방법은 여러 가지가 있습니다. 최신 아키텍처는 하드웨어 지원 컨텍스트 전환 메커니즘을 사용하여 서브 루틴을 호출합니다. JUMP 및 LINK와 같은 명령어는 서브 루틴 실행을 시작하기 전에 현재 컨텍스트를 특수 레지스터에 저장합니다.


LLVM 컴파일 된 EVM 계약은 메모리에 프레임 포인터를 유지하여 콜 프레임의 시작 주소 (메모리에도 저장 됨)를 기록합니다. 자세한 내용은이 위키 페이지를 참조하십시오. 이상적으로는 머신 종속 초기화 코드가 LLVM IR에 나타나지 않아야합니다.


EVM의 경우 함수 디스패처를 구현하는 많은 방법이 있기 때문에 컴파일러는 계약 개발자를 위해 컴파일러를 수행 할 수 없습니다. 언어 프론트 엔드에는 함수 디스패처에 대한 자체 아이디어가있을 수 있습니다. 따라서 함수 디스패처 세부 사항을 언어 프론트 엔드에 노출하면 새로운 스마트 계약을 자유롭게 구현할 수 있습니다. 이 구현에서 작은 컴파일러는 모든 권한을 가지며 초기화를 담당합니다. 다행히 컨텍스트를 초기화하는 단일 IR 명령 만 있습니다. 프레임 포인터는 메모리 주소 0x40에 저장됩니다. 0x60보다 크거나 같은 값으로 초기화해야합니다 (따라서 메모리 스택 섹션은 프레임 포인터 자체와 겹치지 않습니다). 한편, 값은 32 바이트로 정렬되어야합니다. 이 예에서는 값을 128로 초기화합니다. EVM-LLVM은 특히 프레임 포인터를 초기화하는 데 사용되는 EVM 메모리 주소를 명시 적으로 수정하는 내장 함수로 EVM opcode MSTORE를 노출했습니다. 특정 EVM 내장 헤더가 포함되면 내장 Intrinsic :: evm_mstore를 만들어 작업을 수행 할 수 있습니다.


Value* addr = Get256ConstantInt(64);
Value* val  = Get256ConstantInt(128);
Function *mstore = Intrinsic::getDeclaration(
        TheModule.get(), llvm::Intrinsic::evm_mstore, llvm::None);
Builder.CreateCall(mstore, {addr, val});

EVM 입력 값 추출

다른 플랫폼과 달리 EVM은 CALLDATALOAD opcode를 사용하여 블록 체인 인수 입력을 명시 적으로 추출합니다. 입력 인수를 추출하는 일련의 CALLDATALOAD 명령어가 생성됩니다.



std::vector<Value*> extractedParams;
for (size_t i = 0; i < calleeTy->getNumParams(); ++i) {
    Value* val_int  = Get256ConstantInt(i * 32);
    Function *calldataloadF = Intrinsic::getDeclaration(
        TheModule.get(), llvm::Intrinsic::evm_calldataload, llvm::None);
    CallInst * calldataload = Builder.CreateCall(calldataloadF, {val_int});
    extractedParams.push_back(calldataload);
}
CallInst *call_calleeF = Builder.CreateCall(calleeF, extractedParams);

EVM에서 값을 반환

정의 된 기능이 사용자에게 값을 리턴해야하는 경우, 입력 기능은 스마트 계약에서 값을 리턴 할 수 있어야합니다. EVM은 특별한 방법을 사용하여 스마트 계약에서 가치를 반환합니다. 먼저 반환 값은 메모리 주소에 저장해야합니다. 그런 다음 RETURN EVM opcode를 호출하고 메모리 영역의 시작과 끝을 지정하십시오.


Value* addr_int = Get256ConstantInt(0);
Function *mstore = Intrinsic::getDeclaration(
          TheModule.get(),
          llvm::Intrinsic::evm_mstore,
          llvm::None);
Builder.CreateCall(mstore, {addr_int, call_calleeF});Function *evm_return = Intrinsic::getDeclaration(
          TheModule.get(),
          llvm::Intrinsic::evm_return,
          llvm::None);
Builder.CreateCall(evm_return,
                   {Get256ConstantInt(0), Get256ConstantInt(32)});


1 view

Recent Posts

See All

ETC 피닉스 하드포크 업데이트 6월 1일 진행 예정

이더리움 클래식 메인넷은 다가오는 10.500.839블록에서 피닉스 하드포크를 활성화합니다. 예상 시점은 2020년 6월 1일입니다. 이번 하드포크는 아틀란티스, 아가타에 이어 이더리움 클래식의 세 번째 업그레이드입니다. 피닉스 하드포크 이후 이더리움 클래식과 이더리움은 완벽하게 호환되며 동일한 기능을 갖게 됩니다. 이번 하드포크의 주요 목표는 이더리움 이

LLVM을 사용하여 ETC 스마트 컨트랙트 언어 만들기 - 기타주제

LLVM IR 코드 생성 allocatea 명령은 현재 기능의 메모리 프레임에 32바이트 프레임 객체(기능-로컬 객체)를 할당. 메모리 공간 인덱싱 보통 EVM 스마트 계약 ABI 정보를 내보내는 것은 언어에 달려 있다. 하지만 우리는 계약서 ABI를 방출하기 위해 LLVM IR 패스를 작성할 수 있다. 제한 사항 EVM은 결정적 실행을 위해 설계되었습니다