Skip to content

Commit

Permalink
track if a bep20 is bound by mirror and fix decode issue
Browse files Browse the repository at this point in the history
  • Loading branch information
HaoyangLiu committed Dec 18, 2020
1 parent eac7c09 commit c41c471
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 10 deletions.
13 changes: 8 additions & 5 deletions contracts/TokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ contract TokenManager is System, IApplication, IParamSubscriber {
mapping(bytes32 => BindSynPackage) public bindPackageRecord;

mapping(address => bool) public mirrorPendingRecord;
mapping(address => bool) public boundByMirror;
uint256 public mirrorFee;
uint256 public syncFee;

Expand Down Expand Up @@ -201,6 +202,7 @@ contract TokenManager is System, IApplication, IParamSubscriber {
}

function approveBind(address contractAddr, string memory bep2Symbol) payable public returns (bool) {
require(!mirrorPendingRecord[contractAddr], "the bep20 token is in mirror pending status");
bytes32 bep2TokenSymbol = bep2TokenSymbolConvert(bep2Symbol);
BindSynPackage memory bindSynPkg = bindPackageRecord[bep2TokenSymbol];
require(bindSynPkg.bep2TokenSymbol!=bytes32(0x00), "bind request doesn't exist");
Expand Down Expand Up @@ -232,7 +234,6 @@ contract TokenManager is System, IApplication, IParamSubscriber {
}

function rejectBind(address contractAddr, string memory bep2Symbol) payable public returns (bool) {
require(!mirrorPendingRecord[contractAddr], "the bep20 token is in mirror pending status");
bytes32 bep2TokenSymbol = bep2TokenSymbolConvert(bep2Symbol);
BindSynPackage memory bindSynPkg = bindPackageRecord[bep2TokenSymbol];
require(bindSynPkg.bep2TokenSymbol!=bytes32(0x00), "bind request doesn't exist");
Expand Down Expand Up @@ -377,6 +378,7 @@ contract TokenManager is System, IApplication, IParamSubscriber {
if (mirrorAckPackage.errorCode == MIRROR_STATUS_SUCCESS ) {
address(uint160(TOKEN_HUB_ADDR)).transfer(mirrorAckPackage.mirrorFee);
ITokenHub(TOKEN_HUB_ADDR).bindToken(mirrorAckPackage.bep2Symbol, mirrorAckPackage.bep20Addr, mirrorAckPackage.bep20Decimals);
boundByMirror[mirrorAckPackage.bep20Addr] = true;
emit mirrorSuccess(mirrorAckPackage.bep20Addr, mirrorAckPackage.bep2Symbol);
return;
} else if (mirrorAckPackage.errorCode == MIRROR_STATUS_TIMEOUT ) {
Expand All @@ -395,7 +397,7 @@ contract TokenManager is System, IApplication, IParamSubscriber {
(MirrorSynPackage memory mirrorSynPackage, bool decodeSuccess) = decodeMirrorSynPackage(msgBytes);
require(decodeSuccess, "unrecognized mirror syn package");
mirrorPendingRecord[mirrorSynPackage.bep20Addr] = false;
mirrorSynPackage.mirrorSender.call{gas: MAX_GAS_FOR_TRANSFER_BNB, value: mirrorSynPackage.mirrorFee}("");
mirrorSynPackage.mirrorSender.call{gas: MAX_GAS_FOR_TRANSFER_BNB, value: mirrorSynPackage.mirrorFee*TEN_DECIMALS}("");
}

function encodeSyncSynPackage(SyncSynPackage memory syncSynPackage) internal pure returns (bytes memory) {
Expand Down Expand Up @@ -436,7 +438,7 @@ contract TokenManager is System, IApplication, IParamSubscriber {
while (iter.hasNext()) {
if (idx == 0) syncAckPackage.syncSender = iter.next().toAddress();
else if (idx == 1) syncAckPackage.bep20Addr = iter.next().toAddress();
else if (idx == 2) syncAckPackage.syncFee = uint8(iter.next().toUint());
else if (idx == 2) syncAckPackage.syncFee = iter.next().toUint();
else if (idx == 3) {
syncAckPackage.errorCode = uint8(iter.next().toUint());
success = true;
Expand All @@ -449,12 +451,13 @@ contract TokenManager is System, IApplication, IParamSubscriber {

function sync(address bep20Addr, uint64 expireTime) payable public returns (bool) {
require(ITokenHub(TOKEN_HUB_ADDR).getBep2SymbolByContractAddr(bep20Addr) != bytes32(0x00), "the bep20 token is not bound");
require(boundByMirror[bep20Addr], "the bep20 token is not bound by mirror");
uint256 miniRelayFee = ITokenHub(TOKEN_HUB_ADDR).getMiniRelayFee();
require(msg.value%TEN_DECIMALS == 0 && msg.value>=syncFee.add(miniRelayFee), "msg.value must be N * 1e10 and greater than sum of miniRelayFee and syncFee");
require(expireTime>=block.timestamp + 120 && expireTime <= block.timestamp + 86400, "expireTime must be two minutes later and one day earlier");
uint256 totalSupply = IBEP20(bep20Addr).totalSupply();
uint8 decimals = IBEP20(bep20Addr).decimals();
require(convertToBep2Amount(totalSupply, decimals) <= MAX_BEP2_TOTAL_SUPPLY, "bep20 total supply is to large");
require(convertToBep2Amount(totalSupply, decimals) <= MAX_BEP2_TOTAL_SUPPLY, "bep20 total supply is too large");

address(uint160(TOKEN_HUB_ADDR)).transfer(msg.value.sub(syncFee));
SyncSynPackage memory syncSynPackage = SyncSynPackage({
Expand Down Expand Up @@ -488,7 +491,7 @@ contract TokenManager is System, IApplication, IParamSubscriber {
function handleSyncFailAckPackage(bytes memory msgBytes) internal {
(SyncSynPackage memory syncSynPackage, bool decodeSuccess) = decodeSyncSynPackage(msgBytes);
require(decodeSuccess, "unrecognized sync syn package");
syncSynPackage.syncSender.call{gas: MAX_GAS_FOR_TRANSFER_BNB, value: syncSynPackage.syncFee}("");
syncSynPackage.syncSender.call{gas: MAX_GAS_FOR_TRANSFER_BNB, value: syncSynPackage.syncFee*TEN_DECIMALS}("");
}

function updateParam(string calldata key, bytes calldata value) override external onlyGov{
Expand Down
2 changes: 1 addition & 1 deletion genesis.json

Large diffs are not rendered by default.

127 changes: 123 additions & 4 deletions test/TestTokenHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ function buildAckPackagePrefix() {
));
}

function buildFailAckPackagePrefix() {
return Buffer.from(web3.utils.hexToBytes(
"0x02" + toBytes32String(0)
));
}

function buildBindPackage(bindType, bep2TokenSymbol, bep20Addr, totalSupply, peggyAmount, decimals) {
let timestamp = Math.floor(Date.now() / 1000); // counted by second
let initialExpireTimeStr = (timestamp + 3).toString(16); // expire at 5 second later
Expand Down Expand Up @@ -951,13 +957,21 @@ contract('TokenHub', (accounts) => {
try {
let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 300; // expire at five minutes later
const tx1 = await tokenManager.mirror(miniToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
console.log(tx1);
await tokenManager.mirror(miniToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("the bep20 token has already been bound"));
}

try {
let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 100; // expire at five minutes later
await tokenManager.mirror(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("expireTime must be two minutes later and one day earlier"));
}

try {
await xyzToken.setName("", {from: xyzTokenOwner});

Expand Down Expand Up @@ -1037,7 +1051,7 @@ contract('TokenHub', (accounts) => {

let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 300; // expire at five minutes later
const tx1 = await tokenManager.mirror(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
await tokenManager.mirror(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("bep20 total supply is too large"));
Expand Down Expand Up @@ -1133,7 +1147,21 @@ contract('TokenHub', (accounts) => {
let tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(mirrorFee), true, "wrong tokenManager balance");

const mirrorChannelSeq = await crossChain.channelReceiveSequenceMap(MIRROR_CHANNELID);
// mirror fail ack
let mirrorChannelSeq = await crossChain.channelReceiveSequenceMap(MIRROR_CHANNELID);
const mirrorFailAckPackageBytes = Buffer.from(web3.utils.hexToBytes(nestedEventValues.payload));
await crossChain.handlePackage(Buffer.concat([buildFailAckPackagePrefix(), mirrorFailAckPackageBytes.subarray(33, mirrorFailAckPackageBytes.length)]), proof, merkleHeight, mirrorChannelSeq, MIRROR_CHANNELID, {from: relayer});
tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(web3.utils.toBN(0)), true, "wrong tokenManager balance");

// success mirror
timestamp = Math.floor(Date.now() / 1000); // counted by second
expireTime = timestamp + 300; // expire at five minutes later
tx = await tokenManager.mirror(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(mirrorFee), true, "wrong tokenManager balance");

mirrorChannelSeq = await crossChain.channelReceiveSequenceMap(MIRROR_CHANNELID);
const mirrorAckPackageBytes = buildMirrorAckPackage(player, xyzToken.address, 18, "XYZ-123", mirrorFee, 0);
await crossChain.handlePackage(mirrorAckPackageBytes, proof, merkleHeight, mirrorChannelSeq, MIRROR_CHANNELID, {from: relayer});

Expand All @@ -1158,5 +1186,96 @@ contract('TokenHub', (accounts) => {
assert.equal(web3.utils.toBN(decodedSyncSynPackage.syncFee).mul(web3.utils.toBN(1e10)).eq(syncFee), true, "Wrong mirrorFee in sync syn package");
tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(syncFee), true, "wrong tokenManager balance");

// sync fail ack package
let syncChannelSeq = await crossChain.channelReceiveSequenceMap(SYNC_CHANNELID);
const syncFailAckPackageBytes = Buffer.from(web3.utils.hexToBytes(nestedEventValues.payload));
await crossChain.handlePackage(Buffer.concat([buildFailAckPackagePrefix(), syncFailAckPackageBytes.subarray(33, syncFailAckPackageBytes.length)]), proof, merkleHeight, syncChannelSeq, SYNC_CHANNELID, {from: relayer});
tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(web3.utils.toBN(0)), true, "wrong tokenManager balance");

// success sync and sync ack
timestamp = Math.floor(Date.now() / 1000); // counted by second
expireTime = timestamp + 300; // expire at five minutes later
await tokenManager.sync(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(syncFee)});
tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(syncFee), true, "wrong tokenManager balance");

syncChannelSeq = await crossChain.channelReceiveSequenceMap(SYNC_CHANNELID);
await crossChain.handlePackage(buildSyncAckPackage(player, xyzToken.address, syncFee, 0), proof, merkleHeight, syncChannelSeq, SYNC_CHANNELID, {from: relayer});
tokenManagerBalance = await web3.eth.getBalance(tokenManager.address);
assert.equal(web3.utils.toBN(tokenManagerBalance).eq(web3.utils.toBN(0)), true, "wrong tokenManager balance");
});
it('iterate sync failures', async () => {
const tokenManager = await TokenManager.deployed();
const tokenHub = await TokenHub.deployed();
const miniToken = await MiniToken.deployed();
const defToken = await DEFToken.deployed();
const xyzToken = await XYZToken.deployed();
const crossChain = await CrossChain.deployed();
const miniRelayFee = await tokenHub.getMiniRelayFee();
const mirrorFee = await tokenManager.mirrorFee();
const xyzTokenOwner = accounts[0];
const relayer = accounts[1];
const player = accounts[2];

const syncFee = await tokenManager.syncFee();

try {
let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 300; // expire at five minutes later
await tokenManager.sync(defToken.address, expireTime, {
from: player,
value: miniRelayFee.add(syncFee)
});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("the bep20 token is not bound"));
}

try {
let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 300; // expire at five minutes later
await tokenManager.sync(miniToken.address, expireTime, {
from: player,
value: miniRelayFee.add(syncFee)
});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("the bep20 token is not bound by mirror"));
}

try {
let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 100; // expire at five minutes later
await tokenManager.sync(xyzToken.address, expireTime, {
from: player,
value: miniRelayFee.add(syncFee)
});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("expireTime must be two minutes later and one day earlier"));
}

try {
await xyzToken.setTotalSupply(web3.utils.toBN(1e18).mul(web3.utils.toBN(1e18)), {from: xyzTokenOwner});

let timestamp = Math.floor(Date.now() / 1000); // counted by second
let expireTime = timestamp + 300; // expire at five minutes later
await tokenManager.sync(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(mirrorFee)});
assert.fail();
} catch (error) {
assert.ok(error.toString().includes("bep20 total supply is too large"));
}

await xyzToken.setTotalSupply(web3.utils.toBN(1e18).mul(web3.utils.toBN(1e8)), {from: xyzTokenOwner});
const xyzTokenNewTotalSupply = await xyzToken.totalSupply();

timestamp = Math.floor(Date.now() / 1000); // counted by second
expireTime = timestamp + 300; // expire at five minutes later
tx = await tokenManager.sync(xyzToken.address, expireTime, {from: player, value: miniRelayFee.add(syncFee)});
nestedEventValues = (await truffleAssert.createTransactionResult(crossChain, tx.tx)).logs[0].args;
let decodedSyncSynPackage = decodeSyncSynPackage(nestedEventValues.payload);
assert.equal(web3.utils.toBN(decodedSyncSynPackage.bep20Supply).eq(xyzTokenNewTotalSupply), true, "Wrong total supply in sync syn package");
});
});

0 comments on commit c41c471

Please sign in to comment.