Apply comments from PR#20

This commit is contained in:
Roman Storm 2018-04-26 11:28:05 -07:00
parent 75c8338a07
commit b572717af8
7 changed files with 237 additions and 281 deletions

View File

@ -1,79 +0,0 @@
pragma solidity 0.4.21;
import "../IBridgeValidators.sol";
library Helpers {
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
function uintToString(uint256 inputValue) internal pure returns (string) {
// figure out the length of the resulting string
uint256 length = 0;
uint256 currentValue = inputValue;
do {
length++;
currentValue /= 10;
} while (currentValue != 0);
// allocate enough memory
bytes memory result = new bytes(length);
// construct the string backwards
uint256 i = length - 1;
currentValue = inputValue;
do {
result[i--] = byte(48 + currentValue % 10);
currentValue /= 10;
} while (currentValue != 0);
return string(result);
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(_message.length == 116);
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = MessageSigning.hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
}
encounteredAddresses[i] = recoveredAddress;
}
}
}
library MessageSigning {
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(message.length == 116);
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return keccak256(prefix, Helpers.uintToString(message.length), message);
}
}

View File

@ -1,7 +1,36 @@
pragma solidity 0.4.21;
import "../IBridgeValidators.sol";
library Message {
function uintToString(uint256 inputValue) internal pure returns (string) {
// figure out the length of the resulting string
uint256 length = 0;
uint256 currentValue = inputValue;
do {
length++;
currentValue /= 10;
} while (currentValue != 0);
// allocate enough memory
bytes memory result = new bytes(length);
// construct the string backwards
uint256 i = length - 1;
currentValue = inputValue;
do {
result[i--] = byte(48 + currentValue % 10);
currentValue /= 10;
} while (currentValue != 0);
return string(result);
}
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
@ -19,31 +48,61 @@ library Message {
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function getRecipient(bytes message) internal pure returns (address) {
address recipient;
// solium-disable-next-line security/no-inline-assembly
function parseMessage(bytes message)
internal
pure
returns(address recipient, uint256 amount, bytes32 txHash)
{
require(isMessageValid(message));
assembly {
recipient := and(mload(add(message, 20)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
}
return recipient;
}
function getValue(bytes message) internal pure returns (uint256) {
uint256 value;
// solium-disable-next-line security/no-inline-assembly
assembly {
value := mload(add(message, 52))
}
return value;
function isMessageValid(bytes _msg) internal pure returns(bool) {
return _msg.length == 116;
}
function getTransactionHash(bytes message) internal pure returns (bytes32) {
bytes32 hash;
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
hash := mload(add(message, 84))
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return keccak256(prefix, uintToString(message.length), message);
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(_message.length == 116);
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
}
encounteredAddresses[i] = recoveredAddress;
}
return hash;
}
}

View File

@ -1,6 +1,5 @@
pragma solidity 0.4.21;
import "../libraries/SafeMath.sol";
import "../libraries/Helpers.sol";
import "../libraries/Message.sol";
import "./U_Validatable.sol";
import "../IBurnableMintableERC677Token.sol";
@ -123,7 +122,7 @@ contract ForeignBridge is ERC677Receiver, Validatable {
setDepositsSigned(hashSender, true);
uint256 signed = numDepositsSigned(hashMsg);
require(!isDepositProcessed(signed));
require(!isAlreadyProcessed(signed));
signed = signed.add(1);
setNumDepositsSigned(hashMsg, signed);
@ -133,8 +132,7 @@ contract ForeignBridge is ERC677Receiver, Validatable {
if (signed == validatorContract().requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
uint256 protectionNumber = signed | 2**255;
setNumDepositsSigned(hashMsg, protectionNumber);
setNumDepositsSigned(hashMsg, markAsProcessed(signed));
erc677token().mint(recipient, value);
emit Deposit(recipient, value, transactionHash);
}
@ -150,13 +148,15 @@ contract ForeignBridge is ERC677Receiver, Validatable {
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature(bytes signature, bytes message) external onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(msg.sender == MessageSigning.recoverAddressFromSignedMessage(signature, message));
require(Message.isMessageValid(message));
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message));
bytes32 hashMsg = keccak256(message);
bytes32 hashSender = keccak256(msg.sender, hashMsg);
uint256 signed = numMessagesSigned(hashMsg);
require(!isDepositProcessed(signed));
signed = signed.add(1);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
@ -172,13 +172,12 @@ contract ForeignBridge is ERC677Receiver, Validatable {
emit SignedForWithdraw(msg.sender, hashMsg);
if (signed == validatorContract().requiredSignatures()) {
uint256 protectionNumber = signed | 2**255;
setNumMessagesSigned(hashMsg, protectionNumber);
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
emit CollectedSignatures(msg.sender, hashMsg);
}
}
function isDepositProcessed(uint256 _number) public view returns(bool) {
function isAlreadyProcessed(uint256 _number) public view returns(bool) {
return _number & 2**255 == 2**255;
}
@ -238,6 +237,10 @@ contract ForeignBridge is ERC677Receiver, Validatable {
return boolStorage[keccak256("depositsSigned", _deposit)];
}
function markAsProcessed(uint256 _v) private pure returns(uint256) {
return _v | 2 ** 255;
}
function numMessagesSigned(bytes32 _message) private view returns(uint256) {
return uintStorage[keccak256("numMessagesSigned", _message)];
}

View File

@ -1,6 +1,5 @@
pragma solidity 0.4.21;
import "../libraries/SafeMath.sol";
import "../libraries/Helpers.sol";
import "../libraries/Message.sol";
import "./U_Validatable.sol";
import "../upgradeability/EternalStorage.sol";
@ -67,18 +66,18 @@ contract HomeBridge is EternalStorage, Validatable {
}
function withdraw(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) external {
Helpers.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract());
address recipient = Message.getRecipient(message);
uint256 value = Message.getValue(message);
bytes32 hash = Message.getTransactionHash(message);
require(!withdraws(hash));
setWithdraws(hash, true);
Message.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract());
address recipient;
uint256 amount;
bytes32 txHash;
(recipient, amount, txHash) = Message.parseMessage(message);
require(!withdraws(txHash));
setWithdraws(txHash, true);
// pay out recipient
recipient.transfer(value);
recipient.transfer(amount);
emit Withdraw(recipient, value, hash);
emit Withdraw(recipient, amount, txHash);
}
function setHomeDailyLimit(uint256 _homeDailyLimit) external onlyOwner {

View File

@ -58,19 +58,10 @@ interface IBridgeValidators {
function owner() public view returns(address);
}
// File: contracts/libraries/Helpers.sol
// File: contracts/libraries/Message.sol
library Helpers {
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
function uintToString(uint256 inputValue) internal pure returns (string) {
library Message {
function uintToString(uint256 inputValue) internal pure returns (string) {
// figure out the length of the resulting string
uint256 length = 0;
uint256 currentValue = inputValue;
@ -90,55 +81,14 @@ library Helpers {
return string(result);
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(_message.length == 116);
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = MessageSigning.hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
encounteredAddresses[i] = recoveredAddress;
}
return false;
}
}
library MessageSigning {
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(message.length == 116);
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return keccak256(prefix, Helpers.uintToString(message.length), message);
}
}
// File: contracts/libraries/Message.sol
library Message {
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
@ -156,32 +106,62 @@ library Message {
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function getRecipient(bytes message) internal pure returns (address) {
address recipient;
// solium-disable-next-line security/no-inline-assembly
function parseMessage(bytes message)
internal
pure
returns(address recipient, uint256 amount, bytes32 txHash)
{
require(isMessageValid(message));
assembly {
recipient := and(mload(add(message, 20)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
}
return recipient;
}
function getValue(bytes message) internal pure returns (uint256) {
uint256 value;
// solium-disable-next-line security/no-inline-assembly
assembly {
value := mload(add(message, 52))
}
return value;
function isMessageValid(bytes _msg) internal pure returns(bool) {
return _msg.length == 116;
}
function getTransactionHash(bytes message) internal pure returns (bytes32) {
bytes32 hash;
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
hash := mload(add(message, 84))
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return keccak256(prefix, uintToString(message.length), message);
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(_message.length == 116);
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
}
encounteredAddresses[i] = recoveredAddress;
}
return hash;
}
}
@ -387,7 +367,7 @@ contract ForeignBridge is ERC677Receiver, Validatable {
setDepositsSigned(hashSender, true);
uint256 signed = numDepositsSigned(hashMsg);
require(!isDepositProcessed(signed));
require(!isAlreadyProcessed(signed));
signed = signed.add(1);
setNumDepositsSigned(hashMsg, signed);
@ -397,8 +377,7 @@ contract ForeignBridge is ERC677Receiver, Validatable {
if (signed == validatorContract().requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
uint256 protectionNumber = signed | 2**255;
setNumDepositsSigned(hashMsg, protectionNumber);
setNumDepositsSigned(hashMsg, markAsProcessed(signed));
erc677token().mint(recipient, value);
emit Deposit(recipient, value, transactionHash);
}
@ -414,13 +393,15 @@ contract ForeignBridge is ERC677Receiver, Validatable {
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature(bytes signature, bytes message) external onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(msg.sender == MessageSigning.recoverAddressFromSignedMessage(signature, message));
require(Message.isMessageValid(message));
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message));
bytes32 hashMsg = keccak256(message);
bytes32 hashSender = keccak256(msg.sender, hashMsg);
uint256 signed = numMessagesSigned(hashMsg);
require(!isDepositProcessed(signed));
signed = signed.add(1);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
@ -436,13 +417,12 @@ contract ForeignBridge is ERC677Receiver, Validatable {
emit SignedForWithdraw(msg.sender, hashMsg);
if (signed == validatorContract().requiredSignatures()) {
uint256 protectionNumber = signed | 2**255;
setNumMessagesSigned(hashMsg, protectionNumber);
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
emit CollectedSignatures(msg.sender, hashMsg);
}
}
function isDepositProcessed(uint256 _number) public view returns(bool) {
function isAlreadyProcessed(uint256 _number) public view returns(bool) {
return _number & 2**255 == 2**255;
}
@ -502,6 +482,10 @@ contract ForeignBridge is ERC677Receiver, Validatable {
return boolStorage[keccak256("depositsSigned", _deposit)];
}
function markAsProcessed(uint256 _v) private pure returns(uint256) {
return _v | 2 ** 255;
}
function numMessagesSigned(bytes32 _message) private view returns(uint256) {
return uintStorage[keccak256("numMessagesSigned", _message)];
}

View File

@ -8,19 +8,10 @@ interface IBridgeValidators {
function owner() public view returns(address);
}
// File: contracts/libraries/Helpers.sol
// File: contracts/libraries/Message.sol
library Helpers {
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
function uintToString(uint256 inputValue) internal pure returns (string) {
library Message {
function uintToString(uint256 inputValue) internal pure returns (string) {
// figure out the length of the resulting string
uint256 length = 0;
uint256 currentValue = inputValue;
@ -40,55 +31,14 @@ library Helpers {
return string(result);
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(_message.length == 116);
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = MessageSigning.hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
encounteredAddresses[i] = recoveredAddress;
}
return false;
}
}
library MessageSigning {
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(message.length == 116);
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return keccak256(prefix, Helpers.uintToString(message.length), message);
}
}
// File: contracts/libraries/Message.sol
library Message {
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
@ -106,32 +56,62 @@ library Message {
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function getRecipient(bytes message) internal pure returns (address) {
address recipient;
// solium-disable-next-line security/no-inline-assembly
function parseMessage(bytes message)
internal
pure
returns(address recipient, uint256 amount, bytes32 txHash)
{
require(isMessageValid(message));
assembly {
recipient := and(mload(add(message, 20)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
}
return recipient;
}
function getValue(bytes message) internal pure returns (uint256) {
uint256 value;
// solium-disable-next-line security/no-inline-assembly
assembly {
value := mload(add(message, 52))
}
return value;
function isMessageValid(bytes _msg) internal pure returns(bool) {
return _msg.length == 116;
}
function getTransactionHash(bytes message) internal pure returns (bytes32) {
bytes32 hash;
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
hash := mload(add(message, 84))
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return keccak256(prefix, uintToString(message.length), message);
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(_message.length == 116);
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
}
encounteredAddresses[i] = recoveredAddress;
}
return hash;
}
}
@ -283,18 +263,18 @@ contract HomeBridge is EternalStorage, Validatable {
}
function withdraw(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) external {
Helpers.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract());
address recipient = Message.getRecipient(message);
uint256 value = Message.getValue(message);
bytes32 hash = Message.getTransactionHash(message);
require(!withdraws(hash));
setWithdraws(hash, true);
Message.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract());
address recipient;
uint256 amount;
bytes32 txHash;
(recipient, amount, txHash) = Message.parseMessage(message);
require(!withdraws(txHash));
setWithdraws(txHash, true);
// pay out recipient
recipient.transfer(value);
recipient.transfer(amount);
emit Withdraw(recipient, value, hash);
emit Withdraw(recipient, amount, txHash);
}
function setHomeDailyLimit(uint256 _homeDailyLimit) external onlyOwner {

View File

@ -446,4 +446,14 @@ contract('ForeignBridge', async (accounts) => {
'500'.should.be.bignumber.equal(await tokenSecond.balanceOf(accounts[3]))
})
})
describe('#isAlreadyProcessed', async () => {
it('returns ', async () => {
foreignBridge = await ForeignBridge.new();
const bn = new web3.BigNumber(2).pow(255);
const processedNumbers = [bn.add(1).toString(10), bn.add(100).toString(10)];
true.should.be.equal(await foreignBridge.isAlreadyProcessed(processedNumbers[0]));
true.should.be.equal(await foreignBridge.isAlreadyProcessed(processedNumbers[1]));
false.should.be.equal(await foreignBridge.isAlreadyProcessed(10));
})
})
})