diff --git a/package-lock.json b/package-lock.json index af3b606..eec8729 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8438,7 +8438,7 @@ }, "packages/chains/cardano": { "name": "@rosen-chains/cardano", - "version": "0.1.8", + "version": "0.1.9", "license": "GPL-3.0", "dependencies": { "@emurgo/cardano-serialization-lib-nodejs": "^11.3.1", @@ -8468,7 +8468,7 @@ }, "packages/chains/ergo": { "name": "@rosen-chains/ergo", - "version": "0.1.13", + "version": "0.1.14", "license": "GPL-3.0", "dependencies": { "@rosen-bridge/minimum-fee": "^0.1.10", @@ -8497,7 +8497,7 @@ }, "packages/networks/cardano-koios": { "name": "@rosen-chains/cardano-koios-network", - "version": "0.1.3", + "version": "0.1.6", "license": "GPL-3.0", "dependencies": { "@emurgo/cardano-serialization-lib-nodejs": "^11.3.1", @@ -8505,7 +8505,7 @@ "@rosen-bridge/rosen-extractor": "^0.1.8", "@rosen-bridge/tokens": "^0.1.12", "@rosen-chains/abstract-chain": "^0.1.10", - "@rosen-chains/cardano": "^0.1.8", + "@rosen-chains/cardano": "^0.1.9", "@rosen-clients/cardano-koios": "^0.2.2", "json-bigint": "^1.0.0" }, @@ -8545,14 +8545,14 @@ }, "packages/networks/ergo-explorer": { "name": "@rosen-chains/ergo-explorer-network", - "version": "0.1.6", + "version": "0.1.9", "license": "GPL-3.0", "dependencies": { "@rosen-bridge/logger-interface": "^0.1.0", "@rosen-bridge/rosen-extractor": "^0.1.8", "@rosen-bridge/tokens": "^0.1.12", "@rosen-chains/abstract-chain": "^0.1.10", - "@rosen-chains/ergo": "^0.1.13", + "@rosen-chains/ergo": "^0.1.14", "@rosen-clients/ergo-explorer": "^0.3.0", "ergo-lib-wasm-nodejs": "^0.23.0", "it-all": "^3.0.1", @@ -8588,14 +8588,14 @@ }, "packages/networks/ergo-node": { "name": "@rosen-chains/ergo-node-network", - "version": "0.1.8", + "version": "0.1.11", "license": "GPL-3.0", "dependencies": { "@rosen-bridge/logger-interface": "^0.1.0", "@rosen-bridge/rosen-extractor": "^0.1.8", "@rosen-bridge/tokens": "^0.1.12", "@rosen-chains/abstract-chain": "^0.1.10", - "@rosen-chains/ergo": "^0.1.13", + "@rosen-chains/ergo": "^0.1.14", "@rosen-clients/ergo-node": "^0.3.2", "ergo-lib-wasm-nodejs": "^0.23.0", "it-all": "^3.0.1", diff --git a/packages/chains/cardano/lib/constants.ts b/packages/chains/cardano/lib/constants.ts index b77911b..11ccc00 100644 --- a/packages/chains/cardano/lib/constants.ts +++ b/packages/chains/cardano/lib/constants.ts @@ -1,6 +1,7 @@ import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs'; const CARDANO_CHAIN = 'cardano'; +const ADA = 'ada'; const protocolParameters = { minFeeA: CardanoWasm.BigNum.from_str('44'), @@ -27,4 +28,4 @@ const txBuilderConfig: CardanoWasm.TransactionBuilderConfig = .coins_per_utxo_word(protocolParameters.coinsPerUtxoWord) .build(); -export { txBuilderConfig, CARDANO_CHAIN }; +export { txBuilderConfig, CARDANO_CHAIN, ADA }; diff --git a/packages/chains/cardano/package.json b/packages/chains/cardano/package.json index c32216a..14d053b 100644 --- a/packages/chains/cardano/package.json +++ b/packages/chains/cardano/package.json @@ -1,6 +1,6 @@ { "name": "@rosen-chains/cardano", - "version": "0.1.8", + "version": "0.1.9", "description": "this project contains cardano chain for Rosen-bridge", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts", diff --git a/packages/chains/ergo/lib/ErgoChain.ts b/packages/chains/ergo/lib/ErgoChain.ts index 6ecbc42..5240f96 100644 --- a/packages/chains/ergo/lib/ErgoChain.ts +++ b/packages/chains/ergo/lib/ErgoChain.ts @@ -24,7 +24,7 @@ import * as wasm from 'ergo-lib-wasm-nodejs'; import Serializer from './Serializer'; import { blake2b } from 'blakejs'; import { ERGO_CHAIN } from './constants'; -import { ErgoConfigs } from './types'; +import { ErgoConfigs, GuardsPkConfig } from './types'; import { AbstractLogger } from '@rosen-bridge/logger-interface'; import ErgoTransaction from './ErgoTransaction'; import ErgoUtils from './ErgoUtils'; @@ -856,6 +856,54 @@ class ErgoChain extends AbstractUtxoChain { eventBox.tokens().get(0).id().to_str() === expectedRWT ); }; + + /** + * gets guards public keys and required signs from config box in the blockchain + * @param guardNFT the guard NFT tokenId + * @param address address containing guard config box + */ + getGuardsPkConfig = async ( + guardNFT: string, + address: string + ): Promise => { + const guardBox = wasm.ErgoBox.sigma_parse_bytes( + Buffer.from(await this.getGuardsConfigBox(guardNFT, address), 'hex') + ); + try { + const r4 = guardBox.register_value(4)?.to_coll_coll_byte(); + const r5 = guardBox.register_value(5)?.to_i32_array(); + + if (r4 === undefined || r5 === undefined) + throw Error(`R4 or R5 is empty`); + + return { + publicKeys: r4.map((pk) => Buffer.from(pk).toString('hex')), + requiredSigns: r5[0], + }; + } catch (e) { + this.logger.debug( + `Cannot get guards pk config from box [${guardBox + .box_id() + .to_str()}]. R4 [${guardBox + .register_value(4) + ?.encode_to_base16()}], R5 [${guardBox + .register_value(5) + ?.encode_to_base16()}]` + ); + throw Error( + `Failed to get guards public keys from box [${guardBox + .box_id() + .to_str()}] due to invalid registers: ${e}` + ); + } + }; + + /** + * gets the context of blockchain using 10 last blocks + * @returns the state context object + */ + getStateContext = async (): Promise => + await this.network.getStateContext(); } export default ErgoChain; diff --git a/packages/chains/ergo/lib/types.ts b/packages/chains/ergo/lib/types.ts index ccdb645..ecba753 100644 --- a/packages/chains/ergo/lib/types.ts +++ b/packages/chains/ergo/lib/types.ts @@ -13,4 +13,9 @@ interface ErgoTransactionJsonModel extends PaymentTransactionJsonModel { dataInputs: Array; } -export { ErgoConfigs, ErgoTransactionJsonModel }; +interface GuardsPkConfig { + publicKeys: Array; + requiredSigns: number; +} + +export { ErgoConfigs, ErgoTransactionJsonModel, GuardsPkConfig }; diff --git a/packages/chains/ergo/package.json b/packages/chains/ergo/package.json index 270f02d..eef53d0 100644 --- a/packages/chains/ergo/package.json +++ b/packages/chains/ergo/package.json @@ -1,6 +1,6 @@ { "name": "@rosen-chains/ergo", - "version": "0.1.13", + "version": "0.1.14", "description": "this project contains ergo chain for Rosen-bridge", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts", diff --git a/packages/chains/ergo/tests/ErgoChain.spec.ts b/packages/chains/ergo/tests/ErgoChain.spec.ts index 52d3289..14b51e9 100644 --- a/packages/chains/ergo/tests/ErgoChain.spec.ts +++ b/packages/chains/ergo/tests/ErgoChain.spec.ts @@ -2221,4 +2221,75 @@ describe('ErgoChain', () => { expect(result).toEqual(false); }); }); + + describe('getGuardsPkConfig', () => { + /** + * @target ErgoChain.getGuardsPkConfig should get guards public key config successfully + * @dependencies + * @scenario + * - mock guard config box and guardNFT + * - mock a network object with mocked 'getBoxesByTokenId' + * - run test + * - check returned value + * @expected + * - it should return expected public keys and requiredSigns + */ + it('should get guards public key config successfully', async () => { + // mock guard config box and guardNFT + const box = ergoTestUtils.toErgoBox(boxTestData.guardConfigBox); + const guardNFT = boxTestData.guardNFT; + + // mock a network object + const network = new TestErgoNetwork(); + // mock 'getBoxesByTokenId' + const getBoxesByTokenIdSpy = spyOn(network, 'getBoxesByTokenId'); + when(getBoxesByTokenIdSpy) + .calledWith(guardNFT, ergoTestUtils.testLockAddress) + .mockResolvedValue([box]); + + // run test + const ergoChain = generateChainObject(network); + const result = await ergoChain.getGuardsPkConfig( + guardNFT, + ergoTestUtils.testLockAddress + ); + + // check returned value + expect(result).toEqual(boxTestData.guardPks); + }); + + /** + * @target ErgoChain.getGuardsPkConfig should throw error when + * register values are invalid + * @dependencies + * @scenario + * - mock an invalid box and guardNFT + * - mock a network object with mocked 'getBoxesByTokenId' + * - run test and expect exception thrown + * @expected + * - it should throw Error + */ + it('should throw error when register values are invalid', async () => { + // mock an invalid box and guardNFT + const box = ergoTestUtils.toErgoBox(boxTestData.eventBox1); + const guardNFT = boxTestData.guardNFT; + + // mock a network object + const network = new TestErgoNetwork(); + // mock 'getBoxesByTokenId' + const getBoxesByTokenIdSpy = spyOn(network, 'getBoxesByTokenId'); + when(getBoxesByTokenIdSpy) + .calledWith(guardNFT, ergoTestUtils.testLockAddress) + .mockResolvedValue([box]); + + // run test and expect exception thrown + const ergoChain = generateChainObject(network); + await expect(async () => { + await ergoChain.getGuardsPkConfig( + guardNFT, + ergoTestUtils.testLockAddress + ); + }).rejects.toThrow(Error); + }); + }); }); diff --git a/packages/chains/ergo/tests/boxTestData.ts b/packages/chains/ergo/tests/boxTestData.ts index eabbf79..d035c4d 100644 --- a/packages/chains/ergo/tests/boxTestData.ts +++ b/packages/chains/ergo/tests/boxTestData.ts @@ -1,4 +1,5 @@ import { AssetBalance, EventTrigger } from '@rosen-chains/abstract-chain'; +import { GuardsPkConfig } from '../lib'; export const ergoBox1 = `{ "boxId": "09704ca5e07fe502e4974b07ba8ec7e32c242d1dfc06b036465d6596f5a8ed4b", @@ -156,6 +157,42 @@ export const box1Assets: AssetBalance = { ], }; +export const guardConfigBox = `{ + "boxId": "6eccf30dc9e6d40e20f8dddc3d66e7ee45c82216906a2bacf14be9da8a811309", + "value": 1100000, + "ergoTree": "100504000400040004000402d802d601c2a7d602b2a5730000ea02d196830301937201c2b2a473010093c272027201938cb2db63087202730200018cb2db6308a77303000198b2e4c6a70510730400ade4c6a7041ad901030ecdee7203", + "assets": [ + { + "tokenId": "b039e4445337697deaad703fc8d1fcb129c4577e8673394fa2403e54d484eeda", + "amount": 1 + } + ], + "additionalRegisters": { + "R4": "1a0a2103c236922914cf80be4b642b3ea02f532ceb120a82b7bc5b75a8c2b7dc70810a272102b1d6b33c5ef442fa140d52352cab3e80780e98a44bcf6826604f100dbe2cbec8210217b8b16c47f83c6e96324f4d65434cd77aca1ab969878ba8293b87001c4f216f21024df46a0f95903d15cc8c4f390bb3d0f97dee85273591a88575206baf2d54812e2103aa26adb6a56132f52c0919a351b738beee0d2a240d6b5a82eff27a0f662f34592102d64bd38b103af945c5786dfbd63577779f49e7166e69f622dc5c243dcbbdbab921024f0556828a51b54a2b17bf9d4cc4c7fd9faa3226513193ae0aeaf9075bad4aca2102a8b0cc627e16301030b1a74fc33d49dd364a2b818d5d25e02b4d9e3bfc1f4aac2103380c129ffb41ccc52dd58a128082ac381bb449ef67d230255eea28425599852421023605f5625c0f1099c5966a58a9291d7a546d432faeb99d88a99fa6b745b1fd31", + "R5": "10020e10" + }, + "creationHeight": 969636, + "transactionId": "2d6baff61607166b03da7c68eec2d3ea4611ff88cbf36d964f648f64f8c2cfb8", + "index": 0 +}`; +export const guardNFT = + 'b039e4445337697deaad703fc8d1fcb129c4577e8673394fa2403e54d484eeda'; +export const guardPks: GuardsPkConfig = { + publicKeys: [ + '03c236922914cf80be4b642b3ea02f532ceb120a82b7bc5b75a8c2b7dc70810a27', + '02b1d6b33c5ef442fa140d52352cab3e80780e98a44bcf6826604f100dbe2cbec8', + '0217b8b16c47f83c6e96324f4d65434cd77aca1ab969878ba8293b87001c4f216f', + '024df46a0f95903d15cc8c4f390bb3d0f97dee85273591a88575206baf2d54812e', + '03aa26adb6a56132f52c0919a351b738beee0d2a240d6b5a82eff27a0f662f3459', + '02d64bd38b103af945c5786dfbd63577779f49e7166e69f622dc5c243dcbbdbab9', + '024f0556828a51b54a2b17bf9d4cc4c7fd9faa3226513193ae0aeaf9075bad4aca', + '02a8b0cc627e16301030b1a74fc33d49dd364a2b818d5d25e02b4d9e3bfc1f4aac', + '03380c129ffb41ccc52dd58a128082ac381bb449ef67d230255eea284255998524', + '023605f5625c0f1099c5966a58a9291d7a546d432faeb99d88a99fa6b745b1fd31', + ], + requiredSigns: 7, +}; + export const validEvent: EventTrigger = { height: 200, fromChain: 'ergo', diff --git a/packages/networks/cardano-koios/package.json b/packages/networks/cardano-koios/package.json index a3e3929..6118f10 100644 --- a/packages/networks/cardano-koios/package.json +++ b/packages/networks/cardano-koios/package.json @@ -1,6 +1,6 @@ { "name": "@rosen-chains/cardano-koios-network", - "version": "0.1.5", + "version": "0.1.6", "description": "cardano koios network package for rosen cardano chain", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts", @@ -25,7 +25,7 @@ "@rosen-bridge/rosen-extractor": "^0.1.8", "@rosen-bridge/tokens": "^0.1.12", "@rosen-chains/abstract-chain": "^0.1.10", - "@rosen-chains/cardano": "^0.1.8", + "@rosen-chains/cardano": "^0.1.9", "@rosen-clients/cardano-koios": "^0.2.2", "json-bigint": "^1.0.0" }, diff --git a/packages/networks/ergo-explorer/package.json b/packages/networks/ergo-explorer/package.json index 8ce9cd2..cafd2be 100644 --- a/packages/networks/ergo-explorer/package.json +++ b/packages/networks/ergo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@rosen-chains/ergo-explorer-network", - "version": "0.1.8", + "version": "0.1.9", "description": "ergo explorer network package for rosen ergo chain", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts", @@ -24,7 +24,7 @@ "@rosen-bridge/rosen-extractor": "^0.1.8", "@rosen-bridge/tokens": "^0.1.12", "@rosen-chains/abstract-chain": "^0.1.10", - "@rosen-chains/ergo": "^0.1.13", + "@rosen-chains/ergo": "^0.1.14", "@rosen-clients/ergo-explorer": "^0.3.0", "ergo-lib-wasm-nodejs": "^0.23.0", "it-all": "^3.0.1", diff --git a/packages/networks/ergo-node/package.json b/packages/networks/ergo-node/package.json index af092ff..c77ab79 100644 --- a/packages/networks/ergo-node/package.json +++ b/packages/networks/ergo-node/package.json @@ -1,6 +1,6 @@ { "name": "@rosen-chains/ergo-node-network", - "version": "0.1.10", + "version": "0.1.11", "description": "ergo node network package for rosen ergo chain", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts", @@ -24,7 +24,7 @@ "@rosen-bridge/rosen-extractor": "^0.1.8", "@rosen-bridge/tokens": "^0.1.12", "@rosen-chains/abstract-chain": "^0.1.10", - "@rosen-chains/ergo": "^0.1.13", + "@rosen-chains/ergo": "^0.1.14", "@rosen-clients/ergo-node": "^0.3.2", "ergo-lib-wasm-nodejs": "^0.23.0", "it-all": "^3.0.1",