diff --git a/examples/nodejs_client/file-client.js b/examples/nodejs_client/file-client.js index 8c81e2c0..df850dd4 100644 --- a/examples/nodejs_client/file-client.js +++ b/examples/nodejs_client/file-client.js @@ -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") { diff --git a/examples/nodejs_client/hp-client-lib.js b/examples/nodejs_client/hp-client-lib.js index af975b7a..3efcdaf3 100644 --- a/examples/nodejs_client/hp-client-lib.js +++ b/examples/nodejs_client/hp-client-lib.js @@ -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; } \ No newline at end of file diff --git a/examples/nodejs_client/text-client.js b/examples/nodejs_client/text-client.js index 9b0aba7a..2fd69774 100644 --- a/examples/nodejs_client/text-client.js +++ b/examples/nodejs_client/text-client.js @@ -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")); }) diff --git a/examples/nodejs_contract/echo_contract.js b/examples/nodejs_contract/echo_contract.js index 1bf5a97a..536f55be 100644 --- a/examples/nodejs_contract/echo_contract.js +++ b/examples/nodejs_contract/echo_contract.js @@ -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(); diff --git a/src/msg/bson/usrmsg_bson.cpp b/src/msg/bson/usrmsg_bson.cpp index b31bae8d..0d821e3e 100644 --- a/src/msg/bson/usrmsg_bson.cpp +++ b/src/msg/bson/usrmsg_bson.cpp @@ -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_seqno": - * } - */ - void create_status_response(std::vector &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_seqno": + * } + */ + void create_status_response(std::vector &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": "", - * "reason": "", - * "input_sig": - * } - * @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 &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": "", + * "reason": "", + * "input_sig": + * } + * @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 &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": - * } - * @param content The contract binary output content to be put in the message. - */ - void create_contract_read_response_container(std::vector &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": + * } + * @param content The contract binary output content to be put in the message. + */ + void create_contract_read_response_container(std::vector &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_seqno": , - * "content": - * } - * @param content The contract binary output content to be put in the message. - */ - void create_contract_output_container(std::vector &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_seqno": , + * "content": + * } + * @param content The contract binary output content to be put in the message. + */ + void create_contract_output_container(std::vector &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': '' - * ... - * } - * @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(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': '' + * ... + * } + * @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(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(); - 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(); + 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": - * } - * @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": + * } + * @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(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(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": , - * "sig": - * } - * @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": , + * "sig": + * } + * @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(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(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(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(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": , - * "nonce": "", - * "max_lcl_seqno": - * } - * @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(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": , + * "nonce": "", + * "max_lcl_seqno": + * } + * @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(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(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(); - max_lcl_seqno = d[msg::usrmsg::FLD_MAX_LCL_SEQ].as(); - return 0; - } + const jsoncons::byte_string_view &bsv = d[msg::usrmsg::FLD_INPUT].as_byte_string_view(); + input = std::string_view(reinterpret_cast(bsv.data()), bsv.size()); + + nonce = d[msg::usrmsg::FLD_NONCE].as(); + max_lcl_seqno = d[msg::usrmsg::FLD_MAX_LCL_SEQ].as(); + return 0; + } } // namespace msg::usrmsg::bson \ No newline at end of file diff --git a/src/msg/json/usrmsg_json.cpp b/src/msg/json/usrmsg_json.cpp index 58649b7a..0abeaf35 100644 --- a/src/msg/json/usrmsg_json.cpp +++ b/src/msg/json/usrmsg_json.cpp @@ -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": "" + * "content": "" * } * @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 content; - content.resize(contenthex.length() / 2); - if (util::hex2bin( - reinterpret_cast(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(); return 0; } @@ -378,8 +365,8 @@ namespace msg::usrmsg::json * Accepted signed input container format: * { * "type": "contract_input", - * "input_container": "", - * "sig": "" + * "input_container": "", + * "sig": "" * } * @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 input_container; - input_container.resize(input_container_hex.size() / 2); - util::hex2bin(reinterpret_cast(input_container.data()), input_container.length(), input_container_hex); + extracted_input_container = d[msg::usrmsg::FLD_INPUT_CONTAINER].as(); + // Extract the hex signature and convert to binary. const std::string_view sig_hex = d[msg::usrmsg::FLD_SIG].as(); - std::string sig; - sig.resize(crypto_sign_ed25519_BYTES); - util::hex2bin(reinterpret_cast(sig.data()), sig.length(), sig_hex); + extracted_sig.resize(crypto_sign_ed25519_BYTES); + util::hex2bin(reinterpret_cast(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": "", + * "input": "", * "nonce": "", * "max_lcl_seqno": * } @@ -454,19 +436,7 @@ namespace msg::usrmsg::json return -1; } - std::string_view inputhex = d[msg::usrmsg::FLD_INPUT].as(); - - // Convert hex input to binary. - input.resize(inputhex.length() / 2); - if (util::hex2bin( - reinterpret_cast(input.data()), - input.length(), - inputhex) != 0) - { - LOG_DEBUG << "Contract input format invalid."; - return -1; - } - + input = d[msg::usrmsg::FLD_INPUT].as(); nonce = d[msg::usrmsg::FLD_NONCE].as(); max_lcl_seqno = d[msg::usrmsg::FLD_MAX_LCL_SEQ].as(); diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 5c93555e..e77f780f 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -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 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) diff --git a/test/local-cluster/cluster-create.sh b/test/local-cluster/cluster-create.sh index e29b091e..8f3cf1d6 100755 --- a/test/local-cluster/cluster-create.sh +++ b/test/local-cluster/cluster-create.sh @@ -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='. 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.