diff --git a/tests/enclave/accounting/AccountingEnclave/AccountSVD.js b/tests/enclave/accounting/AccountingEnclave/AccountSVD.js new file mode 100644 index 0000000..1a3d5a3 --- /dev/null +++ b/tests/enclave/accounting/AccountingEnclave/AccountSVD.js @@ -0,0 +1,79 @@ +/* The concept of a modeled account allows for the depositing of funds into the account, +the withdrawal of money from the account, as well as the structuring of loans between accounts with a fixed daily interest rate. + However, it is set up in such a way that withdrawals are not possible as long as the account has outstanding debts. */ +module.exports = { + ctor: function(usedID){ + this.usedID = usedID; + this.balance = 0; + this.loans = []; + + }, + actions:{ + deposit: function(amount){ + this.balance += amount; + }, + withdraw: function(amount){ + if(this.balance - this.loanedAmount >= amount){ + this.balance -= amount; + } else { + throw new Error("Assert failed: balance should be greater than or equal to amount"); + } + }, + loan: function(amount, dailyInterestRate, loanerId){ + if(this.balance >= amount){ + let loaner = this.session.lookup(loanerId); + if(loaner.loanedAmount == undefined) { + loaner.loanedAmount = amount; + loaner.dailyInterestRate = dailyInterestRate; + loaner.lenderId = this.usedID; + loaner.lendedTimestamp = this.session.now(); + this.loans.push(loanerId); + this.balance -= amount; + loaner.balance += amount; + } else { + throw new Error("Assert failed: loaner should not have an outstanding loan"); + } + } else { + throw new Error("Assert failed: balance should be greater than or equal to amount"); + } + }, + payLoan: function(amount){ + let lender = this.session.lookup(this.lenderId); + /* compute interest using the number of days and then add interest for each passed day for the amount that is returned*/ + let now = this.session.now(); + let days = Math.floor((now - this.lendedTimestamp) / (1000 * 60 * 60 * 24)); + let interest = days * this.dailyInterestRate * this.loanedAmount; + + if(lender != undefined){ + if(amount < this.balance){ + this.balance -= amount; + lender.balance += amount; + this.loanedAmount -= amount; + this.loanedAmount += interest; + } + if(this.loanedAmount < 0){ + lender.balance += this.loanedAmount; /* get back money that were wrongfully returned in the previous step*/ + this.balance -= this.loanedAmount; + this.loanedAmount = 0; + } + /* if the loaner has no more money to give, it gets removed from the list of loans of the lender*/ + if(this.loanedAmount == 0){ + this.lenderId = undefined; + this.dailyInterestRate = undefined; + this.lendedTimestamp = undefined; + let index = lender.loans.indexOf(this.usedID); + if(index > -1){ + lender.loans.splice(index, 1); + } + } + } + } + }, + getTotalDebt: function(){ + let interest = 0; + let now = this.session.now(); + let days = Math.floor((now - this.lendedTimestamp) / (1000 * 60 * 60 * 24)); + interest = days * this.dailyInterestRate * this.loanedAmount; + return this.loanedAmount + interest; + } +} \ No newline at end of file diff --git a/tests/enclave/accounting/AccountingEnclave/UserProfileSVD.js b/tests/enclave/accounting/AccountingEnclave/UserProfileSVD.js new file mode 100644 index 0000000..bce51a5 --- /dev/null +++ b/tests/enclave/accounting/AccountingEnclave/UserProfileSVD.js @@ -0,0 +1,62 @@ +module.exports = { + ctor: function (userId, name, email, phone, publicDescription, secretToken, userDID, isPrivate) { + this.userId = userId; + this.name = name; + this.email = email; + this.phone = phone; + this.publicDescription=publicDescription; + this.isPrivate = isPrivate; + this.availableInvitesCounter = 0; + this.acceptedInvites = []; + this.followedBrands= []; + this.secretToken=secretToken; + this.userDID = userDID; + /* the email and phone are private by default */ + this.isPrivate = true; + //this.secretToken=secretToken; + this.friends = []; + }, + actions: { + update: function (userId, name, email, phone, publicDescription, secretToken, userDID, isPrivate) { + this.name = name; + this.email = email; + this.userId = userId; + this.phone = phone; + this.publicDescription=publicDescription; + this.secretToken=secretToken; + this.userDID = userDID; + this.isPrivate = isPrivate; + }, + setTemporarySecretToken(temporarySecretToken){ + this.temporarySecretToken=temporarySecretToken; + }, + registeredAsValidatedUser: function () { + this.availableInvitesCounter += 10; + }, + addInvites: function (invites) { + this.availableInvitesCounter += invites; + }, + inviteAccepted: function (inviteId) { + this.availableInvitesCounter--; + this.acceptedInvites.push(inviteId); + this.friends.push(inviteId); + }, + addFriend: function (friendId) { + this.friends.push(friendId); + }, + addBrandToFollowed(brandId) + { + this.followedBrands.push(brandId); + }, + removeBrandFromFollowed(brandId){ + const index = this.followedBrands.indexOf(brandId); + if(index>-1) + { + this.followedBrands.splice(index, 1); + } + else { + console.error(`Error: not found; Failed to remove brandId ${brandId} from user's followedBrands`); + } + } + } +} \ No newline at end of file diff --git a/tests/enclave/accounting/AccountingEnclave/lambdas/index.js b/tests/enclave/accounting/AccountingEnclave/lambdas/index.js new file mode 100644 index 0000000..8bf3458 --- /dev/null +++ b/tests/enclave/accounting/AccountingEnclave/lambdas/index.js @@ -0,0 +1,140 @@ +const UserProfileSVD = require("../UserProfileSVD"); +const {userId} = require("../UserProfileSVD"); +let fsSVDStorage; +function initSVD(remoteEnclaveServer) { + const fastSVD = require("opendsu").loadApi("svd"); + fsSVDStorage = fastSVD.createFsSVDStorage("./SVDS"); + fsSVDStorage.registerType("user", UserProfileSVD); +} +function helloWorld (...args) { + const callback = args.pop(); + callback(undefined, args); +} + +function helloWorldWithAudit (...args) { + const callback = args.pop(); + this.audit(...args); + callback(undefined, args); +} +function updateUser(userId, name, email, phone, publicDescription, secretToken, userDID, isPrivate, callback){ + + fsSVDStorage.createTransaction(function (err, transaction){ + transaction.lookup(userId, (err, userSVD) => { + if(err){ + return callback(err); + } + userSVD.update(email, name, email, phone, publicDescription, secretToken, userDID, isPrivate); + transaction.commit((commitError) => { + if(commitError){ + return callback(commitError); + } + callback({userId:userId, secretToken:secretToken}); + }); + + }); + }); +} +function addNewUser(name, email, phone, publicDescription, secretToken, userDID, isPrivate, callback){ + const self = this; + const crypto = require("opendsu").loadApi("crypto"); + const userSvdUid = "svd:user:" + crypto.getRandomSecret(32); + fsSVDStorage.createTransaction(function (err, transaction){ + let user = transaction.create(userSvdUid, userSvdUid, name, email, phone, publicDescription, secretToken, userDID, isPrivate); + self.insertRecord("", "users", userSvdUid, { email: email }, (err) => { + if (err) { + return callback(err); + } + transaction.commit((commitError) => { + if(commitError){ + return callback(commitError); + } + callback({userId:userSvdUid, secretToken:secretToken}); + }) + }); + }); +} +function getUserSVD(userId,callback){ + + fsSVDStorage.createTransaction(function (err, transaction){ + transaction.lookup(userId, (err, userSVD) => { + if(err){ + return callback(err); + } + transaction.commit((commitError) => { + if(commitError){ + return callback(commitError); + } + + + }); + callback(userSVD.getState()); + }); + + }); +} +function getUserIdByEmail(email, callback){ + const self = this; + self.getAllRecords("", "users", (err, users)=>{ + if(err){ + return callback(err); + } + users.forEach((user)=>{ + if(user.email===email){ + return callback({userId:user.pk}); + } + + }); + return callback(`No userId found for email: ${email}`); + }); +} +function addBrandToFollowed(brandId, userId, callback) { + + fsSVDStorage.createTransaction(function (err, transaction){ + transaction.lookup(userId, (err, userSVD) => { + if(err){ + return callback(err); + } + userSVD.addBrandToFollowed(brandId); + transaction.commit((commitError) => { + if(commitError){ + return callback(commitError); + } + callback({brandId:brandId}); + }); + + }); + }); +} + +function removeBrandFromFollowed(brandId, userId, callback){ + + fsSVDStorage.createTransaction(function (err, transaction){ + transaction.lookup(userId, (err, userSVD) => { + if(err){ + return callback(err); + } + userSVD.removeBrandFromFollowed(brandId); + transaction.commit((commitError) => { + if(commitError){ + return callback(commitError); + } + callback({brandId:brandId}); + }); + + }); + }); +} + +module.exports = { + registerLambdas: async (remoteEnclaveServer) => { + initSVD(remoteEnclaveServer); + remoteEnclaveServer.addEnclaveMethod("helloWorld", helloWorld, "read"); + remoteEnclaveServer.addEnclaveMethod("helloWorldWithAudit", helloWorldWithAudit, "read"); + remoteEnclaveServer.addEnclaveMethod("addNewUser", addNewUser, "write"); + remoteEnclaveServer.addEnclaveMethod("updateUser", updateUser, "write"); + remoteEnclaveServer.addEnclaveMethod("getUserIdByEmail", getUserIdByEmail, "read"); + remoteEnclaveServer.addEnclaveMethod("addBrandToFollowed", addBrandToFollowed, "write"); + remoteEnclaveServer.addEnclaveMethod("removeBrandFromFollowed", removeBrandFromFollowed, "write"); + remoteEnclaveServer.addEnclaveMethod("getUserSVD", getUserSVD, "read"); + } +} \ No newline at end of file diff --git a/tests/enclave/accounting/RemoteEnclaveTest.js b/tests/enclave/accounting/RemoteEnclaveTest.js new file mode 100644 index 0000000..6758b76 --- /dev/null +++ b/tests/enclave/accounting/RemoteEnclaveTest.js @@ -0,0 +1,79 @@ +require("../../../../../builds/output/testsRuntime"); +const tir = require("../../../../../psknode/tests/util/tir"); + +const dc = require("double-check"); +const assert = dc.assert; +const openDSU = require('../../../index'); +$$.__registerModule("opendsu", openDSU); +const enclaveAPI = openDSU.loadAPI("enclave"); +const scAPI = openDSU.loadAPI("sc"); +const w3cDID = openDSU.loadAPI("w3cdid"); +const keySSI = openDSU.loadAPI("keyssi"); + +assert.callback('Remote enclave test', (testFinished) => { + dc.createTestFolder('createDSU', async (err, folder) => { + const vaultDomainConfig = { + "anchoring": { + "type": "FS", + "option": {} + }, + "enable": ["enclave", "mq"] + } + + const domain = "mqtestdomain"; + process.env.CLOUD_ENCLAVE_SECRET = "some secret"; + await tir.launchConfigurableApiHubTestNodeAsync({domains: [{name: domain, config: vaultDomainConfig}], rootFolder: folder}); + const serverDID = await tir.launchConfigurableCloudEnclaveTestNodeAsync({ + rootFolder: folder, + domain, + secret: process.env.CLOUD_ENCLAVE_SECRET, + lambdas: "./AccountingEnclave/lambdas", + name: "cloud-enclave" + }); + + const runAssertions = async () => { + try { + const clientDIDDocument = await $$.promisify(w3cDID.createIdentity)("ssi:name", domain, "client"); + const cloudEnclave = enclaveAPI.initialiseRemoteEnclave(clientDIDDocument.getIdentifier(), serverDID); + const TABLE = "users"; + cloudEnclave.on("initialised", async () => { + try { + const userId = await $$.promisify(cloudEnclave.callLambda)("addNewUser", "", "", "", "", "", "", ""); + const user = await $$.promisify(cloudEnclave.getRecord)("", TABLE, userId.userId); + assert.equal(userId.userId,user.pk,"user input values differ from user output values"); + + const password = "12345678"; + const crypto = require("opendsu").loadApi("crypto"); + const randomNr = crypto.generateRandom(32); + const secretToken = crypto.encrypt(randomNr,crypto.deriveEncryptionKey(password)); + + const didDocument = await $$.promisify(w3cDID.createIdentity)("key", undefined, randomNr); + const dataToSign = "someData"; + const signature = await $$.promisify(didDocument.sign)(dataToSign); + const verificationResult = await $$.promisify(didDocument.verify)(dataToSign, signature); + assert.true(verificationResult, "Failed to verify signature"); + + + + const userId2 = await $$.promisify(cloudEnclave.callLambda)("updateUser", userId.userId, "name1", "email1", "phone1", "publicDescription1", secretToken, "userDID", ""); + const user2 = await $$.promisify(cloudEnclave.getRecord)("", TABLE, userId2.userId); + assert.equal(userId2.userId,user2.pk,"user input values differ from user output values"); + console.log(user2); + testFinished(); + } catch (e) { + return console.log(e); + } + + }); + + } catch (e) { + return console.log(e); + } + } + const sc = scAPI.getSecurityContext(); + if (sc.isInitialised()) { + return runAssertions(); + } + sc.on("initialised", runAssertions); + }); +}, 200000);