This is the first of some blogs we wrote discussing the development of LOCALLY Reward about our experiences with developing on NEM.
If you don’t know, NEM is a blockchain that has its native currency of XEM, it is designed in such a way that users can interface with it through the use of APIs (mostly anyway - more on that later…), this means that rather than obscure and often untested languages (Solidity for example) you can use tools and environments you are comfortable with.
NEM is a little different than something like Ethereum for example, from my experience NEM is designed as a development platform, it doesn’t currently work with specific smart contracts, instead leaving the functionality and infrastructure up to the developers, this is great to work at the speed your business runs because you can use something like NEM to migrate parts of your business to the blockchain without too much trouble.
So what exactly has LOCALLY Reward been developing?
In short - ALL THE THINGS. It has been a great couple of months, personally I love when a team is in that space of just pure creation, and we have a great team building this product.
The product however isn’t only the tangible application that LOCALLY Reward will be initially released as. We have hired Blockchain Directors, Data Scientists, Platform developers, more app developers and QA engineers. LOCALLY Reward is part of a much bigger product and all pieces need to work together.
So for each of these updates I will focus on some of the items I have experience with developing and leave the mobile jargon to articles written by my other developers.
Trusted NIS servers, Mobile applications and Private Keys
The biggest hurdle we had to overcome recently was surrounding the method of payment of rewards for LOCALLY Reward. We decided from the start that the LOCALLY Reward app would generate a wallet that was compatible with NEM’s NanoWallet, this was both a strategic decision and a short-cut for us. For this LOCALLY Reward Beta App we decided that we would be pushing it out to users both technical and non-technical so the decision to generate a working wallet with NanoWallet was preferable to having the app juggle private keys and thus avoiding the complicated process of using that key to setup a working wallet on NanoWallet. It also means that users will have immediate access to their funds and all mosaics, mosaics can only be transferred by having enough XEM to pay for the transaction fee, so this also solved this issue.
The added benefit of this was that because we are moving the onus from the device to initiate blockchain transactions, we could now treat this account as a “receive only” actor within our system meaning that we can use NIS to sign and broadcast these transactions rather than attempting to sign this transaction locally and running into the 'fee' issue.
(NB: NEM2 / Catapult will have a great feature whereby the Requester can choose who pays the fees - our end goal will be to use this functionality when Catapult public chain is ready!)
This again introduces complexity in other ways, because the main LOCALLY Reward account would now be initiating the transaction to transfer mosaics to the user, the app needed to talk to something to let us know how much to pay the user and when to do so! So far we had managed to avoid any centralisation of the infrastructure or data flow but due to the fact that a mobile app cannot run a local NIS node there isn't much choice in this case - so from a technical standpoint how do we handle this without having to potentially spin up a cluster of servers that can scale horizontally to handle future influx?
Enter Lambda
For those of you not aware, serverless programming is the next big thing (apparently?), personally I haven’t fallen in love with it but it offers some great solutions to specific use cases, in this case it was “How can we produce an infinitely scalable API without spending a fortune on servers?”
The Lambda Function
NB: I am not a Javascript programmer, however I have been doing a lot more Node since this article was written and I am aware how horrible the callbacks are below...
The function below basically allows us to build the transaction and reward the user based on the amount and types of data they have sent to us. This will be run at a predetermined interval (undecided exactly when as of yet).
exports.handler = function (event, context, callback) {
// Sanity check the data
if (sanityCheck(event)) {
var nem = require("nem-sdk").default;
var crypto = require('crypto');
var endpoint = nem.model.objects.create("endpoint")('http://ournode', nem.model.nodes.defaultPort);
var common = nem.model.objects.create("common")("", "privatekeyfromenv");
var mosaicDefinitionMetaDataPair = nem.model.objects.get("mosaicDefinitionMetaDataPair");
var transferTransaction = nem.model.objects.create("transferTransaction")(event.address, 0, "Test " + Date.now());
var mosaicAttachment = nem.model.objects.create("mosaicAttachment")("LOCALLY Reward", "lcc", event.amount * 100);
transferTransaction.mosaics.push(mosaicAttachment);
// Need mosaic definition of LOCALLY Reward:lcc to calculate fees, so we get it from network.
nem.com.requests.namespace.mosaicDefinitions(endpoint, mosaicAttachment.mosaicId.namespaceId).then(function (res) {
var neededDefinition = nem.utils.helpers.searchMosaicDefinitionArray(res.data, ["lcc"]);
var fullMosaicName = nem.utils.format.mosaicIdToName(mosaicAttachment.mosaicId);
if (undefined === neededDefinition[fullMosaicName]) return console.error("Mosaic not found !");
mosaicDefinitionMetaDataPair[fullMosaicName] = {};
mosaicDefinitionMetaDataPair[fullMosaicName].mosaicDefinition = neededDefinition[fullMosaicName];
// The difficulty with fee calculation is that it needs the current supply, many libraries out there are incorrectly coded to use
// the initial supply
nem.com.requests.mosaic.supply(endpoint, nem.utils.format.mosaicIdToName(mosaicAttachment.mosaicId)).then(function (res) {
mosaicDefinitionMetaDataPair[nem.utils.format.mosaicIdToName(mosaicAttachment.mosaicId)].supply = res.supply;
nem.com.requests.mosaic.supply(endpoint, fullMosaicName).then(function (res) {
mosaicDefinitionMetaDataPair[fullMosaicName].supply = res.supply;
var transactionEntity = nem.model.transactions.prepare("mosaicTransferTransaction")(common, transferTransaction, mosaicDefinitionMetaDataPair, nem.model.network.data.testnet.id);
// Transaction is broadcast here
nem.model.transactions.send(common, transactionEntity, endpoint).then(function (res) {
response = {
statusCode: 200,
body: JSON.stringify(res)
};
callback(null, response);
});
}, function (err) {
console.error(err);
});
}, function (err) {
console.error(err);
});
}, function (err) {
console.error(err);
});
}
};
Access to the Lambda function will be handled via Amazon Cognito user pools.
How are users rewarded and what data is accepted?
LOCALLY Reward rewards users at the point of collection, in this case the device will cache the data ready to be sent to LOCALLY Reward, the app will do this at a pre-determined interval (We are thinking for the beta to have it every day but possibly once a week when it is live).
Once the data is ready to be sent several things happen:
The device has maintained a rolling hash of the data that has been collected (from the device, Facebook etc), this hash is ‘finalised’ at the cut off point, this is so the user can compare the hashes on the blockchain transaction with their previous chunk of sent data to ensure they have been paid.
The transaction is initiated with this hash as the message
The user can see and verify that their data has been paid for by comparing the hashes.
|
Daniel Voyce - CTO Daniel leads the technology strategy, architecture and development at LOCALLY.
|
Top comments (0)