Skip to content

Commit

Permalink
Merge branch 'bugfix/permit-return-chained-tx' into 'dev'
Browse files Browse the repository at this point in the history
Bugfix/permit return chained tx

See merge request ergo/rosen-bridge/watcher!197
  • Loading branch information
vorujack committed Dec 12, 2023
2 parents 8219fc5 + e294a9c commit 6e2851b
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 23 deletions.
50 changes: 36 additions & 14 deletions services/watcher/src/api/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ export class Transaction {
};

returnPermitTx = async (
height: number,
RWTCount: bigint,
permitBox: wasm.ErgoBox,
repoBox: wasm.ErgoBox,
widBox: wasm.ErgoBox,
wid: string
wid: string,
feeBox: wasm.ErgoBox | undefined
): Promise<{ tx: wasm.Transaction; remainingRwt: bigint }> => {
const height = await ErgoNetwork.getHeight();

const R4 = repoBox.register_value(4);
const R5 = repoBox.register_value(5);
const R6 = repoBox.register_value(6);
Expand All @@ -189,9 +189,20 @@ export class Transaction {
.to_base16_bytes(),
BigInt(Transaction.minBoxValue.as_i64().to_str()),
{},
(box) =>
Buffer.from(box.register_value(4)?.to_js()).toString('hex') == wid
(box) => {
if (!box.register_value(4)) {
logger.debug('Skipping collateral box without wid information');
return false;
}
const collateralWid = Buffer.from(
box.register_value(4)?.to_js()
).toString('hex');
logger.debug(`Collateral is found for wid: [${collateralWid}]`);
return collateralWid == wid;
}
);
if (collateralBoxes.boxes.length == 0)
throw Error('Collateral box for this wid is not found');
inputBoxes.push(collateralBoxes.boxes[0]);
usersOut.splice(widIndex, 1);
usersCountOut.splice(widIndex, 1);
Expand All @@ -212,7 +223,7 @@ export class Transaction {
).toString(),
usersOut,
usersCountOut,
repoBox.register_value(6)!,
R6,
widIndex
)
);
Expand Down Expand Up @@ -249,7 +260,9 @@ export class Transaction {
} else {
// All tokens should be unlocked and no need to create a new permit box
// But it already has some permits so needs the wid token
logger.debug(`Creating a new wid box permit rwts are all returned`);
logger.debug(
`Creating a new wid box for other permits, all permits in the existing box are returned`
);
outputBoxes.push(
Transaction.boxes.createWIDBox(
height,
Expand All @@ -259,6 +272,7 @@ export class Transaction {
)
);
}
if (feeBox) inputBoxes.push(feeBox);
const totalErgIn = inputBoxes
.map((item) => BigInt(item.value().as_i64().to_str()))
.reduce((a, b) => a + b, 0n);
Expand All @@ -269,9 +283,11 @@ export class Transaction {
BigInt(Transaction.fee.as_i64().to_str()) +
BigInt(Transaction.minBoxValue.as_i64().to_str());
if (totalErgOut > totalErgIn) {
const existingBoxIds = [widBox.box_id().to_str()];
if (feeBox) existingBoxIds.push(feeBox.box_id().to_str());
const userBoxes = await Transaction.boxes.getUserPaymentBox(
totalErgOut - totalErgIn,
[widBox.box_id().to_str()]
existingBoxIds
);
userBoxes.forEach((box) => inputBoxes.push(box));
}
Expand Down Expand Up @@ -351,6 +367,8 @@ export class Transaction {
const permitBoxes = await Transaction.boxes.getPermits(WID, RWTCount);
let repoBox = await Transaction.boxes.getRepoBox();
let widBox = await Transaction.boxes.getWIDBox(WID);
const height = await ErgoNetwork.getHeight();

if (widBox.tokens().get(0).id().to_str() != WID) {
try {
await DetachWID.detachWIDtx(
Expand All @@ -372,29 +390,33 @@ export class Transaction {
}
try {
let tx: wasm.Transaction,
remainingRwt = RWTCount;
remainingRwt = RWTCount,
feeBox: wasm.ErgoBox | undefined = undefined;
const unlockTxIds: Array<string> = [];
for (const permitBox of permitBoxes) {
const permitRwt = BigInt(
permitBox.tokens().get(0).amount().as_i64().to_str()
);
const unlockingRwt = RWTCount > permitRwt ? permitRwt : RWTCount;
logger.debug(
`Unlocking ${unlockingRwt} locked in permitBox: [${
permitBox.box_id().to_str
}], using widBox: [${widBox
`Unlocking ${unlockingRwt} locked in permitBox: [${permitBox
.box_id()
.to_str()}], using widBox: [${widBox
.box_id()
.to_str()}] and repoBox: [${repoBox.box_id().to_str()}]`
);
({ tx, remainingRwt } = await this.returnPermitTx(
height,
unlockingRwt,
permitBox,
repoBox,
widBox,
WID
WID,
feeBox
));
repoBox = tx.outputs().get(0);
widBox = tx.outputs().get(1);
feeBox = tx.outputs().len() > 3 ? tx.outputs().get(2) : undefined;
unlockTxIds.push(tx.id().to_str());
}
const isAlreadyWatcher = remainingRwt > 0;
Expand All @@ -406,7 +428,7 @@ export class Transaction {
status: 200,
};
} catch (e) {
logger.warn('Unlock operation exited incomplete');
logger.warn(`Unlock operation exited incomplete by error: ${e.message}`);
if (e instanceof NotEnoughFund) {
return {
response: `Not enough ERG to complete the unlock operation`,
Expand Down
6 changes: 6 additions & 0 deletions services/watcher/src/ergo/boxes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import { NotEnoughFund, NoWID } from '../errors/errors';
import { getConfig } from '../config/config';
import { AddressBalance } from './interfaces';
import { JsonBI } from './network/parser';
import WinstonLogger from '@rosen-bridge/winston-logger';

const logger = WinstonLogger.getInstance().getLogger(import.meta.url);

export class Boxes {
dataBase: WatcherDataBase;
Expand Down Expand Up @@ -479,6 +482,9 @@ export class Boxes {
);
repoBuilder.set_register_value(6, R6);
R7 && repoBuilder.set_register_value(7, wasm.Constant.from_i32(R7));
const boxVal = repoBuilder.calc_min_box_value();
logger.debug(`calculated value for repo: [${boxVal.as_i64().to_str()}]`);
if (boxVal > this.minBoxValue) repoBuilder.set_value(boxVal);
return repoBuilder.build();
};

Expand Down
10 changes: 8 additions & 2 deletions services/watcher/src/ergo/network/ergoNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ergoTreeToBase58Address } from '../../utils/utils';
import { ConnectionError } from '../../errors/errors';
import { getConfig } from '../../config/config';
import { ExplorerBox, ErgoAssetInfo } from '../network/types';
import { MAX_API_LIMIT } from '../../config/constants';
import WinstonLogger from '@rosen-bridge/winston-logger';

const logger = WinstonLogger.getInstance().getLogger(import.meta.url);
Expand Down Expand Up @@ -125,13 +126,18 @@ export class ErgoNetwork {
return tokenRemain + bigIntMax(amount, 0n) > 0;
};
while (offset < total && remaining()) {
const boxes = await this.getBoxesForAddress(tree, offset, 10);
const boxes = await this.getBoxesForAddress(tree, offset, MAX_API_LIMIT);
const ergoBoxes = wasm.ErgoBoxes.from_boxes_json(
boxes.items.map((box) => JsonBI.stringify(box).toString())
);
logger.debug(
`total boxes: ${total}, offset: ${offset}, number of current boxes: ${boxes.items.length}`
);
for (let i = 0; i < ergoBoxes.len(); i++) {
const box = ergoBoxes.get(i);
logger.debug(`processing box with boxId: [${box.box_id().to_str()}]`);
if (filter(box)) {
logger.debug(`added box with boxId: [${box.box_id().to_str()}]`);
res.push(box);
amount -= BigInt(box.value().as_i64().to_str());
if (box.tokens().len() > 0) {
Expand All @@ -148,7 +154,7 @@ export class ErgoNetwork {
if (!remaining()) break;
}
}
offset += 10;
offset += MAX_API_LIMIT;
}
return {
boxes: res,
Expand Down
5 changes: 5 additions & 0 deletions services/watcher/src/utils/watcherUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,11 @@ class TransactionUtils {
height,
requestId
);
logger.debug(
`tx submitted to the queue successfully: [${Buffer.from(
tx.sigma_serialize_bytes()
).toString('hex')}]`
);
};
}

Expand Down
15 changes: 8 additions & 7 deletions services/watcher/tests/ergo/objects/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import { explorerApi, nodeClient } from '../../../src/ergo/network/ergoNetwork';
import { mockedResponseBody } from './mockedResponseBody';
import { validBox0Token, validBox1Token } from '../../database/mockedData';
import { MAX_API_LIMIT } from '../../../src/config/constants';

const mockedExplorer = new MockAdapter(explorerApi);
const mockedNodeClient = new MockAdapter(nodeClient);
Expand Down Expand Up @@ -33,7 +34,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/10130400040004040400040204000e20a40b86c663fbbfefa243c9c6ebbc5690fc4e385f15b44c49ba469c91c5af0f480404040004000400010104020400040004000e20872fee02938af6c93dff43049ec61b379e75c059b05f39304b3f1ce50cf3ad9305020101d807d601b2a5730000d6028cb2db6308a773010001d603aeb5b4a57302b1a5d901036391b1db630872037303d9010363aedb63087203d901054d0e938c7205017202d604e4c6a7041ad605b2a5730400d606db63087205d607ae7206d901074d0e938c720701720295938cb2db63087201730500017306d196830301ef7203938cb2db6308b2a473070073080001b2720473090095720796830201938cb27206730a0001720293c27205c2a7730bd801d608c2a7d196830501ef720393c27201720893e4c67201041a7204938cb2db6308b2a4730c00730d0001b27204730e00957207d801d609b27206730f0096830701938c720901720293cbc272057310e6c67205051ae6c67205060e93e4c67205070ecb720893e4c67205041a7204938c72090273117312',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, [mockedResponseBody.watcherPermitLast10Boxes]);

Expand All @@ -47,7 +48,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/101c040204000e20a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac8530101040204000402040404040400040004020402040204000400040004000e2013fe3ae277a195b83048e3e268529118fa4c18cca0931e3b48a8f5fccec75bc9040404000400040204020400040004000400d801d601b2a473000095938cb2db63087201730100017302d17303d811d602db6308a7d603b27202730400d6048c720302d605b2a5730500d606db63087205d607b27206730600d6088c720702d609e4c6a70511d60ab17209d60be4c672050511d60cb1720bd60de4c6a70611d60eb27206730700d60f8c720e02d610b27202730800d6118c721002d6129683050193c27205c2a793e4c672050611720d938cb27206730900018cb27202730a0001938c7207018c720301938c720e018c721001959172047208d806d613e4c67205041ad6149a720a730bd61599720c730cd616c5a7d6179972047208d618b2a5730d00d196830c01721293b17213721493b47213730e7215e4c6a7041a93b27213721500721693720c721493b4720b730f7215720993b2720b7215007217939c7217b2720d73100099720f7211938cb2db6308721873110002721793cbc27218731293e4c67218041a83010e7216938cb2db6308b2a5731300731400017216d804d613e4c6a7041ad614e4c672050704d6159972087204d616b27209721400d19683040172129383010eb27213721400e4c67201041a939c7215b2720d731500997211720f959172167215968302019372169ab2720b7214007215937213e4c67205041ad803d617e4c67205041ad6189a72147316d61999720a731796830501937216721593b4721373187214b472177319721493b472137218720ab472177214721993b47209731a7214b4720b731b721493b472097218720ab4720b72147219',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, [mockedResponseBody.repoLast10Boxes]);

Expand All @@ -64,7 +65,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/10130400040004040400040204000e20a40b86c663fbbfefa243c9c6ebbc5690fc4e385f15b44c49ba469c91c5af0f480404040004000400010104020400040004000e20872fee02938af6c93dff43049ec61b379e75c059b05f39304b3f1ce50cf3ad9305020101d807d601b2a5730000d6028cb2db6308a773010001d603aeb5b4a57302b1a5d901036391b1db630872037303d9010363aedb63087203d901054d0e938c7205017202d604e4c6a7041ad605b2a5730400d606db63087205d607ae7206d901074d0e938c720701720295938cb2db63087201730500017306d196830301ef7203938cb2db6308b2a473070073080001b2720473090095720796830201938cb27206730a0001720293c27205c2a7730bd801d608c2a7d196830501ef720393c27201720893e4c67201041a7204938cb2db6308b2a4730c00730d0001b27204730e00957207d801d609b27206730f0096830701938c720901720293cbc272057310e6c67205051ae6c67205060e93e4c67205070ecb720893e4c67205041a7204938c72090273117312',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, [mockedResponseBody.thirdWatcherLast10PermitBox]);

Expand All @@ -78,7 +79,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/101c040204000e20a6ac381e6fa99929fd1477b3ba9499790a775e91d4c14c5aa86e9a118dfac8530101040204000402040404040400040004020402040204000400040004000e2013fe3ae277a195b83048e3e268529118fa4c18cca0931e3b48a8f5fccec75bc9040404000400040204020400040004000400d801d601b2a473000095938cb2db63087201730100017302d17303d811d602db6308a7d603b27202730400d6048c720302d605b2a5730500d606db63087205d607b27206730600d6088c720702d609e4c6a70511d60ab17209d60be4c672050511d60cb1720bd60de4c6a70611d60eb27206730700d60f8c720e02d610b27202730800d6118c721002d6129683050193c27205c2a793e4c672050611720d938cb27206730900018cb27202730a0001938c7207018c720301938c720e018c721001959172047208d806d613e4c67205041ad6149a720a730bd61599720c730cd616c5a7d6179972047208d618b2a5730d00d196830c01721293b17213721493b47213730e7215e4c6a7041a93b27213721500721693720c721493b4720b730f7215720993b2720b7215007217939c7217b2720d73100099720f7211938cb2db6308721873110002721793cbc27218731293e4c67218041a83010e7216938cb2db6308b2a5731300731400017216d804d613e4c6a7041ad614e4c672050704d6159972087204d616b27209721400d19683040172129383010eb27213721400e4c67201041a939c7215b2720d731500997211720f959172167215968302019372169ab2720b7214007215937213e4c67205041ad803d617e4c67205041ad6189a72147316d61999720a731796830501937216721593b4721373187214b472177319721493b472137218720ab472177214721993b47209731a7214b4720b731b721493b472097218720ab4720b72147219',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, [mockedResponseBody.thirdRepoBox]);
break;
Expand Down Expand Up @@ -159,7 +160,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/0008cd034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, mockedResponseBody.thirdWatcherLastUnspentBox);

Expand All @@ -173,7 +174,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/0008cd03c880d703131f301badf289ceb9b7f86d674e8cbe390461f66e844f507571a1d6',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, [mockedResponseBody.firstWatcherLast10UnspentBoxes]);

Expand All @@ -187,7 +188,7 @@ export const initMockedAxios = (vector = 0) => {
mockedExplorer
.onGet(
'/api/v1/boxes/unspent/byErgoTree/0008cd03c29ad59831be2e5baded45a03ce9a7d4c2e83d683e11c79790e76f640d0d3e30',
{ params: { offset: 0, limit: 10 } }
{ params: { offset: 0, limit: MAX_API_LIMIT } }
)
.reply(200, [mockedResponseBody.secondWatcherLast10UnspentBox]);

Expand Down

0 comments on commit 6e2851b

Please sign in to comment.