Skip to content

Commit

Permalink
Merge branch 'Add-CommitmentTxBuilder-box-creators' into 'dev'
Browse files Browse the repository at this point in the history
implement CommitmentTxBuilder box creators

Closes #156

See merge request ergo/rosen-bridge/watcher!162
  • Loading branch information
vorujack committed Nov 5, 2023
2 parents 7110fff + 91bb031 commit d42bc72
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 1 deletion.
128 changes: 128 additions & 0 deletions packages/transactions/commitment-tx/lib/commitmentTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ObservationEntity } from '@rosen-bridge/observation-extractor';
import { RWTRepo } from '@rosen-bridge/rwt-repo';
import { blake2b } from 'blakejs';
import * as ergoLib from 'ergo-lib-wasm-nodejs';
import { bigIntToUint8Array, hexToUint8Array } from './utils';

export class CommitmentTx {
private static _instance?: CommitmentTx;
Expand All @@ -11,7 +12,9 @@ export class CommitmentTx {

private constructor(
private permitAddress: string,
private permitBoxValue: bigint,
private commitmentAddress: string,
private commitmentBoxValue: bigint,
private rwt: string,
private txFee: string,
private rwtRepo: RWTRepo,
Expand All @@ -34,7 +37,9 @@ export class CommitmentTx {
*/
static init = (
permitAddress: string,
permitBoxValue: bigint,
commitmentAddress: string,
commitmentBoxValue: bigint,
rwt: string,
txFee: string,
rwtRepo: RWTRepo,
Expand All @@ -46,7 +51,9 @@ export class CommitmentTx {

CommitmentTx._instance = new CommitmentTx(
permitAddress,
permitBoxValue,
commitmentAddress,
commitmentBoxValue,
rwt,
txFee,
rwtRepo,
Expand Down Expand Up @@ -80,8 +87,10 @@ export class CommitmentTx {
newBuilder = (observation: ObservationEntity): CommitmentTxBuilder => {
return new CommitmentTxBuilder(
this.permitAddress,
this.permitBoxValue,
this.permitScriptHash,
this.commitmentAddress,
this.commitmentBoxValue,
this.rwt,
this.txFee,
this.rwtRepo,
Expand All @@ -101,8 +110,10 @@ export class CommitmentTxBuilder {

constructor(
private permitAddress: string,
private permitBoxValue: bigint,
private permitScriptHash: string,
private commitmentAddress: string,
private commitmentBoxValue: bigint,
private rwt: string,
private txFee: string,
private rwtRepo: RWTRepo,
Expand Down Expand Up @@ -201,6 +212,123 @@ export class CommitmentTxBuilder {
this.logger?.debug(`new value set for height=[${this.height}]`);
return this;
};

/**
* creates permit box
*
* @private
* @param {bigint} rwtCount
* @return {ergoLib.ErgoBoxCandidate}
* @memberof CommitmentTxBuilder
*/
private createPermitBox = (rwtCount: bigint): ergoLib.ErgoBoxCandidate => {
const boxBuilder = new ergoLib.ErgoBoxCandidateBuilder(
ergoLib.BoxValue.from_i64(
ergoLib.I64.from_str(this.permitBoxValue.toString())
),
ergoLib.Contract.pay_to_address(
ergoLib.Address.from_base58(this.permitAddress)
),
this.height
);

boxBuilder.add_token(
ergoLib.TokenId.from_str(this.rwt),
ergoLib.TokenAmount.from_i64(ergoLib.I64.from_str(rwtCount.toString()))
);
this.logger?.debug(
`rwtCount=[${rwtCount}] rwt tokens added to output permit box`
);

boxBuilder.set_register_value(
4,
ergoLib.Constant.from_coll_coll_byte([hexToUint8Array(this.wid)])
);

return boxBuilder.build();
};

/**
* creates commitment box
*
* @private
* @return {ergoLib.ErgoBoxCandidate}
* @memberof CommitmentTxBuilder
*/
private createCommitmentBox = (): ergoLib.ErgoBoxCandidate => {
const boxBuilder = new ergoLib.ErgoBoxCandidateBuilder(
ergoLib.BoxValue.from_i64(
ergoLib.I64.from_str(this.commitmentBoxValue.toString())
),
ergoLib.Contract.pay_to_address(
ergoLib.Address.from_base58(this.commitmentAddress)
),
this.height
);

boxBuilder.add_token(
ergoLib.TokenId.from_str(this.rwt),
ergoLib.TokenAmount.from_i64(
ergoLib.I64.from_str(this.rwtRepo.getCommitmentRwtCount().toString())
)
);
this.logger?.debug(
`added rwt token to commitment box with amount=[${this.rwtRepo.getCommitmentRwtCount()}]`
);

boxBuilder.set_register_value(
4,
ergoLib.Constant.from_coll_coll_byte([hexToUint8Array(this.wid)])
);

boxBuilder.set_register_value(
5,
ergoLib.Constant.from_coll_coll_byte([hexToUint8Array(this.eventId)])
);

boxBuilder.set_register_value(
6,
ergoLib.Constant.from_byte_array(this.eventDigest)
);
this.logger?.debug(
`output commitment box R6 register value: eventDigest=[${Buffer.from(
this.eventDigest
).toString('hex')}]`
);

boxBuilder.set_register_value(
7,
ergoLib.Constant.from_byte_array(hexToUint8Array(this.permitScriptHash))
);

return boxBuilder.build();
};

/**
* calculates event digest for this instance's observation and wid
*
* @readonly
* @private
* @memberof CommitmentTxBuilder
*/
private get eventDigest() {
const content = Buffer.concat([
Buffer.from(this.observation.sourceTxId),
Buffer.from(this.observation.fromChain),
Buffer.from(this.observation.toChain),
Buffer.from(this.observation.fromAddress),
Buffer.from(this.observation.toAddress),
bigIntToUint8Array(BigInt(this.observation.amount)),
bigIntToUint8Array(BigInt(this.observation.bridgeFee)),
bigIntToUint8Array(BigInt(this.observation.networkFee)),
Buffer.from(this.observation.sourceChainTokenId),
Buffer.from(this.observation.targetChainTokenId),
Buffer.from(this.observation.sourceBlockId),
bigIntToUint8Array(BigInt(this.observation.height)),
Buffer.from(this.wid, 'hex'),
]);
return blake2b(content, undefined, 32);
}
}

/**
Expand Down
20 changes: 20 additions & 0 deletions packages/transactions/commitment-tx/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* converts bigint to Uint8Array
*
* @param {bigint} num
* @return {Uint8Array}
*/
export const bigIntToUint8Array = (num: bigint): Uint8Array => {
const b = new ArrayBuffer(8);
new DataView(b).setBigUint64(0, num);
return new Uint8Array(b);
};

/**
* converts a hex string to Uint8Array bytes
*
* @param {string} hex
* @return {Uint8Array}
*/
export const hexToUint8Array = (hex: string): Uint8Array =>
Uint8Array.from(Buffer.from(hex, 'hex'));
124 changes: 124 additions & 0 deletions packages/transactions/commitment-tx/tests/commitmentTx.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ describe('CommitmentTx', () => {
properties and passed arguments`, async () => {
CommitmentTx.init(
commitmentTxParams.permitAddress,
commitmentTxParams.permitBoxValue,
commitmentTxParams.commitmentAddress,
commitmentTxParams.commitmentBoxValue,
commitmentTxParams.rwt,
commitmentTxParams.txFee,
commitmentTxParams.rwtRepo
Expand All @@ -63,12 +65,18 @@ describe('CommitmentTx', () => {
expect(commitmentTxBuilder['permitAddress']).toEqual(
commitmentTxParams.permitAddress
);
expect(commitmentTxBuilder['permitBoxValue']).toEqual(
commitmentTxParams.permitBoxValue
);
expect(commitmentTxBuilder['permitScriptHash']).toEqual(
commitmentTxParams.permitScriptHash
);
expect(commitmentTxBuilder['commitmentAddress']).toEqual(
commitmentTxParams.commitmentAddress
);
expect(commitmentTxBuilder['commitmentBoxValue']).toEqual(
commitmentTxParams.commitmentBoxValue
);
expect(commitmentTxBuilder['rwt']).toEqual(commitmentTxParams.rwt);
expect(commitmentTxBuilder['txFee']).toEqual(commitmentTxParams.txFee);
expect(commitmentTxBuilder['rwtRepo']).toEqual(
Expand All @@ -90,8 +98,10 @@ describe('CommitmentTxBuilder', () => {

commitmentTxBuilder = new CommitmentTxBuilder(
commitmentTxParams.permitAddress,
commitmentTxParams.permitBoxValue,
permitScriptHash,
commitmentTxParams.commitmentAddress,
commitmentTxParams.commitmentBoxValue,
commitmentTxParams.rwt,
commitmentTxParams.txFee,
commitmentTxParams.rwtRepo,
Expand Down Expand Up @@ -271,4 +281,118 @@ describe('CommitmentTxBuilder', () => {
expect(commitmentTxBuilder['height']).toEqual(newHeight);
});
});

describe('createPermitBox', () => {
/**
* @target should create permit box from instance's properties
* @dependencies
* - None
* @scenario
* - call setWid
* - call setCreationHeight
* - call createPermitBox with a value for rwtCount
* - check returned box to have the right properties set
* @expected
* - returned box should have the right properties set
*/
it(`should create permit box from instance's properties`, async () => {
const r4 = ergoLib.Constant.decode_from_base16(
samplePermitBoxes[0].additionalRegisters.R4
).to_coll_coll_byte();
const wid = Buffer.from(r4[0]).toString('hex');
commitmentTxBuilder.setWid(wid);

const height = 100;
commitmentTxBuilder.setCreationHeight(height);

const rwtCount = 20n;
const permitBox = commitmentTxBuilder['createPermitBox'](rwtCount);

expect(permitBox.value().as_i64().to_str()).toEqual(
commitmentTxBuilder['permitBoxValue'].toString()
);

expect(
ergoLib.Address.recreate_from_ergo_tree(
permitBox.ergo_tree()
).to_base58(ergoLib.NetworkPrefix.Mainnet)
).toEqual(commitmentTxBuilder['permitAddress']);

expect(permitBox.creation_height()).toEqual(
commitmentTxBuilder['height']
);

expect(permitBox.tokens().get(0).id().to_str()).toEqual(
commitmentTxBuilder['rwt']
);
expect(permitBox.tokens().get(0).amount().as_i64().to_str()).toEqual(
rwtCount.toString()
);

expect(permitBox.register_value(4)?.to_coll_coll_byte()).toEqual(r4);
});
});

describe('createCommitmentBox', () => {
/**
* @target should create a commitment box from instance's properties
* @dependencies
* - None
* @scenario
* - call setWid
* - call setCreationHeight
* - call createCommitmentBox
* - check returned box to have the right properties set
* @expected
* - returned box should have the right properties set
*/
it(`should create a commitment box from instance's properties`, async () => {
const r4 = ergoLib.Constant.decode_from_base16(
samplePermitBoxes[0].additionalRegisters.R4
).to_coll_coll_byte();
const wid = Buffer.from(r4[0]).toString('hex');
commitmentTxBuilder.setWid(wid);

const height = 100;
commitmentTxBuilder.setCreationHeight(height);

const commitmentBox = commitmentTxBuilder['createCommitmentBox']();

expect(commitmentBox.value().as_i64().to_str()).toEqual(
commitmentTxBuilder['commitmentBoxValue'].toString()
);

expect(
ergoLib.Address.recreate_from_ergo_tree(
commitmentBox.ergo_tree()
).to_base58(ergoLib.NetworkPrefix.Mainnet)
).toEqual(commitmentTxBuilder['commitmentAddress']);

expect(commitmentBox.creation_height()).toEqual(
commitmentTxBuilder['height']
);

expect(commitmentBox.tokens().get(0).id().to_str()).toEqual(
commitmentTxBuilder['rwt']
);
expect(commitmentBox.tokens().get(0).amount().as_i64().to_str()).toEqual(
commitmentTxBuilder['rwtRepo'].getCommitmentRwtCount().toString()
);

expect(commitmentBox.register_value(4)?.to_coll_coll_byte()).toEqual(r4);
expect(
Buffer.from(
commitmentBox.register_value(5)!.to_coll_coll_byte()[0]
).toString('hex')
).toEqual(commitmentTxBuilder['eventId']);
expect(commitmentBox.register_value(6)?.to_byte_array()).toEqual(
commitmentTxBuilder['eventDigest']
);
expect(
Buffer.from(commitmentBox.register_value(7)!.to_byte_array()).toString(
'hex'
)
).toEqual(commitmentTxBuilder['permitScriptHash']);
});
});
});
Loading

0 comments on commit d42bc72

Please sign in to comment.