User input json/bson format improvements. (#174)

* Removed hex encoding in json input container.
* Refactored client lib exports.
This commit is contained in:
Ravin Perera
2020-11-28 21:24:35 +05:30
committed by GitHub
parent 79b55258de
commit 332e5a4750
8 changed files with 345 additions and 334 deletions

View File

@@ -3,11 +3,11 @@ const readline = require('readline');
const { exit } = require('process');
const bson = require('bson');
var path = require("path");
const { HotPocketClient, HotPocketKeyGenerator, HotPocketEvents } = require('./hp-client-lib');
const HotPocket = require('./hp-client-lib');
async function main() {
const keys = await HotPocketKeyGenerator.generate();
const keys = await HotPocket.KeyGenerator.generate();
const pkhex = Buffer.from(keys.publicKey).toString('hex');
console.log('My public key is: ' + pkhex);
@@ -15,7 +15,7 @@ async function main() {
let server = 'wss://localhost:8080'
if (process.argv.length == 3) server = 'wss://localhost:' + process.argv[2]
if (process.argv.length == 4) server = 'wss://' + process.argv[2] + ':' + process.argv[3]
const hpc = new HotPocketClient(server, keys);
const hpc = new HotPocket.Client(server, keys, HotPocket.protocols.bson);
// Establish HotPocket connection.
if (!await hpc.connect()) {
@@ -25,13 +25,13 @@ async function main() {
console.log('HotPocket Connected.');
// This will get fired if HP server disconnects unexpectedly.
hpc.on(HotPocketEvents.disconnect, () => {
hpc.on(HotPocket.events.disconnect, () => {
console.log('Server diconnected');
exit();
})
// This will get fired when contract sends an output.
hpc.on(HotPocketEvents.contractOutput, (output) => {
hpc.on(HotPocket.events.contractOutput, (output) => {
const result = bson.deserialize(output);
if (result.type == "uploadResult") {
if (result.status == "ok")
@@ -51,7 +51,7 @@ async function main() {
})
// This will get fired when contract sends a read response.
hpc.on(HotPocketEvents.contractReadResponse, (response) => {
hpc.on(HotPocket.events.contractReadResponse, (response) => {
const result = bson.deserialize(response);
if (result.type == "downloadResult") {
if (result.status == "ok") {

View File

@@ -7,8 +7,8 @@ const bson = require('bson');
const isNodeJS = (typeof window === 'undefined');
const protocols = {
JSON: "json",
BSON: "bson"
json: "json",
bson: "bson"
}
Object.freeze(protocols);
@@ -40,7 +40,7 @@ const HotPocketKeyGenerator = {
},
}
function HotPocketClient(server, keys, protocol = protocols.BSON) {
function HotPocketClient(server, keys, protocol = protocols.json) {
let ws = null;
const msgHelper = new MessageHelper(keys, protocol);
@@ -86,7 +86,7 @@ function HotPocketClient(server, keys, protocol = protocols.BSON) {
msg = rcvd.data;
}
else {
msg = (handshakeResolver || protocol == protocols.JSON) ?
msg = (handshakeResolver || protocol == protocols.json) ?
await rcvd.data.text() :
Buffer.from(await rcvd.data.arrayBuffer());
}
@@ -105,7 +105,7 @@ function HotPocketClient(server, keys, protocol = protocols.BSON) {
// sign the challenge and send back the response
const response = msgHelper.createHandshakeResponse(m.challenge);
ws.send(JSON.stringify(response));
setTimeout(() => {
// If we are still connected, report handshaking as successful.
// (If websocket disconnects, handshakeResolver will be null)
@@ -214,19 +214,25 @@ function MessageHelper(keys, protocol) {
this.binaryEncode = function (data) {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
return protocol == protocols.JSON ? buffer.toString("hex") : buffer;
return protocol == protocols.json ? buffer.toString("hex") : buffer;
}
this.binaryDecode = function (content) {
return (protocol == protocols.JSON) ? Buffer.from(content, "hex") : content.buffer;
return (protocol == protocols.json) ? Buffer.from(content, "hex") : content.buffer;
}
this.serializeObject = function (obj) {
return protocol == protocols.JSON ? Buffer.from(JSON.stringify(obj)) : bson.serialize(obj);
return protocol == protocols.json ? JSON.stringify(obj) : bson.serialize(obj);
}
this.deserializeMessage = function (m) {
return protocol == protocols.JSON ? JSON.parse(m) : bson.deserialize(m);
return protocol == protocols.json ? JSON.parse(m) : bson.deserialize(m);
}
this.serializeInput = function (input) {
return protocol == protocols.json ?
input.toString() :
Buffer.isBuffer(input) ? input : Buffer.from(input);
}
this.createHandshakeResponse = function (challenge) {
@@ -248,17 +254,17 @@ function MessageHelper(keys, protocol) {
return null;
const inpContainer = {
input: this.binaryEncode(input),
input: this.serializeInput(input),
nonce: nonce,
max_lcl_seqno: maxLclSeqNo
}
const inpContainerBytes = this.serializeObject(inpContainer);
const sigBytes = sodium.crypto_sign_detached(Buffer.from(inpContainerBytes), keys.privateKey);
const serlializedInpContainer = this.serializeObject(inpContainer);
const sigBytes = sodium.crypto_sign_detached(Buffer.from(serlializedInpContainer), keys.privateKey);
const signedInpContainer = {
type: "contract_input",
input_container: this.binaryEncode(inpContainerBytes),
input_container: serlializedInpContainer,
sig: this.binaryEncode(sigBytes)
}
@@ -272,7 +278,7 @@ function MessageHelper(keys, protocol) {
return {
type: "contract_read_request",
content: this.binaryEncode(request)
content: this.serializeInput(request)
}
}
@@ -281,17 +287,16 @@ function MessageHelper(keys, protocol) {
}
}
const exportObj = {
KeyGenerator: HotPocketKeyGenerator,
Client: HotPocketClient,
events: events,
protocols: protocols
};
if (isNodeJS) {
module.exports = {
HotPocketKeyGenerator,
HotPocketClient,
HotPocketEvents: events
};
module.exports = exportObj;
}
else {
window.HotPocket = {
KeyGenerator: HotPocketKeyGenerator,
Client: HotPocketClient,
Events: events
}
window.HotPocket = exportObj;
}

View File

@@ -1,10 +1,9 @@
const readline = require('readline');
const { exit } = require('process');
const { HotPocketClient, HotPocketKeyGenerator, HotPocketEvents } = require('./hp-client-lib');
const HotPocket = require('./hp-client-lib');
async function main() {
const keys = await HotPocketKeyGenerator.generate();
const keys = await HotPocket.KeyGenerator.generate();
const pkhex = Buffer.from(keys.publicKey).toString('hex');
console.log('My public key is: ' + pkhex);
@@ -12,7 +11,7 @@ async function main() {
let server = 'wss://localhost:8080'
if (process.argv.length == 3) server = 'wss://localhost:' + process.argv[2]
if (process.argv.length == 4) server = 'wss://' + process.argv[2] + ':' + process.argv[3]
const hpc = new HotPocketClient(server, keys);
const hpc = new HotPocket.Client(server, keys, HotPocket.protocols.json);
// Establish HotPocket connection.
if (!await hpc.connect()) {
@@ -22,18 +21,18 @@ async function main() {
console.log('HotPocket Connected.');
// This will get fired if HP server disconnects unexpectedly.
hpc.on(HotPocketEvents.disconnect, () => {
hpc.on(HotPocket.events.disconnect, () => {
console.log('Server disconnected');
exit();
})
// This will get fired when contract sends an output.
hpc.on(HotPocketEvents.contractOutput, (output) => {
hpc.on(HotPocket.events.contractOutput, (output) => {
console.log("Contract output>> " + Buffer.from(output, "hex"));
})
// This will get fired when contract sends a read response.
hpc.on(HotPocketEvents.contractReadResponse, (response) => {
hpc.on(HotPocket.events.contractReadResponse, (response) => {
console.log("Contract read response>> " + Buffer.from(response, "hex"));
})

View File

@@ -15,14 +15,14 @@ const echoContract = async (ctx) => {
// This user's pubkey can be accessed from 'user.pubKey'
// A reply message can be sent to the user by 'user.send(msg)'
const msg = buf.toString("utf8");
const msg = buf.toString();
if (msg == "ts") {
await user.send(fs.readFileSync("exects.txt"));
}
else {
await user.send("Echoing: " + msg);
}
});
});
// Get list of all users who are connected.
// ctx.users.get();

View File

@@ -6,248 +6,253 @@
namespace msg::usrmsg::bson
{
/**
* Constructs a status response message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "stat_response",
* "lcl": "<lcl id>",
* "lcl_seqno": <integer>
* }
*/
void create_status_response(std::vector<uint8_t> &msg, const uint64_t lcl_seq_no, std::string_view lcl)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_STAT_RESPONSE);
encoder.key(msg::usrmsg::FLD_LCL);
encoder.string_value(lcl);
encoder.key(msg::usrmsg::FLD_LCL_SEQ);
encoder.int64_value(lcl_seq_no);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a status response message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "stat_response",
* "lcl": "<lcl id>",
* "lcl_seqno": <integer>
* }
*/
void create_status_response(std::vector<uint8_t> &msg, const uint64_t lcl_seq_no, std::string_view lcl)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_STAT_RESPONSE);
encoder.key(msg::usrmsg::FLD_LCL);
encoder.string_value(lcl);
encoder.key(msg::usrmsg::FLD_LCL_SEQ);
encoder.int64_value(lcl_seq_no);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a contract input status message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "contract_input_status",
* "status": "<accepted|rejected>",
* "reason": "<reson>",
* "input_sig": <signature of original input message>
* }
* @param is_accepted Whether the original message was accepted or not.
* @param reason Rejected reason. Empty if accepted.
* @param input_sig Binary signature of the original input message which generated this result.
*/
void create_contract_input_status(std::vector<uint8_t> &msg, std::string_view status, std::string_view reason, std::string_view input_sig)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_CONTRACT_INPUT_STATUS);
encoder.key(msg::usrmsg::FLD_STATUS);
encoder.string_value(status);
encoder.key(msg::usrmsg::FLD_REASON);
encoder.string_value(reason);
encoder.key(msg::usrmsg::FLD_INPUT_SIG);
encoder.byte_string_value(input_sig);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a contract input status message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "contract_input_status",
* "status": "<accepted|rejected>",
* "reason": "<reson>",
* "input_sig": <signature of original input message>
* }
* @param is_accepted Whether the original message was accepted or not.
* @param reason Rejected reason. Empty if accepted.
* @param input_sig Binary signature of the original input message which generated this result.
*/
void create_contract_input_status(std::vector<uint8_t> &msg, std::string_view status, std::string_view reason, std::string_view input_sig)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_CONTRACT_INPUT_STATUS);
encoder.key(msg::usrmsg::FLD_STATUS);
encoder.string_value(status);
encoder.key(msg::usrmsg::FLD_REASON);
encoder.string_value(reason);
encoder.key(msg::usrmsg::FLD_INPUT_SIG);
encoder.byte_string_value(input_sig);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a contract read response message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "contract_read_response",
* "content": <contract output>
* }
* @param content The contract binary output content to be put in the message.
*/
void create_contract_read_response_container(std::vector<uint8_t> &msg, std::string_view content)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_CONTRACT_READ_RESPONSE);
encoder.key(msg::usrmsg::FLD_CONTENT);
encoder.byte_string_value(content);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a contract read response message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "contract_read_response",
* "content": <contract output>
* }
* @param content The contract binary output content to be put in the message.
*/
void create_contract_read_response_container(std::vector<uint8_t> &msg, std::string_view content)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_CONTRACT_READ_RESPONSE);
encoder.key(msg::usrmsg::FLD_CONTENT);
encoder.byte_string_value(content);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a contract output container message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "contract_output",
* "lcl": "<lcl id>"
* "lcl_seqno": <integer>,
* "content": <contract output>
* }
* @param content The contract binary output content to be put in the message.
*/
void create_contract_output_container(std::vector<uint8_t> &msg, std::string_view content, const uint64_t lcl_seq_no, std::string_view lcl)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_CONTRACT_OUTPUT);
encoder.key(msg::usrmsg::FLD_LCL);
encoder.string_value(lcl);
encoder.key(msg::usrmsg::FLD_LCL_SEQ);
encoder.int64_value(lcl_seq_no);
encoder.key(msg::usrmsg::FLD_CONTENT);
encoder.byte_string_value(content);
encoder.end_object();
encoder.flush();
}
/**
* Constructs a contract output container message.
* @param msg String reference to copy the generated bson message into.
* Message format:
* {
* "type": "contract_output",
* "lcl": "<lcl id>"
* "lcl_seqno": <integer>,
* "content": <contract output>
* }
* @param content The contract binary output content to be put in the message.
*/
void create_contract_output_container(std::vector<uint8_t> &msg, std::string_view content, const uint64_t lcl_seq_no, std::string_view lcl)
{
jsoncons::bson::bson_bytes_encoder encoder(msg);
encoder.begin_object();
encoder.key(msg::usrmsg::FLD_TYPE);
encoder.string_value(msg::usrmsg::MSGTYPE_CONTRACT_OUTPUT);
encoder.key(msg::usrmsg::FLD_LCL);
encoder.string_value(lcl);
encoder.key(msg::usrmsg::FLD_LCL_SEQ);
encoder.int64_value(lcl_seq_no);
encoder.key(msg::usrmsg::FLD_CONTENT);
encoder.byte_string_value(content);
encoder.end_object();
encoder.flush();
}
/**
* Parses a bson message sent by a user.
* @param d BSON document to which the parsed bson should be loaded.
* @param message The message to parse.
* Accepted message format:
* {
* 'type': '<message type>'
* ...
* }
* @return 0 on successful parsing. -1 for failure.
*/
int parse_user_message(jsoncons::ojson &d, std::string_view message)
{
try
{
d = jsoncons::bson::decode_bson<jsoncons::ojson>(message);
}
catch (const std::exception &e)
{
LOG_DEBUG << "User bson message parsing failed.";
return -1;
}
/**
* Parses a bson message sent by a user.
* @param d BSON document to which the parsed bson should be loaded.
* @param message The message to parse.
* Accepted message format:
* {
* 'type': '<message type>'
* ...
* }
* @return 0 on successful parsing. -1 for failure.
*/
int parse_user_message(jsoncons::ojson &d, std::string_view message)
{
try
{
d = jsoncons::bson::decode_bson<jsoncons::ojson>(message);
}
catch (const std::exception &e)
{
LOG_DEBUG << "User bson message parsing failed.";
return -1;
}
if (!d.contains(FLD_TYPE) || !d[FLD_TYPE].is_string())
{
LOG_DEBUG << "User bson message 'type' missing or invalid.";
return -1;
}
if (!d.contains(FLD_TYPE) || !d[FLD_TYPE].is_string())
{
LOG_DEBUG << "User bson message 'type' missing or invalid.";
return -1;
}
return 0;
}
return 0;
}
/**
* Extracts the message 'type' value from the bson document.
*/
int extract_type(std::string &extracted_type, const jsoncons::ojson &d)
{
extracted_type = d[FLD_TYPE].as<std::string>();
return 0;
}
/**
* Extracts the message 'type' value from the bson document.
*/
int extract_type(std::string &extracted_type, const jsoncons::ojson &d)
{
extracted_type = d[FLD_TYPE].as<std::string>();
return 0;
}
/**
* Extracts a contract read request message sent by user.
*
* @param extracted_content The content to be passed to the contract, extracted from the message.
* @param d The bson document holding the read request message.
* Accepted signed input container format:
* {
* "type": "contract_read_request",
* "content": <content to be passed to the contract>
* }
* @return 0 on successful extraction. -1 for failure.
*/
int extract_read_request(std::string &extracted_content, const jsoncons::ojson &d)
{
if (!d.contains(msg::usrmsg::FLD_CONTENT) || !d[msg::usrmsg::FLD_CONTENT].is_byte_string_view())
{
LOG_DEBUG << "Read request 'content' field missing or invalid.";
return -1;
}
/**
* Extracts a contract read request message sent by user.
*
* @param extracted_content The content to be passed to the contract, extracted from the message.
* @param d The bson document holding the read request message.
* Accepted signed input container format:
* {
* "type": "contract_read_request",
* "content": <binary buffer>
* }
* @return 0 on successful extraction. -1 for failure.
*/
int extract_read_request(std::string &extracted_content, const jsoncons::ojson &d)
{
if (!d.contains(msg::usrmsg::FLD_CONTENT) || !d[msg::usrmsg::FLD_CONTENT].is_byte_string_view())
{
LOG_DEBUG << "Read request 'content' field missing or invalid.";
return -1;
}
const jsoncons::byte_string_view &bsv = d[msg::usrmsg::FLD_CONTENT].as_byte_string_view();
extracted_content = std::string_view(reinterpret_cast<const char *>(bsv.data()), bsv.size());
return 0;
}
const jsoncons::byte_string_view &bsv = d[msg::usrmsg::FLD_CONTENT].as_byte_string_view();
extracted_content = std::string_view(reinterpret_cast<const char *>(bsv.data()), bsv.size());
return 0;
}
/**
* Extracts a signed input container message sent by user.
*
* @param extracted_input_container The input container extracted from the message.
* @param extracted_sig The binary signature extracted from the message.
* @param d The bson document holding the input container.
* Accepted signed input container format:
* {
* "type": "contract_input",
* "input_container": <bson input container message>,
* "sig": <signature of the content>
* }
* @return 0 on successful extraction. -1 for failure.
*/
int extract_signed_input_container(
std::string &extracted_input_container, std::string &extracted_sig, const jsoncons::ojson &d)
{
if (!d.contains(msg::usrmsg::FLD_INPUT_CONTAINER) || !d.contains(msg::usrmsg::FLD_SIG) ||
!d[msg::usrmsg::FLD_INPUT_CONTAINER].is_byte_string_view() || !d[msg::usrmsg::FLD_SIG].is_byte_string_view())
{
LOG_DEBUG << "User signed input required fields missing or invalid.";
return -1;
}
/**
* Extracts a signed input container message sent by user.
*
* @param extracted_input_container The input container extracted from the message.
* @param extracted_sig The binary signature extracted from the message.
* @param d The bson document holding the input container.
* Accepted signed input container format:
* {
* "type": "contract_input",
* "input_container": <bson serialized input container>,
* "sig": <binary signature buffer of the bson serialized content>
* }
* @return 0 on successful extraction. -1 for failure.
*/
int extract_signed_input_container(
std::string &extracted_input_container, std::string &extracted_sig, const jsoncons::ojson &d)
{
if (!d.contains(msg::usrmsg::FLD_INPUT_CONTAINER) || !d.contains(msg::usrmsg::FLD_SIG) ||
!d[msg::usrmsg::FLD_INPUT_CONTAINER].is_byte_string_view() || !d[msg::usrmsg::FLD_SIG].is_byte_string_view())
{
LOG_DEBUG << "User signed input required fields missing or invalid.";
return -1;
}
const jsoncons::byte_string_view &bsv1 = d[msg::usrmsg::FLD_INPUT_CONTAINER].as_byte_string_view();
extracted_input_container = std::string_view(reinterpret_cast<const char *>(bsv1.data()), bsv1.size());
const jsoncons::byte_string_view &bsv1 = d[msg::usrmsg::FLD_INPUT_CONTAINER].as_byte_string_view();
extracted_input_container = std::string_view(reinterpret_cast<const char *>(bsv1.data()), bsv1.size());
const jsoncons::byte_string_view &bsv2 = d[msg::usrmsg::FLD_SIG].as_byte_string_view();
extracted_sig = std::string_view(reinterpret_cast<const char *>(bsv2.data()), bsv2.size());
const jsoncons::byte_string_view &bsv2 = d[msg::usrmsg::FLD_SIG].as_byte_string_view();
extracted_sig = std::string_view(reinterpret_cast<const char *>(bsv2.data()), bsv2.size());
return 0;
}
return 0;
}
/**
* Extract the individual components of a given input container bson.
* @param input The extracted input.
* @param nonce The extracted nonce.
* @param max_lcl_seqno The extracted max ledger sequence no.
* @param contentjson The bson input container message.
* {
* "input": <contract input content>,
* "nonce": "<random string with optional sorted order>",
* "max_lcl_seqno": <integer>
* }
* @return 0 on succesful extraction. -1 on failure.
*/
int extract_input_container(std::string &input, std::string &nonce, uint64_t &max_lcl_seqno, std::string_view contentbson)
{
jsoncons::ojson d;
try
{
d = jsoncons::bson::decode_bson<jsoncons::ojson>(contentbson);
}
catch (const std::exception &e)
{
LOG_DEBUG << "User input container bson parsing failed.";
return -1;
}
/**
* Extract the individual components of a given input container bson.
* @param input The extracted input.
* @param nonce The extracted nonce.
* @param max_lcl_seqno The extracted max ledger sequence no.
* @param contentjson The bson input container message.
* {
* "input": <binary buffer>,
* "nonce": "<random string with optional sorted order>",
* "max_lcl_seqno": <integer>
* }
* @return 0 on succesful extraction. -1 on failure.
*/
int extract_input_container(std::string &input, std::string &nonce, uint64_t &max_lcl_seqno, std::string_view contentbson)
{
jsoncons::ojson d;
try
{
d = jsoncons::bson::decode_bson<jsoncons::ojson>(contentbson);
}
catch (const std::exception &e)
{
LOG_DEBUG << "User input container bson parsing failed.";
return -1;
}
if (!d.contains(msg::usrmsg::FLD_INPUT) || !d.contains(msg::usrmsg::FLD_NONCE) || !d.contains(msg::usrmsg::FLD_MAX_LCL_SEQ) ||
!d[msg::usrmsg::FLD_INPUT].is_byte_string_view() || !d[msg::usrmsg::FLD_NONCE].is_string() || !d[msg::usrmsg::FLD_MAX_LCL_SEQ].is_uint64())
{
LOG_DEBUG << "User input container required fields missing or invalid.";
return -1;
}
if (!d.contains(msg::usrmsg::FLD_INPUT) || !d.contains(msg::usrmsg::FLD_NONCE) || !d.contains(msg::usrmsg::FLD_MAX_LCL_SEQ))
{
LOG_DEBUG << "User input container required fields missing or invalid.";
return -1;
}
const jsoncons::byte_string_view &bsv = d[msg::usrmsg::FLD_INPUT].as_byte_string_view();
input = std::string_view(reinterpret_cast<const char *>(bsv.data()), bsv.size());
if (!d[msg::usrmsg::FLD_INPUT].is_byte_string_view() || !d[msg::usrmsg::FLD_NONCE].is_string() || !d[msg::usrmsg::FLD_MAX_LCL_SEQ].is_uint64())
{
LOG_DEBUG << "User input container invalid field values.";
return -1;
}
nonce = d[msg::usrmsg::FLD_NONCE].as<std::string>();
max_lcl_seqno = d[msg::usrmsg::FLD_MAX_LCL_SEQ].as<uint64_t>();
return 0;
}
const jsoncons::byte_string_view &bsv = d[msg::usrmsg::FLD_INPUT].as_byte_string_view();
input = std::string_view(reinterpret_cast<const char *>(bsv.data()), bsv.size());
nonce = d[msg::usrmsg::FLD_NONCE].as<std::string>();
max_lcl_seqno = d[msg::usrmsg::FLD_MAX_LCL_SEQ].as<uint64_t>();
return 0;
}
} // namespace msg::usrmsg::bson

View File

@@ -303,7 +303,7 @@ namespace msg::usrmsg::json
}
catch (const std::exception &e)
{
LOG_DEBUG << "User json message parsing failed. " << e.what();;
LOG_DEBUG << "User json message parsing failed. " << e.what();
return -1;
}
@@ -334,7 +334,7 @@ namespace msg::usrmsg::json
* Accepted signed input container format:
* {
* "type": "contract_read_request",
* "content": "<hex encoded content to be passed to the contract>"
* "content": "<any string>"
* }
* @return 0 on successful extraction. -1 for failure.
*/
@@ -352,20 +352,7 @@ namespace msg::usrmsg::json
return -1;
}
std::string_view contenthex = d[msg::usrmsg::FLD_CONTENT].as<std::string_view>();
std::string content;
content.resize(contenthex.length() / 2);
if (util::hex2bin(
reinterpret_cast<unsigned char *>(content.data()),
content.length(),
contenthex) != 0)
{
LOG_DEBUG << "Read request format invalid.";
return -1;
}
extracted_content = std::move(content);
extracted_content = d[msg::usrmsg::FLD_CONTENT].as<std::string>();
return 0;
}
@@ -378,8 +365,8 @@ namespace msg::usrmsg::json
* Accepted signed input container format:
* {
* "type": "contract_input",
* "input_container": "<hex encoded stringified json input container message>",
* "sig": "<hex encoded signature of the content>"
* "input_container": "<stringified json input container>",
* "sig": "<hex encoded signature of stringified input container>"
* }
* @return 0 on successful extraction. -1 for failure.
*/
@@ -401,18 +388,13 @@ namespace msg::usrmsg::json
// We do not verify the signature of the content here since we need to let each node
// (including self) to verify that individually after we broadcast the NUP proposal.
const std::string_view input_container_hex = d[msg::usrmsg::FLD_INPUT_CONTAINER].as<std::string_view>();
std::string input_container;
input_container.resize(input_container_hex.size() / 2);
util::hex2bin(reinterpret_cast<unsigned char *>(input_container.data()), input_container.length(), input_container_hex);
extracted_input_container = d[msg::usrmsg::FLD_INPUT_CONTAINER].as<std::string>();
// Extract the hex signature and convert to binary.
const std::string_view sig_hex = d[msg::usrmsg::FLD_SIG].as<std::string_view>();
std::string sig;
sig.resize(crypto_sign_ed25519_BYTES);
util::hex2bin(reinterpret_cast<unsigned char *>(sig.data()), sig.length(), sig_hex);
extracted_sig.resize(crypto_sign_ed25519_BYTES);
util::hex2bin(reinterpret_cast<unsigned char *>(extracted_sig.data()), extracted_sig.length(), sig_hex);
extracted_input_container = std::move(input_container);
extracted_sig = std::move(sig);
return 0;
}
@@ -423,7 +405,7 @@ namespace msg::usrmsg::json
* @param max_lcl_seqno The extracted max ledger sequence no.
* @param contentjson The json string containing the input container message.
* {
* "input": "<hex encoded contract input content>",
* "input": "<any string>",
* "nonce": "<random string with optional sorted order>",
* "max_lcl_seqno": <integer>
* }
@@ -454,19 +436,7 @@ namespace msg::usrmsg::json
return -1;
}
std::string_view inputhex = d[msg::usrmsg::FLD_INPUT].as<std::string_view>();
// Convert hex input to binary.
input.resize(inputhex.length() / 2);
if (util::hex2bin(
reinterpret_cast<unsigned char *>(input.data()),
input.length(),
inputhex) != 0)
{
LOG_DEBUG << "Contract input format invalid.";
return -1;
}
input = d[msg::usrmsg::FLD_INPUT].as<std::string>();
nonce = d[msg::usrmsg::FLD_NONCE].as<std::string>();
max_lcl_seqno = d[msg::usrmsg::FLD_MAX_LCL_SEQ].as<uint64_t>();

View File

@@ -125,7 +125,7 @@ namespace usr
if (msg_type == msg::usrmsg::MSGTYPE_CONTRACT_READ_REQUEST)
{
std::string content;
if (parser.extract_read_request(content) == 0)
if (parser.extract_read_request(content) != -1)
{
read_req::populate_read_req_queue(user.pubkey, std::move(content));
return 0;
@@ -142,29 +142,35 @@ namespace usr
std::string input_container;
std::string sig;
if (parser.extract_signed_input_container(input_container, sig) == 0)
if (parser.extract_signed_input_container(input_container, sig) != -1)
{
std::scoped_lock<std::mutex> lock(ctx.users_mutex);
std::string input_data;
std::string nonce;
uint64_t max_lcl_seqno;
parser.extract_input_container(input_data, nonce, max_lcl_seqno, input_container);
const int nonce_status = nonce_map.check(user.pubkey, nonce, sig, true);
if (nonce_status == 0)
if (parser.extract_input_container(input_data, nonce, max_lcl_seqno, input_container) != -1)
{
//Add to the submitted input list.
user.submitted_inputs.push_back(user_input(
std::move(input_container),
std::move(sig),
user.protocol));
return 0;
const int nonce_status = nonce_map.check(user.pubkey, nonce, sig, true);
if (nonce_status == 0)
{
//Add to the submitted input list.
user.submitted_inputs.push_back(user_input(
std::move(input_container),
std::move(sig),
user.protocol));
return 0;
}
else
{
const char *reason = nonce_status == 1 ? msg::usrmsg::REASON_NONCE_EXPIRED : msg::usrmsg::REASON_ALREADY_SUBMITTED;
send_input_status(parser, user.session, msg::usrmsg::STATUS_REJECTED, reason, sig);
return -1;
}
}
else
{
const char *reason = nonce_status == 1 ? msg::usrmsg::REASON_NONCE_EXPIRED : msg::usrmsg::REASON_ALREADY_SUBMITTED;
send_input_status(parser, user.session, msg::usrmsg::STATUS_REJECTED, reason, sig);
send_input_status(parser, user.session, msg::usrmsg::STATUS_REJECTED, msg::usrmsg::REASON_BAD_MSG_FORMAT, sig);
return -1;
}
}
@@ -294,7 +300,11 @@ namespace usr
msg::usrmsg::usrmsg_parser parser(umsg.protocol);
std::string input_data;
parser.extract_input_container(input_data, nonce, max_lcl_seqno, umsg.input_container);
if (parser.extract_input_container(input_data, nonce, max_lcl_seqno, umsg.input_container) == -1)
{
LOG_DEBUG << "User message bad input format.";
return msg::usrmsg::REASON_BAD_MSG_FORMAT;
}
// Ignore the input if our ledger has passed the input TTL.
if (max_lcl_seqno <= lcl_seq_no)

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# Generate contract sub-directories within this script directory for the given no. of cluster nodes.
# Usage (to generate 8-node cluster): ./cluster-create.sh 8
# Script to generate docker container clusters for local development testing.
# Generate contract sub-directories within "hpcluster" directory for the given no. of cluster nodes.
# Usage (to generate 5-node cluster): ./cluster-create.sh 5
# Validate the node count arg.
if [ -n "$1" ] && [ "$1" -eq "$1" ] 2>/dev/null; then
@@ -11,6 +11,34 @@ else
exit 1
fi
ncount=$1
hpcore=$(realpath ../..)
# Contract can be set with 'export CONTRACT=<name>'. Defaults to nodejs echo contract.
if [ "$CONTRACT" = "cecho" ]; then # C echo contract
echo "Using C echo contract."
pushd $hpcore/examples/c_contract/ > /dev/null 2>&1
gcc echo_contract.c -o echo_contract -pthread
popd > /dev/null 2>&1
copyfiles="$hpcore/examples/c_contract/echo_contract"
binary="/contract/bin/echo_contract"
elif [ "$CONTRACT" = "nodefile" ]; then # nodejs file contract (uses BSON protocol)
echo "Using nodejs file contract."
pushd $hpcore/examples/nodejs_contract/ > /dev/null 2>&1
npm install
popd > /dev/null 2>&1
copyfiles="$hpcore/examples/nodejs_contract/{node_modules,package.json,hp-contract-lib.js,file_contract.js}"
binary="/usr/local/bin/node"
binargs="/contract/bin/file_contract.js"
else # nodejs echo contract (default)
echo "Using nodejs echo contract."
copyfiles="$hpcore/examples/nodejs_contract/{package.json,hp-contract-lib.js,echo_contract.js}"
binary="/usr/local/bin/node"
binargs="/contract/bin/echo_contract.js"
fi
# Delete and recreate 'hpcluster' directory.
rm -rf hpcluster > /dev/null 2>&1
mkdir hpcluster
@@ -19,7 +47,6 @@ clusterloc="./hpcluster"
pushd $clusterloc > /dev/null 2>&1
# Create contract directories for all nodes in the cluster.
ncount=$1
for (( i=0; i<$ncount; i++ ))
do
@@ -46,8 +73,8 @@ do
# Update contract config.
node -p "JSON.stringify({...require('./tmp.json'), \
binary: '/usr/local/bin/node', \
binargs: '/contract/bin/echo_contract.js', \
binary: '$binary', \
binargs: '$binargs', \
appbill: '', \
appbillargs: '', \
peerport: ${peerport}, \
@@ -63,15 +90,10 @@ do
-subj "/C=AU/ST=ST/L=L/O=O/OU=OU/CN=localhost/emailAddress=hpnode${n}@example" > /dev/null 2>&1
popd > /dev/null 2>&1
# Copy the contract executable and appbill.
# Copy the contract files and appbill.
mkdir ./node$n/bin
cp ../../../examples/nodejs_contract/{package.json,echo_contract.js,hp-contract-lib.js} ./node$n/bin/
eval "cp -r $copyfiles ./node$n/bin/"
cp ../bin/appbill ./node$n/bin/
pushd ./node$n/bin > /dev/null 2>&1
# Uncomment this if the contract needs npm install.
# npm install
popd
done
# Function to generate JSON array string while skiping a given index.