I have defined an array Account[] family in my model.cto file and I want to access it from my logic.js. In particular I want to perform the transaction only if the receiver is in the family array of the sender.
My model.cto:
namespace org.digitalpayment
asset Account identified by accountId {
o String accountId
--> Customer owner
o Double balance
}
participant Customer identified by customerId {
o String customerId
o String firstname
o String lastname
--> Account[] family optional
}
transaction AccountTransfer {
--> Account from
--> Account to
o Double amount
}
My logic.js:
/**
* Account transaction
* #param {org.digitalpayment.AccountTransfer} accountTransfer
* #transaction
*/
async function accountTransfer(accountTransfer) {
if (accountTransfer.from.balance < accountTransfer.amount) {
throw new Error("Insufficient funds");
}
if (/*TODO check if the family array contains the receiver account*/) {
// perform transaction
accountTransfer.from.balance -= accountTransfer.amount;
accountTransfer.to.balance += accountTransfer.amount;
let assetRegistry = await getAssetRegistry('org.digitalpayment.Account');
await assetRegistry.update(accountTransfer.from);
await assetRegistry.update(accountTransfer.to);
} else {
throw new Error("Receiver is not part of the family");
}
}
Ok so basically you want to first get all the accounts of a Family asset, and then check if the Customer participant is included in it? Correct me if I am wrong.
A logical set of steps would be -
Retrieve the Account based on the to and from inputs
Retrieve each Customer for each Account using the owner variable
Get the family variable from each Customer
/**
* Account transaction
* #param {org.digitalpayment.AccountTransfer} accountTransfer
* #transaction
*/
async function accountTransfer(accountTransfer) {
if (accountTransfer.from.balance < accountTransfer.amount) {
throw new Error("Insufficient funds");
};
var from = accountTransfer.from;
var to = accountTransfer.to;
var fromCustomer = from.owner;
var toCustomer = to.owner;
var fromCustomerFamily = fromCustomer.family;
if (fromCustomerFamily && fromCustomerFamily.includes(to)) {
// perform transaction
accountTransfer.from.balance -= accountTransfer.amount;
accountTransfer.to.balance += accountTransfer.amount;
let assetRegistry = await getAssetRegistry('org.digitalpayment.Account');
await assetRegistry.update(accountTransfer.from);
await assetRegistry.update(accountTransfer.to);
} else {
throw new Error("Receiver is not part of the family");
}
}
Due to the syntax changes in the last few Composer versions may not work depending on the version you use in your project. If this does not work and you are using an older version, let me know and I will update the answer accordingly.
Related
What I am trying to create is a script in node.js that will be added in a server of a web page that we are building. I want the script to do the following: In the first of every month to count the users inside my database and then create a set of tokens based on the amount of users logged in that current date. Specifically: 0,8 * numberOfUsers * 100. Then in the last day of every month count the users again and refund the tokens that were generated in the first of the month back to all the users (old and new). I also want to try that this runs automatically every time the server goes live and not have to execute it every time.
I am using the cron.schedule method and to check if it works I run for every minute the block that counts the first users(the one that is supposed to be in the first of the month) and every two minutes the block that gets again the new number of users and refunds the Tokens (the one that is supposed to be in the last of the month) . Here is my code:
const { Pool } = require('pg');
const cron = require('node-cron');
// Connection configuration
const config = {
user: '',
password: '',
host: '',
database: ''
};
// Create a new connection pool
const pool = new Pool(config);
async function countUsers() {
// Get number of users
const query = `SELECT COUNT(*) as count FROM users`;
const res = await pool.query(query);
const numUsers = +res.rows[0].count;
console.log(numUsers);
return numUsers;
}
async function refundTokens(tokensToRefund,numUsers) {
console.log("I am here");
console.log(numUsers);
if (numUsers !== 0 && !isNaN(tokensToRefund)) {
// Calculate amount of tokens to give to each user
const tokensPerUser = Math.round(tokensToRefund / numUsers);
// Update tokens for all users
const query = `UPDATE users SET tokens = tokens + $1`;
await pool.query(query, [tokensPerUser]);
}
}
cron.schedule('* * * * *', async () => {
const numUsers = await countUsers();
// Calculate number of tokens to refund (80% of tokens)
const tokensToRefund = 0.8 * numUsers * 100;
// Store number of tokens to refund
localStorage.setItem('tokensToRefund', tokensToRefund);
});
cron.schedule('*/2 * * * *', async () => {
// Get number of tokens to refund from local storage
const tokensToRefund = localStorage.getItem('tokensToRefund');
// Get number of users
const numUsers = await countUsers();
refundTokens(tokensToRefund, numUsers);
});
The first block seems to be running correctly every minute but the second one never does run. I see it because the console.log i have inside the refundTokens() function are not given. What seems to be the problem?
Your problem might be that there's no localStorage in NodeJS, as Marc said in comments. Try saving your tokens in some tmp file using Node's.
const fs = require('fs')
const numUsers = await countUsers();
// Calculate number of tokens to refund (80% of tokens)
const tokensToRefund = 0.8 * numUsers * 100;
// Store number of tokens to refund
await fs.writeFile('tmp', tokensToRefund);
// Get number of tokens to refund from local storage
const tokensFromFile = await fs.readFile('tmp');
const tokensToRefund = parseInt(tokensFromFile);
// Get number of users
const numUsers = await countUsers();
refundTokens(tokensToRefund, numUsers);
I would also write some separate helper function for this and add an Error handling, because both write and read functions could throw an error
P.S.: I would also recommend not to schedule your cronjobs in code for those small tasks, but to set them directly in console or in your backend panel. It would save you a huge amount of time and it will be much easier to configure them later. For your case, I consider it will be better. Just write your code as normal and add a cronjob to execute your js whenever you want
I wrote this code for a Cloudflare worker.
When a new customer is created on Stripe, a webhook is triggered and returns the new customer data to [...].worker.dev/updateCustomers
The Cloudflare worker will then do the following:
Get the name from the customer JSON object
create a version without the " " around the string
add the new customer to an array called customers[]
for debugging reasons it will response back to Stripe with the following content: New customer name, all customers in the array and the result if the new customer name is contained in the array
If a user opens a connection to "https://[...]workers.dev/validate?licence=eee" it will return "legit" if the name is in the customer array and "failed" if it is not.
Through Stripe I can see the following response from my worker when a webhook is fired: "Customers received: eee Other Customers: demo,demo2,demo3,eee customers.includes(key): true"
That means that the new customer was successfully added to the array and that my code is able to check if a customer is included in the array.
But when a user tries to validate their name directly, only the array contents that are defined in the beginning like "demo", "demo2" get a positive response.
Can anyone help me fix this? Thanks a lot in advance.
addEventListener("fetch", (event) => {
event.respondWith(
handleRequest(event.request).catch(
(err) => new Response(err.stack, { status: 500 })
)
);
});
customers = ["demo", "demo2","demo3"];
/**
* #param {Request} request
* #returns {Promise<Response>}
*/
async function handleRequest(request) {
const { pathname } = new URL(request.url);
if (pathname.startsWith("/api")) {
return new Response(JSON.stringify({ pathname }), {
headers: { "Content-Type": "application/json" },
});
}
if (pathname.startsWith("/validate")) {
let params = (new URL(request.url)).searchParams;
let key = params.get('licence');
console.log(key);
if(customers.includes(key)){
return new Response("legit");
}
else {
return new Response("failed");
}
}
if (pathname.startsWith("/updateCustomers")) {
let clonedBody = await request.clone().json();
let newCustomerName = JSON.stringify(clonedBody.data.object.name);
let newCustomerNameRaw = newCustomerName.substring(1, newCustomerName.length-1);
customers.push(newCustomerNameRaw);
return new Response("Customers recievedd: " + newCustomerNameRaw + " Other Customers: " + customers.toString() + " customers.includes(key): " + customers.includes(newCustomerNameRaw) );
}
//fallback **strong text**not relevant
if (pathname.startsWith("/status")) {
const httpStatusCode = Number(pathname.split("/")[2]);
return Number.isInteger(httpStatusCode)
? fetch("https://http.cat/" + httpStatusCode)
: new Response("That's not a valid HTTP status code.");
}
return fetch("https://welcome.developers.workers.dev");
}
That is because the customers "demo", "demo2" and "demo3" are stored in the webworker as part of the code, so they are present anytime the call is made. The new customer is stored just temporarily in the array whilst the code runs but once the execution ends the customers array content is reset. You will need to use some kind of backend to store the customers if you want to persist the new ones between runs
I am trying to reproduce the smart contract process of Ethereum by using NodeJS and the VM.
I have resolved some questions but it also raised a new one.
My goal would be to retrieve the bytecode that I load and execute in a NodeJS VM instance after it's execution;
The initial state would be the smart contract code that I load as bytecode in VM.
I also make two call that are in bytecode. and executed.
How could I retrieve the new state in bytecode from that VM instance?
const vm = require('vm');
const v8 = require('v8');
v8.setFlagsFromString('--no-lazy');
if (Number.parseInt(process.versions.node.split('.')[0], 10) >= 12) {
v8.setFlagsFromString('--no-flush-bytecode'); // Thanks to A-Parser (#a-parser)
}
/**
* Generates v8 bytecode buffer.
* could be the compile for smart contract to compile the bytecode
* the bytecode will go on the blockchain so it can never change.
* The input could be a file with all the JavaScript
* #param {string} javascriptCode JavaScript source that will represent a smart contract on the blockchain
* #returns {Buffer} The generated bytecode.
*/
const compileCode = function (javascriptCode) {
if (typeof javascriptCode !== 'string') {
throw new Error(`javascriptCode must be string. ${typeof javascriptCode} was given.`)
}
let script = new vm.Script(javascriptCode)
let bytecodeBuffer = script.createCachedData()
return bytecodeBuffer;
}
/**
* Runs v8 bytecode buffer and returns the result.
* #param {Buffer} bytecodeBuffer The buffer object that was created using compileCode function.
* #returns {any} The result of the very last statement executed in the script.
*/
const runBytecode = function (bytecodeBuffer) {
if (!Buffer.isBuffer(bytecodeBuffer)) {
throw new Error(`bytecodeBuffer must be a buffer object.`);
}
// fixBytecode(bytecodeBuffer);
let length = readSourceHash(bytecodeBuffer);
let dummyCode = "";
if (length > 1) {
dummyCode = '"' + "\u200b".repeat(length - 2) + '"'; // "\u200b" Zero width space
}
let script = new vm.Script(dummyCode, {
cachedData: bytecodeBuffer
});
if (script.cachedDataRejected) {
throw new Error('Invalid or incompatible cached data (cachedDataRejected)');
}
//console.log(bytecodeBuffer)
return script.runInThisContext();
};
// TODO: rewrite this function
const readSourceHash = function (bytecodeBuffer) {
if (!Buffer.isBuffer(bytecodeBuffer)) {
throw new Error(`bytecodeBuffer must be a buffer object.`)
}
return bytecodeBuffer.slice(8, 12).reduce((sum, number, power) => sum += number * Math.pow(256, power), 0)
}
/*
This is an example of a smart contract in javascript.
It would get stored on the blockchain as bytecode.
It could be compiled from a file to.
*/
let smartContract = `
function setName(_name){
name = _name
console.log('name set')
}
function getName(){
console.log(name)
}
`
// we compile the smart contract into bytecode
// the contract will be loaded in the VM
let bytecode = compileCode(smartContract)
runBytecode(bytecode)
/*
this call will set the name in the Contract that reside in the VM
The name is set but not wrtien anywhere yet. It is only set on this
instance fo the VM. This change the state an we would need to recompile
the contract with the new data and update the blockchain.
*/
let call = "setName('Satoshi')"
let callBytecode = compileCode(call)
runBytecode(callBytecode)
/*
Execute the call in the contract in the VM
the function is just printing out the value that we pass to our javascript contract
the state of the contract did not change
*/
let rCall = "getName()"
let rcallBytecode = compileCode(rCall)
runBytecode(rcallBytecode)
//console.log(bytecode)
How can I retrieve the bytecode that was loaded in the VM and save it with the name remaining there. Imagine an array of name and every time someone load and call that function it adds the name to it. Then save the new bytecode with that extra name in it. Save it and pass it to the next VM that want to run it.
I've been trying for a project I'm working on to develop a function for a Food chatbot. What I'm currently working on is to perform a method for a user to make a purchase of an order that is stored in firebase realtime database.
The method is set as the method for an actionMap and the actionMap is linked to an intent for knowing when to call the method and for retrieving the parameters.
My current method uses a simple check for a user's existence and status within the database before identifying the existence of the order they're trying to make a purchase for by its id by going through the user's reference path and doing a .forEach to check every order found and look at its parent folder name to check if it matches the user's order id. My code is as follows:
const MakePurchaseACTION = 'Make Purchase';
function makePurchase(app){
let email = parameter.email;
let orderId = parameter.orderId;
var currDate = currDateGenerator();
var name = email.split(".com");
//Check if User exists first in database
var userRef = database.ref().child('user/' + name);
return userRef.once('value').then(function(snapshot) {
if (snapshot.exists()) {
let statusRetrieved = snapshot.child('Status').val();
//Check if user's status in database is signed in.
if (statusRetrieved == "Signed In") {
var orderRef = database.ref().child('order/' + name);
//Check the order table for the user.
return orderRef.once('value').then(function(orderSnapshot){
let orderVal = orderSnapshot.val();
console.log(orderVal);
//Check through every child for the matching id.
orderSnapshot.forEach(function(childSnapshot) {
let orderIdFound = childSnapshot.key;
//let cost = childSnapshot.child('Cost').val();
console.log(orderIdFound);
if(orderId == orderIdFound) {
let eateryName = childSnapshot.child('Eatery').val();
let eateryLocation = childSnapshot.child('EateryLocation').val();
let deliveryAddress = childSnapshot.child('DeliveryAddress').val();
let orderItem = childSnapshot.child('OrderItem').val();
let quantity = childSnapshot.child('Quantity').val();
let cost = childSnapshot.child('Cost').val();
var purchaseRef = database.ref().child('purchase/' + name + "/" + currDate + "/" + orderId);
purchaseRef.set({
"Eatery" : eateryName,
"EateryLocation" : eateryLocation,
"DeliveryAddress": deliveryAddress,
"OrderItem" : orderItem,
"Quantity": quantity,
"Cost": cost,
"DateCreated": currDate
});
app.add("You have successfully purchased Order " + orderId);
} else {
app.add("There is no order with that id.");
}
});
});
} else {
app.add("You need to be signed in before you can order!");
}
}
else {
app.add("Sorry pal you don't exist in the database.");
}
});
}
actionMap.set(MakePurchaseACTION, makePurchase);
After checking through some firebase logs
Firebase Logs screenshot here
Firebase Realtime Database Order Table Sample
I found that the method actually completes Purchase table sample but my dialogflow returns with the stated error of:
Error: No responses defined for platform: undefined and displays "Not Available" back to the user. My question is how do I go about resolving this error?
I am learning Node JS and I am learning how to create OOP structure in node. I have a simple class where I am using functions to check and create user in database. I am getting TypeError when I am calling class function inside a function. My codes of class,
var method = AccessRegisterAction.prototype;
function AccessRegisterAction(parameters) {
this._bcrypt = require('bcrypt-nodejs');
this._cassandra = require('cassandra-driver');
this._async = require('async');
this._uname = parameters.uname;
this._email = parameters.email;
this._passwd = parameters.passwd;
//Connect to the cluster
this._client = new this._cassandra.Client({contactPoints: ['127.0.0.1'], keyspace: 'testdb'});
//this._bcrypt.hashSync("bacon");
}
/*
* This method "createUser" is used to create new users
*
* First it will check for user's email is in database or not.
*
* If email is registered we will process accordingly,
* else we will process accordingly.
*
* */
method.createUser = function () {
var selectUserFromDBQuery = "SELECT uid from testdb.users WHERE email = '?' ALLOW FILTERING";
this._client.execute(selectUserFromDBQuery, this._email, function (err, result) {
//if there is not any error while fetching data
if (!err) {
//if there is result, it means we have email already registered
if (result.rows.length > 0) {
//so now we need to show user is already registered error
//to the user
this.errorWhileCreatingAccount(2);
} else {
//here we are checking user's username because
//we have not found user's email in database and we
//are good to go to register user
this.userNameCheck();
}
}
else {
this.errorWhileCreatingAccount(1);
}
});
};
/*
* This method will check for username in database.
*
* If there is username registered in database than we will
* show error that username is taken otherwise
* we will process to register user.
* */
method.userNameCheck = function () {
var checkUserNameQuery = "SELECT uid from testdb.users WHERE uname = '?' ALLOW FILTERING";
this._client.execute(checkUserNameQuery, this._uname, function (err, result) {
//if there is not any error while fetching data
if (!err) {
//if there is result, it means we have email already registered
if (result.rows.length > 0) {
//so username is taken and we need to tell user to
//use different username
this.errorWhileCreatingAccount(3);
} else {
//here we are registering user and adding information into database
this.newUserCreate();
}
}
else {
this.errorWhileCreatingAccount(1);
}
});
};
/*
* This method will create new user into database
*
* Simple as that
* */
method.newUserCreate = function () {
};
/*
* This function will throw an error which was occurred during the account creation process
* */
method.errorWhileCreatingAccount = function (errorCode) {
var _error = {error: "", msg: ""};
switch (errorCode) {
case 1:
_error.error = true;
_error.msg = "There was error while checking your information. Please try again.";
break;
case 2:
_error.error = true;
_error.msg = "You have already created account with this email. " +
"Please use forgot password link if you don't remember your login details.";
break;
case 3:
_error.error = true;
_error.msg = "Username that you chose is already taken. Please try different username.";
break;
default:
_error.error = true;
_error.msg = "There was error an error. Please try again.";
break
}
};
// export the class
module.exports = AccessRegisterAction;
And the error message I am getting is,
events.js:160
throw er; // Unhandled 'error' event
^
TypeError: this.userNameCheck is not a function
at /Users/user/Desktop/Projects/nodejs/testproject/application/classes/UserAccessAction/AccessRegisterAction.js:55:22
at next (/Users/user/node_modules/cassandra-driver/lib/utils.js:616:14)
at readCallback (/Users/user/node_modules/cassandra-driver/lib/request-handler.js:202:5)
at Connection.invokeCallback (/Users/user/node_modules/cassandra-driver/lib/connection.js:584:5)
at Connection.handleResult (/Users/user/node_modules/cassandra-driver/lib/connection.js:522:8)
at emitThree (events.js:116:13)
at ResultEmitter.emit (events.js:194:7)
at ResultEmitter.each (/Users/user/node_modules/cassandra-driver/lib/streams.js:482:17)
at ResultEmitter._write (/Users/user/node_modules/cassandra-driver/lib/streams.js:466:10)
at doWrite (_stream_writable.js:307:12)
and my codes where I calling this function is,
var param = {uname: "testname", email: "fake#test.com", passwd: "test123"};
var AccessRegisterAction = require('../application/classes/UserAccessAction/AccessRegisterAction');
var register = new AccessRegisterAction(param);
register.createUser();
so can anyone tell me where my mistake is? How can I use oop with nodejs?
Thanks
Since it looks like you're using ES5 style, I'm not going to recommend arrow functions. However, as was brought out in the comments, the value of this changes inside each function statement. So, you need to save the context you'd like to use before entering the inner functions. For example:
method.createUser = function () {
var context = this;
var selectUserFromDBQuery = "SELECT uid from testdb.users WHERE email = '?' ALLOW FILTERING";
this._client.execute(selectUserFromDBQuery, this._email, function (err, result) {
//if there is not any error while fetching data
if (!err) {
//if there is result, it means we have email already registered
if (result.rows.length > 0) {
//so now we need to show user is already registered error
//to the user
context.errorWhileCreatingAccount(2);
} else {
//here we are checking user's username because
//we have not found user's email in database and we
//are good to go to register user
context.userNameCheck();
}
}
else {
context.errorWhileCreatingAccount(1);
}
});
};
Do this in each method which attempts to call this from within a new function.
You should be able to use prototypes easily enough in Node
You need to use an object first eg new AccessRegisterAction(), you'd be able to use the methods as usual.
It's hard to be sure as you've not posted the code you are using to access the method.