mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
User input json/bson format improvements. (#174)
* Removed hex encoding in json input container. * Refactored client lib exports.
This commit is contained in:
@@ -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") {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"));
|
||||
})
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user