나노 네트워크에서 송신 블록의 수명 주기 자세히 살펴보기

11630 단어

Nano 네트워크에서 송신 블록이 어떻게 기억됩니까?



A bystander look at the C++ reference implementation



나는 Nano 전류 참조 구현을 살펴보는 데 시간을 보냈습니다. 코드베이스가 방대해서 쉬운 작업은 아니었습니다. 저는 정확한 질문에 집중하고 싶었습니다. 송신 블록의 수명 주기는 무엇입니까? 이것은 내 발견입니다.

임신



이 부분은 send 블록에 관한 것이므로 새 체인을 만드는 것에 대한 모든 것은 범위를 벗어납니다. 사용자가 일부 원시 데이터를 보내려고 한다고 가정해 보겠습니다. 내 노드는 다음과 유사한 헤더가 있는 메시지를 생성합니다.

...
network: live
protocol version: 19
message type: publish
extensions:
  block type: send


나는 700 raw의 잔액을 가진 임의의 사용자가 10 raw를 보내고 싶어하는 척 할 것입니다.
블록 정보를 자세히 살펴보면 다음과 같은 내용을 찾을 수 있습니다.

previous: BBE55A35F79F887...
link/destination: 9A2726664A18FE5...
balance: 690
work: 14b3bc748f2c8e93
signature: B421B88AFBEDFC...


잔액은 700이었기 때문에 690 raw입니다. 저는 10 raw를 보내고 있습니다.
그런 다음 노드는 이 메시지를 피어에게 보냅니다.

다른 노드가 메시지 수신



각 피어에 대해 이미 설정된 TCP 연결이 있으며 메시지가 처리된 후 새 메시지 수신기가 생성됩니다.
이것은 bootstrap_server.cpp:151에 리스너를 설치하는 방법입니다.

void nano::bootstrap_server::receive ()
{
    // ...
    socket->async_read (receive_buffer, 8, [this_l](boost::system::error_code const & ec, size_t size_a) {
        // ...
        // Receive header
        this_l->receive_header_action (ec, size_a);
    });
}


TCP 연결을 통해 수신한 모든 것을 receive_buffer 에 넣습니다.
함수receive_header_action는 바로 뒤에 있으며 다음과 같이 읽습니다.

void nano::bootstrap_server::receive_header_action (boost::system::error_code const & ec, size_t size_a)
{
    if (!ec)
    {
        // ...
        nano::bufferstream type_stream (receive_buffer->data (), size_a);
        auto error (false);
        nano::message_header header (error, type_stream);
        if (!error)
        {
            auto this_l (shared_from_this ());
            switch (header.type) {...}
        }
    }
    else
    {
         // error management ...
    }
}


위에서 일어나는 일은 receive_buffer의 헤드가 type_stream에 할당되고 type_streammessage_header 클래스를 인스턴스화하는 데 사용된다는 것입니다. 생성자의 논리는 스트림을 역직렬화하고 특히 header.type 속성을 채웁니다. 오류가 발생하지 않은 경우 다음에 수행할 작업은 header.type(스위치 구성)에 의존하기 때문입니다. 게시 메시지의 경우를 살펴보겠습니다.

case nano::message_type::publish:
{
    socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
        this_l->receive_publish_action (ec, size_a, header);
    });
    break;
}


동일한 버퍼에 다른 수신기를 설치하고 있습니다. 핸들러는 동일한 파일에서 receive_publish_action 함수를 호출하여 전달된 블록의 작업을 확인합니다. 그런 다음 메시지를 requests 데크에 추가합니다. 이것은 궁극적으로 request_response_visitor 메시지를 entriestcp_message_manager 데크에 넣습니다.

메시지 항목 처리



이때 network 클래스가 스테이지에 진입합니다. 초기화되면 이 클래스는 process_messages 에서 tcp.cpp:279 루프를 실행합니다.

void nano::transport::tcp_channels::process_messages ()
{
    while (!stopped) // while we are not shutting down the node
    {
        auto item (node.network.tcp_message_manager.get_message ());
        if (item.message != nullptr)
        {
            process_message (*item.message, item.endpoint, item.node_id, item.socket, item.type);
        }
    }
}


내부적으로 process_message 메시지 발신자와 채널이 열려 있는지 확인합니다. 그런 다음 채널을 기준으로 network_message_visitor를 생성하고 network.cpp에서 다음 기능에 따라 게시 메시지를 처리합니다.

void publish (nano::publish const & message_a) override
{
    // ... logging and monitoring logic ...
    if (!node.block_processor.full ())
    {
        node.process_active (message_a.block);
    }
    // ...
}


여기서 process_active는 메시지 내부의 블록을 block_arrivalblock_processor 둘 다에 추가합니다. 후자는 블록을 block 대기열에 넣는 역할을 합니다.

블록 처리


node 클래스가 인스턴스화될 때마다 블록 프로세서 스레드가 생성됩니다. 이 스레드는 blockprocessor.cpp 함수 내부에 process_blocks 무한 루프를 가지고 있습니다. 이것은 다양한 잠금을 획득한 후 블록 배치를 처리하는 트랜잭션을 시작합니다. 단일 블록의 처리는 process_one 함수에 정의되어 있으며 적어도 관심 있는 전송 블록에 대해 ledger_processor 에 정의된 ledger.cpp에 의존합니다.

전체 논리는 ledger.cpp 함수의 send_block에서 찾을 수 있습니다. 그 핵심은 잘못될 수 있는 모든 가능한 일을 설명하려고 하는 if의 피라미드입니다. 예를 들어 블록의 작업이 충분한 경우(다른 노드에서 블록을 받았을 때 이미 확인했음을 참고하십시오).

피라미드의 꼭대기에서 우리는 마침내 명령을 실행합니다.

ledger.store.block_put (transaction, hash, block_a);


영구 저장소에 블록을 물리적으로 추가합니다.

결론



이것이 이 블록의 수명이 끝나는 것이 아닙니다. 실제로 블록이 접합되면 종료됩니다. 결합은 합의를 포함하는 다른 프로세스이므로 이중 지출로 감지되는 경우 블록이 삭제될 수도 있습니다. 나는 다른 기사에서 이것에 대해 쓸 것입니다.

좋은 웹페이지 즐겨찾기