How To guarantee that the input of the smart contract is not manipulated? - asch-development

Let's say that my DApp got the following (smart) contract:
module.exports = {
winner: async function(value) {
if (value===10) {
}
}
}
Now the Dapp user can do someting which invoke the contract with some value which can be 10 or not. The Dapp determines if value equals 10 or not. So far so good.
But now it seems that anyone with a valid secret (and some XAS send to the Dapps's side chain) can invoke the contract with a simple PUT request to api/<dappId>//transactions/unsigned with value set to whatever they want.
How to ensure that the value of value is set by the Dapp and can not be manipulated?

As far as i do understand Asch DApps run on a express server, with the cors middleware enabled, which means that anyone can do a request (GET, POST, PUT, etc ) from anywhere.
So one can invoke your contract easily with a script like that shown below:
const axios = require('axios');
var fee = '10000000'
var data = {
secret: "<your secret>",
fee: fee,
type: 1001, //the number for contractfile.function
args: 1000 // a very high score
}
axios.put('http://<domain>:4096/api/dapps/<dappid>/transactions/unsigned',data)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
Due to the above it is not possible to guarantee that in input is not manipulated (send from outside the DApp). Also see: https://github.com/AschPlatform/asch/issues/228

Related

Firebase cloud function error: Maximum call size stack size exceeded

I've made firebase cloud function which adds the claim to a user that he or she has paid (set paid to true for user):
const admin = require("firebase-admin");
exports.addPaidClaim = functions.https.onCall(async (data, context) => {
// add custom claim (paid)
return admin.auth().setCustomUserClaims(data.uid, {
paid: true,
}).then(() => {
return {
message: `Succes! ${data.email} has paid for the course`,
};
}).catch((err) => {
return err;
});
});
However, when I'm running this function: I'm receiving the following error: "Unhandled Rejection (RangeError): Maximum call stack size exceeded". I really don't understand why this is happening. Does somebody see what could cause what's getting recalled which in turn causes the function to never end?
Asynchronous operations need to return a promise as stated in the documentation. Therefore, Cloud Functions is trying to serialize the data contained by promise returned by transaction, then send it in JSON format to the client. I believe your setCustomClaims does not send any object to consider it as an answer to the promise to finish the process so it keeps in a waiting loop that throws the Range Error.
To avoid this error I can think of two different options:
Add a paid parameter to be able to send a JSON response (and remove the setCustomUserClaim if it there isn’t any need to change the user access control because they are not designed to store additional data) .
Insert a promise that resolves and sends any needed information to the client. Something like:
return new Promise(function(resolve, reject) {
request({
url: URL,
method: "POST",
json: true,
body: queryJSON //A json variable I've built previously
}, function (error, response, body) {
if (error) {
reject(error);
}
else {
resolve(body)
}
});
});

Am I doing Firestore Transactions correct?

I've followed the Firestore documentation with relation to transactions, and I think I have it all sorted correctly, but in testing I am noticing issues with my documents not getting updated properly sometimes. It is possible that multiple versions of the document could be submitted to the function in a very short interval, but I am only interested in only ever keeping the most recent version.
My general logic is this:
New/Updated document is sent to cloud function
Check if document already exists in Firestore, and if not, add it.
If it does exist, check that it is "newer" than the instance in firestore, if it is, update it.
Otherwise, don't do anything.
Here is the code from my function that attempts to accomplish this...I would love some feedback if this is correct/best way to do this:
const ocsFlight = req.body;
const procFlight = processOcsFlightEvent(ocsFlight);
try {
const ocsFlightRef = db.collection(collection).doc(procFlight.fltId);
const originalFlight = await ocsFlightRef.get();
if (!originalFlight.exists) {
const response = await ocsFlightRef.set(procFlight);
console.log("Record Added: ", JSON.stringify(procFlight));
res.status(201).json(response); // 201 - Created
return;
}
await db.runTransaction(async (t) => {
const doc = await t.get(ocsFlightRef);
const flightDoc = doc.data();
if (flightDoc.recordModified <= procFlight.recordModified) {
t.update(ocsFlightRef, procFlight);
console.log("Record Updated: ", JSON.stringify(procFlight));
res.status(200).json("Record Updated");
return;
}
console.log("Record isn't newer, nothing changed.");
console.log("Record:", JSON.stringify("Same Flight:", JSON.stringify(procFlight)));
res.status(200).json("Record isn't newer, nothing done.");
return;
});
} catch (error) {
console.log("Error:", JSON.stringify(error));
res.status(500).json(error.message);
}
The Bugs
First, you are trusting the value of req.body to be of the correct shape. If you don't already have type assertions that mirror your security rules for /collection/someFlightId in processOcsFlightEvent, you should add them. This is important because any database operations from the Admin SDKs will bypass your security rules.
The next bug is sending a response to your function inside the transaction. Once you send a response back the client, your function is marked inactive - resources are severely throttled and any network requests may not complete or crash. As a transaction may be retried a handful of times if a database collision is detected, you should make sure to only respond to the client once the transaction has properly completed.
You use set to write the new flight to Firestore, this can lead to trouble when working with transactions as a set operation will cancel all pending transactions at that location. If two function instances are fighting over the same flight ID, this will lead to the problem where the wrong data can be written to the database.
In your current code, you return the result of the ocsFlightRef.set() operation to the client as the body of the HTTP 201 Created response. As the result of the DocumentReference#set() is a WriteResult object, you'll need to properly serialize it if you want to return it to the client and even then, I don't think it will be useful as you don't seem to use it for the other response types. Instead, a HTTP 201 Created response normally includes where the resource was written to as the Location header with no body, but here we'll pass the path in the body. If you start using multiple database instances, including the relevant database may also be useful.
Fixing
The correct way to achieve the desired result would be to do the entire read->check->write process inside of a transaction and only once the transaction has completed, then respond to the client.
So we can send the appropriate response to the client, we can use the return value of the transaction to pass data out of it. We'll pass the type of the change we made ("created" | "updated" | "aborted") and the recordModified value of what was stored in the database. We'll return these along with the resource's path and an appropriate message.
In the case of an error, we'll return a message to show the user as message and the error's Firebase error code (if available) or general message as the error property.
// if not using express to wrangle requests, assert the correct method
if (req.method !== "POST") {
console.log(`Denied ${req.method} request`);
res.status(405) // 405 - Method Not Allowed
.set("Allow", "POST")
.end();
return;
}
const ocsFlight = req.body;
try {
// process AND type check `ocsFlight`
const procFlight = processOcsFlightEvent(ocsFlight);
const ocsFlightRef = db.collection(collection).doc(procFlight.fltId);
const { changeType, recordModified } = await db.runTransaction(async (t) => {
const flightDoc = await t.get(ocsFlightRef);
if (!flightDoc.exists) {
t.set(ocsFlightRef, procFlight);
return {
changeType: "created",
recordModified: procFlight.recordModified
};
}
// only parse the field we need rather than everything
const storedRecordModified = flightDoc.get('recordModified');
if (storedRecordModified <= procFlight.recordModified) {
t.update(ocsFlightRef, procFlight);
return {
changeType: "updated",
recordModified: procFlight.recordModified
};
}
return {
changeType: "aborted",
recordModified: storedRecordModified
};
});
switch (changeType) {
case "updated":
console.log("Record updated: ", JSON.stringify(procFlight));
res.status(200).json({ // 200 - OK
path: ocsFlightRef.path,
message: "Updated",
recordModified,
changeType
});
return;
case "created":
console.log("Record added: ", JSON.stringify(procFlight));
res.status(201).json({ // 201 - Created
path: ocsFlightRef.path,
message: "Created",
recordModified,
changeType
});
return;
case "aborted":
console.log("Outdated record discarded: ", JSON.stringify(procFlight));
res.status(200).json({ // 200 - OK
path: ocsFlightRef.path,
message: "Record isn't newer, nothing done.",
recordModified,
changeType
});
return;
default:
throw new Error("Unexpected value for 'changeType': " + changeType);
}
} catch (error) {
console.log("Error:", JSON.stringify(error));
res.status(500) // 500 - Internal Server Error
.json({
message: "Something went wrong",
// if available, prefer a Firebase error code
error: error.code || error.message
});
}
References
Cloud Firestore Transactions
Cloud Firestore Node SDK Reference
HTTP Event Cloud Functions

switchMap only cancelling some endpoints

I have a form control input to search for results.
component.html
<input matInput [formControl]="input">
I'm using valueChanges observable to check for changes.
component.ts
this.xx = this.input.valueChanges.pipe(
switchMap(res => {
return this.someService.test(res);
}),
);
What i don't understand is that on fast typing the cancellation of the previous http request is only working on some of the endpoints.
someService.ts
public test(q: string): Observable<any> {
// api-1 cancelling
return this.http.get<any>('http://localhost:8080/api/geosuggestion?q=' + q);
// api-2 not cancelling
// return this.http.get<any>('http://localhost:8080/api/items?q=' + q, { headers: { 'auth-token': this.authService.getToken() } });
}
Similar questions always origin from the fact that they used different obsersvable chains. In my case there has to be a different reason as i'm using the exact same logic in both cases and only replace the endpoint url.
api-1 (successfully cancelling)
api-2 (not cancelling)
Why is switchMap cancellation only working on some of my endpoints?

Meteor Server Latency

I feel like I'm doing something wrong because my results seem to go against the very nature of Meteor's pitch of simulating client/sever interactions for speed. When I do any sort of database update using Meteor.call() the app has to wait for the round trip to the server, often resulting in a slow response or the user hitting the button twice. I just want to make sure I'm doing this correctly. Here's what I'm doing:
Client:
Template.shot.events({
'change #shot-status-select': function (event, template) {
var new_status = $(event.target).val();
var shot_id = Session.get('current_shot_id');
Meteor.call('setShotStatus', shot_id, new_status, function (error, result) {
if (result) {
feedbackSuccess('Status changed to <b>'+new_status+'</b>');
} else {
feedbackError('Status change failed');
console.log(error);
}
});
},
});
And Server:
...
'setShotStatus': function(shot_id, status) {
var result = Shots.update({'_id': shot_id}, {$set: {'status': status}});
if (result) {
return true;
} else {
return false;
}
},
There are a couple of things going on here that are preventing your method from being latency compensated (it's making the complete round trip to the server).
First, if you execute a Meteor.call on the client with a callback, it will always wait for the result from the server. Unfortunately, you can't just write it synchronously because a call will always return undefined on the client, and you need the returned result.
If you really want the result of the stub, you'd need to rewrite it like this:
var args = [shot_id, new_status];
var result = Meteor.apply('setShotStatus', args, {returnStubValue: true});
if (result)
feedbackSuccess('Status changed to <b>'+new_status+'</b>');
Note you should wrap the call in a try/catch if errors are likely. Also note that the client and server return values will not always match in the general case, so use this technique with that in mind.
Next, your method definition needs to be in a shared location for both the client and the server code (putting it somewhere under lib or in a package are good choices). If the client doesn't have the method code, it can't simulate it.
Recommended reading:
How to return value on Meteor.call() in client?
Introduction to Latency Compensation
The "Latency Compensation" articles at the Discover Meteor Encyclopedia
Thank you, David. Your answer got me on the right track, but I think there are a couple of nuggets in there that seemed too much to discuss in a comment. The main thing I found was this:
The challenge of getting back to the "Meteor zero-latency" promise was as simple as moving all of my "server" methods to the lib directory.
Literally, no code changes. After making the methods accessible to both client and server, Meteor did all of the heavy lifting of executing first on the client, then checking the result with the server result.
David's answer said that using a callback will always wait for a result from the server. I found that to be partly true, in that it will asynchronously wait for a result. Though depending on the accessibility of your methods, it could be a result from the client that you experience, not a round-trip from the server. Not using the callback will always return undefined, thus result will not work in the given example
Lastly, I moved truly private logic to the server only directory for security reasons.
Here's the code result:
client/shot.js
Template.shot.events({
'change #shot-status-select': function (event, template) {
var new_status = $(event.target).val();
var shot_id = Session.get('current_shot_id');
Meteor.call('setShotStatus', shot_id, new_status, function (error, result) {
if (!(result)) {
feedbackError('Status change failed');
console.log(error);
}
});
},
});
lib/methods.js
Meteor.methods({
'setShotStatus': function(shot_id, status) {
var result = Shots.update({'_id': shot_id}, {$set: {'status': status}});
if (result) {
return true;
} else {
return false;
}
},
});

Dealing with context of server responses in realtime web applications

Finding it hard to describe this issue - so please edit if you know more relevant terms.
I'm building a web application which essentially uses Redis (PubSub) + Node.js + Socket.IO as a distribution server.
I have two-way communication working with no issues - but I need to be able to make a request to the server from the client (asynchronously) and deal with the response while still processing other irrelevant responses that might come in before it.
This is what I have so far, but I'm not particularly happy with this approach:
Server
// Lots of other code
redis.psubscribe('*');
redis.on("pmessage", function(pattern, channel, message) {
// broadcast
});
io.on('connection', function(client) {
client.on('message', function(message) {
switch(message.method) {
// call relevant function
}
});
});
function object_exists(object_id) {
// do stuff to check object exists
client.send({method: 'object_exists', value: object_exists});
}
Client
var call = Array();
$(document).ready(function() {
socket.connect();
socket.on("message", function(obj){
console.log(obj);
call[obj.method](obj.value);
});
});
function object_exists(object_id) {
socket.send({method: 'object_exists', value: object_id});
// Set a function to be called when the next server message with the 'object_exists' method is received.
call['object_exists'] = function(value) {
if(value) {
// object does exist
}
}
}
tl;dr: I need to 'ask' the server something and then deal with the response using Socket.IO.
You don't specifically say why you are unhappy with your approach, but it looks to me like you are almost there. I am not really sure what you are trying to do with the call array, so I just took it out for clarity.
Basically, you just need to set up a switch statement to act as a message router on each side of the socket connection and fire off the appropriate methods based in incoming messages. Send enough state with the message itself so you can handle the work without any additional context. In your reworked code, I send the object_id to the server and back again to the client.
///SERVER
// Lots of other code
redis.psubscribe('*');
redis.on("pmessage", function(pattern, channel, message) {
// broadcast
});
io.on('connection', function(client) {
client.on('message', function(message) {
switch(message.method) {
case 'object_exists':
object_exists(message.objectId);
break;
}
});
});
//Takes an id an returns true if the object exists
function object_exists(object_id) {
// do stuff to check object exists
client.send({method: 'object_exists', objectId: object_id, value: object_exists});
}
///CLIENT
$(document).ready(function() {
//setup the message event handler for any messages coming back from the server
//This won't fire right away
socket.on("message", function(message){
switch(message.method) {
case 'object_exists':
object_exists(message.objectId, message.value);
break;
}
});
//When we connect, send the server the message asking if object_exists
socket.on("connect", function() {
socket.send({method: 'object_exists', objectId: object_id});
});
//Initiate the connection
socket.connect();
});
//Get's called with with objectId and a true if it exists, false if it does not
function object_exists(objectId, value) {
if(value) {
// object does exist, do something with objectId
}
else {
// object does not exist
}
}
If you want to see a bunch more code in the same stack doing work similar to what you are trying to accomplish, check out my nodechat.js project.

Resources