Skip to content

Commit b8c7bbd

Browse files
authored
Merge pull request #352 from rsksmart/add-union-bridge-tests
Add Union Bridge tests, union authorizer contract, and union constants
2 parents 6868bba + 1c532b5 commit b8c7bbd

File tree

10 files changed

+2042
-34
lines changed

10 files changed

+2042
-34
lines changed

contracts/UnionBridgeAuthorizer.sol

Lines changed: 821 additions & 0 deletions
Large diffs are not rendered by default.

contracts/UnionBridgeContract.sol

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
interface BridgeInterface {
5+
function requestUnionBridgeRbtc(uint256 amountRequested) external returns (int256);
6+
7+
function releaseUnionBridgeRbtc() external payable returns (int256);
8+
}
9+
10+
contract UnionBridgeContract {
11+
BridgeInterface public bridge = BridgeInterface(0x0000000000000000000000000000000001000006);
12+
13+
function requestUnionBridgeRbtc(uint256 amountToRequest) external returns (int256) {
14+
return bridge.requestUnionBridgeRbtc(amountToRequest);
15+
}
16+
17+
function releaseUnionBridgeRbtc(uint256 amountToRelease) public payable returns (int256) {
18+
return bridge.releaseUnionBridgeRbtc{value: amountToRelease}();
19+
}
20+
21+
receive() external payable { }
22+
23+
fallback() external payable { }
24+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const { btcToWeis } = require("@rsksmart/btc-eth-unit-converter");
2+
const { getBridgeStorageIndexFromLongKey } = require("../utils");
3+
4+
const INITIAL_UNION_BRIDGE_ADDRESS = "0x0000000000000000000000000000000000000000";
5+
6+
const INITIAL_UNION_LOCKING_CAP = btcToWeis(1);
7+
8+
const UNION_LOCKING_CAP_INCREMENTS_MULTIPLIER = 2;
9+
10+
const CHANGE_UNION_BRIDGE_CONTRACT_ADDRESS_AUTHORIZER_PK =
11+
"7880a81a4591568b0e87947e5150fe8e330091678654f3bc661b516f91a5f00a";
12+
13+
// UnionBridgeAuthorizer multisig members
14+
// seed: UnionBridgeAuthorizer1
15+
const UNION_BRIDGE_AUTHORIZER_1_PK = "a2221797b4e6655bf39e8290d6db2c75536cf9ffb7f0d2bf4a7360f0b3716e5d";
16+
// seed: UnionBridgeAuthorizer2
17+
const UNION_BRIDGE_AUTHORIZER_2_PK = "1769b8128e7eb24d632122e03d64c8cd525cf2cf3d2d00190039918666106282";
18+
// seed: UnionBridgeAuthorizer3
19+
const UNION_BRIDGE_AUTHORIZER_3_PK = "03918409b6d508f31d72879df0813a6d60d8c74eb91197572c9f3df1da9ae5a5";
20+
21+
// seed: UnionBridgeAuthorizerDeployer
22+
const UNION_BRIDGE_AUTHORIZER_DEPLOYER_PK = "b00e82a2aa449171e750e9ccc945ee710d438dbaba1d6410aa781121f1f13099";
23+
24+
const UNION_BRIDGE_EVENTS = {
25+
UNION_LOCKING_CAP_INCREASED: {
26+
name: "union_bridge_locking_cap_increased",
27+
},
28+
UNION_RBTC_REQUESTED: {
29+
name: "union_rbtc_requested",
30+
},
31+
UNION_RBTC_RELEASED: {
32+
name: "union_rbtc_released",
33+
},
34+
UNION_BRIDGE_TRANSFER_PERMISSIONS_UPDATED: {
35+
name: "union_bridge_transfer_permissions_updated",
36+
},
37+
};
38+
39+
const UNION_BRIDGE_STORAGE_INDICES = {
40+
UNION_BRIDGE_CONTRACT_ADDRESS: getBridgeStorageIndexFromLongKey("unionBridgeContractAddress"),
41+
UNION_BRIDGE_LOCKING_CAP: getBridgeStorageIndexFromLongKey("unionBridgeLockingCap"),
42+
WEIS_TRANSFERRED_TO_UNION_BRIDGE: getBridgeStorageIndexFromLongKey("weisTransferredToUnionBridge"),
43+
UNION_BRIDGE_REQUEST_ENABLED: getBridgeStorageIndexFromLongKey("unionBridgeRequestEnabled"),
44+
UNION_BRIDGE_RELEASE_ENABLED: getBridgeStorageIndexFromLongKey("unionBridgeReleaseEnabled"),
45+
};
46+
47+
const UNION_RESPONSE_CODES = {
48+
SUCCESS: "0",
49+
UNAUTHORIZED_CALLER: "-1",
50+
INVALID_VALUE: "-2",
51+
REQUEST_DISABLED: "-3",
52+
RELEASE_DISABLED: "-3",
53+
GENERIC_ERROR: "-10",
54+
};
55+
56+
module.exports = {
57+
INITIAL_UNION_BRIDGE_ADDRESS,
58+
INITIAL_UNION_LOCKING_CAP,
59+
UNION_LOCKING_CAP_INCREMENTS_MULTIPLIER,
60+
CHANGE_UNION_BRIDGE_CONTRACT_ADDRESS_AUTHORIZER_PK,
61+
UNION_BRIDGE_AUTHORIZER_1_PK,
62+
UNION_BRIDGE_AUTHORIZER_2_PK,
63+
UNION_BRIDGE_AUTHORIZER_3_PK,
64+
UNION_BRIDGE_AUTHORIZER_DEPLOYER_PK,
65+
UNION_BRIDGE_EVENTS,
66+
UNION_BRIDGE_STORAGE_INDICES,
67+
UNION_RESPONSE_CODES,
68+
};

lib/contractDeployer.js

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,88 @@
1-
21
const fs = require('node:fs');
32
const path = require('node:path');
43
const solUtils = require('./sol-utils');
54
const TEST_RELEASE_BTC_CONTRACT = '../contracts/CallReleaseBtcContract.sol';
65
const TEST_RELEASE_BTC_CONTRACT_NAME = 'CallReleaseBtcContract';
6+
7+
const TEST_UNION_BRIDGE_CONTRACT = '../contracts/UnionBridgeContract.sol';
8+
const TEST_UNION_BRIDGE_CONTRACT_NAME = 'UnionBridgeContract';
9+
10+
const TEST_UNION_BRIDGE_AUTHORIZER_CONTRACT = '../contracts/UnionBridgeAuthorizer.sol';
11+
const TEST_UNION_BRIDGE_AUTHORIZER_CONTRACT_NAME = 'UnionBridgeAuthorizer';
12+
713
const SOLIDITY_COMPILER_VERSION = 'v0.8.26+commit.8a97fa7a';
814

915
/**
1016
* Deploys the CallReleaseBtcContract contract.
11-
* @param {RskTransactionHelper} rskTxHelper
17+
* @param {RskTransactionHelper} rskTxHelper
1218
* @param {string} from the funded rsk address from which the contract will be deployed.
1319
* @returns {Promise<Contract>} the deployed contract.
1420
*/
1521
const deployCallReleaseBtcContract = async (rskTxHelper, from) => {
16-
17-
const fullPath = path.resolve(__dirname, TEST_RELEASE_BTC_CONTRACT);
18-
const source = fs.readFileSync(fullPath).toString();
19-
20-
const callReleaseBtcContract = await solUtils.compileAndDeploy(
21-
SOLIDITY_COMPILER_VERSION,
22-
source,
23-
TEST_RELEASE_BTC_CONTRACT_NAME,
24-
[],
25-
rskTxHelper,
26-
{
27-
from
28-
}
29-
);
30-
31-
return callReleaseBtcContract;
32-
22+
23+
const fullPath = path.resolve(__dirname, TEST_RELEASE_BTC_CONTRACT);
24+
const source = fs.readFileSync(fullPath).toString();
25+
26+
return await solUtils.compileAndDeploy(
27+
SOLIDITY_COMPILER_VERSION,
28+
source,
29+
TEST_RELEASE_BTC_CONTRACT_NAME,
30+
[],
31+
rskTxHelper,
32+
{
33+
from
34+
}
35+
);
36+
3337
};
3438

39+
/**
40+
* Deploys the unionBridgeContract contract.
41+
* @param {RskTransactionHelper} rskTxHelper
42+
* @param {string} from the funded rsk address from which the contract will be deployed.
43+
* @returns {Promise<Contract>} the deployed contract.
44+
*/
45+
const deployUnionBridgeContract = async (rskTxHelper, from) => {
46+
const fullPath = path.resolve(__dirname, TEST_UNION_BRIDGE_CONTRACT);
47+
const source = fs.readFileSync(fullPath).toString();
48+
49+
return await solUtils.compileAndDeploy(
50+
SOLIDITY_COMPILER_VERSION,
51+
source,
52+
TEST_UNION_BRIDGE_CONTRACT_NAME,
53+
[],
54+
rskTxHelper,
55+
{
56+
from
57+
}
58+
);
59+
}
60+
61+
/**
62+
* Deploys the UnionBridgeAuthorizer contract.
63+
* @param {RskTransactionHelper} rskTxHelper
64+
* @param {string} from the funded rsk address from which the contract will be deployed.
65+
* @returns {Promise<Contract>} the deployed contract.
66+
*/
67+
const deployUnionBridgeAuthorizerContract = async (rskTxHelper, from) => {
68+
const fullPath = path.resolve(__dirname, TEST_UNION_BRIDGE_AUTHORIZER_CONTRACT);
69+
const source = fs.readFileSync(fullPath).toString();
70+
71+
return await solUtils.compileAndDeploy(
72+
SOLIDITY_COMPILER_VERSION,
73+
source,
74+
TEST_UNION_BRIDGE_AUTHORIZER_CONTRACT_NAME,
75+
[],
76+
rskTxHelper,
77+
{
78+
from: from,
79+
gas: '3000000'
80+
}
81+
);
82+
}
83+
3584
module.exports = {
36-
deployCallReleaseBtcContract,
85+
deployCallReleaseBtcContract,
86+
deployUnionBridgeContract,
87+
deployUnionBridgeAuthorizerContract
3788
};

lib/rsk-utils.js

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,10 +665,87 @@ const getAddressFromUncompressedPublicKey = (uncompressedPublicKey) => {
665665
const uncompressedPublicKey0xPrefix = ensure0x(uncompressedPublicKeyWithoutCompressionPrefix);
666666
const hash = keccak256(uncompressedPublicKey0xPrefix);
667667
const hashWithout0xPrefix = removePrefix0x(hash);
668-
const address = hashWithout0xPrefix.substring(24, hash.length);
668+
const address = hashWithout0xPrefix.substring(24, hash.length);
669669
return address;
670670
};
671671

672+
/**
673+
* Imports multiple private keys into the RSK transaction helper
674+
* @param {RskTransactionHelper} rskTxHelper
675+
* @param {string[]} privateKeys array of private keys to import
676+
* @returns {Promise<string[]>} array of imported addresses
677+
*/
678+
const importAccounts = async (rskTxHelper, privateKeys) => {
679+
const importedAddresses = [];
680+
for (const privateKey of privateKeys) {
681+
const address = await rskTxHelper.importAccount(privateKey);
682+
importedAddresses.push(address);
683+
}
684+
return importedAddresses;
685+
};
686+
687+
const decodeLogs = (rskClient, txReceipt, contractAbi) => {
688+
const eventSignatureMap = buildEventSignatureMap(rskClient, contractAbi);
689+
const events = [];
690+
for (let log of txReceipt.logs) {
691+
if (log.topics.length === 0) {
692+
continue;
693+
}
694+
695+
const eventSignature = log.topics[0];
696+
const abiElement = eventSignatureMap[eventSignature];
697+
if (!abiElement) {
698+
continue;
699+
}
700+
const event = decodeLog(rskClient, log, abiElement);
701+
events.push(event);
702+
}
703+
return events;
704+
};
705+
706+
const buildEventSignatureMap = (rskClient, contractAbi) => {
707+
return contractAbi
708+
.flat()
709+
.filter((element) => element.type === "event")
710+
.reduce((acc, element) => {
711+
const signature = rskClient.eth.abi.encodeEventSignature(element);
712+
acc[signature] = element;
713+
return acc;
714+
}, {});
715+
};
716+
717+
const decodeLog = (rskClient, log, abiElement) => {
718+
const decodedLog = rskClient.eth.abi.decodeLog(abiElement.inputs, log.data, log.topics.slice(1));
719+
720+
const args = {};
721+
for (let input of abiElement.inputs) {
722+
args[input.name] = decodedLog[input.name];
723+
}
724+
725+
return {
726+
name: abiElement.name,
727+
signature: log.topics[0],
728+
args: args,
729+
};
730+
};
731+
732+
const assertContractCallFails = async (methodCall, options) => {
733+
await expect(methodCall.call(options)).to.be.rejected;
734+
};
735+
736+
const findEventInTx = async (rskTxHelper, txHash, eventName, contractAbi = []) => {
737+
const rskClient = rskTxHelper.getClient();
738+
// Fetch the transaction receipt to get all the logs (including the ones from internal txs)
739+
const txReceipt = await rskTxHelper.getTxReceipt(txHash);
740+
const events = decodeLogs(rskClient, txReceipt, contractAbi);
741+
return events.find((event) => event.name === eventName);
742+
};
743+
744+
const assertNoEventWasEmitted = async (txReceipt) => {
745+
const isEmpty = Object.keys(txReceipt.events).length === 0;
746+
expect(isEmpty, "No event should have been emitted").to.be.true;
747+
};
748+
672749
module.exports = {
673750
mineAndSync,
674751
waitForBlock,
@@ -697,4 +774,9 @@ module.exports = {
697774
compressPublicKey,
698775
waitForRskMempoolToGetThisCountOfAddSignatureTxs,
699776
waitForRskMempoolToGetThisCountOfRegisterBtcTx,
777+
importAccounts,
778+
decodeLogs,
779+
assertContractCallFails,
780+
findEventInTx,
781+
assertNoEventWasEmitted,
700782
};

lib/tests/change-federation.js

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,6 @@ const parseBtcPublicKeys = (btcPublicKeysInString) => {
7575
return btcPublicKeysInArrayWith0xPrefix;
7676
};
7777

78-
const importAccounts = async (rskTxHelper, privateKeys) => {
79-
const importedAddresses = [];
80-
for (const privateKey of privateKeys) {
81-
const address = await rskTxHelper.importAccount(privateKey);
82-
importedAddresses.push(address);
83-
}
84-
return importedAddresses;
85-
};
8678

8779
const execute = (description, newFederationConfig, segwitToSegwitFederationChange = false) => {
8880

@@ -146,7 +138,7 @@ const execute = (description, newFederationConfig, segwitToSegwitFederationChang
146138

147139
// Import the private keys of the federation change authorizers.
148140

149-
const importedFederationChangeAuthorizers = await importAccounts(rskTxHelper, [regtestFedChangeAuthorizer1PrivateKey, regtestFedChangeAuthorizer2PrivateKey, regtestFedChangeAuthorizer3PrivateKey]);
141+
const importedFederationChangeAuthorizers = await rskUtils.importAccounts(rskTxHelper, [regtestFedChangeAuthorizer1PrivateKey, regtestFedChangeAuthorizer2PrivateKey, regtestFedChangeAuthorizer3PrivateKey]);
150142

151143
fedChangeAuthorizer1Address = importedFederationChangeAuthorizers[0];
152144
fedChangeAuthorizer2Address = importedFederationChangeAuthorizers[1];
@@ -159,7 +151,7 @@ const execute = (description, newFederationConfig, segwitToSegwitFederationChang
159151

160152
// Import the private keys of the not authorized addresses.
161153

162-
const importedNotAuthorizedAddresses = await importAccounts(rskTxHelper, [notAuthorized1PrivateKey, notAuthorized2PrivateKey, notAuthorized3PrivateKey]);
154+
const importedNotAuthorizedAddresses = await rskUtils.importAccounts(rskTxHelper, [notAuthorized1PrivateKey, notAuthorized2PrivateKey, notAuthorized3PrivateKey]);
163155

164156
notAuthorized1Address = importedNotAuthorizedAddresses[0];
165157
notAuthorized2Address = importedNotAuthorizedAddresses[1];
@@ -848,8 +840,8 @@ const execute = (description, newFederationConfig, segwitToSegwitFederationChang
848840
rskTxHelper = rskTxHelpers[0];
849841
bridge = await getBridge(rskTxHelper.getClient());
850842

851-
await importAccounts(rskTxHelper, [regtestFedChangeAuthorizer1PrivateKey, regtestFedChangeAuthorizer2PrivateKey, regtestFedChangeAuthorizer3PrivateKey]);
852-
await importAccounts(rskTxHelper, [notAuthorized1PrivateKey, notAuthorized2PrivateKey, notAuthorized3PrivateKey]);
843+
await rskUtils.importAccounts(rskTxHelper, [regtestFedChangeAuthorizer1PrivateKey, regtestFedChangeAuthorizer2PrivateKey, regtestFedChangeAuthorizer3PrivateKey]);
844+
await rskUtils.importAccounts(rskTxHelper, [notAuthorized1PrivateKey, notAuthorized2PrivateKey, notAuthorized3PrivateKey]);
853845

854846
const blocksToMineToRetireFederation = FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_END;
855847
// Mining enough blocks to complete retiring the old federation.

0 commit comments

Comments
 (0)