Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tron): add trc10 test for ci #33

Merged
merged 5 commits into from Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion offchain-modules/config.json.example
Expand Up @@ -95,7 +95,7 @@
"EE782FE170F680D6CAB340ECA5ED2F6E05B0B9809082CF745207E87734211C72"
]
},
"feeLimit": 1000000
"feeLimit": 10000000
}
}
}
3 changes: 2 additions & 1 deletion offchain-modules/src/packages/handlers/ckb.ts
Expand Up @@ -14,6 +14,7 @@ import Transaction = CKBComponents.Transaction;
import { Script as LumosScript } from '@ckb-lumos/base';
import { BigNumber } from 'ethers';
import { RecipientCellData } from '@force-bridge/ckb/tx-helper/generated/eth_recipient_cell';
import { getAssetTypeByAsset } from '@force-bridge/xchain/tron/utils';

// CKB handler
// 1. Listen CKB chain to get new burn events.
Expand Down Expand Up @@ -44,7 +45,7 @@ export class CkbHandler {
{
ckbTxHash: burn.ckbTxHash,
asset: burn.asset,
assetType: burn.asset,
assetType: getAssetTypeByAsset(burn.asset),
amount: burn.amount,
recipientAddress: burn.recipientAddress,
},
Expand Down
23 changes: 8 additions & 15 deletions offchain-modules/src/packages/handlers/tron.ts
Expand Up @@ -6,6 +6,7 @@ import { ITronLock, TronUnlock, ICkbMint, TronLock } from '@force-bridge/db/mode
import { ChainType } from '@force-bridge/ckb/model/asset';
import { promises } from 'fs';
import { sign } from '@force-bridge/ckb/tx-helper/signer';
import { getAssetTypeByAsset } from '@force-bridge/xchain/tron/utils';
const TronWeb = require('tronweb');
const TronGrid = require('trongrid');

Expand Down Expand Up @@ -77,6 +78,10 @@ export class TronHandler {
fingerprint: fingerprint,
});
for (const data of txs.data) {
if (Object.keys(data.token_info).length == 0) {
logger.debug('invalid trc20 tx, token info is undefined', data);
continue;
}
const tx = await this.tronWeb.trx.getTransaction(data.transaction_id);
const event = {
tx_hash: data.transaction_id,
Expand All @@ -103,17 +108,6 @@ export class TronHandler {
return { ckbRecipient, sudtExtraData };
}

private getAssetTypeByAsset(asset: string) {
switch (asset.length) {
case TRX_ASSET_LENGTH:
return 'trx';
case TRC10_ASSET_LENGTH:
return 'trc10';
default:
return 'trc20';
}
}

private transferEventToCkbMint(event: TronLockEvent) {
const { ckbRecipient, sudtExtraData } = this.analyzeMemo(event.memo);
return {
Expand All @@ -132,7 +126,7 @@ export class TronHandler {
txIndex: 0,
sender: event.sender,
asset: event.asset,
assetType: this.getAssetTypeByAsset(event.asset),
assetType: getAssetTypeByAsset(event.asset),
amount: event.amount,
memo: event.memo,
timestamp: event.timestamp,
Expand Down Expand Up @@ -162,7 +156,7 @@ export class TronHandler {
const trc20LockEvents = await this.getTrc20TxsLockEvents(minTimestamp);

const totalLockEvents = trxAndTrc10Events.concat(trc20LockEvents);
logger.debug('total lock events', totalLockEvents.length);
logger.debug('total lock events', totalLockEvents);

for (const event of totalLockEvents) {
if (event.timestamp <= minTimestamp) {
Expand Down Expand Up @@ -227,7 +221,6 @@ export class TronHandler {
for (const key of this.committee.keys) {
signed_tx = await this.tronWeb.trx.multiSign(signed_tx, key);
}

return signed_tx;
}

Expand Down Expand Up @@ -276,7 +269,6 @@ export class TronHandler {
logger.debug('flush pending tx to confirm');
const pendingRecords = await this.db.getTronUnlockRecords('pending');
for (const pendingRecord of pendingRecords) {
// todo: check tx is confirmed
try {
const confirmedTx = await this.tronWeb.trx.getConfirmedTransaction(pendingRecord.tronTxHash);
console.log(confirmedTx);
Expand Down Expand Up @@ -306,6 +298,7 @@ export class TronHandler {
signedTx = await this.multiSignTransferTrc20(unlockRecord);
break;
}

unlockRecord.tronTxHash = signedTx.txID;
unlockRecord.tronTxIndex = 0;
unlockRecord.status = 'pending';
Expand Down
13 changes: 13 additions & 0 deletions offchain-modules/src/packages/xchain/tron/utils.ts
@@ -0,0 +1,13 @@
const TRX_ASSET_LENGTH = 3;
const TRC10_ASSET_LENGTH = 7;

export function getAssetTypeByAsset(asset: string): string {
switch (asset.length) {
case TRX_ASSET_LENGTH:
return 'trx';
case TRC10_ASSET_LENGTH:
return 'trc10';
default:
return 'trc20';
}
}
151 changes: 103 additions & 48 deletions offchain-modules/src/scripts/integration-test/tron.ts
Expand Up @@ -37,6 +37,36 @@ async function transferTrx(tronWeb, from, to, amount, memo, priv) {
return broad_tx;
}

async function transferTrc10(tronWeb, from, to, amount, tokenID, memo, priv) {
const unsigned_tx = await tronWeb.transactionBuilder.sendToken(to, amount, tokenID, from);
const unsignedWithMemoTx = await tronWeb.transactionBuilder.addUpdateData(unsigned_tx, memo, 'utf8');
const signed_tx = await tronWeb.trx.sign(unsignedWithMemoTx, priv);
const broad_tx = await tronWeb.trx.broadcast(signed_tx);
return broad_tx;
}

async function transferTrc20(tronWeb, from, to, amount, contractAddress, memo, priv) {
const options = {};
const functionSelector = 'transfer(address,uint256)';
const params = [
{ type: 'address', value: to },
{ type: 'uint256', value: amount },
];

const unsigned_tx = await tronWeb.transactionBuilder.triggerSmartContract(
contractAddress,
functionSelector,
options,
params,
from,
);
const unsignedWithMemoTx = await tronWeb.transactionBuilder.addUpdateData(unsigned_tx.transaction, memo, 'utf8');

const signed_tx = await tronWeb.trx.sign(unsignedWithMemoTx, priv);
const broad_tx = await tronWeb.trx.broadcast(signed_tx);
return broad_tx;
}

async function main() {
const conn = await createConnection();
const ckbDb = new CkbDb(conn);
Expand All @@ -61,22 +91,52 @@ async function main() {
const recipientLockscript = 'ckt1qyqyph8v9mclls35p6snlaxajeca97tc062sa5gahk';
const sudtExtraData = 'transfer 100 to ckt1qyqyph8v9mclls35p6snlaxajeca97tc062sa5gahk';
const memo = recipientLockscript.concat(',').concat(sudtExtraData);
const lockRes = await transferTrx(tronWeb, from, to, amount, memo, userPrivateKey);
const txHash: string = lockRes.transaction.txID;

const trxLockRes = await transferTrx(tronWeb, from, to, amount, memo, userPrivateKey);
const trxTxHash: string = trxLockRes.transaction.txID;

const trc10LockRes = await transferTrc10(tronWeb, from, to, amount, '1000696', memo, userPrivateKey);
const trc10TxHash: string = trc10LockRes.transaction.txID;

const trc20LockRes = await transferTrc20(
tronWeb,
from,
to,
amount,
'TVWvkCasxAJUyzPKMQ2Rus1NtmBwrkVyBR',
memo,
userPrivateKey,
);
const trc20TxHash: string = trc20LockRes.transaction.txID;

// create tron unlock
const recipientAddress = 'TS6VejPL8cQy6pA8eDGyusmmhCrXHRdJK6';
// const record = {
// ckbTxHash: genRandomHex(32),
// asset: 'trx',
// assetType: 'trx',
// amount: '100',
// recipientAddress,
// };
// await ckbDb.createTronUnlock([record]);
let sendBurn = false;

let burnTxHash;
const checkEffect = async () => {

const getBalance = async (assetName) => {
const account = new Account(PRI_KEY);
const ownLockHash = ckb.utils.scriptToHash(<CKBComponents.Script>await account.getLockscript());
const asset = new TronAsset(assetName, ownLockHash);
const bridgeCellLockscript = {
codeHash: ForceBridgeCore.config.ckb.deps.bridgeLock.script.codeHash,
hashType: ForceBridgeCore.config.ckb.deps.bridgeLock.script.hashType,
args: asset.toBridgeLockscriptArgs(),
};
const sudtArgs = ckb.utils.scriptToHash(<CKBComponents.Script>bridgeCellLockscript);
const sudtType = {
codeHash: ForceBridgeCore.config.ckb.deps.sudtType.script.codeHash,
hashType: ForceBridgeCore.config.ckb.deps.sudtType.script.hashType,
args: sudtArgs,
};
const balance = await collector.getSUDTBalance(
new Script(sudtType.codeHash, sudtType.args, sudtType.hashType),
await account.getLockscript(),
);
return balance;
};

const checkLock = async (txHash, assetName, assetType, sendBurn) => {
// check TronLock and CkbMint saved.
const tronLockRecords = await conn.manager.find(TronLock, {
where: {
Expand All @@ -89,8 +149,8 @@ async function main() {

assert(tronLockRecord.memo === memo);
assert(tronLockRecord.sender === from);
assert(tronLockRecord.asset === 'trx');
assert(tronLockRecord.assetType === 'trx');
assert(tronLockRecord.asset === assetName);
assert(tronLockRecord.assetType === assetType);

const ckbMintRecords = await conn.manager.find(CkbMint, {
where: {
Expand All @@ -103,60 +163,49 @@ async function main() {
assert(ckbMintRecord.chain === ChainType.TRON);
assert(ckbMintRecord.sudtExtraData === sudtExtraData);
assert(ckbMintRecord.status === 'success');
// assert(ckbMintRecord.asset === 'trx');
assert(ckbMintRecord.asset === assetName);
assert(ckbMintRecord.amount === amount.toString());
assert(ckbMintRecord.recipientLockscript === recipientLockscript);

// check sudt balance.
const account = new Account(PRI_KEY);
const ownLockHash = ckb.utils.scriptToHash(<CKBComponents.Script>await account.getLockscript());
const asset = new TronAsset('trx', ownLockHash);
const bridgeCellLockscript = {
codeHash: ForceBridgeCore.config.ckb.deps.bridgeLock.script.codeHash,
hashType: ForceBridgeCore.config.ckb.deps.bridgeLock.script.hashType,
args: asset.toBridgeLockscriptArgs(),
};
const sudtArgs = ckb.utils.scriptToHash(<CKBComponents.Script>bridgeCellLockscript);
const sudtType = {
codeHash: ForceBridgeCore.config.ckb.deps.sudtType.script.codeHash,
hashType: ForceBridgeCore.config.ckb.deps.sudtType.script.hashType,
args: sudtArgs,
};
const balance = await collector.getSUDTBalance(
new Script(sudtType.codeHash, sudtType.args, sudtType.hashType),
await account.getLockscript(),
);
const balance = await getBalance(assetName);

if (!sendBurn) {
logger.debug('assetName', assetName);
logger.debug('sudt balance:', balance);
// logger.debug('expect balance:', Amount.fromUInt128LE(bigintToSudtAmount(amount)).toHexString());
logger.debug('expect balance:', new Amount(amount.toString()));
assert(balance.eq(new Amount(amount.toString())));
}
};

const burn = async (sendBurn, assetName, txHash) => {
const burnAmount = 1;
// send burn tx
if (!sendBurn) {
const account = new Account(PRI_KEY);
const ownLockHash = ckb.utils.scriptToHash(<CKBComponents.Script>await account.getLockscript());
const generator = new CkbTxGenerator(ckb, new IndexerCollector(indexer));
const burnTx = await generator.burn(
await account.getLockscript(),
recipientAddress,
new TronAsset('trx', ownLockHash),
new TronAsset(assetName, ownLockHash),
new Amount(burnAmount.toString()),
);
const signedTx = ckb.signTransaction(PRI_KEY)(burnTx);
burnTxHash = await ckb.rpc.sendTransaction(signedTx);
console.log(`burn Transaction has been sent with tx hash ${burnTxHash}`);
await waitUntilCommitted(ckb, burnTxHash, 60);
sendBurn = true;
return burnTxHash;
}
return txHash;
};

const checkUnlock = async (burnTxHash, assetName) => {
const burnAmount = 1;
const balance = await getBalance(assetName);

logger.debug('sudt balance:', balance);
const expectBalance = new Amount((amount - burnAmount).toString());
logger.debug('expect sudt balance:', expectBalance);
// logger.debug('expect balance:', Amount.fromUInt128LE(bigintToSudtAmount(amount)).sub(Amount.fromUInt128LE('0x01')));
assert(balance.eq(expectBalance));

// check unlock record send
Expand All @@ -168,22 +217,28 @@ async function main() {
assert(tronUnlockRecords.length === 1);
const tronUnlockRecord = tronUnlockRecords[0];
assert(tronUnlockRecord.status === 'success');

// const unlockReceipt = await provider.getTransactionReceipt(ethUnlockRecord.ethTxHash);
// logger.debug('unlockReceipt', unlockReceipt);
// assert(unlockReceipt.logs.length === 1);
// const parsedLog = iface.parseLog(unlockReceipt.logs[0]);
// logger.debug('parsedLog', parsedLog);
// assert(parsedLog.args.token === record.asset);
// assert(record.amount === parsedLog.args.receivedAmount.toHexString());
// assert(record.recipientAddress === parsedLog.args.recipient);
};

// try 100 times and wait for 3 seconds every time.
let trxSendBurn = false;
let trc10SendBurn = false;
let burnTrxTxHash = '';
let burnTrc10TxHash = '';

for (let i = 0; i < 100; i++) {
await asyncSleep(10000);
try {
await checkEffect();
await checkLock(trxTxHash, 'trx', 'trx', trxSendBurn);
burnTrxTxHash = await burn(trxSendBurn, 'trx', burnTrxTxHash);
trxSendBurn = true;
await checkUnlock(burnTrxTxHash, 'trx');

await checkLock(trc10TxHash, '1000696', 'trc10', trc10SendBurn);
burnTrc10TxHash = await burn(trc10SendBurn, '1000696', burnTrc10TxHash);
trc10SendBurn = true;
await checkUnlock(burnTrc10TxHash, '1000696');

//await checkEffect(trc20TxHash, 'TVWvkCasxAJUyzPKMQ2Rus1NtmBwrkVyBR', 'trc20');
} catch (e) {
logger.warn('The tron component integration not pass yet.', { i, e });
continue;
Expand Down