Skip to content

Commit

Permalink
feat(dapi): implement getIdentityBalance (#2105)
Browse files Browse the repository at this point in the history
Co-authored-by: owl352 <kokosinca123@gmail.com>
Co-authored-by: owl352 <64574305+owl352@users.noreply.github.com>
  • Loading branch information
3 people authored Sep 17, 2024
1 parent 50d42c2 commit 2954e29
Show file tree
Hide file tree
Showing 9 changed files with 466 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ const {
GetTotalCreditsInPlatformResponse: PBJSGetTotalCreditsInPlatformResponse,
GetStatusRequest: PBJSGetStatusRequest,
GetStatusResponse: PBJSGetStatusResponse,
GetIdentityBalanceRequest: PBJSGetIdentityBalanceRequest,
GetIdentityBalanceResponse: PBJSGetIdentityBalanceResponse,
},
},
},
Expand All @@ -88,6 +90,7 @@ const {
GetIdentityKeysResponse: ProtocGetIdentityKeysResponse,
GetTotalCreditsInPlatformResponse: ProtocGetTotalCreditsInPlatformResponse,
GetStatusResponse: ProtocGetStatusResponse,
GetIdentityBalanceResponse: ProtocGetIdentityBalanceResponse,
} = require('./platform_protoc');

const getPlatformDefinition = require('../../../../lib/getPlatformDefinition');
Expand Down Expand Up @@ -186,6 +189,10 @@ class PlatformPromiseClient {
this.client.getStatus.bind(this.client),
);

this.client.getIdentityBalance = promisify(
this.client.getIdentityBalance.bind(this.client),
);

this.protocolVersion = undefined;
}

Expand Down Expand Up @@ -762,6 +769,35 @@ class PlatformPromiseClient {
);
}

getIdentityBalance(
getIdentityBalanceRequest,
metadata = {},
options = {},
) {
if (!isObject(metadata)) {
throw new Error('metadata must be an object');
}

return this.client.getIdentityBalance(
getIdentityBalanceRequest,
convertObjectToMetadata(metadata),
{
interceptors: [
jsonToProtobufInterceptorFactory(
jsonToProtobufFactory(
ProtocGetIdentityBalanceResponse,
PBJSGetIdentityBalanceResponse,
),
protobufToJsonFactory(
PBJSGetIdentityBalanceRequest,
),
),
],
...options,
},
);
}

/**
* @param {string} protocolVersion
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('PlatformPromiseClient', () => {
getIdentityContractNonce: this.sinon.stub().resolves(response),
getIdentityNonce: this.sinon.stub().resolves(response),
getIdentityKeys: this.sinon.stub().resolves(response),
getIdentityBalance: this.sinon.stub().resolves(response),
};
});

Expand Down Expand Up @@ -170,4 +171,14 @@ describe('PlatformPromiseClient', () => {
.to.be.calledOnceWith(request);
});
});

describe('#getIdentityBalance', () => {
it('should get identity balance', async () => {
const result = await platformPromiseClient.getIdentityBalance(request);

expect(result).to.equal(response);
expect(platformPromiseClient.client.getIdentityBalance)
.to.be.calledOnceWith(request);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const getIdentityNonceFactory = require('./getIdentityNonce/getIdentityNonceFact
const getIdentityKeysFactory = require('./getIdentityKeys/getIdentityKeysFactory');
const getTotalCreditsInPlatformFactory = require('./getTotalCreditsInPlatform/getTotalCreditsInPlatformFactory');
const getStatusFactory = require('./getStatus/getStatusFactory');
const getIdentityBalanceFactory = require('./getIdentityBalance/getIdentityBalanceFactory');

class PlatformMethodsFacade {
/**
Expand All @@ -40,6 +41,7 @@ class PlatformMethodsFacade {
this.getIdentityKeys = getIdentityKeysFactory(grpcTransport);
this.getTotalCreditsInPlatform = getTotalCreditsInPlatformFactory(grpcTransport);
this.getStatus = getStatusFactory(grpcTransport);
this.getIdentityBalance = getIdentityBalanceFactory(grpcTransport);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function getEpochsInfoFactory(grpcTransport) {
const getEpochInfosRequest = new GetEpochsInfoRequest();
getEpochInfosRequest.setV0(
new GetEpochsInfoRequestV0()
.setStartEpoch(new UInt32Value([startEpoch]))
.setStartEpoch(typeof startEpoch === 'number' ? new UInt32Value([startEpoch]) : undefined)
.setCount(count)
.setAscending(!!options.ascending)
.setProve(!!options.prove),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const AbstractResponse = require('../response/AbstractResponse');
const InvalidResponseError = require('../response/errors/InvalidResponseError');

class GetIdentityBalanceResponse extends AbstractResponse {
/**
* @param {number} balance
* @param {Metadata} metadata
* @param {Proof} [proof]
*/
constructor(balance, metadata, proof = undefined) {
super(metadata, proof);

this.balance = balance;
}

