blockchain integration
This commit is contained in:
@@ -6,6 +6,10 @@ import App from './App'
|
|||||||
|
|
||||||
globalThis.Buffer = Buffer
|
globalThis.Buffer = Buffer
|
||||||
|
|
||||||
|
BigInt.prototype.toJSON = function() {
|
||||||
|
return this.toString()
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Router>
|
<Router>
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ const Profile = observer (() => {
|
|||||||
const deleteDataParameters = () => {
|
const deleteDataParameters = () => {
|
||||||
const changeData = store.decryptedAccountData.filter(item=>!selectedItems.includes(item.key))
|
const changeData = store.decryptedAccountData.filter(item=>!selectedItems.includes(item.key))
|
||||||
store.pushAccountData(changeData);
|
store.pushAccountData(changeData);
|
||||||
|
if(storeOnBlockchain)
|
||||||
|
store.pushAccountDataToBlockchain(changeData)
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeInitialArray = () => {
|
const changeInitialArray = () => {
|
||||||
@@ -153,6 +155,8 @@ const Profile = observer (() => {
|
|||||||
const changeDataParameters = () => {
|
const changeDataParameters = () => {
|
||||||
const updatedData = changeInitialArray();
|
const updatedData = changeInitialArray();
|
||||||
store.pushAccountData(updatedData);
|
store.pushAccountData(updatedData);
|
||||||
|
if(storeOnBlockchain)
|
||||||
|
store.pushAccountDataToBlockchain(updatedData)
|
||||||
};
|
};
|
||||||
|
|
||||||
const addDataParameters = () => {
|
const addDataParameters = () => {
|
||||||
@@ -162,6 +166,8 @@ const Profile = observer (() => {
|
|||||||
else
|
else
|
||||||
addedValues.key = addedValues.label
|
addedValues.key = addedValues.label
|
||||||
store.pushAccountData(store.decryptedAccountData.concat(addedValues));
|
store.pushAccountData(store.decryptedAccountData.concat(addedValues));
|
||||||
|
if(storeOnBlockchain)
|
||||||
|
store.pushAccountDataToBlockchain(store.decryptedAccountData.concat(addedValues))
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelAddPanel = () => {
|
const cancelAddPanel = () => {
|
||||||
@@ -352,7 +358,7 @@ const Profile = observer (() => {
|
|||||||
required
|
required
|
||||||
onChange={(e) => changeUpdatedValues(e.target.value, 'label', item.key)}
|
onChange={(e) => changeUpdatedValues(e.target.value, 'label', item.key)}
|
||||||
value={item.label}
|
value={item.label}
|
||||||
disabled='disabled'
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {store} from '../store/store';
|
import {store} from '../store/store';
|
||||||
|
import {jsonReplacer} from '../utils/Utils';
|
||||||
|
|
||||||
function prepareUrl(url) {
|
function prepareUrl(url) {
|
||||||
if (url.startsWith('http')) return url;
|
if (url.startsWith('http')) return url;
|
||||||
@@ -14,7 +15,10 @@ function handleRequest(method, url, headers, attempts, token, body) {
|
|||||||
})()
|
})()
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.catch(() => [])
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
return {}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +51,7 @@ function request(method, url, headers = {}, token, body) {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...headers
|
...headers
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body, jsonReplacer),
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
import {reaction, makeAutoObservable, onBecomeObserved, onBecomeUnobserved} from "mobx";
|
import {reaction, makeAutoObservable, onBecomeObserved, onBecomeUnobserved} from "mobx";
|
||||||
import {cryptography} from "@liskhq/lisk-client";
|
import {cryptography} from "@liskhq/lisk-client";
|
||||||
import { passphrase } from "@liskhq/lisk-client";
|
import { passphrase, transactions } from "@liskhq/lisk-client";
|
||||||
import {getNodeInfo} from '../api/node';
|
import {getNodeInfo} from '../api/node';
|
||||||
import {fetchWrapper} from '../shared/fetchWrapper';
|
import {fetchWrapper} from '../shared/fetchWrapper';
|
||||||
import {labelMap} from "../shared/labelMap";
|
import {labelMap} from "../shared/labelMap";
|
||||||
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg";
|
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg";
|
||||||
|
import {
|
||||||
|
encryptAccountData,
|
||||||
|
generateSetTransaction, generateTransaction,
|
||||||
|
hashAccountData,
|
||||||
|
} from '../utils/Utils';
|
||||||
|
import {
|
||||||
|
removeFeatureAssetSchema,
|
||||||
|
setFeatureAssetSchema,
|
||||||
|
} from '../utils/Schemas';
|
||||||
|
|
||||||
class Store {
|
class Store {
|
||||||
_accountData = []
|
_accountData = []
|
||||||
@@ -19,17 +28,20 @@ class Store {
|
|||||||
|
|
||||||
_transactionsInfo = []
|
_transactionsInfo = []
|
||||||
|
|
||||||
|
_accountInfo = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(this, {});
|
makeAutoObservable(this, {});
|
||||||
|
|
||||||
this.fetchNodeInfo()
|
|
||||||
|
|
||||||
reaction(() => this.keysArray, () => this.fetchSharedData())
|
reaction(() => this.keysArray, () => this.fetchSharedData())
|
||||||
|
reaction(() => this.address, () => this.fetchAccountInfo())
|
||||||
onBecomeObserved(this, "decryptedAccountData", () => this.fetchNewAccountData())
|
onBecomeObserved(this, "decryptedAccountData", () => this.fetchNewAccountData())
|
||||||
onBecomeObserved(this, "sharedData", () => this.fetchKeysArray())
|
onBecomeObserved(this, "sharedData", () => this.fetchKeysArray())
|
||||||
onBecomeUnobserved(this, "sharedData", () => this.unobservedSharedData())
|
onBecomeUnobserved(this, "sharedData", () => this.unobservedSharedData())
|
||||||
onBecomeObserved(this, "transactionsInfo", () => this.fetchTransactionsInfo())
|
onBecomeObserved(this, "transactionsInfo", () => this.fetchTransactionsInfo())
|
||||||
onBecomeUnobserved(this, "transactionsInfo", () => this.unobservedTransactionsInfo())
|
onBecomeUnobserved(this, "transactionsInfo", () => this.unobservedTransactionsInfo())
|
||||||
|
|
||||||
|
this.fetchNodeInfo()
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchNewAccountData() {
|
fetchNewAccountData() {
|
||||||
@@ -44,12 +56,8 @@ class Store {
|
|||||||
.catch((err) => this.fetchNodeInfoFailed(err))
|
.catch((err) => this.fetchNodeInfoFailed(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
pushAccountData(data = this.accountData) {
|
fetchAccountInfo() {
|
||||||
fetchWrapper.postAuth('data/private', {
|
fetchWrapper.get(`accounts/${this.address}`).then((res) => this.fetchAccountInfoSuccess(res))
|
||||||
networkIdentifier: this.nodeInfo.networkIdentifier,
|
|
||||||
lastBlockID: this.nodeInfo.lastBlockID
|
|
||||||
}, [...this.encryptAccountData(data)])
|
|
||||||
.then(() => this.fetchNewAccountData())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchKeysArray() {
|
fetchKeysArray() {
|
||||||
@@ -71,6 +79,49 @@ class Store {
|
|||||||
}).then((res) => this.saveInfoTransactions(res))
|
}).then((res) => this.saveInfoTransactions(res))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fetchPassPhrase() {
|
||||||
|
this._passPhrase = sessionStorage.getItem('passPhrase') || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchSharedData() {
|
||||||
|
this._keysArray.forEach((elem) => fetchWrapper.get(`data/shared/${elem}`).then((res) => this.changeSharedData(res)))
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchNodeInfo() {
|
||||||
|
getNodeInfo()
|
||||||
|
.then((info)=>this.fetchNodeInfoSuccess(info))
|
||||||
|
.catch((err) => this.fetchNodeInfoFailed(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
pushAccountData(data = this.accountData) {
|
||||||
|
fetchWrapper.postAuth('data/private', {
|
||||||
|
networkIdentifier: this.nodeInfo.networkIdentifier,
|
||||||
|
lastBlockID: this.nodeInfo.lastBlockID
|
||||||
|
}, [...encryptAccountData(data, this.passPhrase, this.pubKey)])
|
||||||
|
.then(() => this.fetchNewAccountData())
|
||||||
|
}
|
||||||
|
|
||||||
|
pushAccountDataToBlockchain(data = this.decryptedAccountData) {
|
||||||
|
const [removed, changed] = hashAccountData(data, this.decryptedAccountData, this.accountFeaturesMap)
|
||||||
|
|
||||||
|
const builder = generateTransaction(BigInt(this.accountInfo?.sequence?.nonce || 0), this.addressAndPubKey.publicKey, this.nodeInfo.networkIdentifier, this.passPhrase);
|
||||||
|
|
||||||
|
let signedTx = null;
|
||||||
|
|
||||||
|
if(removed.length !== 0)
|
||||||
|
signedTx = builder.remove(removed);
|
||||||
|
|
||||||
|
if(changed.length !== 0)
|
||||||
|
signedTx = builder.update(changed)
|
||||||
|
|
||||||
|
if(signedTx) {
|
||||||
|
fetchWrapper.post('transactions', {}, signedTx);
|
||||||
|
this.accountInfo.sequence.nonce++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
generatePassPhrase() {
|
generatePassPhrase() {
|
||||||
this._passPhrase = passphrase.Mnemonic.generateMnemonic();
|
this._passPhrase = passphrase.Mnemonic.generateMnemonic();
|
||||||
sessionStorage.setItem('passPhrase', this._passPhrase);
|
sessionStorage.setItem('passPhrase', this._passPhrase);
|
||||||
@@ -81,14 +132,6 @@ class Store {
|
|||||||
sessionStorage.setItem('passPhrase', this._passPhrase);
|
sessionStorage.setItem('passPhrase', this._passPhrase);
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchPassPhrase() {
|
|
||||||
this._passPhrase = sessionStorage.getItem('passPhrase') || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchSharedData() {
|
|
||||||
this._keysArray.forEach((elem) => fetchWrapper.get(`data/shared/${elem}`).then((res) => this.changeSharedData(res)))
|
|
||||||
};
|
|
||||||
|
|
||||||
unobservedTransactionsInfo() {
|
unobservedTransactionsInfo() {
|
||||||
this._transactionsInfo = []
|
this._transactionsInfo = []
|
||||||
};
|
};
|
||||||
@@ -111,12 +154,6 @@ class Store {
|
|||||||
this._accountData = [];
|
this._accountData = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchNodeInfo() {
|
|
||||||
getNodeInfo()
|
|
||||||
.then((info)=>this.fetchNodeInfoSuccess(info))
|
|
||||||
.catch((err) => this.fetchNodeInfoFailed(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
saveInfoTransactions(transaction) {
|
saveInfoTransactions(transaction) {
|
||||||
this._transactionsInfo = transaction.data
|
this._transactionsInfo = transaction.data
|
||||||
}
|
}
|
||||||
@@ -133,12 +170,19 @@ class Store {
|
|||||||
|
|
||||||
fetchNodeInfoSuccess(info) {
|
fetchNodeInfoSuccess(info) {
|
||||||
this._nodeInfo = info.data;
|
this._nodeInfo = info.data;
|
||||||
|
this.fetchPassPhrase()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchNodeInfoFailed(err) {
|
fetchNodeInfoFailed(err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchAccountInfoSuccess(res) {
|
||||||
|
this._accountInfo = res.data;
|
||||||
|
if(this.accountInfo?.sequence?.nonce)
|
||||||
|
this.accountInfo.sequence.nonce = parseInt(this.accountInfo.sequence.nonce) || 0
|
||||||
|
}
|
||||||
|
|
||||||
get sharedData() {
|
get sharedData() {
|
||||||
return this._sharedData.map(elem => ({
|
return this._sharedData.map(elem => ({
|
||||||
data: elem.data.map(item => ({
|
data: elem.data.map(item => ({
|
||||||
@@ -167,22 +211,14 @@ class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get decryptedAccountData() {
|
get decryptedAccountData() {
|
||||||
return this._accountData.map((elem) => ({
|
return this._accountData.map((elem) => {
|
||||||
|
const [seed, value] = cryptography.decryptMessageWithPassphrase(elem.value, elem.value_nonce, this.passPhrase, this.pubKey).split(':');
|
||||||
|
return {
|
||||||
...elem,
|
...elem,
|
||||||
key: elem.label,
|
key: elem.label,
|
||||||
label: labelMap[elem.label] || elem.label,
|
label: labelMap[elem.label] || elem.label,
|
||||||
value: cryptography.decryptMessageWithPassphrase(elem.value, elem.value_nonce, this.passPhrase, this.pubKey).split(':')[1]
|
value,
|
||||||
}))
|
seed
|
||||||
}
|
|
||||||
|
|
||||||
encryptAccountData(data) {
|
|
||||||
return data.map((item) => {
|
|
||||||
let value = cryptography.encryptMessageWithPassphrase(cryptography.getRandomBytes(32).toString('hex') + ":" + item.value, this.passPhrase, this.pubKey);
|
|
||||||
return {
|
|
||||||
label: item.key,
|
|
||||||
value: value.encryptedMessage,
|
|
||||||
value_nonce: value.nonce,
|
|
||||||
seed: item.seed,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -204,7 +240,6 @@ class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get passPhrase() {
|
get passPhrase() {
|
||||||
this.fetchPassPhrase()
|
|
||||||
return this._passPhrase
|
return this._passPhrase
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +247,10 @@ class Store {
|
|||||||
return this._accountData
|
return this._accountData
|
||||||
};
|
};
|
||||||
|
|
||||||
|
get accountInfo() {
|
||||||
|
return this._accountInfo;
|
||||||
|
}
|
||||||
|
|
||||||
get nodeInfo() {
|
get nodeInfo() {
|
||||||
return this._nodeInfo;
|
return this._nodeInfo;
|
||||||
}
|
}
|
||||||
@@ -235,6 +274,21 @@ class Store {
|
|||||||
const sign = cryptography.signDataWithPassphrase(Buffer.from(stringToSign, 'hex'), this.passPhrase).toString('hex')
|
const sign = cryptography.signDataWithPassphrase(Buffer.from(stringToSign, 'hex'), this.passPhrase).toString('hex')
|
||||||
return this.pubKey + ':' +sign
|
return this.pubKey + ':' +sign
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get accountIdentity() {
|
||||||
|
return this.accountInfo?.identity || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get accountFeatures() {
|
||||||
|
return this.accountIdentity?.features || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get accountFeaturesMap() {
|
||||||
|
return this.accountFeatures.reduce((acc, item) => ({
|
||||||
|
...acc,
|
||||||
|
[item.label]: item.value
|
||||||
|
}), {})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const store = new Store();
|
export const store = new Store();
|
||||||
|
|||||||
127
src/utils/Schemas.js
Normal file
127
src/utils/Schemas.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
export const IdentityModuleSchema = {
|
||||||
|
$id: 'idntty/identity/module',
|
||||||
|
title: 'Identity module account schema',
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
features: {
|
||||||
|
fieldNumber: 1,
|
||||||
|
type: 'array',
|
||||||
|
maxItems: 256,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['label', 'value'],
|
||||||
|
properties: {
|
||||||
|
label: { fieldNumber: 1, dataType: 'string'},
|
||||||
|
value: { fieldNumber: 2, dataType: 'bytes'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifications: {
|
||||||
|
fieldNumber: 2,
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['label', 'account', 'tx'],
|
||||||
|
properties: {
|
||||||
|
label: { fieldNumber: 1, dataType: 'string'},
|
||||||
|
account: { fieldNumber: 2, dataType: 'bytes'},
|
||||||
|
tx: { fieldNumber: 3, dataType: 'bytes'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {features: [], verifications: []}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const setFeatureAssetSchema = {
|
||||||
|
$id: 'idntty/identity/setfeature',
|
||||||
|
title: 'Asset schema to set or update account features for identity module',
|
||||||
|
type: 'object',
|
||||||
|
required: ['features'],
|
||||||
|
properties: {
|
||||||
|
features: {
|
||||||
|
fieldNumber: 1,
|
||||||
|
type: 'array',
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['label','value'],
|
||||||
|
properties: {
|
||||||
|
label: { fieldNumber: 1, dataType: 'string', maxLength: 16},
|
||||||
|
value: { fieldNumber: 2, dataType: 'bytes', maxLength: 32},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeFeatureAssetSchema = {
|
||||||
|
$id: 'idntty/identity/removefeature',
|
||||||
|
title: 'Asset schema to remove account features for identity module',
|
||||||
|
type: 'object',
|
||||||
|
required: ['features'],
|
||||||
|
properties: {
|
||||||
|
features: {
|
||||||
|
fieldNumber: 1,
|
||||||
|
type: 'array',
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['label'],
|
||||||
|
properties: {
|
||||||
|
label: { fieldNumber: 1, dataType: 'string', maxLength: 16},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateFeatureAssetSchema = {
|
||||||
|
$id: 'idntty/identity/validatefeature',
|
||||||
|
title: 'Asset schema to validate account features for identity module',
|
||||||
|
type: 'object',
|
||||||
|
required: ['recipientAddress','features'],
|
||||||
|
properties: {
|
||||||
|
recipientAddress: {fieldNumber: 1, dataType: 'bytes', minLength: 20, maxLength: 20},
|
||||||
|
features: {
|
||||||
|
fieldNumber: 2,
|
||||||
|
type: 'array',
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['label', 'value'],
|
||||||
|
properties: {
|
||||||
|
label: { fieldNumber: 1, dataType: 'string', maxLength: 16},
|
||||||
|
value: { fieldNumber: 2, dataType: 'bytes', maxLength: 32},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const invalidateFeatureAssetSchema = {
|
||||||
|
$id: 'idntty/identity/invalidatefeature',
|
||||||
|
title: 'Asset schema to invalidate account features for identity module',
|
||||||
|
type: 'object',
|
||||||
|
required: ['recipientAddress','features'],
|
||||||
|
properties: {
|
||||||
|
recipientAddress: {fieldNumber: 1, dataType: 'bytes', minLength: 20, maxLength: 20},
|
||||||
|
features: {
|
||||||
|
fieldNumber: 2,
|
||||||
|
type: 'array',
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['label'],
|
||||||
|
properties: {
|
||||||
|
label: { fieldNumber: 1, dataType: 'string', maxLength: 16},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import resolveConfig from 'tailwindcss/resolveConfig';
|
import resolveConfig from 'tailwindcss/resolveConfig';
|
||||||
|
import {cryptography, transactions} from '@liskhq/lisk-client';
|
||||||
|
import {removeFeatureAssetSchema, setFeatureAssetSchema} from './Schemas';
|
||||||
|
|
||||||
export const tailwindConfig = () => {
|
export const tailwindConfig = () => {
|
||||||
// Tailwind config
|
// Tailwind config
|
||||||
@@ -32,3 +34,126 @@ export const formatThousands = (value) => Intl.NumberFormat('en-US', {
|
|||||||
maximumSignificantDigits: 3,
|
maximumSignificantDigits: 3,
|
||||||
notation: 'compact',
|
notation: 'compact',
|
||||||
}).format(value);
|
}).format(value);
|
||||||
|
|
||||||
|
export const encryptAccountData = (data = [], passPhrase = '', pubKey = '') => {
|
||||||
|
return data.map((item) => {
|
||||||
|
let value = cryptography.encryptMessageWithPassphrase(item.seed + ":" + item.value, passPhrase, pubKey);
|
||||||
|
return {
|
||||||
|
label: item.key,
|
||||||
|
value: value.encryptedMessage,
|
||||||
|
value_nonce: value.nonce,
|
||||||
|
seed: item.seed,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hashAccountData = (data = [], oldData = [], hashMap = {}) => {
|
||||||
|
const accountMap = data.reduce((acc, item) => ({
|
||||||
|
...acc,
|
||||||
|
[item.label]: item.value
|
||||||
|
}), {})
|
||||||
|
|
||||||
|
const oldAccountMap = oldData.reduce((acc, item) => ({
|
||||||
|
...acc,
|
||||||
|
[item.label]: item.value
|
||||||
|
}), {})
|
||||||
|
|
||||||
|
const removed = oldData.reduce((acc, item) => {
|
||||||
|
if(!accountMap[item.label])
|
||||||
|
return [...acc, {label: item.label}];
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const changed = data.reduce((acc, item) => {
|
||||||
|
if(!oldAccountMap[item.label])
|
||||||
|
return [ ...acc, item ]
|
||||||
|
const oldValue = hashMap[item.key];
|
||||||
|
const newValue = hashValue(item.value, item.seed).toString('hex')
|
||||||
|
if(oldValue !== newValue)
|
||||||
|
return [ ...acc, item ]
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return [
|
||||||
|
removed,
|
||||||
|
changed.map((item) => {
|
||||||
|
const value = hashValue(item.value, item.seed);
|
||||||
|
return {
|
||||||
|
label: item.key,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const jsonReplacer = (key, value) => {
|
||||||
|
if (key === 'big') {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hashValue = (value = '', seed = '') => cryptography.hash(Buffer.concat([Buffer.from(seed, 'utf8'), cryptography.hash(value, 'utf8')]));
|
||||||
|
|
||||||
|
export const generateTransaction = (nonce = BigInt(0), senderPublicKey = '', networkIdentifier = '', passPhrase = '', fee = BigInt(500000),) => {
|
||||||
|
return {
|
||||||
|
update: (features) => generateSetTransaction(features, nonce, senderPublicKey, networkIdentifier, passPhrase, fee),
|
||||||
|
remove: (features) => generateRemoveTransaction(features, nonce, senderPublicKey, networkIdentifier, passPhrase, fee),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateSetTransaction = (features, nonce = BigInt(0), senderPublicKey = '', networkIdentifier = '', passPhrase = '', fee = BigInt(500000)) => {
|
||||||
|
const tx = {
|
||||||
|
"moduleID": 1001,
|
||||||
|
"assetID": 1,
|
||||||
|
nonce,
|
||||||
|
senderPublicKey,
|
||||||
|
fee,
|
||||||
|
"asset": {
|
||||||
|
features
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if(features.length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const signedTx = transactions.signTransaction(setFeatureAssetSchema,
|
||||||
|
tx, Buffer.from(networkIdentifier, "hex"),
|
||||||
|
passPhrase)
|
||||||
|
|
||||||
|
signedTx.senderPublicKey = signedTx.senderPublicKey.toString("hex");
|
||||||
|
signedTx.signatures[0] = signedTx.signatures[0].toString("hex");
|
||||||
|
signedTx.asset.features = signedTx.asset.features.map(feature => ({
|
||||||
|
...feature,
|
||||||
|
value: feature.value.toString("hex")
|
||||||
|
}))
|
||||||
|
|
||||||
|
delete signedTx.id;
|
||||||
|
|
||||||
|
return signedTx;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateRemoveTransaction = (features, nonce = BigInt(0), senderPublicKey = '', networkIdentifier = '', passPhrase = '', fee = BigInt(500000)) => {
|
||||||
|
const tx = {
|
||||||
|
"moduleID": 1001,
|
||||||
|
"assetID": 2,
|
||||||
|
nonce,
|
||||||
|
senderPublicKey,
|
||||||
|
fee,
|
||||||
|
"asset": {
|
||||||
|
features
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const signedTx = transactions.signTransaction(removeFeatureAssetSchema,
|
||||||
|
tx, Buffer.from(networkIdentifier, "hex"),
|
||||||
|
passPhrase)
|
||||||
|
|
||||||
|
signedTx.senderPublicKey = signedTx.senderPublicKey.toString(
|
||||||
|
"hex");
|
||||||
|
signedTx.signatures[0] = signedTx.signatures[0].toString("hex");
|
||||||
|
|
||||||
|
delete signedTx.id;
|
||||||
|
|
||||||
|
return signedTx;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user