Skip to content
This repository has been archived by the owner on Jan 4, 2022. It is now read-only.

feat: populate balance in applyIdentityStateTransition function #137

Merged
merged 3 commits into from
Mar 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/DashPlatformProtocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class DashPlatformProtocol {

this.identity = new IdentityFacade(
this.jsonSchemaValidator,
this.dataProvider,
);
}

Expand Down
24 changes: 0 additions & 24 deletions lib/errors/InvalidIdentityLockTransactionError.js

This file was deleted.

24 changes: 24 additions & 0 deletions lib/errors/InvalidIdentityLockTransactionOutputError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const ConsensusError = require('./ConsensusError');

class InvalidIdentityLockTransactionOutputError extends ConsensusError {
/**
* @param {string} message
* @param {Object} output
*/
constructor(message, output) {
super(`Invalid identity lock transaction output: ${message}`);

this.output = output;
}

/**
* Get lock transaction output
*
* @return {Object}
*/
getOutput() {
return this.output;
}
}

module.exports = InvalidIdentityLockTransactionOutputError;
12 changes: 12 additions & 0 deletions lib/identity/Identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ class Identity {
getBalance() {
return this.balance;
}

/**
* Set Identity balance
*
* @param {number} balance
* @return {Identity}
*/
setBalance(balance) {
this.balance = balance;

return this;
}
}

module.exports = Identity;
19 changes: 15 additions & 4 deletions lib/identity/IdentityFacade.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const { Transaction } = require('@dashevo/dashcore-lib');
const IdentityFactory = require('./IdentityFactory');
const validateIdentityFactory = require('./validation/validateIdentityFactory');
const applyIdentityStateTransition = require('./stateTransitions/applyIdentityStateTransition');
const applyIdentityStateTransitionFactory = require('./stateTransitions/applyIdentityStateTransitionFactory');
const getLockedTransactionOutputFactory = require('../stateTransition/getLockedTransactionOutputFactory');
const validatePublicKeysFactory = require('./validation/validatePublicKeysFactory');

