Skip to content

Commit

Permalink
Merge branch '94-evmchain-sign-function' into 'dev'
Browse files Browse the repository at this point in the history
Resolve "EvmChain: sign function"

Closes #94

See merge request ergo/rosen-bridge/rosen-chains!109
  • Loading branch information
vorujack committed Apr 27, 2024
2 parents febf485 + b47572a commit 3a81154
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 27 deletions.
File renamed without changes.
2 changes: 1 addition & 1 deletion package-lock.json

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

40 changes: 29 additions & 11 deletions packages/chains/evm/lib/EvmChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import { EvmRosenExtractor } from '@rosen-bridge/rosen-extractor';
import AbstractEvmNetwork from './network/AbstractEvmNetwork';
import { EvmConfigs, TssSignFunction } from './types';
import { Transaction } from 'ethers';
import { Signature, Transaction } from 'ethers';
import Serializer from './Serializer';
import * as EvmUtils from './EvmUtils';

Expand Down Expand Up @@ -114,8 +114,7 @@ abstract class EvmChain extends AbstractChain<Transaction> {
).length +
serializedSignedTransactions.filter(
(tx) =>
Serializer.signedDeserialize(Buffer.from(tx, 'hex')).nonce ===
nextNonce
Serializer.deserialize(Buffer.from(tx, 'hex')).nonce === nextNonce
).length;
if (waiting > this.configs.maxParallelTx) {
throw new MaxParallelTxError(
Expand Down Expand Up @@ -422,11 +421,7 @@ abstract class EvmChain extends AbstractChain<Transaction> {
let trx: Transaction;

try {
if (signingStatus === SigningStatus.Signed) {
trx = Serializer.signedDeserialize(transaction.txBytes);
} else {
trx = Serializer.deserialize(transaction.txBytes);
}
trx = Serializer.deserialize(transaction.txBytes);
} catch (error) {
this.logger.debug(`Tx [${transaction.txId}] invalid: ${error}`);
return false;
Expand Down Expand Up @@ -456,8 +451,31 @@ abstract class EvmChain extends AbstractChain<Transaction> {
transaction: PaymentTransaction,
requiredSign: number
): Promise<PaymentTransaction> => {
// TODO: implement this function (local:ergo/rosen-bridge/rosen-chains#94)
throw new Error('Not implemented yet.');
const tx = Serializer.deserialize(transaction.txBytes);
return this.signFunction(Buffer.from(tx.unsignedHash, 'hex')).then(
(res) => {
const r = '0x' + res.signature.slice(0, 64);
const s = '0x' + res.signature.slice(64, 128);
const yParity = Number(res.signatureRecovery);
if (yParity !== 0 && yParity !== 1)
throw new ImpossibleBehavior(
`non-binary signature recovery: ${res.signatureRecovery}`
);
const signature = Signature.from({
r,
s,
yParity: yParity,
});
tx.signature = signature;
return new PaymentTransaction(
transaction.network,
transaction.txId,
transaction.eventId,
Serializer.signedSerialize(tx),
transaction.txType
);
}
);
};

/**
Expand All @@ -474,7 +492,7 @@ abstract class EvmChain extends AbstractChain<Transaction> {
// deserialize transaction
let tx: Transaction;
try {
tx = Serializer.signedDeserialize(transaction.txBytes);
tx = Serializer.deserialize(transaction.txBytes);
} catch (error) {
this.logger.debug(`Tx [${transaction.txId}] invalid: ${error}`);
return;
Expand Down
15 changes: 1 addition & 14 deletions packages/chains/evm/lib/Serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Serializer {
};

/**
* converts bytearray representation of the unsigned transaction to the transaction model in the chain
* converts bytearray representation of the transaction to the transaction model in the chain
* @param txBytes bytearray representation of the transaction
* @returns the transaction model in the chain library
*/
Expand All @@ -40,19 +40,6 @@ class Serializer {
throw new SerializationError(`${error}`);
}
};

/**
* converts bytearray representation of the signed transaction to the transaction model in the chain
* @param txBytes bytearray representation of the transaction
* @returns the transaction model in the chain library
*/
static signedDeserialize = (txBytes: Uint8Array): Transaction => {
try {
return Transaction.from('0x' + Buffer.from(txBytes).toString('hex'));
} catch (error) {
throw new SerializationError(`${error}`);
}
};
}

export default Serializer;
2 changes: 1 addition & 1 deletion packages/chains/evm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rosen-chains/evm",
"version": "0.1.0",
"version": "0.1.1",
"description": "this project contains evm chains for Rosen-bridge",
"repository": "https://github.com/rosen-bridge/rosen-chains",
"license": "GPL-3.0",
Expand Down
88 changes: 88 additions & 0 deletions packages/chains/evm/tests/EvmChain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1755,4 +1755,92 @@ describe('EvmChain', () => {
expect(result).toEqual(false);
});
});