/**
* @returns {number}
*/
getBalance() {
return this.balance;
}

/**
* @param proto
* @returns {GetIdentityBalanceResponse}
*/
static createFromProto(proto) {
const balance = proto.getV0().getBalance();
const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto);

if ((balance === null || balance === undefined) && !proof) {
throw new InvalidResponseError('Balance is not defined');
}

return new GetIdentityBalanceResponse(
balance,
metadata,
proof,
);
}
}

module.exports = GetIdentityBalanceResponse;
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const {
v0: {
PlatformPromiseClient,
GetIdentityBalanceRequest,
},
} = require('@dashevo/dapi-grpc');

const GetIdentityBalanceResponse = require('./GetIdentityBalanceResponse');
const InvalidResponseError = require('../response/errors/InvalidResponseError');

/**
* @param {GrpcTransport} grpcTransport
* @returns {getIdentityBalance}
*/
function getIdentityBalanceFactory(grpcTransport) {
/**
* Fetch the identity balance by id
* @typedef {getIdentityBalance}
* @param {Buffer} id
* @param {DAPIClientOptions & {prove: boolean}} [options]
* @returns {Promise<GetIdentityBalanceResponse>}
*/
async function getIdentityBalance(id, options = {}) {
const { GetIdentityBalanceRequestV0 } = GetIdentityBalanceRequest;
const getIdentityBalanceRequest = new GetIdentityBalanceRequest();
// need to convert objects inherited from Buffer to pure buffer as google protobuf
// doesn't support extended buffers
// https://github.com/protocolbuffers/protobuf/blob/master/js/binary/utils.js#L1049
if (Buffer.isBuffer(id)) {
// eslint-disable-next-line no-param-reassign
id = Buffer.from(id);
}

getIdentityBalanceRequest.setV0(
new GetIdentityBalanceRequestV0()
.setId(id)
.setProve(!!options.prove),
);

let lastError;

// TODO: simple retry before the dapi versioning is properly implemented
for (let i = 0; i < 3; i += 1) {
try {
// eslint-disable-next-line no-await-in-loop
const getIdentityBalanceResponse = await grpcTransport.request(
PlatformPromiseClient,
'getIdentityBalance',
getIdentityBalanceRequest,
options,
);

return GetIdentityBalanceResponse.createFromProto(getIdentityBalanceResponse);
} catch (e) {
if (e instanceof InvalidResponseError) {
lastError = e;
} else {
throw e;
}
}
}

// If we made it past the cycle it means that the retry didn't work,
// and we're throwing the last error encountered
throw lastError;
}

return getIdentityBalance;
}

module.exports = getIdentityBalanceFactory;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const {
GetDataContractResponse,
GetDocumentsResponse,
GetIdentityResponse,
GetIdentityBalanceResponse,
GetIdentityByPublicKeyHashResponse,
GetIdentitiesContractKeysResponse,
GetEpochsInfoResponse,
Expand All @@ -27,6 +28,7 @@ const PlatformMethodsFacade = require('../../../../lib/methods/platform/Platform

const { WaitForStateTransitionResultResponseV0 } = WaitForStateTransitionResultResponse;
const { GetIdentityResponseV0 } = GetIdentityResponse;
const { GetIdentityBalanceResponseV0 } = GetIdentityBalanceResponse;
const { GetIdentityByPublicKeyHashResponseV0 } = GetIdentityByPublicKeyHashResponse;
const { GetIdentitiesContractKeysResponseV0 } = GetIdentitiesContractKeysResponse;
const { GetDocumentsResponseV0 } = GetDocumentsResponse;
Expand Down Expand Up @@ -318,4 +320,22 @@ describe('PlatformMethodsFacade', () => {
expect(grpcTransportMock.request).to.be.calledOnce();
});
});

