kaistshadow / blockchain-sim

Scalable blockchain simulator/emulator running on shadow simulator
MIT License
9 stars 1 forks source link

rpc request API #285

Closed tkdlqm2 closed 3 years ago

tkdlqm2 commented 3 years ago

rpc request API

Originally posted by @tkdlqm2 in https://github.com/kaistshadow/blockchain-sim/issues/272#issuecomment-810712964

tkdlqm2 commented 3 years ago

bitcoin-cli를 shadow 환경에서 사용못하는 관계로 rpc-request를 plugin으로 변환하여 이를 통해 시뮬레이션 환경의 bitcoind 를 컨트롤 했음. release가 진행될 수록 다양한 테스트들에서 사용되는 rpc_request plugin의 중복을 피할 수 없음. 그렇기에 rpc_request에 관한 API가 필요함. 또한 다양한 블록체인에 대해 제너럴한 디자인을 생각하면 비트코인 이외의 블록체인에 접목 시킬 수 있음.

구현 위치는 다음 디렉토리에서 작업을 하고 있음.

├── rpcAPI
│   ├── rpc_client.cpp
│   ├── rpc_client.h
│   ├── rpc_request.cpp
│   └── rpc_request.h

[디자인]

xml파일로 부터 실행할 rpc_request function을 정의를함

    <application arguments="1.0.0.1:11111 993 getblockchaininfo_3 getnewaddress_1" plugin="client" time="5"/>

plungin이 시뮬레이션에 로드되고, 실행이 되면 argv 배열에 request function들이 담기게됨. 그 배열에 담긴 함수들은 다음과 같은 형태로 정리를 함.

auto filter_rpc_functions(int rpc_count, char* argv[]) {

    std::list<std::string> receive_list; // getnewaddress
    std::list<std::string> request_list; // setgeneratetoaddress, validateaddress, sendtoaddress
    std::list<std::string> send_list; // getblockchaininfo, getmempoolinfos, listaddressgroupings, rpc_getpeerinfo

    for(int i=0; i<rpc_count; i++) {
        std::string str = argv[i];
        if(str.find("_1") != std::string::npos) {
            receive_list.push_back(str);
            continue;
        }
        if(str.find("_2") != std::string::npos) {
            request_list.push_back(str);
            continue;
        }
        if(str.find("_3") != std::string::npos) {
            send_list.push_back(str);
            continue;
        }
    }
    std::tuple <std::list<std::string>, std::list<std::string>, std::list<std::string>> return_list = make_tuple(receive_list, request_list, send_list);

    return return_list;
}

코드를 보면 "_1", "_2", "_3" 식으로 구분을 지어 줘서 리스트를 따로 관리를 하여 최종적으로 3개의 리스트를 담은 튜플을 리턴을 하게됨. _1 이 붙은 request function은 주석 그대로 getnewaddress와 같이 파라미터 값 없이 return value을 가져오는 rpc function들에 대한 표식이고, _2이 붙은 request function은 sendtoaddress와 같이 wallet이라는 입력 파라미터 값이 있으며, return value 또한 가져오는 rpc function 들에 대한 표식, 마지막으로 _3이 붙은 request function은 getblockchaininfo 같이 입력 파라미터도 없고, return value도 없는 rpc function 임.

xml에 정의된 rpc function들을 이런식으로 구분을 지어서 플러그인에 다음과 같이 디자인 하여 생성을 하면 됨.

int main(int argc, char* argv[]) {

    std::string IP_address = argv[1];
    std::string sim_time = argv[2];
    std::string wallet;

    int i=0;
    int j=0;
    // Only receive function call
    // example) getnewaddress
    for(int i=0; i<argc; i++) {
        j = i;

    }

    // Only request function call more than 2 parameters.
    // example) setgeneratetoaddress
    for(int i=j; i<argc; i++) {
        j = i;

    }

    // Only request function call.
    // example) getblockchaininfo
    for(int i=j; i<argc; i++) {

    }
}

위의 소스코드는 플러그인에 대한 간단한 프로토 타입임. _1, _2, _3 에 따라 분류된 rpc function들을 실행 순서를 디자인하여 반복문에서 호출을 해주면됨.

tkdlqm2 commented 3 years ago

[How to reproduce] git checkout issue/285

tkdlqm2 commented 3 years ago

위에 제시한 API 디자인을 엎고, 새로 디자인을 함.

공통 부분 : JSON 포맷 차이 부분 : rpc function 이름.

void rpc_reqeust_with_params(std::string rpc_function, std::list<std::string> params_list) {
    Json::Value params;
    params.clear();
    params = Json::arrayValue;
    for(auto const& i: params_list) {
        params.append(i);
    }
    bitcoin_rpc_request(rpc_function, params);
}

// example : getnewaddress rpc function
std::string rpc_request_with_no_params(std::string rpc_function) {
    Json::Value params;
    params.clear();
    params = Json::arrayValue;
    bitcoin_rpc_request(rpc_function, params);
    std::string wallet = json_resp["result"].asString();
    return wallet;
}

// example : getblockchaininfo rpc function
void rpc_request_no_return_no_params(std::string rpc_function) {
    Json::Value params;
    params.clear();
    params = Json::arrayValue;
    bitcoin_rpc_request(rpc_function, params);
}

rpc_reqeust_with_params 함수는 rpc request 시 parameter가 함께 필요한 rpc function을 호출할 때 사용 됨. rpc_request_no_return_no_params 함수는 rpc request 시 parameter가 필요 없는 rpc function을 호출할 때 사용 됨. rpc_request_with_no_params 함수는 rpc request 시 parameter가 필요 없으며, return value를 알아야할 때 필요한 rpc function 호출을 할 때 사용 됨.