Refactored NPL message processing. Passed lcl to contract args. (#105)

This commit is contained in:
Ravin Perera
2020-08-05 21:30:48 +05:30
committed by GitHub
parent 1328b06346
commit d4a786e3b9
10 changed files with 190 additions and 142 deletions

View File

@@ -1,9 +1,6 @@
#include "pchheader.hpp"
#include "conf.hpp"
#include "hplog.hpp"
#include "msg/fbuf/common_helpers.hpp"
#include "msg/fbuf/p2pmsg_container_generated.h"
#include "msg/fbuf/p2pmsg_content_generated.h"
#include "sc.hpp"
#include "hpfs/hpfs.hpp"
@@ -24,7 +21,7 @@ namespace sc
if (!ctx.args.readonly)
{
create_iopipes(ctx.nplfds, !ctx.args.nplbufs.inputs.empty());
create_iopipes(ctx.nplfds, !ctx.args.npl_messages.empty());
create_iopipes(ctx.hpscfds, !ctx.args.hpscbufs.inputs.empty());
}
@@ -173,11 +170,12 @@ namespace sc
* "version":"<hp version>",
* "pubkey": "<this node's hex public key>",
* "ts": <this node's timestamp (unix milliseconds)>,
* "readonly": <true|false>,
* "lcl": "<this node's last closed ledger seq no. and hash in hex>", (eg: 169-a1d82eb4c9ed005ec2c4f4f82b6f0c2fd7543d66b1a0f6b8e58ae670b3e2bcfb)
* "hpfd": [fd0, fd1],
* "usrfd":{ "<pkhex>":[fd0, fd1], ... },
* "nplfd":[fd0, fd1],
* "unl":[ "pkhex", ... ],
* "readonly": <true|false>
* "usrfd":{ "<pkhex>":[fd0, fd1], ... },
* "unl":[ "pkhex", ... ]
* }
*/
int write_contract_args(const execution_context &ctx)
@@ -194,7 +192,8 @@ namespace sc
if (!ctx.args.readonly)
{
os << ",\"hpfd\":[" << ctx.hpscfds[FDTYPE::SCREAD] << "," << ctx.hpscfds[FDTYPE::SCWRITE]
os << ",\"lcl\":\"" << ctx.args.lcl
<< "\",\"hpfd\":[" << ctx.hpscfds[FDTYPE::SCREAD] << "," << ctx.hpscfds[FDTYPE::SCWRITE]
<< "],\"nplfd\":[" << ctx.nplfds[FDTYPE::SCREAD] << "," << ctx.nplfds[FDTYPE::SCWRITE] << "]";
}
@@ -250,8 +249,12 @@ namespace sc
int feed_inputs(execution_context &ctx)
{
// Write any hp or npl input messages to hp->sc and npl->sc pipe.
if (!ctx.args.readonly && write_contract_hp_npl_inputs(ctx) != 0)
// Write any input messages to hp->sc pipe.
if (!ctx.args.readonly && write_contract_hp_inputs(ctx) != 0)
return -1;
// Write any NPL messages to contract.
if (!ctx.args.readonly && write_npl_messages(ctx) != 0)
return -1;
// Write any verified (consensus-reached) user inputs to user pipes.
@@ -298,7 +301,7 @@ namespace sc
/**
* Writes any hp input messages to the contract.
*/
int write_contract_hp_npl_inputs(execution_context &ctx)
int write_contract_hp_inputs(execution_context &ctx)
{
if (write_iopipe(ctx.hpscfds, ctx.args.hpscbufs.inputs) != 0)
{
@@ -306,13 +309,69 @@ namespace sc
return -1;
}
if (write_npl_iopipe(ctx.nplfds, ctx.args.nplbufs.inputs) != 0)
return 0;
}
/**
* Write npl messages to the contract.
*/
int write_npl_messages(execution_context &ctx)
{
/**
* npl inputs are feed into the contract in a binary protocol. It follows the following pattern
* |**NPL version (1 byte)**|**Reserved (1 byte)**|**Length of the message (2 bytes)**|**Public key (32 bytes)**|**Npl message data**|
* Length of the message is calculated without including public key length
*/
const int writefd = ctx.nplfds[FDTYPE::HPWRITE];
if (writefd == -1)
return 0;
bool write_error = false;
if (!ctx.args.npl_messages.empty())
{
LOG_ERR << "Error writing NPL inputs to SC";
return -1;
const size_t total_memsegs = ctx.args.npl_messages.size() * 3;
iovec memsegs[total_memsegs];
size_t i = 0;
for (const auto &npl_msg : ctx.args.npl_messages)
{
const uint8_t pre_header_index = i * 3;
const uint8_t pubkey_index = pre_header_index + 1;
const uint8_t msg_index = pre_header_index + 2;
const uint16_t msg_len = npl_msg.data.size();
// Header is |version(1byte)|reserve(1byte)|msg length(2bytes big endian)|
uint8_t header[4];
header[0] = util::MIN_NPL_INPUT_VERSION;
// Store msg length in big endian.
header[2] = msg_len << 8;
header[3] = msg_len;
memsegs[pre_header_index].iov_base = header;
memsegs[pre_header_index].iov_len = sizeof(header);
// Pubkey without the key type prefix.
memsegs[pubkey_index].iov_base = reinterpret_cast<void *>(const_cast<char *>(npl_msg.pubkey.data() + 1));
memsegs[pubkey_index].iov_len = npl_msg.pubkey.size() - 1;
memsegs[msg_index].iov_base = reinterpret_cast<void *>(const_cast<char *>(npl_msg.data.data()));
memsegs[msg_index].iov_len = msg_len;
i++;
}
if (writev(writefd, memsegs, total_memsegs) == -1)
write_error = true;
ctx.args.npl_messages.clear();
}
return 0;
// Close the writefd since we no longer need it.
close(writefd);
ctx.nplfds[FDTYPE::HPWRITE] = -1;
return write_error ? -1 : 0;
}
/**
@@ -330,7 +389,7 @@ namespace sc
return -1;
}
const int npl_res = read_iopipe(ctx.nplfds, ctx.args.nplbufs.output);
const int npl_res = read_iopipe(ctx.nplfds, ctx.args.npl_output);
if (npl_res == -1)
{
LOG_ERR << "Error reading NPL output from the contract.";
@@ -521,85 +580,6 @@ namespace sc
return write_error ? -1 : 0;
}
/**
* Write the given input buffer into the write fd from the HP side.
* @param fds Vector of fd list.
* @param inputs Buffer to write into the HP write fd.
*/
int write_npl_iopipe(std::vector<int> &fds, std::list<std::string> &inputs)
{
/**
* npl inputs are feed into the contract in a binary protocol. It follows the following pattern
* |**NPL version (1 byte)**|**Reserved (1 byte)**|**Length of the message (2 bytes)**|**Public key (4 bytes)**|**Npl message data**|
* Length of the message is calculated without including public key length
*/
const int writefd = fds[FDTYPE::HPWRITE];
if (writefd == -1)
return 0;
bool write_error = false;
if (!inputs.empty())
{
int8_t total_memsegs = inputs.size() * 3;
iovec memsegs[total_memsegs];
size_t i = 0;
for (auto &input : inputs)
{
int8_t pre_header_index = i * 3;
int8_t pubkey_index = pre_header_index + 1;
int8_t msg_index = pre_header_index + 2;
// First binary representation of version, reserve and message length is constructed and feed it into
// memory segment. Then the public key and at last the message data
// At the moment no data is inserted as reserve
uint8_t reserve = 0;
//Get message container
const msg::fbuf::p2pmsg::Container *container = msg::fbuf::p2pmsg::GetContainer(input.data());
const flatbuffers::Vector<uint8_t> *container_content = container->content();
uint16_t msg_length = container_content->size();
/**
* Pre header is constructed using bit shifting. This will generate a bit pattern as explain in the example below
* version = 00000001
* reserve = 00000000
* msg_length = 0000000010001101
* pre_header = 00000001000000000000000010001101
*/
uint32_t pre_header = util::MIN_NPL_INPUT_VERSION;
pre_header = pre_header << 8;
pre_header += reserve;
pre_header = pre_header << 16;
pre_header += msg_length;
memsegs[pre_header_index].iov_base = &pre_header;
memsegs[pre_header_index].iov_len = 4;
std::string_view msg_pubkey = msg::fbuf::flatbuff_bytes_to_sv(container->pubkey());
memsegs[pubkey_index].iov_base = reinterpret_cast<void *>(const_cast<char *>(msg_pubkey.data()));
memsegs[pubkey_index].iov_len = msg_pubkey.size();
memsegs[msg_index].iov_base = reinterpret_cast<void *>(const_cast<uint8_t *>(container_content->Data()));
memsegs[msg_index].iov_len = container_content->size();
i++;
}
if (writev(writefd, memsegs, total_memsegs) == -1)
write_error = true;
inputs.clear();
}
// Close the writefd since we no longer need it.
close(writefd);
fds[FDTYPE::HPWRITE] = -1;
return write_error ? -1 : 0;
}
/**
* Common function to read buffered output from the pipe and populate the output list.
* @param fds Vector representing the pipes fd list.
@@ -701,9 +681,10 @@ namespace sc
args.userbufs.clear();
args.hpscbufs.inputs.clear();
args.hpscbufs.output.clear();
args.nplbufs.inputs.clear();
args.nplbufs.output.clear();
args.npl_messages.clear();
args.npl_output.clear();
args.time = 0;
args.lcl.clear();
args.post_execution_state_hash = hpfs::h32_empty;
}