describe('#getIdentityBalance', () => {
it('should get identity balance', async () => {
const response = new GetIdentityBalanceResponse();

response.setV0(
new GetIdentityBalanceResponseV0()
.setMetadata(new ResponseMetadata())
.setBalance(1337),
);

grpcTransportMock.request.resolves(response);

await platformMethods.getIdentityBalance('41nthkqvHBLnqiMkSbsdTNANzYu9bgdv4etKoRUunY1M');

expect(grpcTransportMock.request).to.be.calledOnce();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
const {
v0: {
GetIdentityBalanceResponse,
ResponseMetadata,
Proof: ProofResponse,
},
} = require('@dashevo/dapi-grpc');

const GetIdentityBalanceResponseClass = require('../../../../../lib/methods/platform/getIdentityBalance/GetIdentityBalanceResponse');
const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture');
const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError');
const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture');
const Proof = require('../../../../../lib/methods/platform/response/Proof');
const Metadata = require('../../../../../lib/methods/platform/response/Metadata');

describe('GetIdentityBalanceResponse', () => {
let getIdentityBalanceResponse;
let metadataFixture;
let balance;
let proto;
let proofFixture;

beforeEach(async () => {
metadataFixture = getMetadataFixture();
proofFixture = getProofFixture();
balance = 1337;

const { GetIdentityBalanceResponseV0 } = GetIdentityBalanceResponse;
proto = new GetIdentityBalanceResponse();

const metadata = new ResponseMetadata();
metadata.setHeight(metadataFixture.height);
metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight);
metadata.setTimeMs(metadataFixture.timeMs);
metadata.setProtocolVersion(metadataFixture.protocolVersion);

proto.setV0(
new GetIdentityBalanceResponseV0()
.setBalance(balance)
.setMetadata(metadata),
);

getIdentityBalanceResponse = new GetIdentityBalanceResponseClass(
balance,
new Metadata(metadataFixture),
);
});

it('should return Identity balance', () => {
const identityBalance = getIdentityBalanceResponse.getBalance();
const identityProof = getIdentityBalanceResponse.getProof();

expect(identityBalance).to.equal(balance);
expect(identityProof).to.equal(undefined);
});

it('should return proof', () => {
getIdentityBalanceResponse = new GetIdentityBalanceResponseClass(
balance,
new Metadata(metadataFixture),
new Proof(proofFixture),
);

const identityBalance = getIdentityBalanceResponse.getBalance();
const proof = getIdentityBalanceResponse.getProof();

expect(identityBalance).to.equal(balance);
expect(proof).to.be.an.instanceOf(Proof);
expect(proof.getGrovedbProof()).to.deep.equal(proofFixture.merkleProof);
expect(proof.getQuorumHash()).to.deep.equal(proofFixture.quorumHash);
expect(proof.getSignature()).to.deep.equal(proofFixture.signature);
expect(proof.getRound()).to.deep.equal(proofFixture.round);
});

it('should create an instance from proto', () => {
getIdentityBalanceResponse = GetIdentityBalanceResponseClass.createFromProto(proto);
expect(getIdentityBalanceResponse).to.be
.an.instanceOf(GetIdentityBalanceResponseClass);
expect(getIdentityBalanceResponse.getBalance()).to.equal(balance);

expect(getIdentityBalanceResponse.getMetadata())
.to.be.an.instanceOf(Metadata);
expect(getIdentityBalanceResponse.getMetadata().getHeight())
.to.equal(metadataFixture.height);
expect(getIdentityBalanceResponse.getMetadata().getCoreChainLockedHeight())
.to.equal(metadataFixture.coreChainLockedHeight);

expect(getIdentityBalanceResponse.getProof()).to.equal(undefined);
});

it('should create an instance with proof from proto', () => {
const proofProto = new ProofResponse();

proofProto.setQuorumHash(proofFixture.quorumHash);
proofProto.setSignature(proofFixture.signature);
proofProto.setGrovedbProof(proofFixture.merkleProof);
proofProto.setRound(proofFixture.round);

proto.getV0().setBalance(undefined);
proto.getV0().setProof(proofProto);

getIdentityBalanceResponse = GetIdentityBalanceResponseClass.createFromProto(proto);

expect(getIdentityBalanceResponse.getBalance()).to.equal(0);
expect(getIdentityBalanceResponse.getMetadata()).to.deep.equal(metadataFixture);

const proof = getIdentityBalanceResponse.getProof();
expect(proof).to.be.an.instanceOf(Proof);
expect(proof.getGrovedbProof()).to.deep.equal(proofFixture.merkleProof);
expect(proof.getQuorumHash()).to.deep.equal(proofFixture.quorumHash);
expect(proof.getSignature()).to.deep.equal(proofFixture.signature);
expect(proof.getRound()).to.deep.equal(proofFixture.round);
});

it('should throw InvalidResponseError if Metadata is not defined', () => {
proto.getV0().setMetadata(undefined);

try {
getIdentityBalanceResponse = GetIdentityBalanceResponseClass.createFromProto(proto);

expect.fail('should throw InvalidResponseError');
} catch (e) {
expect(e).to.be.an.instanceOf(InvalidResponseError);
}
});
});
Loading

0 comments on commit 2954e29

Please sign in to comment.