Facebook
Twitter
Linkedin
Home
Blockchain
Expertise
Company
SmartContracts
Employee Login
Home
SmartContracts
SmartContracts
Smart Contract For Currency Transactions
const R = require('ramda'); const CryptoUtil = require('../util/cryptoUtil'); const CryptoEdDSAUtil = require('../util/cryptoEdDSAUtil'); const TransactionAssertionError = require('./transactionAssertionError'); const Config = require('../config'); /* Transaction structure: { // Transaction "id": "84286bba8d...7477efdae1", // random id (64 bytes) "hash": "f697d4ae63...c1e85f0ac3", // hash taken from the contents of the transaction: sha256 (id + data) (64 bytes) "type": "regular", // transaction type (regular, fee, reward) "data": { "inputs": [ // Transaction inputs { "transaction": "9e765ad30c...e908b32f0c", // transaction hash taken from a previous unspent transaction output (64 bytes) "index": "0", // index of the transaction taken from a previous unspent transaction output "amount": 5000000000, // amount of satoshis "address": "dda3ce5aa5...b409bf3fdc", // from address (64 bytes) "signature": "27d911cac0...6486adbf05" // transaction input hash: sha256 (transaction + index + amount + address) signed with owner address's secret key (128 bytes) } ], "outputs": [ // Transaction outputs { "amount": 10000, // amount of satoshis "address": "4f8293356d...b53e8c5b25" // to address (64 bytes) }, { "amount": 4999989999, // amount of satoshis "address": "dda3ce5aa5...b409bf3fdc" // change address (64 bytes) } ] } } */ class Transaction { construct() { this.id = null; this.hash = null; this.type = null; this.data = { inputs: [], outputs: [] }; } toHash() { // INFO: There are different implementations of the hash algorithm, for example: https://en.bitcoin.it/wiki/Hashcash return CryptoUtil.hash(this.id + this.type + JSON.stringify(this.data)); } check() { // Check if the transaction hash is correct let isTransactionHashValid = this.hash == this.toHash(); if (!isTransactionHashValid) { console.error(`Invalid transaction hash '${this.hash}'`); throw new TransactionAssertionError(`Invalid transaction hash '${this.hash}'`, this); } // Check if the signature of all input transactions are correct (transaction data is signed by the public key of the address) R.map((txInput) => { let txInputHash = CryptoUtil.hash({ transaction: txInput.transaction, index: txInput.index, address: txInput.address }); let isValidSignature = CryptoEdDSAUtil.verifySignature(txInput.address, txInput.signature, txInputHash); if (!isValidSignature) { console.error(`Invalid transaction input signature '${JSON.stringify(txInput)}'`); throw new TransactionAssertionError(`Invalid transaction input signature '${JSON.stringify(txInput)}'`, txInput); } }, this.data.inputs); if (this.type == 'regular') { // Check if the sum of input transactions are greater than output transactions, it needs to leave some room for the transaction fee let sumOfInputsAmount = R.sum(R.map(R.prop('amount'), this.data.inputs)); let sumOfOutputsAmount = R.sum(R.map(R.prop('amount'), this.data.outputs)); let isInputsAmountGreaterOrEqualThanOutputsAmount = R.gte(sumOfInputsAmount, sumOfOutputsAmount); if (!isInputsAmountGreaterOrEqualThanOutputsAmount) { console.error(`Invalid transaction balance: inputs sum '${sumOfInputsAmount}', outputs sum '${sumOfOutputsAmount}'`); throw new TransactionAssertionError(`Invalid transaction balance: inputs sum '${sumOfInputsAmount}', outputs sum '${sumOfOutputsAmount}'`, { sumOfInputsAmount, sumOfOutputsAmount }); } let isEnoughFee = (sumOfInputsAmount - sumOfOutputsAmount) >= Config.FEE_PER_TRANSACTION; // 1 because the fee is 1 satoshi per transaction if (!isEnoughFee) { console.error(`Not enough fee: expected '${Config.FEE_PER_TRANSACTION}' got '${(sumOfInputsAmount - sumOfOutputsAmount)}'`); throw new TransactionAssertionError(`Not enough fee: expected '${Config.FEE_PER_TRANSACTION}' got '${(sumOfInputsAmount - sumOfOutputsAmount)}'`, { sumOfInputsAmount, sumOfOutputsAmount, FEE_PER_TRANSACTION: Config.FEE_PER_TRANSACTION }); } } return true; } static fromJson(data) { let transaction = new Transaction(); R.forEachObjIndexed((value, key) => { transaction[key] = value; }, data); transaction.hash = transaction.toHash(); return transaction; } } module.exports = Transaction;
Roles Based Authentication Permissions
rule ruleBasedOnReadRole { description: "Grant access to read an asset or participant" participant(p): "ownet.roles.User" operation: READ resource(r): "**" condition: (p.role.permissions.indexOf(r) && p.role.permissions[p.role.permissions.indexOf(r)+1].read === true) action: ALLOW } rule ruleBasedOnCreateRole { description: "Grant access to create an asset or participant" participant(p): "ownet.roles.User" operation: CREATE resource(r): "**" condition: (p.role.permissions.indexOf(r) && p.role.permissions[p.role.permissions.indexOf(r)+1].create === true) action: ALLOW }
Booking SmartContract
function bookingFeature(add) { var featureId = add.feature.featureId; var customerId = add.customer.customerId; var bookingId = add.booking.bookingId; return getAssetRegistry('ownet.booking.booking') .then(function (assetRegistry) { return assetRegistry.get(bookingId).then(function () { return assetRegistry.add(); }) }) .then(function () { getParticipantRegistry('ownet.participants.Customer') .then(function (customerRegistry) { return customerRegistry.get(customerId).then(function (customer) { customer.usagePoints -= featureId.usagePoints; booking.paid = true; return customerRegistry.update(customer); }) }) }) }
Asset Transfer
'use strict'; /** * Transfer the asset to another owner * @param {ownet.property.assetTransfer} change - the change to be processed * @transaction */ function assetTransfer(change) { // set the new owner of the asset change.property.owner = change.owner; return getAssetRegistry('ownet.property.property') .then(function (assetRegistry) { // emit a notification that a change has occurred var changeNotification = getFactory().newEvent('ownet.participants', 'TransferNotification'); TransferNotification.owner = change.owner; emit(changeNotification); // persist the state of the commodity return assetRegistry.update(change.property); }); }
EHR SmartContracts
function addPatientInfo(patientInfo) { console.log("Adding patient info") return getAssetRegistry('org.cognier.healthrecords.PatientInfo') .then(function (assetRegistry) { var factory = getFactory(); var newAsset = factory.newResource('org.cognier.healthrecords', 'patientInfo', patientInfo.patientId); newAsset.name = patientInfo.name; newAsset.dateOfBirth = patientInfo.dateOfBirth; newAsset.pastVisitsArray = patientInfo.pastVisitsArray; newAsset.medicationArray = patientInfo.medicationArray; newAsset.description = patientInfo.description; return assetRegistry.add(newAsset); }); } /** * Medication * @param {org.cognier.healthrecords.updateMedication} updateMedication - Transaction for update medication * @transaction */ function updateMedication(updateMedication) { var assetRegistry; console.log('update medication'); var id = updateMedication.patientInfo.patientId; return getAssetRegistry('org.cognier.healthrecords.PatientInfo') .then(function (ar) { assetRegistry = ar; return assetRegistry.get(id); }) .then(function (asset) { asset.medicationArray = updateMedication.medicationArray; return assetRegistry.update(asset); }); }
SmartContract for KYC and Document Sharing
'use strict'; /** * A Member revokes access to their record from another Member. * @param {org.quickkyc.revokeKeyAccess} revoke - the revoke key transaction * @transaction */ function revokeKeyAccess(revoke){ var me = getCurrentParticipant(); if(!me) { throw new Error('A participant/certificate mapping does not exist.'); } var key = getAssetRegistry('org.quickkyc.keys#'+ revoke.keyID); var current = new Date(); console.log("time compare" + key.endTime + "--" + current ); if(key.keyTypeObject === 'time' && key.endTime > current){ console.log("revoked access based on time"); key.status = false; return getAssetRegistry('org.quickkyc.keys').then(function (assetRegistry) { return assetRegistry.update(key); }); } else if(key.keyTypeObject === 'views' && key.views == 0){ //key.views = make.keyObject.views; key.views = key.views - 1; key.status = false; return getAssetRegistry('org.quickkyc.keys').then(function (assetRegistry) { return assetRegistry.update(key); }); } else if(key.keyTypeObject === 'permanent'){ //code to revoke permanent access // if the member is authorized, we remove them var index = me.authorized ? me.authorized.indexOf(revoke.memberId) : -1; if(index>-1) { me.authorized.splice(index, 1); return getParticipantRegistry('org.quickkyc.Member') .then(function (memberRegistry) { // emit an event var event = getFactory().newEvent('org.quickkyc', 'MemberEvent'); event.memberTransaction = revoke; emit(event); // persist the state of the member return memberRegistry.update(me); }); } } else{ throw new Error('Illegal Key type.'); } } /** * A Member revokes access to their record from another Member. * @param {org.quickkyc.searchKey} search - the revoke key transaction * @transaction */ function searchKey(search){ var me = getCurrentParticipant(); var key = getAssetRegistry('org.quickkyc.keys#'+ search.keyObject.keyID); var current = new Date(); console.log("time compare" + key.endTime + "--" + current ); if(key.keyTypeObject === 'time' && key.endTime > current){ console.log("revoked access based on time"); key.status = false; return getAssetRegistry('org.quickkyc.keys').then(function (assetRegistry) { return assetRegistry.update(key); }); } else if(key.keyTypeObject === 'views' && key.views != 0){ //key.views = make.keyObject.views; key.views = key.views - 1; key.status = false; return getAssetRegistry('org.quickkyc.keys').then(function (assetRegistry) { return assetRegistry.update(key); }); } else if(key.keyTypeObject === 'permanent'){ //code to revoke permanent access // if the member is authorized, we remove them var index = me.authorized ? me.authorized.indexOf(search.memberId) : -1; if(index>-1) { me.authorized.splice(index, 1); return getParticipantRegistry('org.quickkyc.Member') .then(function (memberRegistry) { // emit an event var event = getFactory().newEvent('org.quickkyc', 'MemberEvent'); event.memberTransaction = search; emit(event); // persist the state of the member return memberRegistry.update(me); }); } } else{ throw new Error('Illegal Key type.'); } } /** * A Member revokes access to their record from another Member. * @param {org.quickkyc.createKey} make - the create key to be processed * @transaction */ function createKey(make) { console.log(make); var sharedKey = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (var i = 0; i < 64; i++) sharedKey += possible.charAt(Math.floor(Math.random() * possible.length)); var me = getCurrentParticipant(); var key = getFactory().newResource('org.quickkyc','keys',make.keyObject.keyID); //key.cartonId = make.Id; var now = new Date(); key.MemberObject = me; key.startTime = now; key.keyTypeObject = make.keyObject.keyTypeObject; //var documents = ["resource:org.acme.pii.document#2256"]; //query('selectCommoditiesByOwner'); //key.documentObject = query('selectCommoditiesByOwner'); //if(!key) { // throw new Error('A key/certificate mapping does not exist.'); // } key.documentObject = []; if(make.keyObject.keyTypeObject === 'time'){ var revokeTime = make.timestamp; //var hours = 2; //make.hours; key.hours = make.keyObject.hours; if(key.hours > 0 ) revokeTime.setTime(revokeTime.getTime() + (key.hours)*60*60*1000); else revokeTime.setTime(revokeTime.getTime() + 2*60*60*1000); //var tomorrow = make.timestamp; //tomorrow.setDate(tomorrow.getDate() + 1); key.endTime = revokeTime; key.views = 0; return getAssetRegistry('org.quickkyc.keys').then(function (assetRegistry) { return assetRegistry.add(key); }); } else if(key.keyTypeObject === 'views'){ key.views = make.keyObject.views; key.hours = 0; var expTime = new Date(); expTime.setYear(expTime.getYear()+ 1); key.endTime = new Date(); return getAssetRegistry('org.quickkyc.keys').then(function (assetRegistry) { return assetRegistry.add(key); }); } else if(key.keyTypeObject === 'permanent'){ //var me = getCurrentParticipant(); console.log('**** AUTH: ' + me.getIdentifier() + ' granting access to ' + make.memberId ); if(!me) { throw new Error('A participant/certificate mapping does not exist.'); } // if the member is not already authorized, we authorize them var index = -1; if(!me.authorized) { me.authorized = []; } else { index = me.authorized.indexOf(make.memberId); } if(index < 0) { me.authorized.push(make.memberId); return getParticipantRegistry('org.quickkyc.Member') .then(function (memberRegistry) { // emit an event //var event = getFactory().newEvent('org.acme.pii', 'MemberEvent'); //event.memberTransaction = make; //emit(event); // persist the state of the member return memberRegistry.update(me); }); } } }