describe('signTransaction', () => {
/**
* @target EvmChain.signTransaction should return PaymentTransaction of the
* signed transaction
* @dependencies
* @scenario
* - mock a sign function to return signature
* - mock PaymentTransaction of unsigned transaction
* - call the function
* - check returned value
* @expected
* - it should return PaymentTransaction of signed transaction (all fields
* are same as input object, except txBytes which is signed transaction)
* - signed tx bytes and hash should be as expected
*/
it('should return PaymentTransaction of the signed transaction', async () => {
// mock a sign function to return signature
const signFunction = async (txHash: Uint8Array) => {
return {
signature: TestData.transaction2Signature,
signatureRecovery: TestData.transaction2SignatureRecovery,
};
};
const evmChain = generateChainObject(network, signFunction);

// mock PaymentTransaction of unsigned transaction
const eventId = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
const txType = TransactionType.payment;
const tx = Transaction.from(TestData.transaction2UnsignedTx);

const paymentTx = new PaymentTransaction(
evmChain.CHAIN,
tx.unsignedHash,
eventId,
Serializer.serialize(tx),
txType
);

// call the function
const result = await evmChain.signTransaction(paymentTx, 0);

// check returned value
expect(result.txId).toEqual(paymentTx.txId);
expect(result.txType).toEqual(paymentTx.txType);
expect(result.eventId).toEqual(paymentTx.eventId);
expect(result.network).toEqual(paymentTx.network);
const signedTx = Serializer.deserialize(result.txBytes);
expect(signedTx.serialized).toEqual(TestData.transaction2SignedTx);
expect(signedTx.hash).toEqual(TestData.transaction2TxId);
});

/**
* @target EvmChain.signTransaction should throw error when signing failed
* @dependencies
* @scenario
* - mock a sign function to throw error
* - mock PaymentTransaction of unsigned transaction
* - call the function & check thrown exception
* @expected
* - it should throw the exact error thrown by sign function
*/
it('should throw error when signing failed', async () => {
// mock a sign function to throw error
const signFunction = async (txHash: Uint8Array) => {
throw Error(`TestError: sign failed`);
};
const evmChain = generateChainObject(network, signFunction);

// mock PaymentTransaction of unsigned transaction
const eventId = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
const txType = TransactionType.payment;
const tx = Transaction.from(TestData.transaction2UnsignedTx);

const paymentTx = new PaymentTransaction(
evmChain.CHAIN,
tx.unsignedHash,
eventId,
Serializer.serialize(tx),
txType
);

// call the function & check thrown exception
await expect(async () => {
await evmChain.signTransaction(paymentTx, 0);
}).rejects.toThrow('TestError: sign failed');
});
});
});
10 changes: 10 additions & 0 deletions packages/chains/evm/tests/testData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,13 @@ export const paralelTransactions = [
accessList: [],
}),
];

export const transaction2UnsignedTx =
'0x02f0010a841dcd6500850b675899a0825208944606d11ff65b17d29e8c5e4085f9a868a8e5e4f2880149df7b6be0313680c0';
export const transaction2SignedTx =
'0x02f873010a841dcd6500850b675899a0825208944606d11ff65b17d29e8c5e4085f9a868a8e5e4f2880149df7b6be0313680c001a020775ad79e6b7ab8902503c4b3d9b57a14dd9a931b549f2d44d9e335e308a806a007d6a4560e5e586abcea735cb35a2fcaa7c19b6aba460fba37f604ad46dd270d';
export const transaction2Signature =
'20775ad79e6b7ab8902503c4b3d9b57a14dd9a931b549f2d44d9e335e308a80607d6a4560e5e586abcea735cb35a2fcaa7c19b6aba460fba37f604ad46dd270d';
export const transaction2SignatureRecovery = '01';
export const transaction2TxId =
'0x73c9ff5665d84067d98afbbeb2d9ff316c4b5bf885f9f3d31fa56eb1b13b3b90';

0 comments on commit 3a81154

Please sign in to comment.