Skip to content

Commit

Permalink
Merge branch '76-fix-duplication-selection-in-getcoveringboxes' into …
Browse files Browse the repository at this point in the history
…'dev'

Resolve "fix duplication selection in getCoveringBoxes"

Closes #76

See merge request ergo/rosen-bridge/rosen-chains!85
  • Loading branch information
zargarzadehm committed Jan 3, 2024
2 parents bfa80d4 + 13e8acc commit 4fc318a
Show file tree
Hide file tree
Showing 15 changed files with 288 additions and 51 deletions.
40 changes: 20 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion packages/abstract-chain/lib/AbstractUtxoChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ abstract class AbstractUtxoChain<BoxType> extends AbstractChain {
const uncoveredTokens = requiredAssets.tokens.filter(
(info) => info.value > 0n
);
const selectedBoxes: Array<string> = [];

const isRequirementRemaining = () => {
return uncoveredTokens.length > 0 || uncoveredNativeToken > 0n;
Expand Down Expand Up @@ -86,7 +87,11 @@ abstract class AbstractUtxoChain<BoxType> extends AbstractChain {
}

// if tracked to no box or forbidden box, skip it
if (skipBox || forbiddenBoxIds.includes(boxInfo.id)) {
if (
skipBox ||
forbiddenBoxIds.includes(boxInfo.id) ||
selectedBoxes.includes(boxInfo.id)
) {
this.logger.debug(`box [${boxInfo.id}] is skipped`);
continue;
}
Expand All @@ -113,6 +118,7 @@ abstract class AbstractUtxoChain<BoxType> extends AbstractChain {
? boxInfo.assets.nativeToken
: uncoveredNativeToken;
result.push(trackedBox!);
selectedBoxes.push(boxInfo.id);
this.logger.debug(`box [${boxInfo.id}] is selected`);
} else this.logger.debug(`box [${boxInfo.id}] is ignored`);

Expand Down
2 changes: 1 addition & 1 deletion packages/abstract-chain/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rosen-chains/abstract-chain",
"version": "3.2.1",
"version": "3.2.2",
"description": "this project contains abstract classes to implement any chain for Rosen-bridge",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
Expand Down
76 changes: 76 additions & 0 deletions packages/abstract-chain/tests/AbstractUtxoChain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,5 +648,81 @@ describe('AbstractUtxoChain', () => {
expect(result.covered).toEqual(false);
expect(result.boxes).toEqual([]);
});

/**
* @target AbstractUtxoChain.getCoveringBoxes should return all boxes as
* NOT covered when two boxes are tracked to same box
* @dependencies
* @scenario
* - mock a network object to return 2 boxes
* - mock a Map to track first box to a new box
* - mock chain 'getBoxInfo' function to return mocked boxes assets
* - mock an AssetBalance object with assets more than box assets
* - run test
* - check returned value
* @expected
* - it should return serialized tracked boxes
*/
it('should return all boxes as NOT covered when two boxes are tracked to same box', async () => {
// Mock a network object to return 2 boxes
const network = new TestUtxoChainNetwork();
spyOn(network, 'getAddressBoxes')
.mockResolvedValue([])
.mockResolvedValueOnce(['serialized-box-1', 'serialized-box-2']);

// Mock a Map to track first box to a new box
const trackMap = new Map<string, string>();
trackMap.set('box1', 'serialized-tracked-box-1');
trackMap.set('box2', 'serialized-tracked-box-1');

// Mock chain 'getBoxInfo' function to return mocked boxes assets
const chain = generateChainObject(network);
const getBoxInfoSpy = spyOn(chain, 'getBoxInfo');
when(getBoxInfoSpy)
.calledWith('serialized-box-1')
.mockReturnValueOnce({
id: 'box1',
assets: {
nativeToken: 100000n,
tokens: [{ id: 'token1', value: 200n }],
},
});
when(getBoxInfoSpy)
.calledWith('serialized-box-2')
.mockReturnValueOnce({
id: 'box2',
assets: {
nativeToken: 100000n,
tokens: [{ id: 'token1', value: 200n }],
},
});
when(getBoxInfoSpy)
.calledWith('serialized-tracked-box-1')
.mockReturnValue({
id: 'trackedBox1',
assets: {
nativeToken: 80000n,
tokens: [{ id: 'token1', value: 150n }],
},
});

// Mock an AssetBalance object with assets less than box assets
const requiredAssets: AssetBalance = {
nativeToken: 150000n,
tokens: [{ id: 'token1', value: 250n }],
};

// Run test
const result = await chain.getCoveringBoxes(
'',
requiredAssets,
[],
trackMap
);

// Check returned value
expect(result.covered).toEqual(false);
expect(result.boxes).toEqual(['serialized-tracked-box-1']);
});
});
});
17 changes: 14 additions & 3 deletions packages/chains/cardano/lib/CardanoChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ class CardanoChain extends AbstractUtxoChain<CardanoUtxo> {
const txId = Buffer.from(
CardanoWasm.hash_transaction(txBody).to_bytes()
).toString('hex');
if (!CardanoUtils.isTxIdValid(txId)) throw Error(`Invalid txId: [${txId}]`);

const cardanoTx = new CardanoTransaction(
txId,
Expand Down Expand Up @@ -350,8 +351,9 @@ class CardanoChain extends AbstractUtxoChain<CardanoUtxo> {
tokens: [],
};
// extract input box assets
for (let i = 0; i < cardanoTx.inputUtxos.length; i++) {
const input = JSONBigInt.parse(cardanoTx.inputUtxos[i]) as CardanoUtxo;
const inputUtxos = Array.from(new Set(cardanoTx.inputUtxos));
for (let i = 0; i < inputUtxos.length; i++) {
const input = JSONBigInt.parse(inputUtxos[i]) as CardanoUtxo;
const boxAssets = this.getBoxInfo(input).assets;
inputAssets = ChainUtils.sumAssetBalance(inputAssets, boxAssets);
}
Expand Down Expand Up @@ -449,6 +451,9 @@ class CardanoChain extends AbstractUtxoChain<CardanoUtxo> {
transaction: PaymentTransaction,
_signingStatus: SigningStatus = SigningStatus.Signed
): Promise<boolean> => {
// check if txId is valid
if (!CardanoUtils.isTxIdValid(transaction.txId)) return false;

const tx = Serializer.deserialize(transaction.txBytes);
const txBody = tx.body();

Expand All @@ -466,7 +471,13 @@ class CardanoChain extends AbstractUtxoChain<CardanoUtxo> {
)
return false;
}
return true;

// check if input and output assets match
const txAssets = await this.getTransactionAssets(transaction);
return ChainUtils.isEqualAssetBalance(
txAssets.inputAssets,
txAssets.outputAssets
);
};

/**
Expand Down
7 changes: 7 additions & 0 deletions packages/chains/cardano/lib/CardanoUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ class CardanoUtils {
changeAmount
);
};

/**
* verifies that txId doesn't start with zero
* @param txId
* @returns
*/
static isTxIdValid = (txId: string): boolean => txId[0] !== '0';
}

export default CardanoUtils;
4 changes: 2 additions & 2 deletions packages/chains/cardano/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rosen-chains/cardano",
"version": "3.2.1",
"version": "3.2.2",
"description": "this project contains cardano chain for Rosen-bridge",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
Expand All @@ -24,7 +24,7 @@
"@rosen-bridge/logger-interface": "^0.1.0",
"@rosen-bridge/rosen-extractor": "^3.0.0",
"@rosen-bridge/json-bigint": "^0.1.0",
"@rosen-chains/abstract-chain": "^3.2.1",
"@rosen-chains/abstract-chain": "^3.2.2",
"bech32": "^2.0.0",
"blake2b": "^2.1.3"
},
Expand Down
Loading

0 comments on commit 4fc318a

Please sign in to comment.