diff --git a/package-lock.json b/package-lock.json index d594bbca2..b5aea28b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,9 +88,9 @@ "dev": true }, "@babel/polyfill": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.8.7.tgz", - "integrity": "sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.1.tgz", + "integrity": "sha512-TviueJ4PBW5p48ra8IMtLXVkDucrlOZAIZ+EXqS3Ot4eukHbWiqcn7DcqpA1k5PcKtmJ4Xl9xwdv6yQvvcA+3g==", "dev": true, "requires": { "core-js": "^2.6.5", @@ -160,9 +160,9 @@ } }, "@dashevo/dapi-client": { - "version": "0.13.0-dev.2", - "resolved": "https://registry.npmjs.org/@dashevo/dapi-client/-/dapi-client-0.13.0-dev.2.tgz", - "integrity": "sha512-3yPdJ0YikOSlcnI4QV168etyeSSkWChBdz4fyEeDW6S6gNB6Xek9JNCyZqYpYpd59Q0k6wOsxIqBisvCoMv8Sg==", + "version": "0.13.0-dev.3", + "resolved": "https://registry.npmjs.org/@dashevo/dapi-client/-/dapi-client-0.13.0-dev.3.tgz", + "integrity": "sha512-OR+YapplmoO+qjqRGF3Z/ajLqdBF1QWl3r+V6hoZSmxJtc44ltGZ042id/DNRL7NWsYM/9SLHOQM6FuUeOJDvQ==", "dev": true, "requires": { "@babel/polyfill": "^7.8.3", @@ -318,8 +318,9 @@ } }, "@dashevo/dpp": { - "version": "0.13.0-dev.4", - "resolved": "github:dashevo/js-dpp#40643f9cc0ccd219f0d9888bc5cc1a11c33c4161", + "version": "0.13.0-dev.7", + "resolved": "https://registry.npmjs.org/@dashevo/dpp/-/dpp-0.13.0-dev.7.tgz", + "integrity": "sha512-GqhmQ7sslj7VjPRZ2apqaQtjyayBrkedEkwBqV5Lg/KObl1VPT4MCbpFhdMNH9ue50sqa69C/bX507XendjkAQ==", "requires": { "@apidevtools/json-schema-ref-parser": "^8.0.0", "@dashevo/dashcore-lib": "~0.18.1", diff --git a/package.json b/package.json index 2477948d3..57493c463 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,9 @@ }, "dependencies": { "@dashevo/dapi-grpc": "~0.13.0-dev.4", - "@dashevo/dashcore-lib": "~0.18.0", + "@dashevo/dashcore-lib": "~0.18.1", "@dashevo/dashd-rpc": "^2.0.0", - "@dashevo/dpp": "~0.13.0-dev.4", + "@dashevo/dpp": "~0.13.0-dev.7", "@dashevo/grpc-common": "~0.3.0", "ajv": "^6.4.0", "bs58": "^4.0.1", @@ -49,7 +49,7 @@ "zeromq": "^5.2.0" }, "devDependencies": { - "@dashevo/dapi-client": "~0.13.0-dev.2", + "@dashevo/dapi-client": "~0.13.0-dev.3", "@dashevo/dp-services-ctl": "~0.13.0-dev.1", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", diff --git a/test/functional/grpcServer/handlers/platform/topUpIdentity.spec.js b/test/functional/grpcServer/handlers/platform/topUpIdentity.spec.js new file mode 100644 index 000000000..3f1b49f61 --- /dev/null +++ b/test/functional/grpcServer/handlers/platform/topUpIdentity.spec.js @@ -0,0 +1,189 @@ +const { + startDapi, +} = require('@dashevo/dp-services-ctl'); + +const { + PrivateKey, + PublicKey, + Transaction, +} = require('@dashevo/dashcore-lib'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const { convertSatoshiToCredits } = require( + '@dashevo/dpp/lib/identity/creditsConverter', +); + +const wait = require('../../../../../lib/utils/wait'); + +describe('topUpIdentity', function main() { + this.timeout(200000); + + let removeDapi; + let dapiClient; + let dpp; + let identity; + let identityCreateTransition; + let identityTopUpTransition; + let coreAPI; + let addressString; + let publicKeyHash; + let privateKey; + + before(async () => { + const { + dapiCore, + dashCore, + remove, + } = await startDapi(); + + removeDapi = remove; + coreAPI = dashCore.getApi(); + dapiClient = dapiCore.getApi(); + + dpp = new DashPlatformProtocol({ + dataProvider: {}, + }); + }); + + beforeEach(async () => { + ({ result: addressString } = await coreAPI.getNewAddress()); + const { result: privateKeyString } = await coreAPI.dumpPrivKey(addressString); + + privateKey = new PrivateKey(privateKeyString); + const publicKey = new PublicKey({ + ...privateKey.toPublicKey().toObject(), + compressed: true, + }); + const pubKeyBase = publicKey.toBuffer() + .toString('base64'); + + // eslint-disable-next-line no-underscore-dangle + publicKeyHash = PublicKey.fromBuffer(Buffer.from(pubKeyBase, 'base64')) + ._getID(); + + await coreAPI.generateToAddress(500, addressString); + + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter(input => input.address === addressString); + + const transaction = new Transaction(); + + transaction.from(inputs.slice(-1)[0]) + .addBurnOutput(10000, publicKeyHash) + .change(addressString) + .fee(668) + .sign(privateKey); + + await coreAPI.sendrawtransaction(transaction.serialize()); + + await coreAPI.generateToAddress(1, addressString); + + await wait(2000); // wait a couple of seconds for tx to be confirmed + + const outPoint = transaction.getOutPointBuffer(0); + + identity = dpp.identity.create( + outPoint, + [publicKey], + ); + + identityCreateTransition = dpp.identity.createIdentityCreateTransition(identity); + identityCreateTransition.signByPrivateKey(privateKey); + + await dapiClient.applyStateTransition(identityCreateTransition); + }); + + after(async () => { + await removeDapi(); + }); + + it('should top up created identity', async () => { + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter(input => input.address === addressString); + const topUpTransaction = new Transaction(); + const topUpAmount = 3000; + + topUpTransaction.from(inputs.slice(-1)[0]) + .addBurnOutput(topUpAmount, publicKeyHash) + .change(addressString) + .fee(668) + .sign(privateKey); + + await coreAPI.sendrawtransaction(topUpTransaction.serialize()); + + await coreAPI.generateToAddress(1, addressString); + + await wait(2000); // wait a couple of seconds for tx to be confirmed + + const topUpOutPoint = topUpTransaction.getOutPointBuffer(0); + + identityTopUpTransition = dpp.identity.createIdentityTopUpTransition( + identity.getId(), + topUpOutPoint, + ); + identityTopUpTransition.signByPrivateKey(privateKey); + + await dapiClient.applyStateTransition(identityTopUpTransition); + + const serializedIdentity = await dapiClient.getIdentity( + identityCreateTransition.getIdentityId(), + ); + + const receivedIdentity = dpp.identity.createFromSerialized( + serializedIdentity, + { skipValidation: true }, + ); + + const balance = convertSatoshiToCredits(10000) + + convertSatoshiToCredits(topUpAmount) + - identityCreateTransition.calculateFee() + - identityTopUpTransition.calculateFee(); + + expect(balance).to.equal(receivedIdentity.getBalance()); + }); + + it('should fail top up created identity ', async () => { + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter(input => input.address === addressString); + const topUpTransaction = new Transaction(); + const topUpAmount = 3000; + + topUpTransaction.from(inputs.slice(-1)[0]) + .addBurnOutput(topUpAmount, publicKeyHash) + .change(addressString) + .fee(668) + .sign(privateKey); + + const topUpOutPoint = topUpTransaction.getOutPointBuffer(0); + + identityTopUpTransition = dpp.identity.createIdentityTopUpTransition( + identity.getId(), + topUpOutPoint, + ); + identityTopUpTransition.signByPrivateKey(privateKey); + + try { + await dapiClient.applyStateTransition(identityTopUpTransition); + + expect.fail('Should fail with error'); + } catch (e) { + expect(e.code).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(e.details).to.equal('State Transition is invalid'); + } + + const serializedIdentity = await dapiClient.getIdentity( + identityCreateTransition.getIdentityId(), + ); + + const receivedIdentity = dpp.identity.createFromSerialized( + serializedIdentity, + { skipValidation: true }, + ); + + expect(convertSatoshiToCredits(10000) - identityCreateTransition.calculateFee()) + .to.equal(receivedIdentity.getBalance()); + }); +});