/**
Expand All @@ -9,9 +11,10 @@ const validatePublicKeysFactory = require('./validation/validatePublicKeysFactor
*/
class IdentityFacade {
/**
* @param {DataProvider} dataProvider
* @param {JsonSchemaValidator} validator
*/
constructor(validator) {
constructor(validator, dataProvider) {
const validatePublicKeys = validatePublicKeysFactory(
validator,
);
Expand All @@ -20,7 +23,15 @@ class IdentityFacade {
validatePublicKeys,
);
this.factory = new IdentityFactory(this.validateIdentity);
this.applyIdentityStateTransition = applyIdentityStateTransition;

const getLockedTransactionOutput = getLockedTransactionOutputFactory(
dataProvider,
Transaction.parseOutPointBuffer,
);

this.applyIdentityStateTransition = applyIdentityStateTransitionFactory(
getLockedTransactionOutput,
);
}

/**
Expand Down Expand Up @@ -65,7 +76,7 @@ class IdentityFacade {
* @param {Identity|null} identity
* @return {Identity|null}
*/
applyStateTransition(stateTransition, identity) {
async applyStateTransition(stateTransition, identity) {
return this.applyIdentityStateTransition(stateTransition, identity);
}

Expand Down
37 changes: 0 additions & 37 deletions lib/identity/stateTransitions/applyIdentityStateTransition.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const Identity = require('../Identity');

const stateTransitionTypes = require('../../stateTransition/stateTransitionTypes');

const WrongStateTransitionTypeError = require('../errors/WrongStateTransitionTypeError');
const IdentityAlreadyExistsError = require('../../errors/IdentityAlreadyExistsError');

const { convertSatoshiToCredits } = require('../creditsConverter');
const calculateStateTransitionFee = require('../../stateTransition/calculateStateTransitionFee');

/**
*
* @param {getLockedTransactionOutput} getLockedTransactionOutput
* @return {applyIdentityStateTransition}
*/
function applyIdentityStateTransitionFactory(
getLockedTransactionOutput,
) {
/**
* Applies a state transition to the identity model.
* Only identity state transitions are allowed
*
* @typedef applyIdentityStateTransition
* @param {IdentityCreateTransition} stateTransition
* @param {Identity|null} identity
* @return {Identity|null}
*/
async function applyIdentityStateTransition(stateTransition, identity) {
// noinspection JSRedundantSwitchStatement
switch (stateTransition.getType()) {
case stateTransitionTypes.IDENTITY_CREATE: {
if (identity) {
throw new IdentityAlreadyExistsError(stateTransition);
}

const fee = calculateStateTransitionFee(stateTransition);
const output = await getLockedTransactionOutput(stateTransition.getLockedOutPoint());
const creditsAmount = convertSatoshiToCredits(output.satoshi);

const balance = creditsAmount - fee;

return new Identity({
id: stateTransition.getIdentityId(),
publicKeys: stateTransition.getPublicKeys().map((key) => key.toJSON()),
balance,
});
}
default:
throw new WrongStateTransitionTypeError(stateTransition);
}
}

return applyIdentityStateTransition;
}

module.exports = applyIdentityStateTransitionFactory;
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
const { Transaction, Signer: { verifyHashSignature } } = require('@dashevo/dashcore-lib');
const WrongOutPointError = require('@dashevo/dashcore-lib/lib/errors/WrongOutPointError');
const { Signer: { verifyHashSignature } } = require('@dashevo/dashcore-lib');
const ValidationResult = require('../../../validation/ValidationResult');

const InvalidIdentityLockTransactionError = require('../../../errors/InvalidIdentityLockTransactionError');
const ConsensusError = require('../../../errors/ConsensusError');
const InvalidIdentityLockTransactionOutputError = require('../../../errors/InvalidIdentityLockTransactionOutputError');
const InvalidStateTransitionSignatureError = require('../../../errors/InvalidStateTransitionSignatureError');
const IdentityLockTransactionNotFoundError = require('../../../errors/IdentityLockTransactionNotFoundError');
const InvalidIdentityOutPointError = require('../../../errors/InvalidIdentityOutPointError');
/**
*
* @param {DataProvider} dataProvider
* @param {function} parseTransactionOutPointBuffer
* @param {getLockedTransactionOutput} getLockedTransactionOutput
* @return {validateLockTransaction}
*/
function validateLockTransactionFactory(dataProvider, parseTransactionOutPointBuffer) {
function validateLockTransactionFactory(getLockedTransactionOutput) {
/**
* Validates identityCreateTransition signature against lock transaction
*
* @typedef validateLockTransaction
* @param {IdentityCreateTransition} identityCreateTransition
* @returns {Promise<ValidationResult>}
*/
async function validateLockTransaction(identityCreateTransition) {
const result = new ValidationResult();
let transactionHash;
let outputIndex;
// fetch lock transaction output, extract pubkey from it and verify signature

// fetch lock transaction, extract pubkey from it and verify signature
const result = new ValidationResult();

const lockedOutBuffer = Buffer.from(identityCreateTransition.getLockedOutPoint(), 'base64');
let output;

try {
({ transactionHash, outputIndex } = parseTransactionOutPointBuffer(lockedOutBuffer));
output = await getLockedTransactionOutput(identityCreateTransition.getLockedOutPoint());
} catch (e) {
if (e instanceof WrongOutPointError) {
result.addError(new InvalidIdentityOutPointError(e.message));
if (e instanceof ConsensusError) {
result.addError(e);
} else {
throw e;
}
Expand All @@ -41,36 +38,16 @@ function validateLockTransactionFactory(dataProvider, parseTransactionOutPointBu
return result;
}

const rawTransaction = await dataProvider.fetchTransaction(transactionHash);

if (!rawTransaction) {
result.addError(new IdentityLockTransactionNotFoundError(transactionHash));
}

if (!result.isValid()) {
return result;
}

const transaction = new Transaction(rawTransaction);

if (!transaction.outputs[outputIndex]) {
result.addError(new InvalidIdentityOutPointError(`Output with index ${outputIndex} not found`));
}

if (!result.isValid()) {
return result;
}

const { script } = transaction.outputs[outputIndex];
const { script } = output;

if (!script.isDataOut()) {
result.addError(new InvalidIdentityLockTransactionError('Output is not a valid standard OP_RETURN output', transaction));
result.addError(new InvalidIdentityLockTransactionOutputError('Output is not a valid standard OP_RETURN output', output));
}

const publicKeyHash = script.getData();

if (publicKeyHash.length !== 20) {
result.addError(new InvalidIdentityLockTransactionError('Output has invalid public key hash', transaction));
result.addError(new InvalidIdentityLockTransactionOutputError('Output has invalid public key hash', output));
}

if (!result.isValid()) {
Expand All @@ -93,10 +70,6 @@ function validateLockTransactionFactory(dataProvider, parseTransactionOutPointBu
result.addError(new InvalidStateTransitionSignatureError(identityCreateTransition));
}

if (result.isValid()) {
result.setData(transaction.outputs[outputIndex]);
}

return result;
}

Expand Down
9 changes: 7 additions & 2 deletions lib/stateTransition/StateTransitionFacade.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const validateLockTransactionFactory = require('../identity/stateTransitions/ide
const validateIdentityCreateSTStructureFactory = require('../identity/stateTransitions/identityCreateTransition/validateIdentityCreateSTStructureFactory');
const validateStateTransitionSignatureFactory = require('../stateTransition/validation/validateStateTransitionSignatureFactory');
const validateStateTransitionFeeFactory = require('./validation/validateStateTransitionFeeFactory');
const getLockedTransactionOutputFactory = require('./getLockedTransactionOutputFactory');

const enrichDataContractWithBaseSchema = require('../dataContract/enrichDataContractWithBaseSchema');
const findDuplicatesById = require('../document/stateTransition/validation/structure/findDuplicatesById');
Expand Down Expand Up @@ -115,11 +116,15 @@ class StateTransitionFacade {
dataProvider,
);

const validateLockTransaction = validateLockTransactionFactory(
const getLockedTransactionOutput = getLockedTransactionOutputFactory(
dataProvider,
Transaction.parseOutPointBuffer,
);

const validateLockTransaction = validateLockTransactionFactory(
getLockedTransactionOutput,
);

const validateIdentityCreateSTData = validateIdentityCreateSTDataFactory(
dataProvider,
validateLockTransaction,
Expand Down Expand Up @@ -154,7 +159,7 @@ class StateTransitionFacade {

this.validateStateTransitionFee = validateStateTransitionFeeFactory(
dataProvider,
validateLockTransaction,
getLockedTransactionOutput,
);

this.factory = new StateTransitionFactory(
Expand Down
18 changes: 18 additions & 0 deletions lib/stateTransition/calculateStateTransitionFee.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const PRICE_PER_BYTE = 1;

/**
* Get State Transition fee size
*
* @typedef calculateStateTransitionFee
* @param { DataContractStateTransition|
* DocumentsBatchTransition|
* IdentityCreateTransition} stateTransition
* @return {number}
*/
function calculateStateTransitionFee(stateTransition) {
const serializedStateTransition = stateTransition.serialize({ skipSignature: true });
const byteSize = Buffer.byteLength(serializedStateTransition);
return byteSize * PRICE_PER_BYTE;
}

module.exports = calculateStateTransitionFee;
Loading