diff --git a/cleanEnv.js b/cleanEnv.js index 236eda6..f3b46ed 100644 --- a/cleanEnv.js +++ b/cleanEnv.js @@ -25,7 +25,7 @@ async function clearLogFiles(directory) { } } -function deleteBitcoinDataDirectory() { +function clearBitcoinDataDirectory() { return new Promise((resolve, reject) => { const directory = process.env.BITCOIN_DATA_DIR; @@ -33,20 +33,28 @@ function deleteBitcoinDataDirectory() { return reject(new Error("BITCOIN_DATA_DIR is not set.")); } - fs.rm(directory, { recursive: true, force: true }, (err) => { + fs.readdir(directory, (err, files) => { if (err) { - reject(new Error(`Error deleting directory ${directory}: ${err.message}`)); - } else { - console.log(`Successfully deleted directory: ${directory}`); - resolve(); + return reject(new Error(`Error reading directory ${directory}: ${err.message}`)); } + + const deletePromises = files.map(file => { + const filePath = path.join(directory, file); + return fs.promises.rm(filePath, { recursive: true, force: true }); + }); + + Promise.all(deletePromises) + .then(() => { + console.log(`Successfully cleared contents of: ${directory}`); + resolve(); + }) + .catch(reject); }); }); } - const cleanEnvironment = async () => { console.info('Cleaning environment...'); - await deleteBitcoinDataDirectory(); + await clearBitcoinDataDirectory(); await clearLogFiles(process.env.LOG_HOME); shell.exec(killServicesCommand); console.info('Environment cleaned.'); diff --git a/config/node-configs/third-federation/fed1.conf b/config/node-configs/third-federation/fed1.conf new file mode 100644 index 0000000..b51bd69 --- /dev/null +++ b/config/node-configs/third-federation/fed1.conf @@ -0,0 +1,63 @@ + +peer { + privateKey = "0c6768b0905fa1ff4a63313a6e61e89e6bb0a511b44ec5a449921b24abfa1b4f" # Generated with seed firstSvpFederartionFed1 +} + +# federator configuration +federator { + enabled = true + bitcoinPeerAddresses = [ + "127.0.0.1:18444" + ] + gasPrice = 1000 +} + +rpc { + modules = [ + { + name: "eth", + version: "1.0", + enabled: "true", + }, + { + name: "net", + version: "1.0", + enabled: "true", + }, + { + name: "rpc", + version: "1.0", + enabled: "true", + }, + { + name: "web3", + version: "1.0", + enabled: "true", + }, + { + name: "evm", + version: "1.0", + enabled: "true" + }, + { + name: "mnr", + version: "1.0", + enabled: "true" + }, + { + name: "personal", + version: "1.0", + enabled: "true" + }, + { + name: "fed", + version: "1.0", + enabled: "true" + }, + { + name: "rsk", + version: "1.0", + enabled: "true", + } + ] +} diff --git a/config/node-configs/third-federation/fed2.conf b/config/node-configs/third-federation/fed2.conf new file mode 100644 index 0000000..2eefd0b --- /dev/null +++ b/config/node-configs/third-federation/fed2.conf @@ -0,0 +1,63 @@ + +peer { + privateKey = "7e878d8199b6e7159aa79fa8077c5e8129b1bd6ce1cd5016dfd568d9944a0a06" # Generated with seed firstSvpFederartionFed2 +} + +# federator configuration +federator { + enabled = true + bitcoinPeerAddresses = [ + "127.0.0.1:18444" + ] + gasPrice = 1000 +} + +rpc { + modules = [ + { + name: "eth", + version: "1.0", + enabled: "true", + }, + { + name: "net", + version: "1.0", + enabled: "true", + }, + { + name: "rpc", + version: "1.0", + enabled: "true", + }, + { + name: "web3", + version: "1.0", + enabled: "true", + }, + { + name: "evm", + version: "1.0", + enabled: "true" + }, + { + name: "mnr", + version: "1.0", + enabled: "true" + }, + { + name: "personal", + version: "1.0", + enabled: "true" + }, + { + name: "fed", + version: "1.0", + enabled: "true" + }, + { + name: "rsk", + version: "1.0", + enabled: "true", + } + ] +} diff --git a/config/node-configs/third-federation/fed3.conf b/config/node-configs/third-federation/fed3.conf new file mode 100644 index 0000000..8098bd3 --- /dev/null +++ b/config/node-configs/third-federation/fed3.conf @@ -0,0 +1,63 @@ + +peer { + privateKey = "e7272a960b8b7ca61b815b63d44db8f0aebd418e1613108027007a82541ac2f4" # Generated with seed thirdSvpFederartionFed3 +} + +# federator configuration +federator { + enabled = true + bitcoinPeerAddresses = [ + "127.0.0.1:18444" + ] + gasPrice = 1000 +} + +rpc { + modules = [ + { + name: "eth", + version: "1.0", + enabled: "true", + }, + { + name: "net", + version: "1.0", + enabled: "true", + }, + { + name: "rpc", + version: "1.0", + enabled: "true", + }, + { + name: "web3", + version: "1.0", + enabled: "true", + }, + { + name: "evm", + version: "1.0", + enabled: "true" + }, + { + name: "mnr", + version: "1.0", + enabled: "true" + }, + { + name: "personal", + version: "1.0", + enabled: "true" + }, + { + name: "fed", + version: "1.0", + enabled: "true" + }, + { + name: "rsk", + version: "1.0", + enabled: "true", + } + ] +} diff --git a/config/node-keys/third-federation/fed1.key b/config/node-keys/third-federation/fed1.key new file mode 100644 index 0000000..951ebab --- /dev/null +++ b/config/node-keys/third-federation/fed1.key @@ -0,0 +1 @@ +0c6768b0905fa1ff4a63313a6e61e89e6bb0a511b44ec5a449921b24abfa1b4f \ No newline at end of file diff --git a/config/node-keys/third-federation/fed2.key b/config/node-keys/third-federation/fed2.key new file mode 100644 index 0000000..7de6078 --- /dev/null +++ b/config/node-keys/third-federation/fed2.key @@ -0,0 +1 @@ +7e878d8199b6e7159aa79fa8077c5e8129b1bd6ce1cd5016dfd568d9944a0a06 \ No newline at end of file diff --git a/config/node-keys/third-federation/fed3.key b/config/node-keys/third-federation/fed3.key new file mode 100644 index 0000000..f1780fa --- /dev/null +++ b/config/node-keys/third-federation/fed3.key @@ -0,0 +1 @@ +e7272a960b8b7ca61b815b63d44db8f0aebd418e1613108027007a82541ac2f4 \ No newline at end of file diff --git a/config/pipeline.conf b/config/pipeline.conf deleted file mode 100755 index c417330..0000000 --- a/config/pipeline.conf +++ /dev/null @@ -1,130 +0,0 @@ -const path = require('path'); -const version = 'SNAPSHOT-7.1.0.0'; -const nodesConfigPath = 'config/node-configs/'; -const keysPathResolve = 'node-keys/'; -const keysPath = 'config/node-keys/'; -const classpath = '../build/libs/federate-node-' + version + '-all.jar'; -const hsmV1ServerPath = '../hsm/fedhm-mockup.py'; -const hsmV2ServerPath = '../hsm/v2/sim/0.1.3/sim'; - -module.exports = { - init: { - mineInitialBitcoin: true, - }, - btc: { // Bitcoind configuration - rpcUser: 'testuser', - rpcPassword: 'testpassword' - }, - federate: [ - { // Federate node configuration, can be an array to start multiple federate nodes - removeDataOnDirOnStop: false, - classpath: classpath, - configFile: nodesConfigPath + 'rsk-reg-1.conf', - publicKeys: { - btc: '0362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a124', - rsk: '0362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a124', - mst: '0362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a124', - }, - customConfig: { - 'federator.signers.BTC.type': 'keyFile', - 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}reg1.key`), - 'federator.signers.RSK.type': 'keyFile', - 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}reg1.key`), - 'federator.signers.MST.type': 'keyFile', - 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}reg1.key`) - }, - nodeId: '62634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1243bd206c2c7a218d6ff4c9a185e71f066bd354e5267875b7683fbc70a1d455e87' - }, - { // Federate node configuration, can be an array to start multiple federate nodes - removeDataOnDirOnStop: false, - classpath: classpath, - configFile: nodesConfigPath + 'rsk-reg-2.conf', - hsmConfigs: { - btc: { - serverPath: hsmV1ServerPath, - keyPath : keysPath + 'reg2.key', - }, - rsk: { - serverPath: hsmV1ServerPath, - keyPath : keysPath + 'reg2.key', - }, - mst: { - serverPath: hsmV1ServerPath, - keyPath : keysPath + 'reg2.key', - }, - }, - nodeId: 'c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04dbb0bde4f3854613b16032fb214f9cc00f75363976ee078cc4409cdc543036ccfd', - customConfig: {} - }, - { // Federate node configuration, can be an array to start multiple federate nodes - removeDataOnDirOnStop: false, - classpath: classpath, - configFile: nodesConfigPath + 'rsk-reg-3.conf', - hsmConfigs: { - btc: { - serverPath: hsmV1ServerPath, - keyPath : keysPath + 'reg3.key', - }, - rsk: { - serverPath: hsmV1ServerPath, - keyPath : keysPath + 'reg3.key', - }, - mst: { - serverPath: hsmV1ServerPath, - keyPath : keysPath + 'reg3.key', - }, - }, - nodeId: 'cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be19e9da12b897b83765fbaebe717fab74fcb1b57c82f7978b8be3296239909e626', - customConfig: {} - }], - additionalFederateNodes: [ - { // Federate node configuration, can be an array to start multiple federate nodes - removeDataOnDirOnStop: false, - classpath: classpath, - configFile: nodesConfigPath + 'rsk-reg-4.conf', - hsmConfigs: { - btc: { - version: '2', - serverPath: hsmV2ServerPath, - keyPath : keysPath + 'reg4-v2-key.json', - }, - rsk: { - version: '2', - serverPath: hsmV2ServerPath, - keyPath : keysPath + 'reg4-v2-key.json', - }, - mst: { - version: '2', - serverPath: hsmV2ServerPath, - keyPath : keysPath + 'reg4-v2-key.json', - }, - }, - nodeId: '72634ab57dae9cb373a5d536e76a8c4f67468bbcfb063809bab643072d78a1243bd206c2c7a218d6ff4c9a185e71f066bd354e5267875b7683fbc70a1d455e84', - customConfig: {} - }, - { // Federate node configuration, can be an array to start multiple federate nodes - removeDataOnDirOnStop: false, - classpath: classpath, - configFile: nodesConfigPath + 'rsk-reg-5.conf', - hsmConfigs: { - btc: { - version: '2', - serverPath: hsmV2ServerPath, - keyPath : keysPath + 'reg5-v2-key.json', - }, - rsk: { - version: '2', - serverPath: hsmV2ServerPath, - keyPath : keysPath + 'reg5-v2-key.json', - }, - mst: { - version: '2', - serverPath: hsmV2ServerPath, - keyPath : keysPath + 'reg5-v2-key.json', - }, - }, - nodeId: '82634ab57dae9cb373a5d536e76a8c4f67468bbcfb063809bab643072d78a1243bd206c2c7a218d6ff4c9a185e71f066bd354e5267875b7683fbc70a1d455e85', - customConfig: {} - } - ] -} diff --git a/config/regtest-all-keyfiles.js b/config/regtest-all-keyfiles.js index b3f0f15..18c3b8a 100644 --- a/config/regtest-all-keyfiles.js +++ b/config/regtest-all-keyfiles.js @@ -176,7 +176,7 @@ module.exports = { 'peer.active.0.port': 40000, 'peer.active.0.nodeId': '328105ab6744914e61bcfcb729d741f23528fd6eb1b42628120ab027d82c9c2d8c445f8c6727e291e1abcaa8398bf47d4cca6ea6600b137b875b8df22251e325', 'peer.active.1.ip': '127.0.0.1', - 'peer.active.1.port': 40002, + 'peer.active.1.port': 40004, 'peer.active.1.nodeId': '5ba2b832b97cfba1626eb264bd1ec2733a7ca02602153c57c0c95c9700030dccb0edb612fd01bd806c0202aa3380fb8dcbeddb7a1386259d4b9679c0003d04bc', 'federator.amountOfHeadersToSend': 500, }, @@ -217,6 +217,104 @@ module.exports = { nodeId: '5ba2b832b97cfba1626eb264bd1ec2733a7ca02602153c57c0c95c9700030dccb0edb612fd01bd806c0202aa3380fb8dcbeddb7a1386259d4b9679c0003d04bc' } ] + }, + thirdFederation: { // First svp federation + federationId: 'third-federation', + members: [ + { + id: 'federator-1-third-federation', + federationId: 'third-federation', + logbackFile: `${federatesLogbackPath}/third-federation/fed1.xml`, + classpath: powpegNodeJarPath, + configFile: `${nodesConfigPath}/third-federation/fed1.conf`, + publicKeys: { + btc: '0x034ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d4226', + rsk: '0x034ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d4226', + mst: '0x034ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d4226', + }, + customConfig: { + 'federator.signers.BTC.type': 'keyFile', + 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed1.key`), + 'federator.signers.RSK.type': 'keyFile', + 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed1.key`), + 'federator.signers.MST.type': 'keyFile', + 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed1.key`), + 'peer.active.0.ip': '127.0.0.1', + 'peer.active.0.port': 40000, + 'peer.active.0.nodeId': '328105ab6744914e61bcfcb729d741f23528fd6eb1b42628120ab027d82c9c2d8c445f8c6727e291e1abcaa8398bf47d4cca6ea6600b137b875b8df22251e325', + 'peer.active.1.ip': '127.0.0.1', + 'peer.active.1.port': 50002, + 'peer.active.1.nodeId': '0b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394d3cd349b9b265b7427607736dcee323d9e4a893b1d15549c928278a4e6012c08', + 'federator.amountOfHeadersToSend': 500, + }, + bookkeepingConfigurations, + port: 50000, + rpcPort: 50001, + nodeId: '4ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d42265a5550cda65aa54de279a79a55e5513598b183e64f351e790d6370a6c67b141d' + }, + { + id: 'federator-2-third-federation', + federationId: 'third-federation', + logbackFile: `${federatesLogbackPath}/third-federation/fed2.xml`, + classpath: powpegNodeJarPath, + configFile: `${nodesConfigPath}/third-federation/fed2.conf`, + publicKeys: { + btc: '0x020b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394', + rsk: '0x020b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394', + mst: '0x020b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394', + }, + customConfig: { + 'federator.signers.BTC.type': 'keyFile', + 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed2.key`), + 'federator.signers.RSK.type': 'keyFile', + 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed2.key`), + 'federator.signers.MST.type': 'keyFile', + 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed2.key`), + 'peer.active.0.ip': '127.0.0.1', + 'peer.active.0.port': 50000, + 'peer.active.0.nodeId': '4ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d42265a5550cda65aa54de279a79a55e5513598b183e64f351e790d6370a6c67b141d', + 'peer.active.1.ip': '127.0.0.1', + 'peer.active.1.port': 50004, + 'peer.active.1.nodeId': '501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab01646d8c1f285b44074c45255fc693f19486819256ef98a9be8b62e4e9af4b54542c', + 'federator.amountOfHeadersToSend': 500, + }, + bookkeepingConfigurations, + port: 50002, + rpcPort: 50003, + nodeId: '0b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394d3cd349b9b265b7427607736dcee323d9e4a893b1d15549c928278a4e6012c08' + }, + { + id: 'federator-3-third-federation', + federationId: 'third-federation', + logbackFile: `${federatesLogbackPath}/third-federation/fed3.xml`, + classpath: powpegNodeJarPath, + configFile: `${nodesConfigPath}/third-federation/fed3.conf`, + publicKeys: { + btc: '0x02501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab0164', + rsk: '0x02501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab0164', + mst: '0x02501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab0164', + }, + customConfig: { + 'federator.signers.BTC.type': 'keyFile', + 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed3.key`), + 'federator.signers.RSK.type': 'keyFile', + 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed3.key`), + 'federator.signers.MST.type': 'keyFile', + 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed3.key`), + 'peer.active.0.ip': '127.0.0.1', + 'peer.active.0.port': 50000, + 'peer.active.0.nodeId': '4ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d42265a5550cda65aa54de279a79a55e5513598b183e64f351e790d6370a6c67b141d', + 'peer.active.1.ip': '127.0.0.1', + 'peer.active.1.port': 50002, + 'peer.active.1.nodeId': '0b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394d3cd349b9b265b7427607736dcee323d9e4a893b1d15549c928278a4e6012c08', + 'federator.amountOfHeadersToSend': 500, + }, + bookkeepingConfigurations, + port: 50004, + rpcPort: 50005, + nodeId: '501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab01646d8c1f285b44074c45255fc693f19486819256ef98a9be8b62e4e9af4b54542c' + } + ] } }, } diff --git a/configure.sh b/configure.sh index 8f80456..e1fb4b0 100755 --- a/configure.sh +++ b/configure.sh @@ -7,3 +7,7 @@ chmod 400 genesis-federation/fed3.key chmod 400 second-federation/fed1.key chmod 400 second-federation/fed2.key chmod 400 second-federation/fed3.key + +chmod 400 third-federation/fed1.key +chmod 400 third-federation/fed2.key +chmod 400 third-federation/fed3.key diff --git a/container-action/rit-local-configs/logbacks/third-federation/fed1.xml b/container-action/rit-local-configs/logbacks/third-federation/fed1.xml new file mode 100644 index 0000000..0e70d27 --- /dev/null +++ b/container-action/rit-local-configs/logbacks/third-federation/fed1.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + System.out + + %date{yyyy-MM-dd-HH:mm:ss.SSSS} %p [%c{1}] %m%n + + + + + /usr/src/logs/fed1-third-federation.log + + + %date{yyyy-MM-dd-HH:mm:ss.SSS} %p [%c{1}] %m%n + + + + ./logs/rskj-%d{yyyy-MM-dd}.%i.log.gz + 100MB + 7 + 1GB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/container-action/rit-local-configs/logbacks/third-federation/fed2.xml b/container-action/rit-local-configs/logbacks/third-federation/fed2.xml new file mode 100644 index 0000000..5ea2426 --- /dev/null +++ b/container-action/rit-local-configs/logbacks/third-federation/fed2.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + System.out + + %date{yyyy-MM-dd-HH:mm:ss.SSSS} %p [%c{1}] %m%n + + + + + /usr/src/logs/fed2-third-federation.log + + + %date{yyyy-MM-dd-HH:mm:ss.SSS} %p [%c{1}] %m%n + + + + ./logs/rskj-%d{yyyy-MM-dd}.%i.log.gz + 100MB + 7 + 1GB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ + + + + + + + + + + + + + diff --git a/container-action/rit-local-configs/logbacks/third-federation/fed3.xml b/container-action/rit-local-configs/logbacks/third-federation/fed3.xml new file mode 100644 index 0000000..38f2c50 --- /dev/null +++ b/container-action/rit-local-configs/logbacks/third-federation/fed3.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + System.out + + %date{yyyy-MM-dd-HH:mm:ss.SSSS} %p [%c{1}] %m%n + + + + + /usr/src/logs/fed3-third-federation.log + + + %date{yyyy-MM-dd-HH:mm:ss.SSS} %p [%c{1}] %m%n + + + + ./logs/rskj-%d{yyyy-MM-dd}.%i.log.gz + 100MB + 7 + 1GB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ + + + + + + + + + + + + + diff --git a/container-action/rit-local-configs/regtest-all-keyfiles.js b/container-action/rit-local-configs/regtest-all-keyfiles.js index 1540e3e..0105e25 100644 --- a/container-action/rit-local-configs/regtest-all-keyfiles.js +++ b/container-action/rit-local-configs/regtest-all-keyfiles.js @@ -10,6 +10,8 @@ const bookkeepingConfigurations = { blockHeadersToSend: 27 }; +// TODO:find a way to use the `regtest-all-keyfiles.js` file instead from `config` directory in the GitHub Jobs to avoid duplication. + module.exports = { init: { mineInitialBitcoin: true, @@ -126,7 +128,7 @@ module.exports = { { id: 'federator-1-second-federation', federationId: 'second-federation', - logbackFile: `${federatesLogbackPath}/second-federation/fed4.xml`, + logbackFile: `${federatesLogbackPath}/second-federation/fed1.xml`, classpath: powpegNodeJarPath, configFile: `${nodesConfigPath}/second-federation/fed1.conf`, publicKeys: { @@ -157,7 +159,7 @@ module.exports = { { id: 'federator-2-second-federation', federationId: 'second-federation', - logbackFile: `${federatesLogbackPath}/second-federation/fed5.xml`, + logbackFile: `${federatesLogbackPath}/second-federation/fed2.xml`, classpath: powpegNodeJarPath, configFile: `${nodesConfigPath}/second-federation/fed2.conf`, publicKeys: { @@ -176,7 +178,7 @@ module.exports = { 'peer.active.0.port': 40000, 'peer.active.0.nodeId': '328105ab6744914e61bcfcb729d741f23528fd6eb1b42628120ab027d82c9c2d8c445f8c6727e291e1abcaa8398bf47d4cca6ea6600b137b875b8df22251e325', 'peer.active.1.ip': '127.0.0.1', - 'peer.active.1.port': 40002, + 'peer.active.1.port': 40004, 'peer.active.1.nodeId': '5ba2b832b97cfba1626eb264bd1ec2733a7ca02602153c57c0c95c9700030dccb0edb612fd01bd806c0202aa3380fb8dcbeddb7a1386259d4b9679c0003d04bc', 'federator.amountOfHeadersToSend': 500, }, @@ -188,7 +190,7 @@ module.exports = { { id: 'federator-3-second-federation', federationId: 'second-federation', - logbackFile: `${federatesLogbackPath}/second-federation/fed6.xml`, + logbackFile: `${federatesLogbackPath}/second-federation/fed3.xml`, classpath: powpegNodeJarPath, configFile: `${nodesConfigPath}/second-federation/fed3.conf`, publicKeys: { @@ -217,6 +219,104 @@ module.exports = { nodeId: '5ba2b832b97cfba1626eb264bd1ec2733a7ca02602153c57c0c95c9700030dccb0edb612fd01bd806c0202aa3380fb8dcbeddb7a1386259d4b9679c0003d04bc' } ] + }, + thirdFederation: { // First svp federation + federationId: 'third-federation', + members: [ + { + id: 'federator-1-third-federation', + federationId: 'third-federation', + logbackFile: `${federatesLogbackPath}/third-federation/fed1.xml`, + classpath: powpegNodeJarPath, + configFile: `${nodesConfigPath}/third-federation/fed1.conf`, + publicKeys: { + btc: '0x034ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d4226', + rsk: '0x034ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d4226', + mst: '0x034ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d4226', + }, + customConfig: { + 'federator.signers.BTC.type': 'keyFile', + 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed1.key`), + 'federator.signers.RSK.type': 'keyFile', + 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed1.key`), + 'federator.signers.MST.type': 'keyFile', + 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed1.key`), + 'peer.active.0.ip': '127.0.0.1', + 'peer.active.0.port': 40000, + 'peer.active.0.nodeId': '328105ab6744914e61bcfcb729d741f23528fd6eb1b42628120ab027d82c9c2d8c445f8c6727e291e1abcaa8398bf47d4cca6ea6600b137b875b8df22251e325', + 'peer.active.1.ip': '127.0.0.1', + 'peer.active.1.port': 50002, + 'peer.active.1.nodeId': '0b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394d3cd349b9b265b7427607736dcee323d9e4a893b1d15549c928278a4e6012c08', + 'federator.amountOfHeadersToSend': 500, + }, + bookkeepingConfigurations, + port: 50000, + rpcPort: 50001, + nodeId: '4ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d42265a5550cda65aa54de279a79a55e5513598b183e64f351e790d6370a6c67b141d' + }, + { + id: 'federator-2-third-federation', + federationId: 'third-federation', + logbackFile: `${federatesLogbackPath}/third-federation/fed2.xml`, + classpath: powpegNodeJarPath, + configFile: `${nodesConfigPath}/third-federation/fed2.conf`, + publicKeys: { + btc: '0x020b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394', + rsk: '0x020b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394', + mst: '0x020b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394', + }, + customConfig: { + 'federator.signers.BTC.type': 'keyFile', + 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed2.key`), + 'federator.signers.RSK.type': 'keyFile', + 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed2.key`), + 'federator.signers.MST.type': 'keyFile', + 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed2.key`), + 'peer.active.0.ip': '127.0.0.1', + 'peer.active.0.port': 50000, + 'peer.active.0.nodeId': '4ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d42265a5550cda65aa54de279a79a55e5513598b183e64f351e790d6370a6c67b141d', + 'peer.active.1.ip': '127.0.0.1', + 'peer.active.1.port': 50004, + 'peer.active.1.nodeId': '501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab01646d8c1f285b44074c45255fc693f19486819256ef98a9be8b62e4e9af4b54542c', + 'federator.amountOfHeadersToSend': 500, + }, + bookkeepingConfigurations, + port: 50002, + rpcPort: 50003, + nodeId: '0b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394d3cd349b9b265b7427607736dcee323d9e4a893b1d15549c928278a4e6012c08' + }, + { + id: 'federator-3-third-federation', + federationId: 'third-federation', + logbackFile: `${federatesLogbackPath}/third-federation/fed3.xml`, + classpath: powpegNodeJarPath, + configFile: `${nodesConfigPath}/third-federation/fed3.conf`, + publicKeys: { + btc: '0x02501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab0164', + rsk: '0x02501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab0164', + mst: '0x02501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab0164', + }, + customConfig: { + 'federator.signers.BTC.type': 'keyFile', + 'federator.signers.BTC.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed3.key`), + 'federator.signers.RSK.type': 'keyFile', + 'federator.signers.RSK.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed3.key`), + 'federator.signers.MST.type': 'keyFile', + 'federator.signers.MST.path': path.resolve(__dirname, `${keysPathResolve}/third-federation/fed3.key`), + 'peer.active.0.ip': '127.0.0.1', + 'peer.active.0.port': 50000, + 'peer.active.0.nodeId': '4ba6ec42eab139697c3614653e130e76fc15d1d7e5c91b3df63d3c06195d42265a5550cda65aa54de279a79a55e5513598b183e64f351e790d6370a6c67b141d', + 'peer.active.1.ip': '127.0.0.1', + 'peer.active.1.port': 50002, + 'peer.active.1.nodeId': '0b1d25b03d041028326ac5b27af941524c31bf09df5fece7476d3940f9cd2394d3cd349b9b265b7427607736dcee323d9e4a893b1d15549c928278a4e6012c08', + 'federator.amountOfHeadersToSend': 500, + }, + bookkeepingConfigurations, + port: 50004, + rpcPort: 50005, + nodeId: '501878fb22fdf374921d168bb1ea02b324f00eb2c7610cb452167a9dcdab01646d8c1f285b44074c45255fc693f19486819256ef98a9be8b62e4e9af4b54542c' + } + ] } }, } diff --git a/lib/assertions/fed.js b/lib/assertions/fed.js index fbac61a..d703831 100644 --- a/lib/assertions/fed.js +++ b/lib/assertions/fed.js @@ -6,8 +6,7 @@ const rskUtilsLegacy = require('../rsk-utils-legacy'); const { getBridge } = require('../bridge-provider'); const rskUtils = require('../rsk-utils'); const { getRskTransactionHelpers } = require('../rsk-tx-helper-provider'); - -const BRIDGE_ADDRESS = "0x0000000000000000000000000000000001000006"; +const { BRIDGE_ADDRESS } = require('../constants/bridge-constants'); var assertKeyControl = (federates) => (begin, end, amountToTransferInWeis) => { var rskClients = federates diff --git a/lib/btc-utils.js b/lib/btc-utils.js index 5e97e14..6736918 100644 --- a/lib/btc-utils.js +++ b/lib/btc-utils.js @@ -107,7 +107,7 @@ const waitForBitcoinTxToBeInMempool = async (btcTxHelper, btcTxHash, maxAttempts const isTxInMempool = bitcoinMempool.includes(btcTxHash); if(!isTxInMempool) { logger.debug(`[${waitForBitcoinTxToBeInMempool.name}::${bitcoinMempoolHasTx.name}] Attempting to check if the btc tx (${btcTxHash}) was already mined since it's not in the mempool yet.`); - const tx = await btcTransactionHelper.getTransaction(btcTxHash); + const tx = await btcTxHelper.getTransaction(btcTxHash); if(tx) { logger.debug(`[${waitForBitcoinTxToBeInMempool.name}::${bitcoinMempoolHasTx.name}] The btc tx (${btcTxHash}) was already mined.`); return true; diff --git a/lib/constants/bridge-constants.js b/lib/constants/bridge-constants.js new file mode 100644 index 0000000..88f7171 --- /dev/null +++ b/lib/constants/bridge-constants.js @@ -0,0 +1,5 @@ +const BRIDGE_ADDRESS = '0x0000000000000000000000000000000001000006'; + +module.exports = { + BRIDGE_ADDRESS +}; diff --git a/lib/constants/federation-constants.js b/lib/constants/federation-constants.js index e0f2817..d9dff81 100644 --- a/lib/constants/federation-constants.js +++ b/lib/constants/federation-constants.js @@ -1,3 +1,5 @@ +const { getBridgeStorageIndexFromKey } = require('../utils'); + const KEY_TYPE_BTC = 'btc'; const KEY_TYPE_RSK = 'rsk'; const KEY_TYPE_MST = 'mst'; @@ -38,6 +40,12 @@ const GENESIS_FEDERATION_REDEEM_SCRIPT = '0x522102cd53fc53a07f211641a677d250f6de const FEDERATION_ACTIVATION_AGE = 150; const FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_BEGIN = 15; const FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_END = 150; +const VALIDATION_PERIOD_DURATION_IN_BLOCKS = 125; + +const svpFundTxHashUnsignedStorageIndex = getBridgeStorageIndexFromKey('svpFundTxHashUnsigned'); +const svpFundTxSignedStorageIndex = getBridgeStorageIndexFromKey('svpFundTxSigned'); +const svpSpendTxHashUnsignedStorageIndex = getBridgeStorageIndexFromKey('svpSpendTxHashUnsigned'); +const svpSpendTxWaitingForSignaturesStorageIndex = getBridgeStorageIndexFromKey('svpSpendTxWaitingForSignatures'); module.exports = { KEY_TYPE_BTC, @@ -54,4 +62,9 @@ module.exports = { FEDERATION_ACTIVATION_AGE, FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_BEGIN, FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_END, + VALIDATION_PERIOD_DURATION_IN_BLOCKS, + svpFundTxHashUnsignedStorageIndex, + svpFundTxSignedStorageIndex, + svpSpendTxHashUnsignedStorageIndex, + svpSpendTxWaitingForSignaturesStorageIndex, }; diff --git a/lib/federation-utils.js b/lib/federation-utils.js index cc9795b..505694c 100644 --- a/lib/federation-utils.js +++ b/lib/federation-utils.js @@ -34,10 +34,16 @@ const getNewFederationPublicKeysFromNewFederationConfig = (newFederationConfig) const stopPreviousFederators = async (newFederationConfig) => { - // TODO: right now there is only the genesis federation and a second federation. So the previous is the genesis federation. - // When more are added, we need to change this logic to dynamically select the previous federation. - // I tried with a `switch` statement ready for future changes but sonar complains about it. - const previousFederationId = 'genesis-federation'; + let previousFederationId; + + switch(newFederationConfig.federationId) { + case 'second-federation': + previousFederationId = 'genesis-federation'; + break; + case 'third-federation': + previousFederationId = 'second-federation'; + break; + } Runners.fedRunners.forEach((fedRunner) => { if(fedRunner.options.federationId === previousFederationId) { @@ -66,6 +72,147 @@ const fundNewFederators = async (rskTxHelper, newFederationConfig) => { } }; +const getActiveFederationPublicKeys = async (bridge) => { + + const initialFederationKeys = []; + + const initialFederationSize = Number(await bridge.methods.getFederationSize().call()); + + for(let i = 0; i < initialFederationSize; i++) { + + const federatorBtcPublicKey = await bridge.methods.getFederatorPublicKeyOfType(i, KEY_TYPE_BTC).call(); + const federatorRskPublicKey = await bridge.methods.getFederatorPublicKeyOfType(i, KEY_TYPE_RSK).call(); + const federatorMstPublicKey = await bridge.methods.getFederatorPublicKeyOfType(i, KEY_TYPE_MST).call(); + + initialFederationKeys.push({ + [KEY_TYPE_BTC]: federatorBtcPublicKey, + [KEY_TYPE_RSK]: federatorRskPublicKey, + [KEY_TYPE_MST]: federatorMstPublicKey + }); + + } + + return initialFederationKeys; + +}; + +const getProposedFederationPublicKeys = async (bridge) => { + + const proposedFederationKeys = []; + + const proposedFederationSize = Number(await bridge.methods.getProposedFederationSize().call()); + + for(let i = 0; i < proposedFederationSize; i++) { + + const federatorBtcPublicKey = await bridge.methods.getProposedFederatorPublicKeyOfType(i, KEY_TYPE_BTC).call(); + const federatorRskPublicKey = await bridge.methods.getProposedFederatorPublicKeyOfType(i, KEY_TYPE_RSK).call(); + const federatorMstPublicKey = await bridge.methods.getProposedFederatorPublicKeyOfType(i, KEY_TYPE_MST).call(); + + proposedFederationKeys.push({ + [KEY_TYPE_BTC]: federatorBtcPublicKey, + [KEY_TYPE_RSK]: federatorRskPublicKey, + [KEY_TYPE_MST]: federatorMstPublicKey + }); + + } + + return proposedFederationKeys; + +}; + +const getRetiringFederationPublicKeys = async (bridge) => { + + const retiringFederationKeys = []; + + const retiringFederationSize = Number(await bridge.methods.getRetiringFederationSize().call()); + + for(let i = 0; i < retiringFederationSize; i++) { + + const federatorBtcPublicKey = await bridge.methods.getRetiringFederatorPublicKeyOfType(i, KEY_TYPE_BTC).call(); + const federatorRskPublicKey = await bridge.methods.getRetiringFederatorPublicKeyOfType(i, KEY_TYPE_RSK).call(); + const federatorMstPublicKey = await bridge.methods.getRetiringFederatorPublicKeyOfType(i, KEY_TYPE_MST).call(); + + retiringFederationKeys.push({ + [KEY_TYPE_BTC]: federatorBtcPublicKey, + [KEY_TYPE_RSK]: federatorRskPublicKey, + [KEY_TYPE_MST]: federatorMstPublicKey + }); + + } + + return retiringFederationKeys; + +}; + +const getProposedFederationInfo = async (bridge) => { + + const proposedFederationInfoResponses = await Promise.all([ + bridge.methods.getProposedFederationSize().call(), + bridge.methods.getProposedFederationAddress().call(), + bridge.methods.getProposedFederationCreationBlockNumber().call(), + bridge.methods.getProposedFederationCreationTime().call() + ]); + + const size = Number(proposedFederationInfoResponses[0]); + const address = proposedFederationInfoResponses[1]; + const creationBlockNumber = Number(proposedFederationInfoResponses[2]); + const creationTime = Number(proposedFederationInfoResponses[3]); + + return { + size, + address, + creationBlockNumber, + creationTime, + }; + +}; + +const getActiveFederationInfo = async (bridge) => { + + const activeFederationInfoResponses = await Promise.all([ + bridge.methods.getFederationSize().call(), + bridge.methods.getFederationAddress().call(), + bridge.methods.getFederationCreationBlockNumber().call(), + bridge.methods.getFederationCreationTime().call() + ]); + + const size = Number(activeFederationInfoResponses[0]); + const address = activeFederationInfoResponses[1]; + const creationBlockNumber = Number(activeFederationInfoResponses[2]); + const creationTime = Number(activeFederationInfoResponses[3]); + + return { + size, + address, + creationBlockNumber, + creationTime, + }; + +}; + +const getRetiringFederationInfo = async (bridge) => { + + const retiringFederationInfoResponses = await Promise.all([ + bridge.methods.getRetiringFederationSize().call(), + bridge.methods.getRetiringFederationAddress().call(), + bridge.methods.getRetiringFederationCreationBlockNumber().call(), + bridge.methods.getRetiringFederationCreationTime().call() + ]); + + const size = Number(retiringFederationInfoResponses[0]); + const address = retiringFederationInfoResponses[1]; + const creationBlockNumber = Number(retiringFederationInfoResponses[2]); + const creationTime = Number(retiringFederationInfoResponses[3]); + + return { + size, + address, + creationBlockNumber, + creationTime, + }; + +}; + module.exports = { comparePublicKeys, compareFederateKeys, @@ -73,4 +220,10 @@ module.exports = { stopPreviousFederators, startNewFederationNodes, fundNewFederators, + getProposedFederationPublicKeys, + getProposedFederationInfo, + getActiveFederationPublicKeys, + getRetiringFederationPublicKeys, + getActiveFederationInfo, + getRetiringFederationInfo, }; diff --git a/lib/liquidity-bridge-contract.js b/lib/liquidity-bridge-contract.js index 2c9cdd0..034f6e2 100644 --- a/lib/liquidity-bridge-contract.js +++ b/lib/liquidity-bridge-contract.js @@ -4,9 +4,9 @@ const { getRskTransactionHelper } = require('../lib/rsk-tx-helper-provider'); const { compileAndDeploy } = require('./sol-utils'); const { sendFromCow } = require('./rsk-utils'); const { btcToWeis } = require('@rsksmart/btc-eth-unit-converter'); +const { BRIDGE_ADDRESS } = require('./constants/bridge-constants'); const INITIAL_RSK_BALANCE_IN_BTC = 1; -const BRIDGE_ADDRESS = '0x0000000000000000000000000000000001000006'; const LIQUIDITY_BRIDGE_CONTRACT_FILE = './contracts/LBC.sol'; // Relative to tests root const LIQUIDITY_BRIDGE_CONTRACT_NAME = 'LiquidityBridgeContractImpl'; const SOLIDITY_COMPILER_VERSION = 'v0.7.4+commit.3f05b770'; diff --git a/lib/rsk-utils.js b/lib/rsk-utils.js index db96da4..17805ee 100644 --- a/lib/rsk-utils.js +++ b/lib/rsk-utils.js @@ -11,6 +11,7 @@ const { getLogger } = require('../logger'); const { PEGOUT_EVENTS } = require('./constants/pegout-constants'); const { FEE_PER_KB_CHANGER_PRIVATE_KEY, FEE_PER_KB_CHANGER_ADDRESS, FEE_PER_KB_RESPONSE_CODES } = require('./constants/fee-per-kb-constants'); const { DEFAULT_RSK_ADDRESS_FUNDING_IN_BTC } = require('./constants/pegin-constants'); +const { BRIDGE_ADDRESS } = require('./constants/bridge-constants'); const BtcTransactionHelper = require('btc-transaction-helper/btc-transaction-helper'); const { ethToWeis } = require('@rsksmart/btc-eth-unit-converter'); @@ -162,14 +163,17 @@ const increaseBlockToNextPegoutHeight = async (rskTransactionHelpers) => { * @param {number} timeInMilliseconds defaults to 1000 * @returns {Promise} */ -const waitAndUpdateBridge = async (rskTxHelper, timeInMilliseconds = 1000) => { +const waitAndUpdateBridge = async (rskTxHelper, timeInMilliseconds = 1000, shouldMineAndSync = true) => { await wait(timeInMilliseconds); await rskTxHelper.updateBridge(); // Wait for the rsk `updateBridge` tx to be in the rsk mempool before mining await waitForRskMempoolToGetNewTxs(rskTxHelper); - await mineAndSync(getRskTransactionHelpers()); + if(shouldMineAndSync) { + await mineAndSync(getRskTransactionHelpers()); + } + }; /** @@ -177,9 +181,10 @@ const waitAndUpdateBridge = async (rskTxHelper, timeInMilliseconds = 1000) => { * @param {RskTransactionHelper} rskTxHelper * @returns {Promise} array of tx hashes in the mempool */ -const getRskMempoolTransactionHashes = async (rskTxHelper) => { +const getRskMempoolTransactionsToTheBridge = async (rskTxHelper) => { const mempoolBlock = await rskTxHelper.getClient().eth.getBlock('pending'); - return mempoolBlock.transactions; + const transactionsToTheBridge = mempoolBlock.transactions.filter((tx) => tx.to === BRIDGE_ADDRESS); + return transactionsToTheBridge; }; /** @@ -238,15 +243,15 @@ const waitForRskTxToBeInTheMempool = async (rskTxHelper, txHash, maxAttempts = 3 */ const waitForRskMempoolToGetNewTxs = async (rskTxHelper, maxAttempts = 3, checkEveryMilliseconds = 500) => { - const initialRskMempoolTxHashes = await getRskMempoolTransactionHashes(rskTxHelper); + const initialRskMempoolTxs = await getRskMempoolTransactionsToTheBridge(rskTxHelper); - logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] initial rsk mempool size: ${initialRskMempoolTxHashes.length}`); + logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] initial rsk mempool size: ${initialRskMempoolTxs.length}`); logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] Will wait and attempt to check if the rsk mempool has received any new transactions ${maxAttempts} times.`); const areThereNewTxsInTheMempool = async () => { - const mempoolTxHashes = await getRskMempoolTransactionHashes(rskTxHelper); - if(mempoolTxHashes.length > initialRskMempoolTxHashes.length) { - logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] The mempool got ${mempoolTxHashes.length - initialRskMempoolTxHashes.length} new transactions`); + const mempoolTxHashes = await getRskMempoolTransactionsToTheBridge(rskTxHelper); + if(mempoolTxHashes.length > initialRskMempoolTxs.length) { + logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] The mempool got ${mempoolTxHashes.length - initialRskMempoolTxs.length} new transactions`); return true; } return false; @@ -258,9 +263,9 @@ const waitForRskMempoolToGetNewTxs = async (rskTxHelper, maxAttempts = 3, checkE const { result: newTxsWhereFoundInTheRskMempool, attempts } = await retryWithCheck(areThereNewTxsInTheMempool, check, maxAttempts, checkEveryMilliseconds); - const finalRskMempoolTxHashes = await getRskMempoolTransactionHashes(rskTxHelper); + const finalRskMempoolTxHashes = await getRskMempoolTransactionsToTheBridge(rskTxHelper); - logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] final rsk mempool size: ${finalRskMempoolTxHashes.length}, after ${attempts} attempts. Difference with initial mempool size: ${finalRskMempoolTxHashes.length - initialRskMempoolTxHashes.length}`); + logger.debug(`[${waitForRskMempoolToGetNewTxs.name}] final rsk mempool size: ${finalRskMempoolTxHashes.length}, after ${attempts} attempts. Difference with initial mempool size: ${finalRskMempoolTxHashes.length - initialRskMempoolTxs.length}`); return newTxsWhereFoundInTheRskMempool; @@ -274,7 +279,7 @@ const waitForRskMempoolToGetNewTxs = async (rskTxHelper, maxAttempts = 3, checkE */ const triggerRelease = async (rskTransactionHelpers, btcClient, callbacks = {}) => { - const rskTxHelper = rskTransactionHelpers[rskTransactionHelpers.length - 1]; + const rskTxHelper = rskTransactionHelpers[0]; await increaseBlockToNextPegoutHeight(rskTransactionHelpers); @@ -392,6 +397,9 @@ const sendTxWithCheck = async (rskTxHelper, method, from, checkCallback) => { * @returns {BridgeEvent} event */ const findEventInBlock = async (rskTxHelper, eventName, fromBlockHashOrBlockNumber, toBlockHashOrBlockNumber, check) => { + if(typeof eventName !== 'string') { + throw new Error('`eventName` parameter must be a string. It is of type: ' + typeof eventName); + } if(!fromBlockHashOrBlockNumber) { fromBlockHashOrBlockNumber = await rskTxHelper.getBlockNumber(); } @@ -478,14 +486,8 @@ const getPegoutEventsInBlockRange = async (rskTxHelper, fromBlockHashOrBlockNumb }; const findBridgeTransactionsInThisBlock = async (web3Client, blockHashOrBlockNumber) => { - const bridgeTxParser = new BridgeTransactionParser(web3Client); - - // TODO: uncomment this when lovell700 is active. - // const bridgeTransactionParser = new BridgeTransactionParser(web3Client); - // return bridgeTransactionParser.getBridgeTransactionsInThisBlock(blockHashOrBlockNumber); - - return bridgeTxParser.getBridgeTransactionsInThisBlock(blockHashOrBlockNumber); + return await bridgeTxParser.getBridgeTransactionsInThisBlock(blockHashOrBlockNumber); } /** @@ -555,7 +557,7 @@ const getNewFundedRskAddress = async (rskTxHelper, fundingAmountInRbtc = DEFAULT const waitForRskMempoolToGetAtLeastThisManyTxs = async (rskTxHelper, atLeastExpectedCount, waitTimeInMilliseconds = 500, maxAttempts = 20) => { let attempts = 1; while(attempts <= maxAttempts) { - const rskMempoolTxs = await getRskMempoolTransactionHashes(rskTxHelper); + const rskMempoolTxs = await getRskMempoolTransactionsToTheBridge(rskTxHelper); logger.debug(`[${waitForRskMempoolToGetAtLeastThisManyTxs.name}] rsk mempool txs: ${rskMempoolTxs.length}`); if(rskMempoolTxs.length >= atLeastExpectedCount) { logger.debug(`[${waitForRskMempoolToGetAtLeastThisManyTxs.name}] rsk mempool has ${rskMempoolTxs.length} (at least expected was ${atLeastExpectedCount}) txs after ${attempts} attempts`); @@ -611,7 +613,7 @@ module.exports = { findEventInBlock, getUnlockedAddress, getFedsPubKeys, - getRskMempoolTransactionHashes, + getRskMempoolTransactionsToTheBridge, waitForRskTxToBeInTheMempool, waitForRskMempoolToGetNewTxs, waitAndUpdateBridge, diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index 8eaefac..1230ab4 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -3,7 +3,7 @@ const BN = require('bn.js'); const { createPeginV1TxData } = require('pegin-address-verificator'); const { getBridge } = require('../bridge-provider'); const { getBtcClient } = require('../btc-client-provider'); -const { getRskTransactionHelper, getRskTransactionHelpers } = require('../rsk-tx-helper-provider'); +const { getRskTransactionHelpers } = require('../rsk-tx-helper-provider'); const { satoshisToBtc, btcToSatoshis, satoshisToWeis } = require('@rsksmart/btc-eth-unit-converter'); const { findEventInBlock, @@ -52,7 +52,7 @@ const execute = (description, getRskHost) => { btcTxHelper = getBtcClient(); rskTxHelpers = getRskTransactionHelpers(); - rskTxHelper = getRskTransactionHelper(getRskHost()); + rskTxHelper = rskTxHelpers[0]; bridge = await getBridge(rskTxHelper.getClient()); federationAddress = await bridge.methods.getFederationAddress().call(); @@ -63,6 +63,13 @@ const execute = (description, getRskHost) => { }); + after(async () => { + + // Ensure there's no pegout txs left to be processed + await triggerRelease(rskTxHelpers, btcTxHelper); + + }); + describe('Pegin tests', () => { it('should do a basic legacy pegin with the exact minimum value', async () => { diff --git a/lib/tests/activate-fork b/lib/tests/activate-fork index dabbfcf..462d663 100644 --- a/lib/tests/activate-fork +++ b/lib/tests/activate-fork @@ -3,13 +3,13 @@ const rskUtils = require('../rsk-utils'); const CustomError = require('../CustomError'); const { getRskTransactionHelpers } = require('../rsk-tx-helper-provider'); -const execute = (fork, getFederates) => { +const execute = (fork) => { describe(`Activate ${fork.name} fork`, () => { it(`should mine blocks until reach ${fork.activationHeight}th block in order to activate the fork`, async () => { try { - const rskTransactionHelpers = getRskTransactionHelpers(getFederates()); + const rskTransactionHelpers = getRskTransactionHelpers(); const currentBlockNumber = await rskTransactionHelpers[0].getBlockNumber(); expect(currentBlockNumber).to.be.below(fork.activationHeight); diff --git a/lib/tests/bridge-calls.js b/lib/tests/bridge-calls.js index efba9cf..7dbff86 100644 --- a/lib/tests/bridge-calls.js +++ b/lib/tests/bridge-calls.js @@ -8,9 +8,9 @@ const solUtils = require('../sol-utils'); const btcEthUnitConverter = require('@rsksmart/btc-eth-unit-converter'); const { getRskTransactionHelper } = require('../rsk-tx-helper-provider'); const { getBridge } = require('../bridge-provider'); +const { BRIDGE_ADDRESS } = require('../constants/bridge-constants'); const INITIAL_RSK_BALANCE_IN_BTC = 1; -const BRIDGE_ADDRESS = '0x0000000000000000000000000000000001000006'; const BRIDGE_TESTER_FILE = './contracts/contract-calls-tester.sol'; // Relative to tests root const BRIDGE_TESTER_CONTRACT_NAME = 'ContractCallsTester'; const SOLIDITY_COMPILER_VERSION = 'v0.4.16+commit.d7661dd9'; diff --git a/lib/tests/call_receive_header.js b/lib/tests/call_receive_header.js index e35389e..298e988 100644 --- a/lib/tests/call_receive_header.js +++ b/lib/tests/call_receive_header.js @@ -12,10 +12,10 @@ let bridge; const HEADER_RECEIVED_OK = 0; const RECEIVE_HEADER_CALLED_TOO_SOON = -1; -const execute = (description, getRskHost) => { +const execute = (description) => { describe(description, function() { before(async () => { - rskTxHelper = getRskTransactionHelper(getRskHost()); + rskTxHelper = getRskTransactionHelper(); btcTxHelper = getBtcClient(); bridge = await getBridge(rskTxHelper.getClient()); diff --git a/lib/tests/change-federation.js b/lib/tests/change-federation.js index 62ea9a5..9622342 100644 --- a/lib/tests/change-federation.js +++ b/lib/tests/change-federation.js @@ -1,11 +1,22 @@ const rskUtils = require('../rsk-utils'); +const bitcoinJsLib = require('bitcoinjs-lib'); +const { parseRLPToPegoutWaitingSignatures } = require('@rsksmart/bridge-state-data-parser/pegout-waiting-signature'); const { getRskTransactionHelpers } = require('../rsk-tx-helper-provider'); const { getBridge } = require('../bridge-provider'); -const { btcToWeis } = require('@rsksmart/btc-eth-unit-converter'); -const { removePrefix0x, ensure0x, splitStringIntoChunks } = require('../utils'); +const { + removePrefix0x, + ensure0x, + splitStringIntoChunks, + wait, + getBridgeStorageValueDecodedHexString, + decodeRlp, + retryWithCheck, +} = require('../utils'); const BridgeTransactionParser = require('@rsksmart/bridge-transaction-parser'); const redeemScriptParser = require('@rsksmart/powpeg-redeemscript-parser'); +const { btcToWeis, satoshisToBtc } = require('@rsksmart/btc-eth-unit-converter'); const { expect } = require('chai'); +const { BRIDGE_ADDRESS } = require('../constants/bridge-constants'); const { KEY_TYPE_BTC, KEY_TYPE_RSK, @@ -15,38 +26,35 @@ const { FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_END, ERP_PUBKEYS, ERP_CSV_VALUE, + FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_BEGIN, + svpFundTxHashUnsignedStorageIndex, + svpFundTxSignedStorageIndex, + svpSpendTxHashUnsignedStorageIndex, + svpSpendTxWaitingForSignaturesStorageIndex, } = require('../constants/federation-constants'); const { getNewFederationPublicKeysFromNewFederationConfig, stopPreviousFederators, startNewFederationNodes, fundNewFederators, + getProposedFederationInfo, + getProposedFederationPublicKeys, + getRetiringFederationInfo, + getActiveFederationPublicKeys, + getActiveFederationInfo, + getRetiringFederationPublicKeys, } = require('../federation-utils'); const { getBtcClient } = require('../btc-client-provider'); - -const getCurrentFederationKeys = async (bridge) => { - - const initialFederationKeys = []; - - const initialFederationSize = Number(await bridge.methods.getFederationSize().call()); - - for(let i = 0; i < initialFederationSize; i++) { - - const federatorBtcPublicKey = await bridge.methods.getFederatorPublicKeyOfType(i, KEY_TYPE_BTC).call(); - const federatorRskPublicKey = await bridge.methods.getFederatorPublicKeyOfType(i, KEY_TYPE_RSK).call(); - const federatorMstPublicKey = await bridge.methods.getFederatorPublicKeyOfType(i, KEY_TYPE_MST).call(); - - initialFederationKeys.push({ - [KEY_TYPE_BTC]: federatorBtcPublicKey, - [KEY_TYPE_RSK]: federatorRskPublicKey, - [KEY_TYPE_MST]: federatorMstPublicKey - }); - - } - - return initialFederationKeys; - -}; +const { PEGOUT_EVENTS, MINIMUM_PEGOUT_AMOUNT_IN_SATOSHIS } = require('../constants/pegout-constants'); +const { + createSenderRecipientInfo, + sendPegin, + ensurePeginIsRegistered, + BTC_TO_RSK_MINIMUM_CONFIRMATIONS +} = require('../2wp-utils'); +const { getBridgeState } = require('@rsksmart/bridge-state-data-parser'); +const { waitForBitcoinTxToBeInMempool } = require('../btc-utils'); +const { decodeOutpointValues } = require('../varint'); const parseBtcPublicKeys = (btcPublicKeysInString) => { const publicKeyLengthWithoutOxPrefix = 66; @@ -56,7 +64,7 @@ const parseBtcPublicKeys = (btcPublicKeysInString) => { return btcPublicKeysInArrayWith0xPrefix; }; -const execute = (description, newFederationConfig) => { +const execute = (description, newFederationConfig, makeDonationPegins = false) => { describe(description, async function() { @@ -74,13 +82,19 @@ const execute = (description, newFederationConfig) => { let fedChangeAuthorizer3Address; let initialFederationPublicKeys; let newFederationBtcPublicKeys; - let initialFederationAddress; let commitFederationEvent; let expectedNewFederationAddress; let commitFederationCreationBlockNumber; let expectedNewFederationErpRedeemScript; let newFederationPublicKeys; let btcTxHelper; + let minimumPeginValueInSatoshis; + let bridgeStateBeforeActivation; + let isLovellActive; + let expectedFlyoverAddress; + let svpSpendBtcTransaction; + let proposedFederationInfo; + let initialActiveFederationInfo; before(async () => { @@ -107,19 +121,36 @@ const execute = (description, newFederationConfig) => { bridge = await getBridge(rskTxHelper.getClient()); - initialFederationPublicKeys = await getCurrentFederationKeys(bridge); + initialFederationPublicKeys = await getActiveFederationPublicKeys(bridge); newFederationPublicKeys = getNewFederationPublicKeysFromNewFederationConfig(newFederationConfig); + newFederationBtcPublicKeys = newFederationPublicKeys.map(federator => federator[KEY_TYPE_BTC]); const expectedNewFederationErpRedeemScriptBuffer = redeemScriptParser.getP2shErpRedeemScript(newFederationBtcPublicKeys.map(key => removePrefix0x(key)), ERP_PUBKEYS, ERP_CSV_VALUE); expectedNewFederationErpRedeemScript = expectedNewFederationErpRedeemScriptBuffer.toString('hex'); expectedNewFederationAddress = redeemScriptParser.getAddressFromRedeemScript('REGTEST', expectedNewFederationErpRedeemScriptBuffer); - initialFederationAddress = await bridge.methods.getFederationAddress().call(); + const flyoverPowpegRedeemScript = redeemScriptParser.getFlyoverRedeemScript(expectedNewFederationErpRedeemScriptBuffer, "0000000000000000000000000000000000000000000000000000000000000001"); + expectedFlyoverAddress = redeemScriptParser.getAddressFromRedeemScript('REGTEST', flyoverPowpegRedeemScript); - await btcTxHelper.importAddress(initialFederationAddress, 'federations'); + initialActiveFederationInfo = await getActiveFederationInfo(bridge); + + await btcTxHelper.importAddress(initialActiveFederationInfo.address, 'federations'); await btcTxHelper.importAddress(expectedNewFederationAddress, 'federations'); + minimumPeginValueInSatoshis = Number(await bridge.methods.getMinimumLockTxValue().call()); + + if(makeDonationPegins) { + // Making donation pegins to ensure there are enough utxos for migration. + const senderRecipientInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper, 'legacy', satoshisToBtc(minimumPeginValueInSatoshis) * 4); + await sendPegin(rskTxHelper, btcTxHelper, senderRecipientInfo.btcSenderAddressInfo, satoshisToBtc(minimumPeginValueInSatoshis)); + await sendPegin(rskTxHelper, btcTxHelper, senderRecipientInfo.btcSenderAddressInfo, satoshisToBtc(minimumPeginValueInSatoshis + 1000)); + const btcPeginTxHash = await sendPegin(rskTxHelper, btcTxHelper, senderRecipientInfo.btcSenderAddressInfo, satoshisToBtc(minimumPeginValueInSatoshis + 2000)); + await ensurePeginIsRegistered(rskTxHelper, btcPeginTxHash); + } + + isLovellActive = await Runners.common.forks.lovell700.isAlreadyActive(); + }); it('should create a pending federation', async () => { @@ -179,6 +210,8 @@ const execute = (description, newFederationConfig) => { it('should commit the pending federation', async () => { + bridgeStateBeforeActivation = await getBridgeState(rskTxHelper.getClient()); + const pendingFederationHash = await bridge.methods.getPendingFederationHash().call(); const commitPendingFederationMethod = bridge.methods.commitFederation(pendingFederationHash); @@ -205,7 +238,7 @@ const execute = (description, newFederationConfig) => { // Assert the old federation address in the commit federation event is the initial one. const oldFederationAddress = commitFederationEvent.arguments.oldFederationBtcAddress; - expect(oldFederationAddress).to.be.equal(initialFederationAddress, 'The old federation address in the commit_federation event should now be the same as the initial.'); + expect(oldFederationAddress).to.be.equal(initialActiveFederationInfo.address, 'The old federation address in the commit_federation event should now be the same as the initial.'); // Assert the new federation address in the commit federation event is the expected one. const newActiveFederationAddress = commitFederationEvent.arguments.newFederationBtcAddress; @@ -223,6 +256,191 @@ const execute = (description, newFederationConfig) => { }); + it('should create the proposed federation', async function () { + + if(!isLovellActive) { + return this.skip(); + } + + proposedFederationInfo = await getProposedFederationInfo(bridge); + + expect(proposedFederationInfo.size).to.be.equal(newFederationPublicKeys.length, 'The proposed federation size should be the expected one.'); + expect(proposedFederationInfo.address).to.be.equal(expectedNewFederationAddress, 'The proposed federation address should be the expected one.'); + expect(proposedFederationInfo.creationBlockNumber).to.be.equal(commitFederationCreationBlockNumber, 'The proposed federation creation block number should be the expected one.'); + + const proposedFederationPublicKeys = await getProposedFederationPublicKeys(bridge); + expect(proposedFederationPublicKeys).to.be.deep.equal(newFederationPublicKeys, 'The proposed federation public keys should be the expected ones.'); + + }); + + it('should not have created svp fund transaction and there should not be any SVP values in storage yet', async function () { + + if(!isLovellActive) { + return this.skip(); + } + + const bridgeState = await getBridgeState(rskTxHelper.getClient()); + + expect(bridgeState.pegoutsWaitingForConfirmations.length).to.be.equal(0, 'No pegout should be waiting for confirmations.'); + expect(bridgeState.pegoutsWaitingForSignatures.length).to.be.equal(0, 'No pegout should be waiting for signatures.'); + + await assertSvpValuesNotPresentInStorage(rskTxHelper); + + }); + + it('should create the SVP Fund transaction on the next updateCollections call', async function () { + + if(!isLovellActive) { + return this.skip(); + } + + // Act + + const initialBridgeState = await getBridgeState(rskTxHelper.getClient()); + + await rskUtils.waitAndUpdateBridge(rskTxHelper); + + const finalBridgeState = await getBridgeState(rskTxHelper.getClient()); + + // Assert + + // The SVP fund transaction should be created and put in waiting for confirmations. + expect(finalBridgeState.pegoutsWaitingForConfirmations.length).to.be.equal(1, 'There should be one pegout waiting for confirmations.'); + expect(finalBridgeState.pegoutsWaitingForSignatures.length).to.be.equal(0, 'No pegout should be waiting for signatures.'); + + const svpPegoutWaitingForConfirmations = finalBridgeState.pegoutsWaitingForConfirmations[0]; + + const pegoutCreationBlockNumber = Number(svpPegoutWaitingForConfirmations.pegoutCreationBlockNumber); + + const expectedPegoutCreationBlockNumber = commitFederationCreationBlockNumber + 1; + expect(pegoutCreationBlockNumber).to.be.equal(expectedPegoutCreationBlockNumber, 'The svp fund tx pegout creation block number should be the block that contains the first updateCollections call right after the commitFederation call.'); + + const rawSvpBtcTransaction = svpPegoutWaitingForConfirmations.btcRawTx; + + const btcTransaction = bitcoinJsLib.Transaction.fromHex(rawSvpBtcTransaction); + + expect(btcTransaction.outs.length).to.be.equal(3, 'The SVP fund transaction should have 3 outputs.'); + + const proposedFederationAddress = await bridge.methods.getProposedFederationAddress().call(); + + // The output addresses should be in the expected order. + const proposedFederationOutput = btcTransaction.outs[0]; + const proposedFederationFlyoverOutput = btcTransaction.outs[1]; + const activeFederationOutput = btcTransaction.outs[2]; + + const actualProposedFederationAddress = bitcoinJsLib.address.fromOutputScript(proposedFederationOutput.script, btcTxHelper.btcConfig.network); + expect(actualProposedFederationAddress).to.be.equal(proposedFederationAddress, 'The proposed federation address in the SVP fund transaction should be the first output.'); + + const actualProposedFederationFlyoverAddress = bitcoinJsLib.address.fromOutputScript(proposedFederationFlyoverOutput.script, btcTxHelper.btcConfig.network); + expect(actualProposedFederationFlyoverAddress).to.be.equal(expectedFlyoverAddress, 'The flyover address in the SVP fund transaction should be the second expected output.'); + + const actualActiveFederationAddress = bitcoinJsLib.address.fromOutputScript(activeFederationOutput.script, btcTxHelper.btcConfig.network); + expect(actualActiveFederationAddress).to.be.equal(initialActiveFederationInfo.address, 'The active federation address in the SVP fund transaction should be the last output.'); + + // The proposed federation and flyover addresses output values should be double the minimum pegout value. + const expectedProposedFederationOutputValue = MINIMUM_PEGOUT_AMOUNT_IN_SATOSHIS * 2; + expect(proposedFederationOutput.value).to.be.equal(expectedProposedFederationOutputValue, 'The proposed federation output value should be double the minimum pegout value.'); + + const expectedFlyoverOutputValue = MINIMUM_PEGOUT_AMOUNT_IN_SATOSHIS * 2; + expect(proposedFederationFlyoverOutput.value).to.be.equal(expectedFlyoverOutputValue, 'The flyover output value should be double the minimum pegout value.'); + + // Only the svp fund tx hash unsigned value should be in storage + await assertOnlySvpFundTxHashUnsignedIsInStorage(rskTxHelper, btcTransaction.getId()); + + // The release_requested event should be emitted with the expected values + const releaseRequestedEvent = await rskUtils.findEventInBlock(rskTxHelper, PEGOUT_EVENTS.RELEASE_REQUESTED.name, expectedPegoutCreationBlockNumber); + const expectedReleaseRequestedAmount = expectedProposedFederationOutputValue + expectedFlyoverOutputValue; + expect(Number(releaseRequestedEvent.arguments.amount)).to.be.equal(expectedReleaseRequestedAmount, 'The amount in the release requested event should be the sum of the proposed federation and flyover output values.'); + expect(releaseRequestedEvent, 'The release requested event should be emitted.').to.not.be.null; + expect(removePrefix0x(releaseRequestedEvent.arguments.btcTxHash)).to.be.equal(btcTransaction.getId(), 'The btc tx hash in the release requested event should be the tx id of the SVP fund tx.'); + + // The pegout_transaction_created event should be emitted with the expected values + const pegoutTransactionCreatedEvent = await rskUtils.findEventInBlock(rskTxHelper, PEGOUT_EVENTS.PEGOUT_TRANSACTION_CREATED.name, expectedPegoutCreationBlockNumber); + expect(pegoutTransactionCreatedEvent, 'The pegout transaction created event should be emitted.').to.not.be.null; + expect(removePrefix0x(pegoutTransactionCreatedEvent.arguments.btcTxHash)).to.be.equal(btcTransaction.getId(), 'The btc tx hash in the pegout transaction created event should be the tx id of the SVP fund tx.'); + + await assertPegoutTransactionCreatedOutpointValues(initialBridgeState, finalBridgeState, btcTransaction, pegoutTransactionCreatedEvent); + + }); + + it('should release and register the svp fund transaction and create the svp spend transaction', async function () { + + if(!isLovellActive) { + return this.skip(); + } + + // Mining to have enough confirmations for the SVP fund transaction and updating the bridge. + await rskTxHelper.mine(3); + await rskUtils.waitAndUpdateBridge(rskTxHelper); + + const initialBridgeState = await getBridgeState(rskTxHelper.getClient()); + expect(initialBridgeState.pegoutsWaitingForSignatures.length).to.be.equal(1, 'The svp fund transaction should be waiting for signatures.'); + + const blockNumberBeforeRelease = await rskTxHelper.getBlockNumber(); + await rskUtils.triggerRelease(rskTxHelpers, btcTxHelper); + const blockNumberAfterRelease = await rskTxHelper.getBlockNumber(); + const releaseBtcEvent = await rskUtils.findEventInBlock(rskTxHelper, PEGOUT_EVENTS.RELEASE_BTC.name, blockNumberBeforeRelease, blockNumberAfterRelease); + + const releaseBtcTransaction = bitcoinJsLib.Transaction.fromHex(removePrefix0x(releaseBtcEvent.arguments.btcRawTransaction)); + const finalBridgeState = await getBridgeState(rskTxHelper.getClient()); + const registeredSvpFundTxUtxo = finalBridgeState.activeFederationUtxos.find(utxo => utxo.btcTxHash === releaseBtcTransaction.getId()); + + expect(registeredSvpFundTxUtxo, 'The SVP fund tx should be registered in the Bridge by now.').to.not.be.undefined; + + const utxoToActiveFederation = releaseBtcTransaction.outs[2]; + expect(registeredSvpFundTxUtxo.valueInSatoshis).to.be.equal(utxoToActiveFederation.value, 'The SVP fund tx registered utxo value should be the same as the utxo to the active federation.'); + + await assertOnlySvpSpendTxValuesAreInStorage(rskTxHelper); + + await assertProposedFederationIsStillInStorage(bridge, expectedNewFederationAddress, newFederationPublicKeys); + + }); + + it('should register the SVP Spend transaction and finish the SVP process', async function () { + + if(!isLovellActive) { + return this.skip(); + } + + const blockNumberBeforeUpdateCollections = await rskTxHelper.getBlockNumber(); + const expectedCountOfSignatures = Math.floor(newFederationPublicKeys.length / 2) + 1; + + await waitForExpectedCountOfAddSignatureEventsToBeEmitted(rskTxHelpers, blockNumberBeforeUpdateCollections, expectedCountOfSignatures); + + // Finding the SVP Spend transaction release_btc event + const blockNumberAfterRelease = await rskTxHelper.getBlockNumber(); + const releaseBtcEvent = await rskUtils.findEventInBlock(rskTxHelper, PEGOUT_EVENTS.RELEASE_BTC.name, blockNumberBeforeUpdateCollections, blockNumberAfterRelease); + svpSpendBtcTransaction = bitcoinJsLib.Transaction.fromHex(removePrefix0x(releaseBtcEvent.arguments.btcRawTransaction)); + + // Mining the SVP Spend transaction in bitcoin to register it in the Bridge + await waitForBitcoinTxToBeInMempool(btcTxHelper, svpSpendBtcTransaction.getId()); + await btcTxHelper.mine(BTC_TO_RSK_MINIMUM_CONFIRMATIONS); + + // Even though a proposed federator cannot call `updateCollections`, inside the powpeg `updateBridge` there are 4 calls to the Bridge. + // One of them is `registerBtcTransaction`. The last one is `updateCollections`. Since the active federation's federators don't recognize the spend svp tx + // as a valid btc tx to send to the Bridge, they will not send it. But the proposed federation's federators will send it. + // This is why we are calling that `updateBridge` method with a proposed federator, so `registerBtcTransaction` gets called, and then we can call `updateCollections` latest with an active federation's federator. + const proposedFedRskTxHelper = rskTxHelpers[rskTxHelpers.length - 1]; + const shouldMineAndSync = false; // We will mine and sync in the next `waitAndUpdateBridge` call. + await rskUtils.waitAndUpdateBridge(proposedFedRskTxHelper, 1000, shouldMineAndSync); + + // Now calling `updateCollections` with a federator from the active federation to really call `updateCollections`. + await rskUtils.waitAndUpdateBridge(rskTxHelper); + + const destinationAddress = bitcoinJsLib.address.fromOutputScript(svpSpendBtcTransaction.outs[0].script, btcTxHelper.btcConfig.network); + expect(destinationAddress).to.be.equal(initialActiveFederationInfo.address, 'The destination address in the SVP Spend transaction should be the active federation address.'); + + const svpSpendTxRegistered = await bridge.methods.isBtcTxHashAlreadyProcessed(svpSpendBtcTransaction.getId()).call(); + expect(svpSpendTxRegistered, 'The SVP Spend transaction should be registered in the Bridge by now.').to.be.true; + + await rskUtils.waitAndUpdateBridge(rskTxHelper); + + await assertProposedFederationIsNotInStorage(bridge); + + await assertSvpValuesNotPresentInStorage(rskTxHelper); + + }); + it('should activate federation', async () => { const federationActivationBlockNumber = commitFederationCreationBlockNumber + FEDERATION_ACTIVATION_AGE; @@ -241,25 +459,142 @@ const execute = (description, newFederationConfig) => { const newActiveFederationErpRedeemScript = removePrefix0x(await bridge.methods.getActivePowpegRedeemScript().call()); expect(newActiveFederationErpRedeemScript).to.be.equal(expectedNewFederationErpRedeemScript, 'The new active federation erp redeem script should be the expected one.'); + const activeFederationAddressInfo = await getActiveFederationInfo(bridge); + + expect(activeFederationAddressInfo.address).to.be.equal(expectedNewFederationAddress, 'The new active federation address should be the expected one.'); + expect(activeFederationAddressInfo.creationBlockNumber).to.be.equal(commitFederationCreationBlockNumber, 'The new active federation creation block number should be the expected one.'); + expect(activeFederationAddressInfo.size).to.be.equal(newFederationPublicKeys.length, 'The new active federation size should be the expected one.'); + + if(isLovellActive) { + expect(activeFederationAddressInfo.creationTime).to.be.equal(proposedFederationInfo.creationTime, 'The new active federation creation time should be the same as the proposed federation creation time.'); + } + // Assert the active federation address is the expected one. const newFederationAddress = await bridge.methods.getFederationAddress().call(); - expect(newFederationAddress).to.not.be.equal(initialFederationAddress, 'The new active federation address should be different from the initial federation address.'); expect(newFederationAddress).to.be.equal(expectedNewFederationAddress, 'The new active federation address should be the expected one.'); const retiringFederationSize = Number(await bridge.methods.getRetiringFederationSize().call()); expect(retiringFederationSize).to.be.equal(initialFederationPublicKeys.length, 'The retiring federation size should be the same as the initial federation size.'); const retiringFederationAddress = await bridge.methods.getRetiringFederationAddress().call(); - expect(retiringFederationAddress).to.be.equal(initialFederationAddress, 'The retiring federation address should be the initial federation address.'); + expect(retiringFederationAddress).to.be.equal(initialActiveFederationInfo.address, 'The retiring federation address should be the initial federation address.'); + + const retiringFederationInformation = await getRetiringFederationInfo(bridge); + + expect(retiringFederationInformation.address).to.be.equal(initialActiveFederationInfo.address, 'The retiring federation address should be the initial federation address.'); + expect(retiringFederationInformation.creationBlockNumber).to.be.equal(initialActiveFederationInfo.creationBlockNumber, 'The retiring federation creation block number should be the same as the active federation creation block number.'); + expect(retiringFederationInformation.creationTime).to.be.equal(initialActiveFederationInfo.creationTime, 'The retiring federation creation time should be the same as the initial federation creation time.'); + expect(retiringFederationInformation.size).to.be.equal(initialFederationPublicKeys.length, 'The retiring federation size should be the same as the initial federation size.'); + + const retiringFederationPublicKeys = await getRetiringFederationPublicKeys(bridge); + expect(retiringFederationPublicKeys).to.be.deep.equal(initialFederationPublicKeys, 'The retiring federation public keys should be the same as the initial federation public keys.'); }); + it('should migrate utxos', async () => { + + const bridgeStateBeforeMigration = await getBridgeState(rskTxHelper.getClient()); + expect(bridgeStateBeforeMigration.activeFederationUtxos.length).to.be.equal(0, 'The new federation is now the active one and it should not have any utxos yet.'); + expect(bridgeStateBeforeMigration.pegoutsWaitingForConfirmations.length).to.be.equal(0, 'No pegout should be waiting for confirmations.'); + expect(bridgeStateBeforeMigration.pegoutsWaitingForSignatures.length).to.be.equal(0, 'No pegout should be waiting for signatures.'); + + // Mining to activate the migration age + await rskTxHelper.mine(FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_BEGIN + 1); + + // Start migration + await rskUtils.waitAndUpdateBridge(rskTxHelper); + const bridgeStateAfterUpdatingCollections = await getBridgeState(rskTxHelper.getClient()); + + const migrationPegouts = bridgeStateAfterUpdatingCollections.pegoutsWaitingForConfirmations; + + expect(bridgeStateAfterUpdatingCollections.pegoutsWaitingForConfirmations.length).to.be.greaterThan(0, 'There should be at least one pegout waiting for confirmations.'); + + await wait(1000); + await rskTxHelper.mine(BTC_TO_RSK_MINIMUM_CONFIRMATIONS); + await wait(1000); + await rskUtils.waitAndUpdateBridge(rskTxHelper); + + const method = async () => { + const currentBridgeState = await getBridgeState(rskTxHelper.getClient()); + if(currentBridgeState.pegoutsWaitingForSignatures.length === 0 && currentBridgeState.pegoutsWaitingForConfirmations.length === 0) { + return true; + } + await wait(1000); + await rskUtils.mineAndSync(rskTxHelpers); + return false; + }; + + const { result: wasPegoutBroadcasted, attempts } = await retryWithCheck(method, pegoutIsBroadcasted => pegoutIsBroadcasted, 20, 2000); + + console.log('wasPegoutBroadcasted: ', wasPegoutBroadcasted) + console.log('attempts: ', attempts) + + const bridgeStateAfterMiningPegout = await getBridgeState(rskTxHelper.getClient()); + + expect(bridgeStateAfterMiningPegout.pegoutsWaitingForConfirmations.length).to.be.equal(0, 'No pegout should be waiting for confirmations.'); + expect(bridgeStateAfterMiningPegout.pegoutsWaitingForSignatures.length).to.be.equal(0, 'No pegout should be waiting for signatures.'); + + const latestBlockNumber = await rskTxHelper.getBlockNumber(); + const expectedCountOfSignatures = (Math.floor(initialFederationPublicKeys.length / 2) + 1) * migrationPegouts.length; + await waitForExpectedCountOfAddSignatureEventsToBeEmitted(rskTxHelpers, latestBlockNumber, expectedCountOfSignatures); + + // Finding the migration transaction release_btc event + const blockNumberAfterRelease = await rskTxHelper.getBlockNumber(); + + const releaseBtcEvents = []; + + await rskUtils.findEventInBlock(rskTxHelper, PEGOUT_EVENTS.RELEASE_BTC.name, latestBlockNumber, blockNumberAfterRelease, foundEvent => { + releaseBtcEvents.push(foundEvent); + }); + + const allMigrationBtcTransactions = releaseBtcEvents.map(event => bitcoinJsLib.Transaction.fromHex(removePrefix0x(event.arguments.btcRawTransaction))); + + const allMigrationInputs = allMigrationBtcTransactions.map(tx => tx.ins).flat(); + + if(isLovellActive) { + expect(allMigrationInputs.length).to.be.equal(bridgeStateBeforeActivation.activeFederationUtxos.length + 1, 'The migration transactions should have one more input than the active federation utxos before activation, due to the utxo that the proposed federation sent to the active federation that should also get migrated.'); + } else { + expect(allMigrationInputs.length).to.be.equal(bridgeStateBeforeActivation.activeFederationUtxos.length, 'The migration transactions should have the same number of inputs as the active federation utxos before activation.'); + } + + const migrationReleaseBtcTransactionInputHashes = allMigrationInputs.map(input => Buffer.from(input.hash).reverse().toString('hex')).sort(); + const oldFederationUtxosHashes = bridgeStateBeforeActivation.activeFederationUtxos.map(utxo => utxo.btcTxHash).sort(); + + if(isLovellActive) { + // Adding the utxo that the proposed federation sent to the active federation that should also get migrated. + oldFederationUtxosHashes.push(Buffer.from(svpSpendBtcTransaction.outs[0].hash).reverse().toString('hex')).sort(); + } + + const allMigrationOutputs = allMigrationBtcTransactions.map(tx => tx.outs).flat(); + + expect(migrationReleaseBtcTransactionInputHashes).to.be.deep.equal(oldFederationUtxosHashes, 'The migration transaction inputs should be the same as the active federation utxos before activation.'); + + for(const migrationOutput of allMigrationOutputs) { + const destinationAddress = bitcoinJsLib.address.fromOutputScript(migrationOutput.script, btcTxHelper.btcConfig.network); + expect(destinationAddress).to.be.equal(expectedNewFederationAddress, 'The destination address of the migration transaction should be the new federation address.'); + } + + await wait(1000); + await btcTxHelper.mine(BTC_TO_RSK_MINIMUM_CONFIRMATIONS); + await rskUtils.waitAndUpdateBridge(rskTxHelper); + + const finalBridgeState = await getBridgeState(rskTxHelper.getClient()); + expect(finalBridgeState.activeFederationUtxos.length).to.be.equal(migrationPegouts.length, 'There should be one utxo in the active federation now.'); + + for(const migrationBtcTx of allMigrationBtcTransactions) { + const migrationUtxo = finalBridgeState.activeFederationUtxos.find(utxo => utxo.btcTxHash === migrationBtcTx.getId()); + expect(migrationUtxo, 'The migration utxo should be registered in the Bridge by now.').to.not.be.undefined; + expect(migrationUtxo.valueInSatoshis).to.be.equal(migrationBtcTx.outs[0].value, 'The migration output value should be the same as the migration utxo value.'); + } + + }); + it('should complete retiring the old federation', async () => { await rskUtils.waitForSync(rskTxHelpers); await stopPreviousFederators(newFederationConfig); - // The first federates are no longer part of the new federation, so we need to recreate the following instances to exclude them. + // The old federates are no longer part of the new federation, so we need to recreate the following instances to exclude them. rskTxHelpers = getRskTransactionHelpers(); rskTxHelper = rskTxHelpers[0]; bridge = await getBridge(rskTxHelper.getClient()); @@ -271,10 +606,12 @@ const execute = (description, newFederationConfig) => { // Updating bridge since this is required for the retiring federation to be removed. await rskUtils.waitAndUpdateBridge(rskTxHelper); - // Assert the retiring federation does not exist anymore. - const actualRetiringFederationSize = Number(await bridge.methods.getRetiringFederationSize().call()); - const expectedRetiringFederationSize = -1; - expect(actualRetiringFederationSize).to.be.equal(expectedRetiringFederationSize, 'The retiring federation should not exist anymore.'); + const retiringFederationInformation = await getRetiringFederationInfo(bridge); + + expect(retiringFederationInformation.size).to.be.equal(-1, 'The retiring federation size should be -1.'); + expect(retiringFederationInformation.address).to.be.equal('', 'The retiring federation address should be the initial federation address.'); + expect(retiringFederationInformation.creationBlockNumber).to.be.equal(-1, 'The retiring federation creation block number should be -1.'); + expect(retiringFederationInformation.creationTime).to.be.equal(-1, 'The retiring federation creation time should be -1.'); }); @@ -282,6 +619,132 @@ const execute = (description, newFederationConfig) => { }; +const waitForExpectedCountOfAddSignatureEventsToBeEmitted = async (rskTxHelpers, fromBlockNumber, expectedCountOfSignatures, checkEveryMilliseconds = 1000, maxAttempts = 64) => { + + const addSignatureEvents = []; + + while(addSignatureEvents.length < expectedCountOfSignatures) { + const rskTxHelper = rskTxHelpers[0]; + await wait(checkEveryMilliseconds); + await rskUtils.findEventInBlock(rskTxHelper, PEGOUT_EVENTS.ADD_SIGNATURE.name, fromBlockNumber, fromBlockNumber, event => { + if(event.name === PEGOUT_EVENTS.ADD_SIGNATURE.name) { + addSignatureEvents.push(event); + } + }); + await rskTxHelper.mine(); + await rskUtils.waitForSync(rskTxHelpers); + fromBlockNumber++; + + if(maxAttempts === 0) { + throw new Error(`The expected count of add signature events was not reached after ${maxAttempts} attempts checking every ${checkEveryMilliseconds} milliseconds. Expected count: ${expectedCountOfSignatures}, actual count: ${addSignatureEvents.length}`); + } + + maxAttempts--; + + } + +}; + +const assertSvpValuesNotPresentInStorage = async (rskTxHelper) => { + + const svpFundTxHashUnsigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpFundTxHashUnsignedStorageIndex); + expect(svpFundTxHashUnsigned).to.be.equal('0x0', 'The SVP fund tx hash unsigned storage value should be empty.'); + + const svpFundTxSigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpFundTxSignedStorageIndex); + expect(svpFundTxSigned).to.be.equal('0x0', 'The SVP fund tx signed storage value should be empty.'); + + const svpSpendTxHashUnsigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpSpendTxHashUnsignedStorageIndex); + expect(svpSpendTxHashUnsigned).to.be.equal('0x0', 'The SVP spend tx hash unsigned storage value should be empty.'); + + const svpSpendTxWaitingForSignatures = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpSpendTxWaitingForSignaturesStorageIndex); + expect(svpSpendTxWaitingForSignatures).to.be.equal('0x0', 'The SVP spend tx waiting for signatures storage value should be empty.'); + +}; + +const assertOnlySvpFundTxHashUnsignedIsInStorage = async (rskTxHelper, pegoutBtcTxHash) => { + + const svpFundTxHashUnsignedRlpEncoded = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpFundTxHashUnsignedStorageIndex); + const svpFundTxHashUnsigned = getBridgeStorageValueDecodedHexString(svpFundTxHashUnsignedRlpEncoded, false); + expect(svpFundTxHashUnsigned).to.be.equal(pegoutBtcTxHash, 'The SVP fund tx hash unsigned storage value should be the tx id of the SVP fund tx.'); + + const svpFundTxSigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpFundTxSignedStorageIndex); + expect(svpFundTxSigned).to.be.equal('0x0', 'The SVP fund tx signed storage value should be empty.'); + + const svpSpendTxHashUnsigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpSpendTxHashUnsignedStorageIndex); + expect(svpSpendTxHashUnsigned).to.be.equal('0x0', 'The SVP spend tx hash unsigned storage value should be empty.'); + + const svpSpendTxWaitingForSignatures = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpSpendTxWaitingForSignaturesStorageIndex); + expect(svpSpendTxWaitingForSignatures).to.be.equal('0x0', 'The SVP spend tx waiting for signatures storage value should be empty.'); + +}; + +const assertPegoutTransactionCreatedOutpointValues = async (initialBridgeState, finalBridgeState, svpBtcTransaction, pegoutTransactionCreatedEvent) => { + + const svpFundTxInput = svpBtcTransaction.ins[0]; + const svpFundTxInputIndex = svpFundTxInput.index; + const svpFundTxInputTxHash = Buffer.from(svpFundTxInput.hash).reverse().toString('hex'); + + expect(finalBridgeState.activeFederationUtxos.length).to.be.equal(initialBridgeState.activeFederationUtxos.length - 1, 'There should be one less active federation utxo.'); + + // Asserting that the expected utxo was used up and removed from the Bridge + const svpFundTxUtxoInInitialBridgeState = initialBridgeState.activeFederationUtxos.find(utxo => utxo.btcTxHash === svpFundTxInputTxHash && utxo.btcTxOutputIndex === svpFundTxInputIndex); + expect(svpFundTxUtxoInInitialBridgeState).to.not.be.undefined; + const svpFundTxUtxoInFinalridgeState = finalBridgeState.activeFederationUtxos.find(utxo => utxo.btcTxHash === svpFundTxInputTxHash && utxo.btcTxOutputIndex === svpFundTxInputIndex); + expect(svpFundTxUtxoInFinalridgeState).to.be.undefined; + + // Asserting the outpoint values in the pegout transaction created event are the same as the used up utxo value + const encodedUtxoOutpointValues = Buffer.from(removePrefix0x(pegoutTransactionCreatedEvent.arguments.utxoOutpointValues), 'hex'); + const outpointValues = decodeOutpointValues(encodedUtxoOutpointValues); + expect(outpointValues.length).to.be.equal(1); + const outpointValue = outpointValues[0]; + expect(Number(outpointValue)).to.be.equal(svpFundTxUtxoInInitialBridgeState.valueInSatoshis, 'The outpoint value should be the same as the utxo value.'); + +}; + +const assertOnlySvpSpendTxValuesAreInStorage = async (rskTxHelper) => { + + const svpFundTxHashUnsignedRlpEncoded = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpFundTxHashUnsignedStorageIndex); + expect(svpFundTxHashUnsignedRlpEncoded).to.be.equal('0x0', 'The SVP fund tx hash unsigned storage value should be empty.'); + + const svpFundTxSigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpFundTxSignedStorageIndex); + expect(svpFundTxSigned).to.be.equal('0x0', 'The SVP fund tx signed storage value should be empty.'); + + const svpSpendTxHashUnsigned = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpSpendTxHashUnsignedStorageIndex); + expect(svpSpendTxHashUnsigned).to.not.be.equal('0x0', 'The SVP spend tx hash unsigned storage value should not be empty.'); + const svpSpendTxHashUnsignedDecoded = decodeRlp(svpSpendTxHashUnsigned).toString('hex'); + + const svpSpendTxWaitingForSignatures = await rskTxHelper.getClient().rsk.getStorageBytesAt(BRIDGE_ADDRESS, svpSpendTxWaitingForSignaturesStorageIndex); + expect(svpSpendTxWaitingForSignatures).to.not.be.equal('0x0', 'The SVP spend tx waiting for signatures storage value should not be empty.'); + + const decodedSvpSpendTxWaitingForSignature = parseRLPToPegoutWaitingSignatures(svpSpendTxWaitingForSignatures)[0]; + const svpSpendBtcTx = bitcoinJsLib.Transaction.fromHex(removePrefix0x(decodedSvpSpendTxWaitingForSignature.btcRawTx)); + expect(svpSpendTxHashUnsignedDecoded).to.be.equal(svpSpendBtcTx.getId(), 'The SVP spend tx hash unsigned storage value should be the tx id of the SVP spend tx.'); + +}; + +const assertProposedFederationIsStillInStorage = async (bridge, expectedProposedFederationAddress, expectedProposedFederationPublicKeys) => { + + const proposedFederationAddress = await bridge.methods.getProposedFederationAddress().call(); + expect(proposedFederationAddress).to.be.equal(expectedProposedFederationAddress, 'The proposed federation address should still be in storage.'); + + const proposedFederationPublicKeys = await getProposedFederationPublicKeys(bridge); + expect(proposedFederationPublicKeys).to.be.deep.equal(expectedProposedFederationPublicKeys, 'The proposed federation public keys should still be in storage.'); + +}; + +const assertProposedFederationIsNotInStorage = async (bridge) => { + + const proposedFederationAddress = await bridge.methods.getProposedFederationAddress().call(); + expect(proposedFederationAddress).to.be.equal(''); + + const proposedFederationSize = Number(await bridge.methods.getProposedFederationSize().call()); + expect(proposedFederationSize).to.be.equal(-1); + + const proposedFederationMembers = await getProposedFederationPublicKeys(bridge); + expect(proposedFederationMembers).to.be.empty; + +}; + module.exports = { execute, }; diff --git a/lib/utils.js b/lib/utils.js index fb3cfe7..933c277 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,5 +1,6 @@ var fs = require('fs-extra'); var utils = require('peglib').utils; +const RLP = require('rlp'); var sequentialPromise = function(n, promiseReturn) { if (n <= 0) { @@ -160,6 +161,37 @@ function splitStringIntoChunks(inputString, chunkSize) { return inputString.match(regex); } +const bytesToHexString = (bytes) => { + return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); +}; + +const removeEmptyLeftBytes = (storageValue) => { + const result = `${storageValue.replaceAll(/^0x0+/g, '')}`; + if(!result.startsWith('0x')) { + return `0x${result}`; + } + return result; +}; + +const decodeRlp = (rlpEncoded) => { + const uint8ArrayDecoded = RLP.decode(rlpEncoded); + const bytesStr = bytesToHexString(uint8ArrayDecoded); + return bytesStr; +}; + +const getBridgeStorageValueDecodedHexString = (bridgeStorageValueEncodedAsRlp, append0xPrefix = true) => { + const rlpBytesWithoutEmptyBytes = removeEmptyLeftBytes(bridgeStorageValueEncodedAsRlp); + const decodedHexFromRlp = decodeRlp(rlpBytesWithoutEmptyBytes); + return append0xPrefix ? `0x${decodedHexFromRlp}` : decodedHexFromRlp; +}; + +const getBridgeStorageIndexFromKey = (storageKey) => { + return Buffer.from(storageKey) + .toString('hex') + .padStart(64, '0') + .padStart(66, '0x'); +}; + module.exports = { sequentialPromise, mapPromiseAll, @@ -180,4 +212,9 @@ module.exports = { remove: removeAdditionalFederationAddress }, splitStringIntoChunks, + bytesToHexString, + removeEmptyLeftBytes, + decodeRlp, + getBridgeStorageValueDecodedHexString, + getBridgeStorageIndexFromKey, } diff --git a/tests/00_00_05-change-federation.js b/tests/00_00_05-change-federation.js index 700e6f4..01b2f88 100644 --- a/tests/00_00_05-change-federation.js +++ b/tests/00_00_05-change-federation.js @@ -1,3 +1,5 @@ const federationChangeTests = require('../lib/tests/change-federation'); -federationChangeTests.execute('Initial Federation change', Runners.config.federations.secondFederation); +const makeDonationPegins = true; + +federationChangeTests.execute('Initial Federation change', Runners.config.federations.secondFederation, makeDonationPegins); diff --git a/tests/01_07_00-activate-latest-fork.js b/tests/01_07_00-activate-latest-fork.js index 389e941..7475ead 100644 --- a/tests/01_07_00-activate-latest-fork.js +++ b/tests/01_07_00-activate-latest-fork.js @@ -1,6 +1,5 @@ const activateForkTest = require('../lib/tests/activate-fork'); activateForkTest.execute( - Runners.common.forks.lovell700, - () => Runners.hosts.federates + Runners.common.forks.lovell700 ); diff --git a/tests/01_07_01-2wp_after_latest_fork_activated.js b/tests/01_07_01-2wp_after_latest_fork_activated.js index e999abd..43bcebd 100644 --- a/tests/01_07_01-2wp_after_latest_fork_activated.js +++ b/tests/01_07_01-2wp_after_latest_fork_activated.js @@ -1,6 +1,5 @@ const twoWpTests = require('../lib/tests/2wp'); twoWpTests.execute( - 'BTC <=> RSK 2WP after federation change', - () => Runners.hosts.federates[Runners.hosts.federates.length-1].host + 'BTC <=> RSK 2WP after latest fork activates' ); diff --git a/tests/04_00_01-second-federation-change.js b/tests/04_00_01-second-federation-change.js new file mode 100644 index 0000000..8610312 --- /dev/null +++ b/tests/04_00_01-second-federation-change.js @@ -0,0 +1,3 @@ +const federationChangeTests = require('../lib/tests/change-federation'); + +federationChangeTests.execute('Second Federation change', Runners.config.federations.thirdFederation); // First svp federation change diff --git a/tests/05_00_01-2wp-after-svp-federation-change.js b/tests/05_00_01-2wp-after-svp-federation-change.js new file mode 100644 index 0000000..0ea2bab --- /dev/null +++ b/tests/05_00_01-2wp-after-svp-federation-change.js @@ -0,0 +1,5 @@ +const twoWpTests = require('../lib/tests/2wp'); + +twoWpTests.execute( + 'BTC <=> RSK 2WP after svp federation change' +); diff --git a/tests/05_00_03-call_receive_header.js b/tests/05_00_03-call_receive_header.js index bfb8e83..b6f6722 100644 --- a/tests/05_00_03-call_receive_header.js +++ b/tests/05_00_03-call_receive_header.js @@ -1,3 +1,3 @@ const receiveHeaderTests = require('../lib/tests/call_receive_header'); -receiveHeaderTests.execute('Calling receiveHeader after federation change', () => Runners.hosts.federates[Runners.hosts.federates.length - 1].host); +receiveHeaderTests.execute('Calling receiveHeader after federation change'); diff --git a/tests/05_00_01-powpeg_redeem_script.js b/tests/05_00_04-powpeg_redeem_script.js similarity index 100% rename from tests/05_00_01-powpeg_redeem_script.js rename to tests/05_00_04-powpeg_redeem_script.js