From 2615f8d7ed16e802a90a625d4a4e57d89e67c0c0 Mon Sep 17 00:00:00 2001 From: Manish Date: Thu, 4 Aug 2022 13:17:20 +0545 Subject: [PATCH 1/6] solidity side implementation of blacklist, tokenlimit-#45 --- solidity/bts/contracts/BTSCore.sol | 45 +- solidity/bts/contracts/BTSPeriphery.sol | 144 +++- .../bts/contracts/interfaces/IBTSCore.sol | 4 +- .../contracts/interfaces/IBTSPeriphery.sol | 20 +- .../contracts/libraries/RLPDecodeStruct.sol | 49 +- solidity/bts/contracts/libraries/Types.sol | 16 +- solidity/bts/contracts/test/AnotherHolder.sol | 126 ++-- .../bts/contracts/test/BTSPeripheryV1.sol | 664 ++++++++--------- .../bts/contracts/test/BTSPeripheryV2.sol | 691 +++++++++--------- 9 files changed, 1012 insertions(+), 747 deletions(-) diff --git a/solidity/bts/contracts/BTSCore.sol b/solidity/bts/contracts/BTSCore.sol index 4fd707005..79e62a127 100644 --- a/solidity/bts/contracts/BTSCore.sol +++ b/solidity/bts/contracts/BTSCore.sol @@ -83,6 +83,9 @@ contract BTSCore is Initializable, IBTSCore, ReentrancyGuardUpgradeable { ); } + function getNativeCoinName() external override view returns (string memory) { + return nativeCoinName; + } /** @notice Adding another Onwer. @dev Caller must be an Onwer of BTP network @@ -217,6 +220,11 @@ contract BTSCore is Initializable, IBTSCore, ReentrancyGuardUpgradeable { NON_NATIVE_TOKEN_TYPE ); } + string[] memory tokenArr = new string[](1); + tokenArr[0] = _name; + uint[] memory valArr = new uint[](1); + valArr[0] = type(uint256).max; + btsPeriphery.setTokenLimit(tokenArr, valArr); } /** @@ -368,6 +376,12 @@ contract BTSCore is Initializable, IBTSCore, ReentrancyGuardUpgradeable { @param _to An address that a user expects to receive an amount of tokens. */ function transferNativeCoin(string calldata _to) external payable override { + + btsPeriphery.checkTransferRestrictions( + nativeCoinName, + msg.sender, + msg.value + ); // Aggregation Fee will be charged on BSH Contract // A new charging fee has been proposed. `fixedFee` is introduced // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR @@ -408,6 +422,13 @@ contract BTSCore is Initializable, IBTSCore, ReentrancyGuardUpgradeable { require(!_coinName.compareTo(nativeCoinName), "InvalidWrappedCoin"); address _erc20Address = coins[_coinName]; require(_erc20Address != address(0), "UnregisterCoin"); + + btsPeriphery.checkTransferRestrictions( + _coinName, + msg.sender, + _value + ); + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR // Thus, it's likely that _chargeAmt is always greater than 0 // require(_chargeAmt > 0) can be omitted @@ -495,35 +516,45 @@ contract BTSCore is Initializable, IBTSCore, ReentrancyGuardUpgradeable { uint256[] memory _amounts = new uint256[](size); uint256[] memory _chargeAmts = new uint256[](size); Coin memory _coin; + string memory coinName; + uint value; for (uint256 i = 0; i < _coinNames.length; i++) { address _erc20Addresses = coins[_coinNames[i]]; // Does not need to check if _coinNames[i] == native_coin // If _coinNames[i] is a native_coin, coins[_coinNames[i]] = 0 require(_erc20Addresses != address(0), "UnregisterCoin"); + coinName = _coinNames[i]; + value = _values[i]; + + btsPeriphery.checkTransferRestrictions( + coinName, + msg.sender, + value + ); IERC20Tradable(_erc20Addresses).transferFrom( msg.sender, address(this), - _values[i] + value ); - _coin = coinDetails[_coinNames[i]]; + _coin = coinDetails[coinName]; // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR // Thus, it's likely that _chargeAmt is always greater than 0 // require(_chargeAmt > 0) can be omitted - _coins[i] = _coinNames[i]; - _chargeAmts[i] = _values[i] + _coins[i] = coinName; + _chargeAmts[i] = value .mul(_coin.feeNumerator) .div(FEE_DENOMINATOR) .add(_coin.fixedFee); - _amounts[i] = _values[i].sub(_chargeAmts[i]); + _amounts[i] = value.sub(_chargeAmts[i]); // Lock this requested _value as a record of a pending transferring transaction // @dev Note that: _value is a requested amount to transfer from a Requester including charged fee // The true amount to receive at a destination receiver is calculated by // _amounts[i] = _values[i].sub(_chargeAmts[i]); - lockBalance(msg.sender, _coinNames[i], _values[i]); + lockBalance(msg.sender, coinName, value); } if (msg.value != 0) { @@ -724,4 +755,4 @@ contract BTSCore is Initializable, IBTSCore, ReentrancyGuardUpgradeable { .lockedBalance .add(_value); } -} +} \ No newline at end of file diff --git a/solidity/bts/contracts/BTSPeriphery.sol b/solidity/bts/contracts/BTSPeriphery.sol index 329716bed..a1f686c96 100644 --- a/solidity/bts/contracts/BTSPeriphery.sol +++ b/solidity/bts/contracts/BTSPeriphery.sol @@ -83,6 +83,9 @@ contract BTSPeriphery is Initializable, IBTSPeriphery { uint256 private serialNo; // a counter of sequence number of service message uint256 private numOfPendingRequests; + mapping(address => bool) public blacklist; + mapping(string => uint) public tokenLimit; + modifier onlyBMC() { require(msg.sender == address(bmc), "Unauthorized"); _; @@ -91,6 +94,7 @@ contract BTSPeriphery is Initializable, IBTSPeriphery { function initialize(address _bmc, address _btsCore) public initializer { bmc = IBMCPeriphery(_bmc); btsCore = IBTSCore(_btsCore); + tokenLimit[btsCore.getNativeCoinName()] = type(uint256).max; } /** @@ -101,6 +105,54 @@ contract BTSPeriphery is Initializable, IBTSPeriphery { return numOfPendingRequests != 0; } + /** + @notice Add users to blacklist + @param _address Address to blacklist + */ + function addToBlacklist(string[] memory _address) external { + require(msg.sender == address(this), "Unauthorized"); + for (uint i = 0; i < _address.length; i++) { + try this.checkParseAddress(_address[i]) { + blacklist[_address[i].parseAddress()] = true; + } catch { + revert("InvalidAddress"); + } + } + } + + /** + @notice Remove users from blacklist + @param _address Address to blacklist + */ + function removeFromBlacklist(string[] memory _address) external { + require(msg.sender == address(this), "Unauthorized"); + for (uint i = 0; i < _address.length; i++) { + try this.checkParseAddress(_address[i]) { + require( blacklist[_address[i].parseAddress()], "UserNotBlacklisted"); + blacklist[_address[i].parseAddress()] = false; + } catch { + revert("InvalidAddress"); + } + } + } + + /** + @notice Set token limit + @param _coinNames Array of names of the coin + @param _tokenLimits Token limit for coins + */ + function setTokenLimit( + string[] memory _coinNames, + uint256[] memory _tokenLimits + ) external override { + require(msg.sender == address(this) || msg.sender == address(btsCore), "Unauthorized"); + require(_coinNames.length == _tokenLimits.length,"InvalidParams"); + for(uint i = 0; i < _coinNames.length; i++) { + require(btsCore.isValidCoin(_coinNames[i]), "NotRegistered"); + tokenLimit[_coinNames[i]] = _tokenLimits[i]; + } + } + function sendServiceMessage( address _from, string memory _to, @@ -209,6 +261,82 @@ contract BTSPeriphery is Initializable, IBTSPeriphery { errMsg, RC_ERR ); + } else if (_sm.serviceType == Types.ServiceType.ADD_TO_BLACKLIST) { + Types.BlacklistMessage memory _bm = _sm.data.decodeBlackListMsg(); + string[] memory addresses = _bm.addrs; + + try this.addToBlacklist(addresses) { + // send message to bmc + sendResponseMessage( + Types.ServiceType.ADD_TO_BLACKLIST, + _from, + _sn, + "AddedToBlacklist", + RC_OK + ); + return; + } catch { + errMsg = "ErrorAddToBlackList"; + } + + sendResponseMessage( + Types.ServiceType.ADD_TO_BLACKLIST, + _from, + _sn, + errMsg, + RC_ERR + ); + + } else if (_sm.serviceType == Types.ServiceType.REMOVE_FROM_BLACKLIST) { + Types.BlacklistMessage memory _bm = _sm.data.decodeBlackListMsg(); + string[] memory addresses = _bm.addrs; + try this.removeFromBlacklist(addresses) { + // send message to bmc + sendResponseMessage( + Types.ServiceType.REMOVE_FROM_BLACKLIST, + _from, + _sn, + "RemovedFromBlacklist", + RC_OK + ); + return; + } catch { + errMsg = "ErrorRemoveFromBlackList"; + } + + sendResponseMessage( + Types.ServiceType.REMOVE_FROM_BLACKLIST, + _from, + _sn, + errMsg, + RC_ERR + ); + + } else if (_sm.serviceType == Types.ServiceType.CHANGE_TOKEN_LIMIT) { + Types.TokenLimitMessage memory _tl = _sm.data.decodeTokenLimitMsg(); + string[] memory coinNames = _tl.coinName; + uint256[] memory tokenLimits = _tl.tokenLimit; + + try this.setTokenLimit(coinNames, tokenLimits) { + sendResponseMessage( + Types.ServiceType.CHANGE_TOKEN_LIMIT, + _from, + _sn, + "ChangeTokenLimit", + RC_OK + ); + return; + } catch { + errMsg = "ErrorChangeTokenLimit"; + sendResponseMessage( + Types.ServiceType.CHANGE_TOKEN_LIMIT, + _from, + _sn, + errMsg, + RC_ERR + ); + } + } else if ( _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE ) { @@ -302,6 +430,11 @@ contract BTSPeriphery is Initializable, IBTSPeriphery { btsCore.isValidCoin(_assets[i].coinName) == true, "UnregisteredCoin" ); + checkTransferRestrictions( + _assets[i].coinName, + _to.parseAddress(), + _assets[i].value) + ; // @dev There might be many errors generating by BTSCore contract // which includes also low-level error // Thus, must use try_catch at this point so that it can return an expected response @@ -364,4 +497,13 @@ contract BTSPeriphery is Initializable, IBTSPeriphery { function checkParseAddress(string calldata _to) external pure { _to.parseAddress(); } -} + + function checkTransferRestrictions( + string memory _coinName, + address _user, + uint256 _value + ) public view override { + require(!blacklist[_user],"Blacklisted"); + require(tokenLimit[_coinName] >= _value,"LimitExceed"); + } +} \ No newline at end of file diff --git a/solidity/bts/contracts/interfaces/IBTSCore.sol b/solidity/bts/contracts/interfaces/IBTSCore.sol index c0299e3c2..898dad5c2 100644 --- a/solidity/bts/contracts/interfaces/IBTSCore.sol +++ b/solidity/bts/contracts/interfaces/IBTSCore.sol @@ -19,7 +19,7 @@ interface IBTSCore { @param _owner Address of a new Onwer. */ function addOwner(address _owner) external; - + function getNativeCoinName() external view returns (string memory); /** @notice Removing an existing Owner. @dev Caller must be an Owner of BTP network @@ -240,4 +240,4 @@ interface IBTSCore { uint256 _fee, uint256 _rspCode ) external; -} +} \ No newline at end of file diff --git a/solidity/bts/contracts/interfaces/IBTSPeriphery.sol b/solidity/bts/contracts/interfaces/IBTSPeriphery.sol index dc86eb48e..1d2188df4 100644 --- a/solidity/bts/contracts/interfaces/IBTSPeriphery.sol +++ b/solidity/bts/contracts/interfaces/IBTSPeriphery.sol @@ -31,6 +31,11 @@ interface IBTSPeriphery is IBSH { uint256[] memory _fees ) external; + /** */ + function setTokenLimit( + string[] memory _coinNames, + uint256[] memory _tokenLimits + ) external; /** @notice BSH handle BTP Message from BMC contract @dev Caller must be BMC contract only @@ -71,4 +76,17 @@ interface IBTSPeriphery is IBSH { function handleFeeGathering(string calldata _fa, string calldata _svc) external override; -} + + /** + @notice Check if transfer is restricted + @param _coinName Name of the coin + @param _user Address to transfer from + @param _value Amount to transfer + */ + function checkTransferRestrictions( + string memory _coinName, + address _user, + uint256 _value + ) external; + +} \ No newline at end of file diff --git a/solidity/bts/contracts/libraries/RLPDecodeStruct.sol b/solidity/bts/contracts/libraries/RLPDecodeStruct.sol index 9499ea7da..958de6ecd 100644 --- a/solidity/bts/contracts/libraries/RLPDecodeStruct.sol +++ b/solidity/bts/contracts/libraries/RLPDecodeStruct.sol @@ -124,6 +124,53 @@ library RLPDecodeStruct { ); } + function decodeBlackListMsg(bytes memory _rlp) + internal + pure + returns(Types.BlacklistMessage memory) + { + + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + RLPDecode.RLPItem[] memory subList = ls[0].toList(); + string[] memory _addrs = new string[](subList.length); + for (uint256 i = 0; i < subList.length; i++) { + _addrs[i] = string(subList[i].toBytes()); + } + return + Types.BlacklistMessage( + _addrs, + string(ls[1].toBytes()) + ); + } + + function decodeTokenLimitMsg(bytes memory _rlp) + internal + pure + returns(Types.TokenLimitMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + RLPDecode.RLPItem[] memory subList1 = ls[0].toList(); + string[] memory _names = new string[](subList1.length); + for (uint256 i = 0; i < subList1.length; i++) { + _names[i] = string(subList1[i].toBytes()); + } + + RLPDecode.RLPItem[] memory subList2 = ls[1].toList(); + uint256[] memory _limits = new uint256[](subList2.length); + for (uint256 i = 0; i < subList2.length; i++) { + _limits[i] = uint256(subList2[i].toUint()); + } + + return + Types.TokenLimitMessage( + _names, + _limits, + string(ls[2].toBytes()) + ); + } + function decodeResponse(bytes memory _rlp) internal pure @@ -385,4 +432,4 @@ library RLPDecodeStruct { } return Types.RelayMessage(_buArray, _bp, isBPEmpty, _rp, isRPEmpty); } -} +} \ No newline at end of file diff --git a/solidity/bts/contracts/libraries/Types.sol b/solidity/bts/contracts/libraries/Types.sol index b0b7d2f1d..6aca729c1 100644 --- a/solidity/bts/contracts/libraries/Types.sol +++ b/solidity/bts/contracts/libraries/Types.sol @@ -91,6 +91,9 @@ library Types { REQUEST_COIN_TRANSFER, REQUEST_COIN_REGISTER, REPONSE_HANDLE_SERVICE, + ADD_TO_BLACKLIST, + REMOVE_FROM_BLACKLIST, + CHANGE_TOKEN_LIMIT, UNKNOWN_TYPE } @@ -108,6 +111,17 @@ library Types { Asset[] assets; } + struct BlacklistMessage { + string[] addrs; + string net; + } + + struct TokenLimitMessage { + string[] coinName; + uint256[] tokenLimit; + string net; + } + struct Asset { string coinName; uint256 value; @@ -236,4 +250,4 @@ library Types { string fa; // BTP address of Fee Aggregator string[] svcs; // a list of services } -} +} \ No newline at end of file diff --git a/solidity/bts/contracts/test/AnotherHolder.sol b/solidity/bts/contracts/test/AnotherHolder.sol index 1705ffc98..169a87802 100644 --- a/solidity/bts/contracts/test/AnotherHolder.sol +++ b/solidity/bts/contracts/test/AnotherHolder.sol @@ -1,71 +1,71 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; -pragma abicoder v2; +// // SPDX-License-Identifier: Apache-2.0 +// pragma solidity >=0.8.0; +// pragma abicoder v2; -import "./BTSPeripheryV1.sol"; -import "./BTSCoreV1.sol"; +// import "./BTSPeripheryV1.sol"; +// import "./BTSCoreV1.sol"; -contract AnotherHolder { - BTSPeripheryV1 private btss; - BTSCoreV1 private btsc; - using String for string; +// contract AnotherHolder { +// BTSPeripheryV1 private btss; +// BTSCoreV1 private btsc; +// using String for string; - function deposit() external payable {} +// function deposit() external payable {} - function addBSHContract(address _btss, address _btsc) external { - btss = BTSPeripheryV1(_btss); - btsc = BTSCoreV1(_btsc); - } +// function addBSHContract(address _btss, address _btsc) external { +// btss = BTSPeripheryV1(_btss); +// btsc = BTSCoreV1(_btsc); +// } - function setApprove( - address _erc20, - address _operator, - uint256 _value - ) external { - IERC20Tradable(_erc20).approve(_operator, _value); - } +// function setApprove( +// address _erc20, +// address _operator, +// uint256 _value +// ) external { +// IERC20Tradable(_erc20).approve(_operator, _value); +// } - function callTransfer( - string calldata _coinName, - uint256 _value, - string calldata _to - ) external { - btsc.transfer(_coinName, _value, _to); - } +// function callTransfer( +// string calldata _coinName, +// uint256 _value, +// string calldata _to +// ) external { +// btsc.transfer(_coinName, _value, _to); +// } - // function isSendingNative(string[] memory _coinNames) - // private - // pure - // returns (int256) - // { - // for (uint256 i = 0; i < _coinNames.length; i++) { - // if (_coinNames[i].compareTo("PARA")) { - // return int256(i); - // } - // } - // return -1; - // } +// // function isSendingNative(string[] memory _coinNames) +// // private +// // pure +// // returns (int256) +// // { +// // for (uint256 i = 0; i < _coinNames.length; i++) { +// // if (_coinNames[i].compareTo("PARA")) { +// // return int256(i); +// // } +// // } +// // return -1; +// // } - function callTransferBatch( - address _bsh, - string[] memory _coinNames, - uint256[] memory _values, - string calldata _to, - uint256 _native - ) external { - // int256 pos = isSendingNative(_coinNames); - if (_native != 0) { - (bool success, bytes memory err) = _bsh.call{ value: _native }( - abi.encodeWithSignature( - "transferBatch(string[],uint256[],string)", - _coinNames, - _values, - _to - ) - ); - require(success, string(err)); - } else { - btsc.transferBatch(_coinNames, _values, _to); - } - } -} +// function callTransferBatch( +// address _bsh, +// string[] memory _coinNames, +// uint256[] memory _values, +// string calldata _to, +// uint256 _native +// ) external { +// // int256 pos = isSendingNative(_coinNames); +// if (_native != 0) { +// (bool success, bytes memory err) = _bsh.call{ value: _native }( +// abi.encodeWithSignature( +// "transferBatch(string[],uint256[],string)", +// _coinNames, +// _values, +// _to +// ) +// ); +// require(success, string(err)); +// } else { +// btsc.transferBatch(_coinNames, _values, _to); +// } +// } +// } diff --git a/solidity/bts/contracts/test/BTSPeripheryV1.sol b/solidity/bts/contracts/test/BTSPeripheryV1.sol index 568eb0216..5fe3ec480 100644 --- a/solidity/bts/contracts/test/BTSPeripheryV1.sol +++ b/solidity/bts/contracts/test/BTSPeripheryV1.sol @@ -1,349 +1,355 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; -pragma abicoder v2; -import "../interfaces/IBTSPeriphery.sol"; -import "../interfaces/IBTSCore.sol"; -import "../interfaces/IBMCPeriphery.sol"; -import "../libraries/Types.sol"; -import "../libraries/RLPEncodeStruct.sol"; -import "../libraries/RLPDecodeStruct.sol"; -import "../libraries/ParseAddress.sol"; -import "../libraries/String.sol"; -import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +// // SPDX-License-Identifier: Apache-2.0 +// pragma solidity >=0.8.0; +// pragma abicoder v2; +// import "../interfaces/IBTSPeriphery.sol"; +// import "../interfaces/IBTSCore.sol"; +// import "../interfaces/IBMCPeriphery.sol"; +// import "../libraries/Types.sol"; +// import "../libraries/RLPEncodeStruct.sol"; +// import "../libraries/RLPDecodeStruct.sol"; +// import "../libraries/ParseAddress.sol"; +// import "../libraries/String.sol"; +// import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; +// import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -/** - @title BTSPeriphery contract - @dev This contract is used to handle communications among BMCService and BTSCore contract - @dev OwnerUpgradeable has been removed. This contract does not have its own Owners - Instead, BTSCore manages ownership roles. - Thus, BTSPeriphery should call btsCore.isOwner() and pass an address for verification - in case of implementing restrictions, if needed, in the future. -*/ -contract BTSPeripheryV1 is Initializable, IBTSPeriphery { - using RLPEncodeStruct for Types.TransferCoin; - using RLPEncodeStruct for Types.ServiceMessage; - using RLPEncodeStruct for Types.Response; - using RLPDecodeStruct for bytes; - using SafeMathUpgradeable for uint256; - using ParseAddress for address; - using ParseAddress for string; - using String for string; +// /** +// @title BTSPeriphery contract +// @dev This contract is used to handle communications among BMCService and BTSCore contract +// @dev OwnerUpgradeable has been removed. This contract does not have its own Owners +// Instead, BTSCore manages ownership roles. +// Thus, BTSPeriphery should call btsCore.isOwner() and pass an address for verification +// in case of implementing restrictions, if needed, in the future. +// */ +// contract BTSPeripheryV1 is Initializable, IBTSPeriphery { +// using RLPEncodeStruct for Types.TransferCoin; +// using RLPEncodeStruct for Types.ServiceMessage; +// using RLPEncodeStruct for Types.Response; +// using RLPDecodeStruct for bytes; +// using SafeMathUpgradeable for uint256; +// using ParseAddress for address; +// using ParseAddress for string; +// using String for string; - /** @notice Sends a receipt to user - The `_from` sender - The `_to` receiver. - The `_sn` sequence number of service message. - The `_assetDetails` a list of `_coinName` and `_value` - */ - event TransferStart( - address indexed _from, - string _to, - uint256 _sn, - Types.AssetTransferDetail[] _assetDetails - ); +// /** @notice Sends a receipt to user +// The `_from` sender +// The `_to` receiver. +// The `_sn` sequence number of service message. +// The `_assetDetails` a list of `_coinName` and `_value` +// */ +// event TransferStart( +// address indexed _from, +// string _to, +// uint256 _sn, +// Types.AssetTransferDetail[] _assetDetails +// ); - /** @notice Sends a final notification to a user - The `_from` sender - The `_sn` sequence number of service message. - The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 - The `_response` message of response if error - */ - event TransferEnd( - address indexed _from, - uint256 _sn, - uint256 _code, - string _response - ); +// /** @notice Sends a final notification to a user +// The `_from` sender +// The `_sn` sequence number of service message. +// The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 +// The `_response` message of response if error +// */ +// event TransferEnd( +// address indexed _from, +// uint256 _sn, +// uint256 _code, +// string _response +// ); - /** @notice Notify that BSH contract has received unknown response - The `_from` sender - The `_sn` sequence number of service message - */ - event UnknownResponse(string _from, uint256 _sn); +// /** @notice Notify that BSH contract has received unknown response +// The `_from` sender +// The `_sn` sequence number of service message +// */ +// event UnknownResponse(string _from, uint256 _sn); - IBMCPeriphery private bmc; - IBTSCore internal btsCore; - mapping(uint256 => Types.PendingTransferCoin) internal requests; // a list of transferring requests - string public serviceName; // BSH Service Name +// IBMCPeriphery private bmc; +// IBTSCore internal btsCore; +// mapping(uint256 => Types.PendingTransferCoin) internal requests; // a list of transferring requests +// string public serviceName; // BSH Service Name - uint256 private constant RC_OK = 0; - uint256 private constant RC_ERR = 1; - uint256 private serialNo; // a counter of sequence number of service message - uint256 private numOfPendingRequests; +// uint256 private constant RC_OK = 0; +// uint256 private constant RC_ERR = 1; +// uint256 private serialNo; // a counter of sequence number of service message +// uint256 private numOfPendingRequests; - modifier onlyBMC() { - require(msg.sender == address(bmc), "Unauthorized"); - _; - } +// modifier onlyBMC() { +// require(msg.sender == address(bmc), "Unauthorized"); +// _; +// } - function initialize( - address _bmc, - address _btsCore, - string memory _serviceName - ) public initializer { - bmc = IBMCPeriphery(_bmc); - btsCore = IBTSCore(_btsCore); - serviceName = _serviceName; - } +// function initialize( +// address _bmc, +// address _btsCore, +// string memory _serviceName +// ) public initializer { +// bmc = IBMCPeriphery(_bmc); +// btsCore = IBTSCore(_btsCore); +// serviceName = _serviceName; +// } - /** - @notice Check whether BTSPeriphery has any pending transferring requests - @return true or false - */ - function hasPendingRequest() external view override returns (bool) { - return numOfPendingRequests != 0; - } +// /** +// @notice Check whether BTSPeriphery has any pending transferring requests +// @return true or false +// */ +// function hasPendingRequest() external view override returns (bool) { +// return numOfPendingRequests != 0; +// } - function sendServiceMessage( - address _from, - string memory _to, - string[] memory _coinNames, - uint256[] memory _values, - uint256[] memory _fees - ) external override { - // Send Service Message to BMC - // If '_to' address is an invalid BTP Address format - // VM throws an error and revert(). Thus, it does not need - // a try_catch at this point - (string memory _toNetwork, string memory _toAddress) = _to - .splitBTPAddress(); - Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); - Types.AssetTransferDetail[] - memory _assetDetails = new Types.AssetTransferDetail[]( - _coinNames.length - ); - for (uint256 i = 0; i < _coinNames.length; i++) { - _assets[i] = Types.Asset(_coinNames[i], _values[i]); - _assetDetails[i] = Types.AssetTransferDetail( - _coinNames[i], - _values[i], - _fees[i] - ); - } +// function sendServiceMessage( +// address _from, +// string memory _to, +// string[] memory _coinNames, +// uint256[] memory _values, +// uint256[] memory _fees +// ) external override { +// // Send Service Message to BMC +// // If '_to' address is an invalid BTP Address format +// // VM throws an error and revert(). Thus, it does not need +// // a try_catch at this point +// (string memory _toNetwork, string memory _toAddress) = _to +// .splitBTPAddress(); +// Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); +// Types.AssetTransferDetail[] +// memory _assetDetails = new Types.AssetTransferDetail[]( +// _coinNames.length +// ); +// for (uint256 i = 0; i < _coinNames.length; i++) { +// _assets[i] = Types.Asset(_coinNames[i], _values[i]); +// _assetDetails[i] = Types.AssetTransferDetail( +// _coinNames[i], +// _values[i], +// _fees[i] +// ); +// } - serialNo++; +// serialNo++; - // Because `stack is too deep`, must create `_strFrom` to waive this error - // `_strFrom` is a string type of an address `_from` - string memory _strFrom = _from.toString(); - bmc.sendMessage( - _toNetwork, - serviceName, - serialNo, - Types - .ServiceMessage( - Types.ServiceType.REQUEST_COIN_TRANSFER, - Types - .TransferCoin(_strFrom, _toAddress, _assets) - .encodeTransferCoinMsg() - ) - .encodeServiceMessage() - ); - // Push pending tx into Record list - requests[serialNo] = Types.PendingTransferCoin( - _strFrom, - _to, - _coinNames, - _values, - _fees - ); - numOfPendingRequests++; - emit TransferStart(_from, _to, serialNo, _assetDetails); - } +// // Because `stack is too deep`, must create `_strFrom` to waive this error +// // `_strFrom` is a string type of an address `_from` +// string memory _strFrom = _from.toString(); +// bmc.sendMessage( +// _toNetwork, +// serviceName, +// serialNo, +// Types +// .ServiceMessage( +// Types.ServiceType.REQUEST_COIN_TRANSFER, +// Types +// .TransferCoin(_strFrom, _toAddress, _assets) +// .encodeTransferCoinMsg() +// ) +// .encodeServiceMessage() +// ); +// // Push pending tx into Record list +// requests[serialNo] = Types.PendingTransferCoin( +// _strFrom, +// _to, +// _coinNames, +// _values, +// _fees +// ); +// numOfPendingRequests++; +// emit TransferStart(_from, _to, serialNo, _assetDetails); +// } - /** - @notice BSH handle BTP Message from BMC contract - @dev Caller must be BMC contract only - @param _from An originated network address of a request - @param _svc A service name of BSH contract - @param _sn A serial number of a service request - @param _msg An RLP message of a service request/service response - */ - function handleBTPMessage( - string calldata _from, - string calldata _svc, - uint256 _sn, - bytes calldata _msg - ) external override onlyBMC { - require(_svc.compareTo(serviceName) == true, "InvalidSvc"); - Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); - string memory errMsg; +// /** +// @notice BSH handle BTP Message from BMC contract +// @dev Caller must be BMC contract only +// @param _from An originated network address of a request +// @param _svc A service name of BSH contract +// @param _sn A serial number of a service request +// @param _msg An RLP message of a service request/service response +// */ +// function handleBTPMessage( +// string calldata _from, +// string calldata _svc, +// uint256 _sn, +// bytes calldata _msg +// ) external override onlyBMC { +// require(_svc.compareTo(serviceName) == true, "InvalidSvc"); +// Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); +// string memory errMsg; - if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { - Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); - // checking receiving address whether is a valid address - // revert() if not a valid one - try this.checkParseAddress(_tc.to) { - try this.handleRequestService(_tc.to, _tc.assets) { - sendResponseMessage( - Types.ServiceType.REPONSE_HANDLE_SERVICE, - _from, - _sn, - "", - RC_OK - ); - return; - } catch Error(string memory _err) { - errMsg = _err; - } - } catch { - errMsg = "InvalidAddress"; - } - sendResponseMessage( - Types.ServiceType.REPONSE_HANDLE_SERVICE, - _from, - _sn, - errMsg, - RC_ERR - ); - } else if ( - _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE - ) { - // Check whether '_sn' is pending state - require(bytes(requests[_sn].from).length != 0, "InvalidSN"); - Types.Response memory response = _sm.data.decodeResponse(); - // @dev Not implement try_catch at this point - // + If RESPONSE_REQUEST_SERVICE: - // If RC_ERR, BTSCore proceeds a refund. If a refund is failed, BTSCore issues refundable Balance - // If RC_OK: - // - requested coin = native -> update aggregation fee (likely no issue) - // - requested coin = wrapped coin -> BTSCore calls itself to burn its tokens and update aggregation fee (likely no issue) - // The only issue, which might happen, is BTSCore's token balance lower than burning amount - // If so, there might be something went wrong before - // + If RESPONSE_FEE_GATHERING - // If RC_ERR, BTSCore saves charged fees back to `aggregationFee` state mapping variable - // If RC_OK: do nothing - handleResponseService(_sn, response.code, response.message); - } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { - emit UnknownResponse(_from, _sn); - } else { - // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE - sendResponseMessage( - Types.ServiceType.UNKNOWN_TYPE, - _from, - _sn, - "Unknown", - RC_ERR - ); - } - } +// if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { +// Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); +// // checking receiving address whether is a valid address +// // revert() if not a valid one +// try this.checkParseAddress(_tc.to) { +// try this.handleRequestService(_tc.to, _tc.assets) { +// sendResponseMessage( +// Types.ServiceType.REPONSE_HANDLE_SERVICE, +// _from, +// _sn, +// "", +// RC_OK +// ); +// return; +// } catch Error(string memory _err) { +// errMsg = _err; +// } +// } catch { +// errMsg = "InvalidAddress"; +// } +// sendResponseMessage( +// Types.ServiceType.REPONSE_HANDLE_SERVICE, +// _from, +// _sn, +// errMsg, +// RC_ERR +// ); +// } else if ( +// _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE +// ) { +// // Check whether '_sn' is pending state +// require(bytes(requests[_sn].from).length != 0, "InvalidSN"); +// Types.Response memory response = _sm.data.decodeResponse(); +// // @dev Not implement try_catch at this point +// // + If RESPONSE_REQUEST_SERVICE: +// // If RC_ERR, BTSCore proceeds a refund. If a refund is failed, BTSCore issues refundable Balance +// // If RC_OK: +// // - requested coin = native -> update aggregation fee (likely no issue) +// // - requested coin = wrapped coin -> BTSCore calls itself to burn its tokens and update aggregation fee (likely no issue) +// // The only issue, which might happen, is BTSCore's token balance lower than burning amount +// // If so, there might be something went wrong before +// // + If RESPONSE_FEE_GATHERING +// // If RC_ERR, BTSCore saves charged fees back to `aggregationFee` state mapping variable +// // If RC_OK: do nothing +// handleResponseService(_sn, response.code, response.message); +// } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { +// emit UnknownResponse(_from, _sn); +// } else { +// // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE +// sendResponseMessage( +// Types.ServiceType.UNKNOWN_TYPE, +// _from, +// _sn, +// "Unknown", +// RC_ERR +// ); +// } +// } - /** - @notice BSH handle BTP Error from BMC contract - @dev Caller must be BMC contract only - @param _svc A service name of BSH contract - @param _sn A serial number of a service request - @param _code A response code of a message (RC_OK / RC_ERR) - @param _msg A response message - */ - function handleBTPError( - string calldata, /* _src */ - string calldata _svc, - uint256 _sn, - uint256 _code, - string calldata _msg - ) external override onlyBMC { - require(_svc.compareTo(serviceName) == true, "InvalidSvc"); - require(bytes(requests[_sn].from).length != 0, "InvalidSN"); - handleResponseService(_sn, _code, _msg); - } +// /** +// @notice BSH handle BTP Error from BMC contract +// @dev Caller must be BMC contract only +// @param _svc A service name of BSH contract +// @param _sn A serial number of a service request +// @param _code A response code of a message (RC_OK / RC_ERR) +// @param _msg A response message +// */ +// function handleBTPError( +// string calldata, /* _src */ +// string calldata _svc, +// uint256 _sn, +// uint256 _code, +// string calldata _msg +// ) external override onlyBMC { +// require(_svc.compareTo(serviceName) == true, "InvalidSvc"); +// require(bytes(requests[_sn].from).length != 0, "InvalidSN"); +// handleResponseService(_sn, _code, _msg); +// } - function handleResponseService( - uint256 _sn, - uint256 _code, - string memory _msg - ) private { - address _caller = requests[_sn].from.parseAddress(); - uint256 loop = requests[_sn].coinNames.length; - for (uint256 i = 0; i < loop; i++) { - btsCore.handleResponseService( - _caller, - requests[_sn].coinNames[i], - requests[_sn].amounts[i], - requests[_sn].fees[i], - _code - ); - } - delete requests[_sn]; - numOfPendingRequests--; - emit TransferEnd(_caller, _sn, _code, _msg); - } +// function handleResponseService( +// uint256 _sn, +// uint256 _code, +// string memory _msg +// ) private { +// address _caller = requests[_sn].from.parseAddress(); +// uint256 loop = requests[_sn].coinNames.length; +// for (uint256 i = 0; i < loop; i++) { +// btsCore.handleResponseService( +// _caller, +// requests[_sn].coinNames[i], +// requests[_sn].amounts[i], +// requests[_sn].fees[i], +// _code +// ); +// } +// delete requests[_sn]; +// numOfPendingRequests--; +// emit TransferEnd(_caller, _sn, _code, _msg); +// } - /** - @notice Handle a list of minting/transferring coins/tokens - @dev Caller must be BMC contract only - @param _to An address to receive coins/tokens - @param _assets A list of requested coin respectively with an amount - */ - function handleRequestService( - string memory _to, - Types.Asset[] memory _assets - ) external { - require(msg.sender == address(this), "Unauthorized"); - for (uint256 i = 0; i < _assets.length; i++) { - require( - btsCore.isValidCoin(_assets[i].coinName) == true, - "UnregisteredCoin" - ); - // @dev There might be many errors generating by BTSCore contract - // which includes also low-level error - // Thus, must use try_catch at this point so that it can return an expected response - try - btsCore.mint( - _to.parseAddress(), - _assets[i].coinName, - _assets[i].value - ) - {} catch { - revert("TransferFailed"); - } - } - } +// /** +// @notice Handle a list of minting/transferring coins/tokens +// @dev Caller must be BMC contract only +// @param _to An address to receive coins/tokens +// @param _assets A list of requested coin respectively with an amount +// */ +// function handleRequestService( +// string memory _to, +// Types.Asset[] memory _assets +// ) external { +// require(msg.sender == address(this), "Unauthorized"); +// for (uint256 i = 0; i < _assets.length; i++) { +// require( +// btsCore.isValidCoin(_assets[i].coinName) == true, +// "UnregisteredCoin" +// ); +// // @dev There might be many errors generating by BTSCore contract +// // which includes also low-level error +// // Thus, must use try_catch at this point so that it can return an expected response +// try +// btsCore.mint( +// _to.parseAddress(), +// _assets[i].coinName, +// _assets[i].value +// ) +// {} catch { +// revert("TransferFailed"); +// } +// } +// } - function sendResponseMessage( - Types.ServiceType _serviceType, - string memory _to, - uint256 _sn, - string memory _msg, - uint256 _code - ) private { - bmc.sendMessage( - _to, - serviceName, - _sn, - Types - .ServiceMessage( - _serviceType, - Types.Response(_code, _msg).encodeResponse() - ) - .encodeServiceMessage() - ); - } +// function sendResponseMessage( +// Types.ServiceType _serviceType, +// string memory _to, +// uint256 _sn, +// string memory _msg, +// uint256 _code +// ) private { +// bmc.sendMessage( +// _to, +// serviceName, +// _sn, +// Types +// .ServiceMessage( +// _serviceType, +// Types.Response(_code, _msg).encodeResponse() +// ) +// .encodeServiceMessage() +// ); +// } - /** - @notice BSH handle Gather Fee Message request from BMC contract - @dev Caller must be BMC contract only - @param _fa A BTP address of fee aggregator - @param _svc A name of the service - */ - function handleFeeGathering(string calldata _fa, string calldata _svc) - external - override - onlyBMC - { - require(_svc.compareTo(serviceName) == true, "InvalidSvc"); - // If adress of Fee Aggregator (_fa) is invalid BTP address format - // revert(). Then, BMC will catch this error - _fa.splitBTPAddress(); - btsCore.transferFees(_fa); - } +// /** +// @notice BSH handle Gather Fee Message request from BMC contract +// @dev Caller must be BMC contract only +// @param _fa A BTP address of fee aggregator +// @param _svc A name of the service +// */ +// function handleFeeGathering(string calldata _fa, string calldata _svc) +// external +// override +// onlyBMC +// { +// require(_svc.compareTo(serviceName) == true, "InvalidSvc"); +// // If adress of Fee Aggregator (_fa) is invalid BTP address format +// // revert(). Then, BMC will catch this error +// _fa.splitBTPAddress(); +// btsCore.transferFees(_fa); +// } - // @dev Solidity does not allow to use try_catch with internal function - // Thus, this is a work-around solution - // Since this function is basically checking whether a string address - // can be parsed to address type. Hence, it would not have any restrictions - function checkParseAddress(string calldata _to) external pure { - _to.parseAddress(); - } -} +// // @dev Solidity does not allow to use try_catch with internal function +// // Thus, this is a work-around solution +// // Since this function is basically checking whether a string address +// // can be parsed to address type. Hence, it would not have any restrictions +// function checkParseAddress(string calldata _to) external pure { +// _to.parseAddress(); +// } + +// function checkTransferRestrictions( +// string memory _coinName, +// address _user, +// uint256 _value +// ) external; +// } \ No newline at end of file diff --git a/solidity/bts/contracts/test/BTSPeripheryV2.sol b/solidity/bts/contracts/test/BTSPeripheryV2.sol index 87020f784..052d5aad9 100644 --- a/solidity/bts/contracts/test/BTSPeripheryV2.sol +++ b/solidity/bts/contracts/test/BTSPeripheryV2.sol @@ -1,364 +1,371 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; -pragma abicoder v2; -import "../interfaces/IBTSPeriphery.sol"; -import "../interfaces/IBTSCore.sol"; -import "../interfaces/IBMCPeriphery.sol"; -import "../libraries/Types.sol"; -import "../libraries/RLPEncodeStruct.sol"; -import "../libraries/RLPDecodeStruct.sol"; -import "../libraries/ParseAddress.sol"; -import "../libraries/String.sol"; -import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +// // SPDX-License-Identifier: Apache-2.0 +// pragma solidity >=0.8.0; +// pragma abicoder v2; +// import "../interfaces/IBTSPeriphery.sol"; +// import "../interfaces/IBTSCore.sol"; +// import "../interfaces/IBMCPeriphery.sol"; +// import "../libraries/Types.sol"; +// import "../libraries/RLPEncodeStruct.sol"; +// import "../libraries/RLPDecodeStruct.sol"; +// import "../libraries/ParseAddress.sol"; +// import "../libraries/String.sol"; +// import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; +// import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -/** - @title Interface of BSH Coin transfer service - @dev This contract use to handle coin transfer service - Note: The coin of following interface can be: - Native Coin : The native coin of this chain - Wrapped Native Coin : A tokenized ERC20 version of another native coin like ICX -*/ -contract BTSPeripheryV2 is Initializable, IBTSPeriphery { - using RLPEncodeStruct for Types.TransferCoin; - using RLPEncodeStruct for Types.ServiceMessage; - using RLPEncodeStruct for Types.Response; - using RLPDecodeStruct for bytes; - using SafeMathUpgradeable for uint256; - using ParseAddress for address; - using ParseAddress for string; - using String for string; +// /** +// @title Interface of BSH Coin transfer service +// @dev This contract use to handle coin transfer service +// Note: The coin of following interface can be: +// Native Coin : The native coin of this chain +// Wrapped Native Coin : A tokenized ERC20 version of another native coin like ICX +// */ +// contract BTSPeripheryV2 is Initializable, IBTSPeriphery { +// using RLPEncodeStruct for Types.TransferCoin; +// using RLPEncodeStruct for Types.ServiceMessage; +// using RLPEncodeStruct for Types.Response; +// using RLPDecodeStruct for bytes; +// using SafeMathUpgradeable for uint256; +// using ParseAddress for address; +// using ParseAddress for string; +// using String for string; - /** @notice Sends a receipt to user - The `_from` sender - The `_to` receiver. - The `_sn` sequence number of service message. - The `_assetDetails` a list of `_coinName` and `_value` - */ - event TransferStart( - address indexed _from, - string _to, - uint256 _sn, - Types.AssetTransferDetail[] _assetDetails - ); +// /** @notice Sends a receipt to user +// The `_from` sender +// The `_to` receiver. +// The `_sn` sequence number of service message. +// The `_assetDetails` a list of `_coinName` and `_value` +// */ +// event TransferStart( +// address indexed _from, +// string _to, +// uint256 _sn, +// Types.AssetTransferDetail[] _assetDetails +// ); - /** @notice Sends a final notification to a user - The `_from` sender - The `_sn` sequence number of service message. - The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 - The `_response` message of response if error - */ - event TransferEnd( - address indexed _from, - uint256 _sn, - uint256 _code, - string _response - ); +// /** @notice Sends a final notification to a user +// The `_from` sender +// The `_sn` sequence number of service message. +// The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 +// The `_response` message of response if error +// */ +// event TransferEnd( +// address indexed _from, +// uint256 _sn, +// uint256 _code, +// string _response +// ); - /** @notice Notify that BSH contract has received unknown response - The `_from` sender - The `_sn` sequence number of service message - */ - event UnknownResponse(string _from, uint256 _sn); +// /** @notice Notify that BSH contract has received unknown response +// The `_from` sender +// The `_sn` sequence number of service message +// */ +// event UnknownResponse(string _from, uint256 _sn); - IBMCPeriphery private bmc; - IBTSCore internal btsCore; - mapping(uint256 => Types.PendingTransferCoin) internal requests; // a list of transferring requests - string public serviceName; // BSH Service Name +// IBMCPeriphery private bmc; +// IBTSCore internal btsCore; +// mapping(uint256 => Types.PendingTransferCoin) internal requests; // a list of transferring requests +// string public serviceName; // BSH Service Name - uint256 private constant RC_OK = 0; - uint256 private constant RC_ERR = 1; - uint256 private serialNo; // a counter of sequence number of service message - uint256 private numOfPendingRequests; +// uint256 private constant RC_OK = 0; +// uint256 private constant RC_ERR = 1; +// uint256 private serialNo; // a counter of sequence number of service message +// uint256 private numOfPendingRequests; - modifier onlyBMC() { - require(msg.sender == address(bmc), "Unauthorized"); - _; - } +// modifier onlyBMC() { +// require(msg.sender == address(bmc), "Unauthorized"); +// _; +// } - /** - @notice Check whether BTSPeriphery has any pending transferring requests - @return true or false - */ - function hasPendingRequest() external view override returns (bool) { - return numOfPendingRequests != 0; - } +// /** +// @notice Check whether BTSPeriphery has any pending transferring requests +// @return true or false +// */ +// function hasPendingRequest() external view override returns (bool) { +// return numOfPendingRequests != 0; +// } - // @notice This is just an example of how to add more function in upgrading a contract - function getServiceName() external view returns (string memory) { - return serviceName; - } +// // @notice This is just an example of how to add more function in upgrading a contract +// function getServiceName() external view returns (string memory) { +// return serviceName; +// } - // @notice This is just an example of how to add more function in upgrading a contract - function getPendingRequest(uint256 _sn) - external - view - returns (Types.PendingTransferCoin memory) - { - return requests[_sn]; - } +// // @notice This is just an example of how to add more function in upgrading a contract +// function getPendingRequest(uint256 _sn) +// external +// view +// returns (Types.PendingTransferCoin memory) +// { +// return requests[_sn]; +// } - // @notice This is just an example of how to add more function in upgrading a contract - function getAggregationFeeOf(string calldata _coinName) - external - view - returns (uint256 _fee) - { - Types.Asset[] memory _fees = btsCore.getAccumulatedFees(); - for (uint256 i = 0; i < _fees.length; i++) { - if (_coinName.compareTo(_fees[i].coinName)) return _fees[i].value; - } - } +// // @notice This is just an example of how to add more function in upgrading a contract +// function getAggregationFeeOf(string calldata _coinName) +// external +// view +// returns (uint256 _fee) +// { +// Types.Asset[] memory _fees = btsCore.getAccumulatedFees(); +// for (uint256 i = 0; i < _fees.length; i++) { +// if (_coinName.compareTo(_fees[i].coinName)) return _fees[i].value; +// } +// } - function sendServiceMessage( - address _from, - string memory _to, - string[] memory _coinNames, - uint256[] memory _values, - uint256[] memory _fees - ) external override { - // Send Service Message to BMC - // If '_to' address is an invalid BTP Address format - // VM throws an error and revert(). Thus, it does not need - // a try_catch at this point - (string memory _toNetwork, string memory _toAddress) = _to - .splitBTPAddress(); - Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); - Types.AssetTransferDetail[] - memory _assetDetails = new Types.AssetTransferDetail[]( - _coinNames.length - ); - for (uint256 i = 0; i < _coinNames.length; i++) { - _assets[i] = Types.Asset(_coinNames[i], _values[i]); - _assetDetails[i] = Types.AssetTransferDetail( - _coinNames[i], - _values[i], - _fees[i] - ); - } +// function sendServiceMessage( +// address _from, +// string memory _to, +// string[] memory _coinNames, +// uint256[] memory _values, +// uint256[] memory _fees +// ) external override { +// // Send Service Message to BMC +// // If '_to' address is an invalid BTP Address format +// // VM throws an error and revert(). Thus, it does not need +// // a try_catch at this point +// (string memory _toNetwork, string memory _toAddress) = _to +// .splitBTPAddress(); +// Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); +// Types.AssetTransferDetail[] +// memory _assetDetails = new Types.AssetTransferDetail[]( +// _coinNames.length +// ); +// for (uint256 i = 0; i < _coinNames.length; i++) { +// _assets[i] = Types.Asset(_coinNames[i], _values[i]); +// _assetDetails[i] = Types.AssetTransferDetail( +// _coinNames[i], +// _values[i], +// _fees[i] +// ); +// } - serialNo++; +// serialNo++; - // Because `stack is too deep`, must create `_strFrom` to waive this error - // `_strFrom` is a string type of an address `_from` - string memory _strFrom = _from.toString(); - bmc.sendMessage( - _toNetwork, - serviceName, - serialNo, - Types - .ServiceMessage( - Types.ServiceType.REQUEST_COIN_TRANSFER, - Types - .TransferCoin(_strFrom, _toAddress, _assets) - .encodeTransferCoinMsg() - ) - .encodeServiceMessage() - ); - // Push pending tx into Record list - requests[serialNo] = Types.PendingTransferCoin( - _strFrom, - _to, - _coinNames, - _values, - _fees - ); - numOfPendingRequests++; - emit TransferStart(_from, _to, serialNo, _assetDetails); - } +// // Because `stack is too deep`, must create `_strFrom` to waive this error +// // `_strFrom` is a string type of an address `_from` +// string memory _strFrom = _from.toString(); +// bmc.sendMessage( +// _toNetwork, +// serviceName, +// serialNo, +// Types +// .ServiceMessage( +// Types.ServiceType.REQUEST_COIN_TRANSFER, +// Types +// .TransferCoin(_strFrom, _toAddress, _assets) +// .encodeTransferCoinMsg() +// ) +// .encodeServiceMessage() +// ); +// // Push pending tx into Record list +// requests[serialNo] = Types.PendingTransferCoin( +// _strFrom, +// _to, +// _coinNames, +// _values, +// _fees +// ); +// numOfPendingRequests++; +// emit TransferStart(_from, _to, serialNo, _assetDetails); +// } - /** - @notice BSH handle BTP Message from BMC contract - @dev Caller must be BMC contract only - @param _from An originated network address of a request - @param _svc A service name of BSH contract - @param _sn A serial number of a service request - @param _msg An RLP message of a service request/service response - */ - function handleBTPMessage( - string calldata _from, - string calldata _svc, - uint256 _sn, - bytes calldata _msg - ) external override onlyBMC { - require(_svc.compareTo(serviceName) == true, "InvalidSvc"); - Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); - string memory errMsg; +// /** +// @notice BSH handle BTP Message from BMC contract +// @dev Caller must be BMC contract only +// @param _from An originated network address of a request +// @param _svc A service name of BSH contract +// @param _sn A serial number of a service request +// @param _msg An RLP message of a service request/service response +// */ +// function handleBTPMessage( +// string calldata _from, +// string calldata _svc, +// uint256 _sn, +// bytes calldata _msg +// ) external override onlyBMC { +// require(_svc.compareTo(serviceName) == true, "InvalidSvc"); +// Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); +// string memory errMsg; - if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { - Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); - // checking receiving address whether is a valid address - // revert() if not a valid one - try this.checkParseAddress(_tc.to) { - try this.handleRequestService(_tc.to, _tc.assets) { - sendResponseMessage( - Types.ServiceType.REPONSE_HANDLE_SERVICE, - _from, - _sn, - "", - RC_OK - ); - return; - } catch Error(string memory _err) { - errMsg = _err; - } - } catch { - errMsg = "InvalidAddress"; - } - sendResponseMessage( - Types.ServiceType.REPONSE_HANDLE_SERVICE, - _from, - _sn, - errMsg, - RC_ERR - ); - } else if ( - _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE - ) { - // Check whether '_sn' is pending state - require(bytes(requests[_sn].from).length != 0, "InvalidSN"); - Types.Response memory response = _sm.data.decodeResponse(); - // @dev Not implement try_catch at this point - // + If RESPONSE_REQUEST_SERVICE: - // If RC_ERR, BTSCore proceeds a refund. If a refund is failed, BTSCore issues refundable Balance - // If RC_OK: - // - requested coin = native -> update aggregation fee (likely no issue) - // - requested coin = wrapped coin -> BTSCore calls itself to burn its tokens and update aggregation fee (likely no issue) - // The only issue, which might happen, is BTSCore's token balance lower than burning amount - // If so, there might be something went wrong before - // + If RESPONSE_FEE_GATHERING - // If RC_ERR, BTSCore saves charged fees back to `aggregationFee` state mapping variable - // If RC_OK: do nothing - handleResponseService(_sn, response.code, response.message); - } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { - emit UnknownResponse(_from, _sn); - } else { - // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE - sendResponseMessage( - Types.ServiceType.UNKNOWN_TYPE, - _from, - _sn, - "Unknown", - RC_ERR - ); - } - } +// if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { +// Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); +// // checking receiving address whether is a valid address +// // revert() if not a valid one +// try this.checkParseAddress(_tc.to) { +// try this.handleRequestService(_tc.to, _tc.assets) { +// sendResponseMessage( +// Types.ServiceType.REPONSE_HANDLE_SERVICE, +// _from, +// _sn, +// "", +// RC_OK +// ); +// return; +// } catch Error(string memory _err) { +// errMsg = _err; +// } +// } catch { +// errMsg = "InvalidAddress"; +// } +// sendResponseMessage( +// Types.ServiceType.REPONSE_HANDLE_SERVICE, +// _from, +// _sn, +// errMsg, +// RC_ERR +// ); +// } else if ( +// _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE +// ) { +// // Check whether '_sn' is pending state +// require(bytes(requests[_sn].from).length != 0, "InvalidSN"); +// Types.Response memory response = _sm.data.decodeResponse(); +// // @dev Not implement try_catch at this point +// // + If RESPONSE_REQUEST_SERVICE: +// // If RC_ERR, BTSCore proceeds a refund. If a refund is failed, BTSCore issues refundable Balance +// // If RC_OK: +// // - requested coin = native -> update aggregation fee (likely no issue) +// // - requested coin = wrapped coin -> BTSCore calls itself to burn its tokens and update aggregation fee (likely no issue) +// // The only issue, which might happen, is BTSCore's token balance lower than burning amount +// // If so, there might be something went wrong before +// // + If RESPONSE_FEE_GATHERING +// // If RC_ERR, BTSCore saves charged fees back to `aggregationFee` state mapping variable +// // If RC_OK: do nothing +// handleResponseService(_sn, response.code, response.message); +// } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { +// emit UnknownResponse(_from, _sn); +// } else { +// // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE +// sendResponseMessage( +// Types.ServiceType.UNKNOWN_TYPE, +// _from, +// _sn, +// "Unknown", +// RC_ERR +// ); +// } +// } - /** - @notice BSH handle BTP Error from BMC contract - @dev Caller must be BMC contract only - @param _svc A service name of BSH contract - @param _sn A serial number of a service request - @param _code A response code of a message (RC_OK / RC_ERR) - @param _msg A response message - */ - function handleBTPError( - string calldata, /* _src */ - string calldata _svc, - uint256 _sn, - uint256 _code, - string calldata _msg - ) external override onlyBMC { - require(_svc.compareTo(serviceName) == true, "InvalidSvc"); - require(bytes(requests[_sn].from).length != 0, "InvalidSN"); - handleResponseService(_sn, _code, _msg); - } +// /** +// @notice BSH handle BTP Error from BMC contract +// @dev Caller must be BMC contract only +// @param _svc A service name of BSH contract +// @param _sn A serial number of a service request +// @param _code A response code of a message (RC_OK / RC_ERR) +// @param _msg A response message +// */ +// function handleBTPError( +// string calldata, /* _src */ +// string calldata _svc, +// uint256 _sn, +// uint256 _code, +// string calldata _msg +// ) external override onlyBMC { +// require(_svc.compareTo(serviceName) == true, "InvalidSvc"); +// require(bytes(requests[_sn].from).length != 0, "InvalidSN"); +// handleResponseService(_sn, _code, _msg); +// } - function handleResponseService( - uint256 _sn, - uint256 _code, - string memory _msg - ) private { - address _caller = requests[_sn].from.parseAddress(); - uint256 loop = requests[_sn].coinNames.length; - for (uint256 i = 0; i < loop; i++) { - btsCore.handleResponseService( - _caller, - requests[_sn].coinNames[i], - requests[_sn].amounts[i], - requests[_sn].fees[i], - _code - ); - } - delete requests[_sn]; - numOfPendingRequests--; - emit TransferEnd(_caller, _sn, _code, _msg); - } +// function handleResponseService( +// uint256 _sn, +// uint256 _code, +// string memory _msg +// ) private { +// address _caller = requests[_sn].from.parseAddress(); +// uint256 loop = requests[_sn].coinNames.length; +// for (uint256 i = 0; i < loop; i++) { +// btsCore.handleResponseService( +// _caller, +// requests[_sn].coinNames[i], +// requests[_sn].amounts[i], +// requests[_sn].fees[i], +// _code +// ); +// } +// delete requests[_sn]; +// numOfPendingRequests--; +// emit TransferEnd(_caller, _sn, _code, _msg); +// } - /** - @notice Handle a list of minting/transferring coins/tokens - @dev Caller must be BMC contract only - @param _to An address to receive coins/tokens - @param _assets A list of requested coin respectively with an amount - */ - function handleRequestService( - string memory _to, - Types.Asset[] memory _assets - ) external { - require(msg.sender == address(this), "Unauthorized"); - for (uint256 i = 0; i < _assets.length; i++) { - require( - btsCore.isValidCoin(_assets[i].coinName) == true, - "UnregisteredCoin" - ); - // @dev There might be many errors generating by BTSCore contract - // which includes also low-level error - // Thus, must use try_catch at this point so that it can return an expected response - try - btsCore.mint( - _to.parseAddress(), - _assets[i].coinName, - _assets[i].value - ) - {} catch { - revert("TransferFailed"); - } - } - } +// /** +// @notice Handle a list of minting/transferring coins/tokens +// @dev Caller must be BMC contract only +// @param _to An address to receive coins/tokens +// @param _assets A list of requested coin respectively with an amount +// */ +// function handleRequestService( +// string memory _to, +// Types.Asset[] memory _assets +// ) external { +// require(msg.sender == address(this), "Unauthorized"); +// for (uint256 i = 0; i < _assets.length; i++) { +// require( +// btsCore.isValidCoin(_assets[i].coinName) == true, +// "UnregisteredCoin" +// ); +// // @dev There might be many errors generating by BTSCore contract +// // which includes also low-level error +// // Thus, must use try_catch at this point so that it can return an expected response +// try +// btsCore.mint( +// _to.parseAddress(), +// _assets[i].coinName, +// _assets[i].value +// ) +// {} catch { +// revert("TransferFailed"); +// } +// } +// } - function sendResponseMessage( - Types.ServiceType _serviceType, - string memory _to, - uint256 _sn, - string memory _msg, - uint256 _code - ) private { - bmc.sendMessage( - _to, - serviceName, - _sn, - Types - .ServiceMessage( - _serviceType, - Types.Response(_code, _msg).encodeResponse() - ) - .encodeServiceMessage() - ); - } +// function sendResponseMessage( +// Types.ServiceType _serviceType, +// string memory _to, +// uint256 _sn, +// string memory _msg, +// uint256 _code +// ) private { +// bmc.sendMessage( +// _to, +// serviceName, +// _sn, +// Types +// .ServiceMessage( +// _serviceType, +// Types.Response(_code, _msg).encodeResponse() +// ) +// .encodeServiceMessage() +// ); +// } - /** - @notice BSH handle Gather Fee Message request from BMC contract - @dev Caller must be BMC contract only - @param _fa A BTP address of fee aggregator - @param _svc A name of the service - */ - function handleFeeGathering(string calldata _fa, string calldata _svc) - external - override - onlyBMC - { - require(_svc.compareTo(serviceName) == true, "InvalidSvc"); - // If adress of Fee Aggregator (_fa) is invalid BTP address format - // revert(). Then, BMC will catch this error - _fa.splitBTPAddress(); - btsCore.transferFees(_fa); - } +// /** +// @notice BSH handle Gather Fee Message request from BMC contract +// @dev Caller must be BMC contract only +// @param _fa A BTP address of fee aggregator +// @param _svc A name of the service +// */ +// function handleFeeGathering(string calldata _fa, string calldata _svc) +// external +// override +// onlyBMC +// { +// require(_svc.compareTo(serviceName) == true, "InvalidSvc"); +// // If adress of Fee Aggregator (_fa) is invalid BTP address format +// // revert(). Then, BMC will catch this error +// _fa.splitBTPAddress(); +// btsCore.transferFees(_fa); +// } - // @dev Solidity does not allow to use try_catch with internal function - // Thus, this is a work-around solution - // Since this function is basically checking whether a string address - // can be parsed to address type. Hence, it would not have any restrictions - function checkParseAddress(string calldata _to) external pure { - _to.parseAddress(); - } -} +// // @dev Solidity does not allow to use try_catch with internal function +// // Thus, this is a work-around solution +// // Since this function is basically checking whether a string address +// // can be parsed to address type. Hence, it would not have any restrictions +// function checkParseAddress(string calldata _to) external pure { +// _to.parseAddress(); +// } + +// function checkTransferRestrictions( +// string memory _coinName, +// address _from, +// string memory _to, +// uint256 _value +// ) public override {} +// } \ No newline at end of file From 77689f74c96db0c4b1eb4fe0a97cbfa0e8c9d2e7 Mon Sep 17 00:00:00 2001 From: Manish Date: Thu, 4 Aug 2022 13:19:56 +0545 Subject: [PATCH 2/6] omit obsolete token-bsh and bsr-#45 --- javascore/integration-tests/build.gradle | 4 ++-- javascore/settings.gradle | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/javascore/integration-tests/build.gradle b/javascore/integration-tests/build.gradle index a53ffc039..28e50f889 100644 --- a/javascore/integration-tests/build.gradle +++ b/javascore/integration-tests/build.gradle @@ -15,7 +15,7 @@ allprojects { dependencies { implementation project(':bts') - implementation project(':token-bsh') + // implementation project(':token-bsh') implementation 'foundation.icon:javaee-api:0.9.0' implementation 'foundation.icon:javaee-rt:0.9.0' implementation 'foundation.icon:javaee-tooling:0.9.0' @@ -200,4 +200,4 @@ task testJavaScoreSejong(type: ChainTest) { // need to add this option to retrieve formal parameter names compileJava { options.compilerArgs += ['-parameters'] -} +} \ No newline at end of file diff --git a/javascore/settings.gradle b/javascore/settings.gradle index 8a8ffd031..2d289d709 100644 --- a/javascore/settings.gradle +++ b/javascore/settings.gradle @@ -4,6 +4,5 @@ include ( 'lib', 'bmc', 'bts', - 'bsr', - 'token-bsh' -) + 'integration-tests' +) \ No newline at end of file From 5ab769dcb7e1ebaf95b4c5e5ac1c6e3cd8228092 Mon Sep 17 00:00:00 2001 From: Manish Date: Thu, 4 Aug 2022 13:42:27 +0545 Subject: [PATCH 3/6] javascore side implementation of blacklist, tokenlimit-#45 --- javascore/bts/build.gradle | 12 +- .../icon/btp/bts/BTPTokenService.java | 460 +++++-- .../java/foundation/icon/btp/bts/BTS.java | 59 +- .../foundation/icon/btp/bts/BTSException.java | 12 +- .../foundation/icon/btp/bts/BTSMessage.java | 7 +- .../foundation/icon/btp/bts/BlacklistDB.java | 108 ++ ...Properties.java => BlacklistResponse.java} | 65 +- .../icon/btp/bts/BlacklistTransaction.java | 100 ++ .../java/foundation/icon/btp/bts/Coin.java | 8 +- .../icon/btp/bts/TokenLimitResponse.java | 92 ++ .../icon/btp/bts/TokenLimitTransaction.java | 142 +++ .../icon/btp/bts/irc2/IRC2ScoreInterface.java | 4 +- .../icon/btp/bts/utils/EnumerableSet.java | 76 ++ .../icon/btp/bts/AbstractBTPTokenService.java | 200 +++ .../foundation/icon/btp/bts/AssertBTS.java | 130 +- .../icon/btp/bts/AssertBTSException.java | 82 +- .../icon/btp/bts/BTPTokenServiceTest.java | 1067 +++++++++-------- .../icon/btp/bts/BTSIntegrationTest.java | 106 +- .../java/foundation/icon/btp/bts/BTSTest.java | 824 +++++++++++++ .../icon/btp/bts/OwnershipTest.java | 236 ++-- .../icon/btp/bts/TransferEndEventLog.java | 134 +-- .../icon/btp/bts/TransferStartEventLog.java | 160 +-- .../icon/btp/bts/UnknownResponseEventLog.java | 130 +- 23 files changed, 3037 insertions(+), 1177 deletions(-) create mode 100644 javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java rename javascore/bts/src/main/java/foundation/icon/btp/bts/{BTSProperties.java => BlacklistResponse.java} (51%) create mode 100644 javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistTransaction.java create mode 100644 javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitResponse.java create mode 100644 javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitTransaction.java create mode 100644 javascore/bts/src/main/java/foundation/icon/btp/bts/utils/EnumerableSet.java create mode 100644 javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java create mode 100644 javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java diff --git a/javascore/bts/build.gradle b/javascore/bts/build.gradle index 9195402ad..d4717312c 100644 --- a/javascore/bts/build.gradle +++ b/javascore/bts/build.gradle @@ -5,7 +5,7 @@ dependencies { implementation("foundation.icon:javaee-scorex:$scorexVersion") implementation fileTree(dir: '../lib', include: 'score-util.jar') implementation fileTree(dir: '../lib', include: 'lib-0.1.0.jar') - implementation project(':bsr') + implementation("com.github.sink772:javaee-tokens:0.6.0") testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiterVersion") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion") @@ -16,6 +16,14 @@ dependencies { testImplementation("foundation.icon:javaee-score-client:$scoreClientVersion") testImplementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") testImplementation("foundation.icon:icon-sdk:$iconsdkVersion") + testImplementation("com.github.javafaker:javafaker:1.0.2") + testImplementation('org.mockito:mockito-core:4.3.1') + testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.3.0' + testImplementation 'org.bouncycastle:bcprov-jdk15on:1.60' +} + +test { + useJUnitPlatform() } optimizedJar { @@ -25,4 +33,4 @@ optimizedJar { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } { exclude "score/*" } enableDebug = debugJar -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java index dde4fd69a..2cae39b16 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java @@ -19,16 +19,15 @@ import foundation.icon.btp.bts.irc2.IRC2ScoreInterface; import foundation.icon.btp.bts.irc2.IRC2SupplierScoreInterface; import foundation.icon.btp.lib.BMCScoreInterface; +import foundation.icon.btp.bts.utils.EnumerableSet; import foundation.icon.btp.lib.BSH; import foundation.icon.btp.lib.BTPAddress; import foundation.icon.btp.lib.OwnerManager; import foundation.icon.btp.lib.OwnerManagerImpl; -import foundation.icon.btp.restrictions.RestrictionsScoreInterface; import foundation.icon.score.util.ArrayUtil; import foundation.icon.score.util.Logger; import foundation.icon.score.util.StringUtil; import score.Address; -import score.ArrayDB; import score.BranchDB; import score.ByteArrayObjectWriter; import score.Context; @@ -54,78 +53,58 @@ public class BTPTokenService implements BTS, BTSEvents, BSH, OwnerManager { public static final BigInteger FEE_DENOMINATOR = BigInteger.valueOf(10000); public static final int NATIVE_COIN_TYPE = 0; - public static final int NATIVE_WRAPPED_COIN_TYPE = 1; + public static final int WRAPPED_COIN_TYPE = 1; public static final int NON_NATIVE_TOKEN_TYPE = 2; - // + public static final Address ZERO_SCORE_ADDRESS = Address.fromString("cx0000000000000000000000000000000000000000"); + private final Address bmc; private final String net; private final byte[] serializedIrc2; private final String name; - private final VarDB properties = Context.newVarDB("properties", BTSProperties.class); - // private final OwnerManager ownerManager = new OwnerManagerImpl("owners"); - // - private final ArrayDB coinNames = Context.newArrayDB("coinNames", String.class); + private final EnumerableSet coinNames = new EnumerableSet<>("coinNames", String.class); private final DictDB coinAddresses = Context.newDictDB("coinAddresses", Address.class); + private final DictDB coinAddressName = Context.newDictDB("coinAddressNames", String.class); - // private final BranchDB> balances = Context.newBranchDB("balances", Balance.class); private final DictDB feeBalances = Context.newDictDB("feeBalances", BigInteger.class); private final DictDB transactions = Context.newDictDB("transactions", TransferTransaction.class); + private final DictDB blacklistTxn = Context.newDictDB("blacklistTransaction", + BlacklistTransaction.class); + private final DictDB tokenLimitTxn = Context.newDictDB("tokenLimitTransaction", + TokenLimitTransaction.class); + private final BranchDB> tokenLimitStatus = Context.newBranchDB("tokenLimitStatus", + Boolean.class); private final DictDB coinDb = Context.newDictDB("coins", Coin.class); - private final DictDB coinAddressName = Context.newDictDB("coinAddressNames", String.class); + private final DictDB tokenLimit = Context.newDictDB("tokenLimit", BigInteger.class); + private final VarDB sn = Context.newVarDB("serviceNumber", BigInteger.class); - // - private final VarDB
bsrDb = Context.newVarDB("bsr", Address.class); - VarDB restriction = Context.newVarDB("restricton", Boolean.class); - RestrictionsScoreInterface restrictionsInterface; + VarDB restriction = Context.newVarDB("restriction", Boolean.class); + private final BlacklistDB blacklistDB; - public BTPTokenService(Address _bmc, String _name, byte[] _serializedIrc2) { + public BTPTokenService(Address _bmc, String _name, int _decimals, byte[] _serializedIrc2) { bmc = _bmc; BMCScoreInterface bmcInterface = new BMCScoreInterface(bmc); BTPAddress btpAddress = BTPAddress.valueOf(bmcInterface.getBtpAddress()); net = btpAddress.net(); name = _name; serializedIrc2 = _serializedIrc2; - coinDb.set(_name, new Coin(Address.fromString("cx0000000000000000000000000000000000000000"), _name, _name, 0, - BigInteger.ZERO, BigInteger.ZERO, NATIVE_COIN_TYPE)); - } + blacklistDB = new BlacklistDB(); - public BTSProperties getProperties() { - return properties.getOrDefault(BTSProperties.DEFAULT); - } - - public void setProperties(BTSProperties properties) { - this.properties.set(properties); - } - - private boolean isRegistered(String name) { - int len = coinNames.size(); - for (int i = 0; i < len; i++) { - if (coinNames.get(i).equals(name)) { - return true; - } - } - return false; - } + // set sn to zero + sn.set(BigInteger.ZERO); - private List getCoinNamesAsList() { - List coinNames = new ArrayList<>(); - int len = this.coinNames.size(); - for (int i = 0; i < len; i++) { - coinNames.add(this.coinNames.get(i)); - } - return coinNames; + coinDb.set(_name, new Coin(ZERO_SCORE_ADDRESS, _name, _name, _decimals, + BigInteger.ZERO, BigInteger.ZERO, NATIVE_COIN_TYPE)); } - static void require(boolean condition, String message) { - if (!condition) { - throw BTSException.unknown(message); - } + @External(readonly = true) + public String name() { + return "BTP Token Service"; } /** @@ -142,7 +121,8 @@ public void setFeeRatio(String _name, BigInteger _feeNumerator, BigInteger _fixe Context.require(_feeNumerator.compareTo(BigInteger.ONE) >= 0 && _feeNumerator.compareTo(FEE_DENOMINATOR) < 0, "The feeNumerator should be less than FEE_DENOMINATOR and feeNumerator should be greater than 1"); - require(name.equals(_name) || isRegistered(_name), "Not supported Coin"); + Context.require(_fixedFee.compareTo(BigInteger.ZERO) >= 0, "Fixed fee cannot be less than zero"); + require( isRegistered(_name), "Not supported Coin"); Coin _coin = coinDb.get(_name); if (_coin == null) { throw BTSException.unknown("Coin Not Registered"); @@ -153,18 +133,35 @@ public void setFeeRatio(String _name, BigInteger _feeNumerator, BigInteger _fixe coinDb.set(_name, _coin); } + @External(readonly = true) + public Map feeRatio(String _name) { + Coin coinDetail = coinDb.get(_name); + if ( coinDetail != null ) { + return Map.of( + "fixedFee", coinDetail.getFixedFee(), + "feeNumerator", coinDetail.getFeeNumerator() + ); + } + return Map.of( + "fixedFee", BigInteger.ZERO, + "feeNumerator", BigInteger.ZERO + ); + } + @External public void register(String _name, String _symbol, int _decimals, BigInteger _feeNumerator, BigInteger _fixedFee, @Optional Address _addr) { requireOwnerAccess(); - require(!name.equals(_name) && !isRegistered(_name), "already existed"); + require(!isRegistered(_name), "already existed"); + coinNames.add(_name); - if (_addr == null) { + if (_addr == null || _addr.equals(ZERO_SCORE_ADDRESS)) { Address irc2Address = Context.deploy(serializedIrc2, _name, _symbol, _decimals); coinAddresses.set(_name, irc2Address); + coinAddressName.set(irc2Address, _name); coinDb.set(_name, new Coin(irc2Address, _name, _symbol, _decimals, _feeNumerator, _fixedFee, - NATIVE_WRAPPED_COIN_TYPE)); + WRAPPED_COIN_TYPE)); } else { coinAddresses.set(_name, _addr); coinDb.set(_name, @@ -173,15 +170,114 @@ public void register(String _name, String _symbol, int _decimals, BigInteger _fe } } + @External + public void setTokenLimit(String[] _coinNames, BigInteger[] _tokenLimits) { + requireOwnerAccess(); + require(_coinNames.length == _tokenLimits.length, "Invalid arguments"); + int size = _coinNames.length; + for (int i = 0; i < size; i++) { + require(isRegistered(_coinNames[i]), "Not registered"); + require((_tokenLimits[i].compareTo(BigInteger.ZERO) >= 0), + "Invalid value"); + tokenLimit.set(_coinNames[i], _tokenLimits[i]); + } + + BigInteger sn = increaseSn(); + String[] links = getLinks(); + for (String link: links) { + BTPAddress linkAddr = BTPAddress.valueOf(link); + String net = linkAddr.net(); + for (String name: _coinNames) { + tokenLimitStatus.at(net).set(name, false); + } + TokenLimitTransaction request = new TokenLimitTransaction(_coinNames, _tokenLimits, net); + sendMessage(net, BTSMessage.CHANGE_TOKEN_LIMIT, sn, request.toBytes()); + } + + // to save to tokenLimitTxn Db + TokenLimitTransaction request = new TokenLimitTransaction(_coinNames, _tokenLimits); + tokenLimitTxn.set(sn, request); + } + @External(readonly = true) - public String[] coinNames() { - int len = coinNames.size(); - String[] names = new String[len + 1]; - names[0] = name; - for (int i = 0; i < len; i++) { - names[i + 1] = coinNames.get(i); + public BigInteger getTokenLimit(String _name) { + return tokenLimit.get(_name); + } + + @External(readonly = true) + public BigInteger getSn() { + return sn.get(); + } + + @External + public void addBlacklistAddress(String _net, String[] _addresses) { + requireOwnerAccess(); + + // check for valid link + require(isValidLink(_net), "Invalid link"); + + for (String addr: _addresses) { + require(!isUserBlackListed(addr, _net), "User already blacklisted"); } - return names; + + BigInteger sn = increaseSn(); + BlacklistTransaction request = new BlacklistTransaction(_addresses, _net); + + blacklistTxn.set(sn, request); + + sendMessage(_net, BTSMessage.ADD_TO_BLACKLIST, sn, request.toBytes()); + } + + @External + public void removeBlacklistAddress(String _net, String[] _addresses) { + requireOwnerAccess(); + + // check for valid link + require(isValidLink(_net), "Invalid link"); + + for (String addr: _addresses) { + require(isUserBlackListed(addr, _net), "User not in blacklist"); + } + + BigInteger sn = increaseSn(); + BlacklistTransaction request = new BlacklistTransaction(_addresses, _net); + + blacklistTxn.set(sn, request); + + sendMessage(_net, BTSMessage.REMOVE_FROM_BLACKLIST, sn, request.toBytes()); + } + + @External(readonly = true) + public boolean isUserBlackListed(String _address, String _net) { + return blacklistDB.contains(_net, _address); + } + + @External(readonly = true) + public List getBlackListedUsers(String _net, int start, int end) { + if ((end - start) > 100) { + throw BTSException.unknown("Can only fetch 100 users at a time"); + } + return blacklistDB.range(_net, start, end); + } + + @External(readonly = true) + public int blackListedUsersCount(String _net) { + return blacklistDB.length(_net); + } + + @External(readonly = true) + public int getRegisteredTokensCount() { + return coinNames.length(); + } + + @External(readonly = true) + public boolean tokenLimitStatus(String _net, String _coinName) { + return tokenLimitStatus.at(_net).getOrDefault(_coinName, false); + } + + @External(readonly = true) + public List coinNames() { + return getCoinNamesAsList(); } @External(readonly = true) @@ -201,7 +297,7 @@ public Map balanceOf(Address _owner, String _coinName) { BigInteger icxBalance = Context.getBalance(_owner); balance.setUsable(icxBalance); return balance.addUserBalance(icxBalance); - } else if (_coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE) { + } else if (_coin.getCoinType() == WRAPPED_COIN_TYPE) { IRC2SupplierScoreInterface _irc2 = new IRC2SupplierScoreInterface(_coin.getAddress()); BigInteger allowance = _irc2.allowance(_owner, Context.getAddress()); BigInteger tokenBalance = _irc2.balanceOf(_owner); @@ -223,6 +319,11 @@ public List> balanceOfBatch(Address _owner, String[] _co return balances; } + @External(readonly = true) + public BigInteger getAccumulatedFees(String coinName) { + return feeBalances.getOrDefault(coinName, BigInteger.ZERO); + } + // To receive IRC2 token from existing Contract @External public void tokenFallback(Address _from, BigInteger _value, byte[] _data) { @@ -232,6 +333,8 @@ public void tokenFallback(Address _from, BigInteger _value, byte[] _data) { Balance _userBalance = getBalance(_coinName, _from); _userBalance.setUsable(_userBalance.getUsable().add(_value)); setBalance(_coinName, _from, _userBalance); + } else { + throw BTSException.unknown("Token not registered"); } } @@ -261,20 +364,22 @@ public void reclaim(String _coinName, BigInteger _value) { @External public void transferNativeCoin(String _to) { BigInteger value = Context.getValue(); + BTPAddress to = BTPAddress.valueOf(_to); require(value != null && value.compareTo(BigInteger.ZERO) > 0, "Invalid amount"); - checkTransferRestrictions(name, Context.getCaller().toString(), BTPAddress.valueOf(_to).account(), value); - sendRequest(Context.getCaller(), BTPAddress.valueOf(_to), List.of(name), List.of(value)); + checkTransferRestrictions(to.net(), name, Context.getCaller().toString(), BTPAddress.valueOf(_to).account(), value); + sendRequest(Context.getCaller(), to, List.of(name), List.of(value)); } @External public void transfer(String _coinName, BigInteger _value, String _to) { require(_value != null && _value.compareTo(BigInteger.ZERO) > 0, "Invalid amount"); - require(!name.equals(_coinName) && isRegistered(_coinName), "Not supported Token"); + require(isRegistered(_coinName), "Not supported Token"); Address owner = Context.getCaller(); - checkTransferRestrictions(_coinName, owner.toString(), BTPAddress.valueOf(_to).account(), _value); + BTPAddress to = BTPAddress.valueOf(_to); + checkTransferRestrictions(to.net(), _coinName, owner.toString(), BTPAddress.valueOf(_to).account(), _value); transferFrom(owner, Context.getAddress(), _coinName, _value); - sendRequest(owner, BTPAddress.valueOf(_to), List.of(_coinName), List.of(_value)); + sendRequest(owner, to, List.of(_coinName), List.of(_value)); } @Payable @@ -282,28 +387,31 @@ public void transfer(String _coinName, BigInteger _value, String _to) { public void transferBatch(String[] _coinNames, BigInteger[] _values, String _to) { require(_coinNames.length == _values.length, "Invalid arguments"); - List registeredCoinNames = getCoinNamesAsList(); - List coinNames = new ArrayList<>(); + List coinNameList = new ArrayList<>(); List values = new ArrayList<>(); int len = _coinNames.length; + Address owner = Context.getCaller(); + BTPAddress to = BTPAddress.valueOf(_to); + for (int i = 0; i < len; i++) { String coinName = _coinNames[i]; - require(!name.equals(coinName) && registeredCoinNames.contains(coinName), "Not supported Token"); - coinNames.add(coinName); + BigInteger value = _values[i]; + require(!name.equals(coinName) && this.coinNames.contains(coinName), "Not supported Token"); + coinNameList.add(coinName); values.add(_values[i]); + checkTransferRestrictions(to.net(), coinName, owner.toString(), to.account() ,value); } - Address owner = Context.getCaller(); transferFromBatch(owner, Context.getAddress(), _coinNames, _values); BigInteger value = Context.getValue(); if (value != null && value.compareTo(BigInteger.ZERO) > 0) { - coinNames.add(name); + coinNameList.add(name); values.add(value); } - sendRequest(owner, BTPAddress.valueOf(_to), coinNames, values); + sendRequest(owner, to, coinNameList, values); } - + @EventLog(indexed = 1) public void TransferStart(Address _from, String _to, BigInteger _sn, byte[] _assetDetails) { } @@ -320,6 +428,15 @@ protected void TransferReceived(String _from, Address _to, BigInteger _sn, byte[ public void UnknownResponse(String _from, BigInteger _sn) { } + @EventLog(indexed = 1) + public void AddedToBlacklist(BigInteger sn, byte[] bytes) { } + + @EventLog(indexed = 1) + public void RemovedFromBlacklist(BigInteger sn, byte[] bytes) { } + + @EventLog(indexed = 1) + public void TokenLimitSet(BigInteger sn, byte[] bytes) { } + @External(readonly = true) public TransferTransaction getTransaction(BigInteger _sn) { return transactions.get(_sn); @@ -327,7 +444,6 @@ public TransferTransaction getTransaction(BigInteger _sn) { private void sendRequest(Address owner, BTPAddress to, List coinNames, List amounts) { logger.println("sendRequest", "begin"); - BTSProperties properties = getProperties(); int len = coinNames.size(); AssetTransferDetail[] assetTransferDetails = new AssetTransferDetail[len]; @@ -351,9 +467,7 @@ private void sendRequest(Address owner, BTPAddress to, List coinNames, L transaction.setTo(to.toString()); transaction.setAssets(assetTransferDetails); - BigInteger sn = properties.getSn().add(BigInteger.ONE); - properties.setSn(sn); - setProperties(properties); + BigInteger sn = increaseSn(); transactions.set(sn, transaction); sendMessage(to.net(), BTSMessage.REQUEST_COIN_TRANSFER, sn, request.toBytes()); @@ -412,12 +526,22 @@ public void handleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _ BTSMessage message = BTSMessage.fromBytes(_msg); int serviceType = message.getServiceType(); + if (serviceType == BTSMessage.REQUEST_COIN_TRANSFER) { TransferRequest request = TransferRequest.fromBytes(message.getData()); handleRequest(request, _from, _sn); } else if (serviceType == BTSMessage.REPONSE_HANDLE_SERVICE) { TransferResponse response = TransferResponse.fromBytes(message.getData()); handleResponse(_sn, response); + } else if (serviceType == BTSMessage.ADD_TO_BLACKLIST) { + BlacklistResponse response = BlacklistResponse.fromBytes(message.getData()); + handleAddToBlacklist(_sn, response); + } else if (serviceType == BTSMessage.REMOVE_FROM_BLACKLIST) { + BlacklistResponse response = BlacklistResponse.fromBytes(message.getData()); + handleRemoveFromBlacklist(_sn, response); + } else if (serviceType == BTSMessage.CHANGE_TOKEN_LIMIT) { + TokenLimitResponse response = TokenLimitResponse.fromBytes(message.getData()); + handleChangeTokenLimit(_from, _sn, response); } else if (serviceType == BTSMessage.UNKNOWN_TYPE) { // If receiving a RES_UNKNOWN_TYPE, ignore this message // or re-send another correct message @@ -472,7 +596,7 @@ public void handleFeeGathering(String _fa, String _svc) { } } - private Balance getBalance(String coinName, Address owner) { + public Balance getBalance(String coinName, Address owner) { Balance balance = balances.at(coinName).get(owner); if (balance == null) { balance = new Balance(); @@ -552,9 +676,8 @@ private void handleRequest(TransferRequest request, String from, BigInteger sn) BigInteger nativeCoinTransferAmount = null; Asset[] assets = request.getAssets(); - List coinNames = new ArrayList<>(); + List coinNamesList = new ArrayList<>(); List amounts = new ArrayList<>(); - List registeredCoinNames = getCoinNamesAsList(); for (Asset asset : assets) { String coinName = asset.getCoinName(); BigInteger amount = asset.getAmount(); @@ -562,15 +685,17 @@ private void handleRequest(TransferRequest request, String from, BigInteger sn) throw BTSException.unknown("Amount must be positive value"); } - if (registeredCoinNames.contains(coinName)) { - coinNames.add(coinName); + // not coin + if (this.coinNames.contains(coinName)) { + coinNamesList.add(coinName); amounts.add(amount); - } else if (name.equals(coinName)) { + } // nativecoin + else if (name.equals(coinName)) { nativeCoinTransferAmount = amount; } else { throw BTSException.unknown("Invalid Token"); } - checkTransferRestrictions(coinName, from, request.getTo(), amount); + checkTransferRestrictions(net, coinName, from, request.getTo(), amount); } if (nativeCoinTransferAmount != null) { @@ -581,8 +706,8 @@ private void handleRequest(TransferRequest request, String from, BigInteger sn) } } - if (coinNames.size() > 0) { - mintBatch(to, ArrayUtil.toStringArray(coinNames), ArrayUtil.toBigIntegerArray(amounts)); + if (coinNamesList.size() > 0) { + mintBatch(to, ArrayUtil.toStringArray(coinNamesList), ArrayUtil.toBigIntegerArray(amounts)); } logger.println("handleRequest", "responseSuccess"); @@ -594,7 +719,6 @@ private void handleRequest(TransferRequest request, String from, BigInteger sn) private void handleResponse(BigInteger sn, TransferResponse response) { logger.println("handleResponse", "begin", "sn:", sn); TransferTransaction transaction = transactions.get(sn); - List registeredCoinNames = getCoinNamesAsList(); // ignore when not exists pending request if (transaction != null) { BigInteger code = response.getCode(); @@ -603,7 +727,7 @@ private void handleResponse(BigInteger sn, TransferResponse response) { logger.println("handleResponse", "code:", code); if (TransferResponse.RC_OK.equals(code)) { - List coinNames = new ArrayList<>(); + List coinNameList = new ArrayList<>(); List amounts = new ArrayList<>(); for (AssetTransferDetail asset : assets) { String coinName = asset.getCoinName(); @@ -611,11 +735,11 @@ private void handleResponse(BigInteger sn, TransferResponse response) { BigInteger fee = asset.getFee(); BigInteger locked = amount.add(fee); boolean isNativeCoin = name.equals(coinName); - if (isNativeCoin || registeredCoinNames.contains(coinName)) { + if (isRegistered(coinName)) { unlock(coinName, owner, locked); addFee(coinName, fee); if (!isNativeCoin) { - coinNames.add(coinName); + coinNameList.add(coinName); amounts.add(amount); } } else { @@ -624,8 +748,8 @@ private void handleResponse(BigInteger sn, TransferResponse response) { } } - if (coinNames.size() > 0) { - burnBatch(ArrayUtil.toStringArray(coinNames), ArrayUtil.toBigIntegerArray(amounts)); + if (coinNameList.size() > 0) { + burnBatch(ArrayUtil.toStringArray(coinNameList), ArrayUtil.toBigIntegerArray(amounts)); } } else { for (AssetTransferDetail asset : assets) { @@ -634,7 +758,7 @@ private void handleResponse(BigInteger sn, TransferResponse response) { BigInteger fee = asset.getFee(); BigInteger locked = amount.add(fee); boolean isNativeCoin = name.equals(coinName); - if (isNativeCoin || registeredCoinNames.contains(coinName)) { + if (isNativeCoin || coinNames.contains(coinName)) { refund(coinName, owner, locked); } else { // This should not happen @@ -649,10 +773,80 @@ private void handleResponse(BigInteger sn, TransferResponse response) { logger.println("handleResponse", "end"); } - @External(readonly = true) - public BigInteger feeRatio() { - BTSProperties properties = getProperties(); - return properties.getFeeRatio(); + private void handleAddToBlacklist(BigInteger sn, BlacklistResponse response) { + logger.println("handleAddToBlacklist", "begin", "sn:", sn); + BlacklistTransaction txn = blacklistTxn.get(sn); + if (txn != null) { + BigInteger code = response.getCode(); + if (BlacklistResponse.RC_OK.equals(code)) { + String[] addresses = txn.getAddress(); + String net = txn.getNet(); + for(String addr : addresses) { + addToBlacklistInternal(net, addr); + } + } else { + throw BTSException.unknown("Invalid add to blacklist transaction"); + } + blacklistTxn.set(sn, null); + AddedToBlacklist(sn, response.getMessage() != null ? response.getMessage().getBytes() : null); + + } + logger.println("handleAddToBlacklist", "end"); + } + + private void handleRemoveFromBlacklist(BigInteger sn, BlacklistResponse response) { + logger.println("handleRemoveFromBlacklist", "begin", "sn:", sn); + BlacklistTransaction txn = blacklistTxn.get(sn); + if (txn != null) { + BigInteger code = response.getCode(); + if (BlacklistResponse.RC_OK.equals(code)) { + String[] addresses = txn.getAddress(); + String net = txn.getNet(); + for(String addr : addresses) { + removeFromBlacklistInternal(net, addr); + } + } else { + throw BTSException.unknown("Invalid remove from blacklist transaction"); + } + blacklistTxn.set(sn, null); + RemovedFromBlacklist(sn, response.getMessage() != null ? response.getMessage().getBytes() : null); + + } + logger.println("handleRemoveFromBlacklist", "end"); + } + + private void handleChangeTokenLimit(String from, BigInteger sn, TokenLimitResponse response) { + logger.println("handleChangeTokenLimit", "begin", "sn:", sn); + TokenLimitTransaction txn = tokenLimitTxn.get(sn); + if (txn != null) { + BigInteger code = response.getCode(); + if (BlacklistResponse.RC_OK.equals(code)) { + String[] coinNames = txn.getCoinName(); + int size = coinNames.length; + for (int i = 0; i < size; i++) { + tokenLimitStatus.at(from).set(coinNames[i], true); + } + } else { + throw BTSException.unknown("Invalid change limit transaction"); + } + TokenLimitSet(sn, response.getMessage() != null ? response.getMessage().getBytes() : null); + + } + logger.println("handleChangeTokenLimit", "end"); + } + + private void addToBlacklistInternal(String net, String addr) { + if (blacklistDB.contains(net, addr)) { + throw BTSException.unknown("User already blacklisted"); + } + blacklistDB.addToBlacklist(net, addr); + } + + private void removeFromBlacklistInternal(String net, String addr) { + if (! blacklistDB.contains(net, addr)) { + throw BTSException.unknown("User not in blacklist"); + } + blacklistDB.removeFromBlacklist(net, addr); } private AssetTransferDetail newAssetTransferDetail(String coinName, BigInteger amount, Address owner) { @@ -688,7 +882,7 @@ private void transferFrom(Address from, Address to, String coinName, BigInteger logger.println("transferFrom", from, to, coinName, amount); try { Coin _coin = coinDb.get(coinName); - if (_coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE) { + if (_coin.getCoinType() == WRAPPED_COIN_TYPE) { Address coinAddress = this.getCoinAddress(coinName); IRC2SupplierScoreInterface irc2 = new IRC2SupplierScoreInterface(coinAddress); irc2.transferFrom(from, to, amount, null); @@ -722,7 +916,7 @@ private void _transferBatch(Address from, Address to, List coinNames, Li int len = coinNames.size(); for (int i = 0; i < len; i++) { Coin _coin = coinDb.get(coinNames.get(i)); - if (_coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE) { + if (_coin.getCoinType() == WRAPPED_COIN_TYPE) { this.approve(from, coinNames.get(i), amounts.get(i)); this.transferFrom(from, to, coinNames.get(i), amounts.get(i)); } else if (_coin.getCoinType() == NON_NATIVE_TOKEN_TYPE) { @@ -742,7 +936,7 @@ private void mint(Address to, String coinName, BigInteger amount) { logger.println("mint", to, coinName, amount); try { Coin _coin = coinDb.get(coinName); - if (_coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE) { + if (_coin.getCoinType() == WRAPPED_COIN_TYPE) { IRC2SupplierScoreInterface irc2 = new IRC2SupplierScoreInterface(_coin.getAddress()); irc2.mint(to, amount); } else { @@ -785,7 +979,7 @@ private void burn(String coinName, BigInteger amount) { logger.println("burn", coinName, amount); try { Coin _coin = coinDb.get(coinName); - if (_coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE) { + if (_coin.getCoinType() == WRAPPED_COIN_TYPE) { IRC2SupplierScoreInterface irc2 = new IRC2SupplierScoreInterface(_coin.getAddress()); irc2.burn(amount); } @@ -849,25 +1043,75 @@ public boolean isOwner(Address _addr) { } @External - public void addRestrictor(Address _address) { + public void addRestriction() { requireOwnerAccess(); - bsrDb.set(_address); restriction.set(true); - restrictionsInterface = new RestrictionsScoreInterface(_address); } @External - public void disableRestrictions(Address _address) { + public void disableRestrictions() { requireOwnerAccess(); - bsrDb.set(_address); restriction.set(false); } - private void checkTransferRestrictions(String _tokenName, String _from, String _to, BigInteger _value) { - if (restriction.get() != null && bsrDb.get() != null && restriction.get()) { - // restictonsInterface.validateRestriction(_tokenName, _from, _to, _value); - Context.call(bsrDb.get(), "validateRestriction", _tokenName, _from, _to, _value); + private void checkTransferRestrictions(String _net, String _tokenName, String _from, String _to, BigInteger _value) { + if (restriction.get() != null && restriction.get()) { + validateRestriction(_net, _tokenName, _from, _to, _value); + } + } + + private void validateRestriction(String _net, String _token, String _from, String _to, BigInteger _value) { + if (isUserBlackListed(_from, _net)) { + throw BTSException.restricted("_from user is Blacklisted"); + } + if (isUserBlackListed(_to, _net)) { + throw BTSException.restricted("_to user is Blacklisted"); } + BigInteger tokenLimit = getTokenLimit(_token); + if (tokenLimit.compareTo(_value) < 0) { + throw BTSException.restricted("Transfer amount exceeds the transaction limit"); + } + } + + private boolean isRegistered(String name) { + return coinNames.contains(name) || this.name.equals(name); + } + + private List getCoinNamesAsList() { + return coinNames.range(0, coinNames.length()); + } + + static void require(boolean condition, String message) { + if (!condition) { + throw BTSException.unknown(message); + } + } + + private BigInteger increaseSn() { + BigInteger newSn = sn.get().add(BigInteger.ONE); + sn.set(newSn); + return newSn; + } + + private String lowercase(String word) { + return word.trim().toLowerCase(); + } + + private String[] getLinks() { + BMCScoreInterface bmcInterface = new BMCScoreInterface(bmc); + String[] links = bmcInterface.getLinks(); + return links; + } + + private boolean isValidLink(String net) { + String[] links = getLinks(); + for (String link : links) { + BTPAddress btpAddress = BTPAddress.valueOf(link); + if (btpAddress.net().equals(net)) { + return true; + } + } + return false; } } \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTS.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTS.java index c2210348c..8799c30a5 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTS.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTS.java @@ -26,6 +26,9 @@ public interface BTS { + @External + String name(); + /** * Registers a wrapped token smart contract and id number of a supporting coin. * Caller must be the owner @@ -36,7 +39,40 @@ public interface BTS { * @param _name A coin name. */ @External - void register(String _name, String _symbol, int _decimals, BigInteger _feeNumerator, BigInteger _fixedFee, Address address); + void register(String _name, String _symbol, int _decimals, BigInteger _feeNumerator, BigInteger _fixedFee, + Address address); + + /** + * Change max allowable transfer limit + * @param _coinNames List of name of the coins + * @param _tokenLimits Maximum allowable limit for all tokens + */ + @External + void setTokenLimit(String[] _coinNames, BigInteger[] _tokenLimits); + + @External(readonly = true) + BigInteger getTokenLimit(String _name); + + @External + void addBlacklistAddress(String _net, String[] _addresses); + + @External + void removeBlacklistAddress(String _net, String[] _addresses); + + @External(readonly = true) + boolean isUserBlackListed(String _address, String _net); + + @External(readonly = true) + List getBlackListedUsers(String _net, int start, int end); + + @External(readonly = true) + int blackListedUsersCount(String _net); + + @External(readonly = true) + int getRegisteredTokensCount(); + + @External(readonly = true) + boolean tokenLimitStatus(String _net, String _coinName); /** * Return all supported coins names in other networks by the BSH contract @@ -44,7 +80,7 @@ public interface BTS { * @return An array of strings. */ @External(readonly = true) - String[] coinNames(); + List coinNames(); /** @@ -92,6 +128,12 @@ public interface BTS { @External(readonly = true) List> balanceOfBatch(Address _owner, String[] _coinNames); + @External(readonly = true) + BigInteger getAccumulatedFees(String coinName); + + @External + void tokenFallback(Address _from, BigInteger _value, byte[] _data); + /** * Reclaim the coin's refundable balance by an owner. * @@ -143,6 +185,9 @@ public interface BTS { @External void transferBatch(String[] _coinNames, BigInteger[] _values, String _to); + @External(readonly = true) + TransferTransaction getTransaction(BigInteger _sn); + /** * Sets a new transfer fee ratio. * @@ -162,6 +207,12 @@ public interface BTS { * */ @External(readonly = true) - BigInteger feeRatio(); + Map feeRatio(String _name); + + @External + void addRestriction(); + + @External + void disableRestrictions(); -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSException.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSException.java index 52378260e..35520b69b 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSException.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSException.java @@ -53,13 +53,21 @@ public static BTSException irc31Reverted(String message) { return new BTSException(Code.IRC31Reverted, message); } + public static BTSException restricted() { + return new BTSException(Code.Restricted); + } + public static BTSException restricted(String message) { + return new BTSException(Code.Restricted, message); + } + //BTPException.BSH => 40 ~ 54 public enum Code implements BTPException.Coded{ Unknown(0), Unauthorized(1), IRC31Failure(2), - IRC31Reverted(3); + IRC31Reverted(3), + Restricted(4); final int code; Code(int code){ this.code = code; } @@ -68,4 +76,4 @@ public enum Code implements BTPException.Coded{ public int code() { return code; } } -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSMessage.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSMessage.java index 47b1d4599..b6e6c9849 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSMessage.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSMessage.java @@ -26,7 +26,10 @@ public class BTSMessage { public static int REQUEST_COIN_TRANSFER = 0; public static int REQUEST_COIN_REGISTER = 1; public static int REPONSE_HANDLE_SERVICE = 2; - public static int UNKNOWN_TYPE = 3; + public static int ADD_TO_BLACKLIST= 3; + public static int REMOVE_FROM_BLACKLIST= 4; + public static int CHANGE_TOKEN_LIMIT = 5; + public static int UNKNOWN_TYPE = 6; private int serviceType; private byte[] data; @@ -86,4 +89,4 @@ public byte[] toBytes() { BTSMessage.writeObject(writer, this); return writer.toByteArray(); } -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java new file mode 100644 index 000000000..4bf84e6f9 --- /dev/null +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java @@ -0,0 +1,108 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bts; + +import java.util.List; +import score.ArrayDB; +import score.BranchDB; +import score.Context; +import score.DictDB; +import scorex.util.ArrayList; + +public class BlacklistDB { + + BranchDB> blacklistIndex = Context.newBranchDB("isBlacklistedDB", Integer.class); + BranchDB> blacklistedUsers = Context.newBranchDB("blackListedUsers", String.class); + + public BlacklistDB() {} + + public int length(String net) { + var a = blacklistedUsers.at(net); + if (a != null) { + return blacklistedUsers.at(net).size(); + } + return 0; + } + + public String at(String net, int index) { + var a = blacklistedUsers.at(net); + if (a != null) { + return a.get(index); + } + return null; + } + + private String lowercase(String user) { + return user.trim().toLowerCase(); + } + + public Integer indexOf(String net, String user) { + var a = blacklistIndex.at(net); + if (a != null) { + Integer result = blacklistIndex.at(net).get(user); + if (result != null) { + return result - 1; + } + return null; + } + return null; + } + + public boolean contains(String net, String user) { + var a = blacklistIndex.at(net); + if (a != null) { + return a.get(lowercase(user)) != null; + } + return false; + } + + public void addToBlacklist(String net, String user) { + user = lowercase(user); + if (!contains(net, user)) { + blacklistedUsers.at(net).add(user); + int size = length(net); + blacklistIndex.at(net).set(user, size); + } + } + + public String removeFromBlacklist(String net, String user) { + user = lowercase(user); + Integer valueIdx = indexOf(net, user); + var netUsers = blacklistedUsers.at(net); + var netIndex = blacklistIndex.at(net); + if (valueIdx != null) { + int lastIdx = length(net) - 1; + String lastVal = netUsers.pop(); + netIndex.set(user, null); + if (lastIdx != valueIdx) { + netUsers.set(valueIdx, lastVal); + netIndex.set(lastVal, valueIdx +1); + return lastVal; + } + } + return null; + } + + public List range(String net, int start, int end) { + List result = new ArrayList<>(); + int _end = Math.min(end, length(net)); + for (int i = start; i < _end; i++) { + result.add(at(net, i)); + } + return result; + } +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSProperties.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistResponse.java similarity index 51% rename from javascore/bts/src/main/java/foundation/icon/btp/bts/BTSProperties.java rename to javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistResponse.java index d143cc443..3dfae168b 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTSProperties.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistResponse.java @@ -16,79 +16,76 @@ package foundation.icon.btp.bts; +import java.math.BigInteger; import score.ByteArrayObjectWriter; import score.Context; import score.ObjectReader; import score.ObjectWriter; -import java.math.BigInteger; - -public class BTSProperties { - public static final BTSProperties DEFAULT; +public class BlacklistResponse { + public static BigInteger RC_OK = BigInteger.ZERO; + public static BigInteger RC_ERR = BigInteger.ONE; + public static String OK_MSG = "Transfer Success"; + public static String ERR_MSG_UNKNOWN_TYPE = "UNKNOWN_TYPE"; - static { - DEFAULT = new BTSProperties(); - DEFAULT.setSn(BigInteger.ZERO); - DEFAULT.setFeeRatio(BigInteger.valueOf(10)); - } - - private BigInteger sn; - private BigInteger feeRatio; + private BigInteger code; + private String message; - public BigInteger getSn() { - return sn; + public BigInteger getCode() { + return code; } - public void setSn(BigInteger sn) { - this.sn = sn; + public void setCode(BigInteger code) { + this.code = code; } - public BigInteger getFeeRatio() { - return feeRatio; + public String getMessage() { + return message; } - public void setFeeRatio(BigInteger feeRatio) { - this.feeRatio = feeRatio; + public void setMessage(String message) { + this.message = message; } @Override public String toString() { - final StringBuilder sb = new StringBuilder("BTSProperties{"); - sb.append("sn=").append(sn); - sb.append(", feeRatio=").append(feeRatio); + final StringBuilder sb = new StringBuilder("BlacklistResponse{"); + sb.append("code=").append(code); + sb.append(", message='").append(message).append('\''); sb.append('}'); return sb.toString(); } - public static void writeObject(ObjectWriter writer, BTSProperties obj) { + + public static void writeObject(ObjectWriter writer, BlacklistResponse obj) { obj.writeObject(writer); } - public static BTSProperties readObject(ObjectReader reader) { - BTSProperties obj = new BTSProperties(); + public static BlacklistResponse readObject(ObjectReader reader) { + BlacklistResponse obj = new BlacklistResponse(); reader.beginList(); - obj.setSn(reader.readNullable(BigInteger.class)); - obj.setFeeRatio(reader.readNullable(BigInteger.class)); + obj.setCode(reader.readBigInteger()); + obj.setMessage(reader.readNullable(String.class)); reader.end(); return obj; } public void writeObject(ObjectWriter writer) { writer.beginList(2); - writer.writeNullable(this.getSn()); - writer.writeNullable(this.getFeeRatio()); + writer.write(this.getCode()); + writer.writeNullable(this.getMessage()); writer.end(); } - public static BTSProperties fromBytes(byte[] bytes) { + public static BlacklistResponse fromBytes(byte[] bytes) { ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); - return BTSProperties.readObject(reader); + return BlacklistResponse.readObject(reader); } public byte[] toBytes() { ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); - BTSProperties.writeObject(writer, this); + BlacklistResponse.writeObject(writer, this); return writer.toByteArray(); } -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistTransaction.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistTransaction.java new file mode 100644 index 000000000..0ca4c8074 --- /dev/null +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistTransaction.java @@ -0,0 +1,100 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bts; + +import java.util.List; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +public class BlacklistTransaction { + private String[] address; + private String net; + + public String[] getAddress() { + return address; + } + + public String getNet() { + return net; + } + + public void setAddress(String[] address) { + this.address = address; + } + + public void setNet(String net) { + this.net = net; + } + + public BlacklistTransaction() {} + + public BlacklistTransaction(String[] address, String net) { + this.address = address; + this.net = net; + } + + public static BlacklistTransaction readObject(ObjectReader reader) { + BlacklistTransaction obj = new BlacklistTransaction(); + reader.beginList(); + if (reader.beginNullableList()) { + String[] addreses = null; + List addressList = new ArrayList<>(); + while (reader.hasNext()) { + addressList.add(reader.readNullable(String.class)); + } + addreses = new String[addressList.size()]; + for (int i = 0; i < addreses.length; i++) { + addreses[i] = addressList.get(i); + } + obj.setAddress(addreses); + reader.end(); + } + obj.setNet(reader.readNullable(String.class)); + reader.end(); + return obj; + } + + public static void writeObject(ObjectWriter writer, BlacklistTransaction obj) { + obj.writeObject(writer); + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + + String[] addresses = this.getAddress(); + if (addresses != null) { + writer.beginNullableList(addresses.length); + for(String s : addresses) { + writer.writeNullable(s); + } + writer.end(); + } else { + writer.writeNull(); + } + writer.writeNullable(this.getNet()); + writer.end(); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BlacklistTransaction.writeObject(writer, this); + return writer.toByteArray(); + } +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/Coin.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/Coin.java index a936ea30d..6e162be69 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/Coin.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/Coin.java @@ -15,7 +15,8 @@ public class Coin { private int coinType; private Address address; - public Coin(Address address, String name, String symbol, int decimals, BigInteger feeNumerator, BigInteger fixedFee, int coinType) { + public Coin(Address address, String name, String symbol, int decimals, BigInteger feeNumerator, + BigInteger fixedFee, int coinType) { this.address = address; this.name = name; this.symbol = symbol; @@ -40,7 +41,8 @@ public static void writeObject(ObjectWriter w, Coin v) { public static Coin readObject(ObjectReader r) { r.beginList(); - Coin result = new Coin(r.readAddress(), r.readString(), r.readString(), r.readInt(), r.readBigInteger(), r.readBigInteger(), r.readInt()); + Coin result = new Coin(r.readAddress(), r.readString(), r.readString(), + r.readInt(), r.readBigInteger(), r.readBigInteger(), r.readInt()); r.end(); return result; } @@ -100,4 +102,4 @@ public int getCoinType() { public void setCoinType(int coinType) { this.coinType = coinType; } -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitResponse.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitResponse.java new file mode 100644 index 000000000..b7b0a3fde --- /dev/null +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitResponse.java @@ -0,0 +1,92 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bts; + +import java.math.BigInteger; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class TokenLimitResponse { + public static BigInteger RC_OK = BigInteger.ZERO; + public static BigInteger RC_ERR = BigInteger.ONE; + public static String OK_MSG = "Transfer Success"; + public static String ERR_MSG_UNKNOWN_TYPE = "UNKNOWN_TYPE"; + + private BigInteger code; + private String message; + + public BigInteger getCode() { + return code; + } + + public void setCode(BigInteger code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TokenLimitResponse{"); + sb.append("code=").append(code); + sb.append(", message='").append(message).append('\''); + sb.append('}'); + return sb.toString(); + } + + + public static void writeObject(ObjectWriter writer, TokenLimitResponse obj) { + obj.writeObject(writer); + } + + public static TokenLimitResponse readObject(ObjectReader reader) { + TokenLimitResponse obj = new TokenLimitResponse(); + reader.beginList(); + obj.setCode(reader.readBigInteger()); + obj.setMessage(reader.readNullable(String.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.write(this.getCode()); + writer.writeNullable(this.getMessage()); + writer.end(); + } + + public static TokenLimitResponse fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return TokenLimitResponse.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + TokenLimitResponse.writeObject(writer, this); + return writer.toByteArray(); + } + + +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitTransaction.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitTransaction.java new file mode 100644 index 000000000..bac4a5fe3 --- /dev/null +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/TokenLimitTransaction.java @@ -0,0 +1,142 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bts; + +import java.math.BigInteger; +import java.util.List; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +public class TokenLimitTransaction { + private String[] coinName; + private BigInteger[] tokenLimit; + private String net; + + public TokenLimitTransaction(){} + + public TokenLimitTransaction(String[] coinName, BigInteger[] tokenLimit, String net) { + this.coinName = coinName; + this.tokenLimit = tokenLimit; + this.net = net; + } + public TokenLimitTransaction(String[] coinName, BigInteger[] tokenLimit) { + this.coinName = coinName; + this.tokenLimit = tokenLimit; + this.net = "ALL"; + } + + public String[] getCoinName() { + return coinName; + } + + public BigInteger[] getTokenLimit() { + return tokenLimit; + } + + public String getNet() { + return net; + } + + public void setCoinName(String[] coinName) { + this.coinName = coinName; + } + + public void setTokenLimit(BigInteger[] tokenLimit) { + this.tokenLimit = tokenLimit; + } + + public void setNet(String net) { + this.net = net; + } + + public static TokenLimitTransaction readObject(ObjectReader reader) { + TokenLimitTransaction obj = new TokenLimitTransaction(); + reader.beginList(); + if (reader.beginNullableList()) { + String[] coinNames = null; + List coinNamesList = new ArrayList<>(); + while (reader.hasNext()) { + coinNamesList.add(reader.readNullable(String.class)); + } + coinNames = new String[coinNamesList.size()]; + for (int i = 0; i < coinNames.length; i++) { + coinNames[i] = coinNamesList.get(i); + } + obj.setCoinName(coinNames); + reader.end(); + } + + if (reader.beginNullableList()) { + BigInteger[] tokenLimits = null; + List tokenLimitList = new ArrayList<>(); + while (reader.hasNext()) { + tokenLimitList.add(reader.readNullable(BigInteger.class)); + } + tokenLimits = new BigInteger[tokenLimitList.size()]; + for (int i = 0; i < tokenLimits.length; i++) { + tokenLimits[i] = tokenLimitList.get(i); + } + obj.setTokenLimit(tokenLimits); + reader.end(); + } + + obj.setNet(reader.readNullable(String.class)); + reader.end(); + return obj; + } + + public static void writeObject(ObjectWriter writer, TokenLimitTransaction obj) { + obj.writeObject(writer); + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + String[] coinNames = this.getCoinName(); + if (coinNames != null) { + writer.beginNullableList(coinNames.length); + for(String s : coinNames) { + writer.writeNullable(s); + } + writer.end(); + } else { + writer.writeNull(); + } + + BigInteger[] tokenLimits = this.getTokenLimit(); + if (tokenLimits != null) { + writer.beginNullableList(tokenLimits.length); + for(BigInteger s : tokenLimits) { + writer.writeNullable(s); + } + writer.end(); + } else { + writer.writeNull(); + } + writer.writeNullable(this.getNet()); + + writer.end(); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + TokenLimitTransaction.writeObject(writer, this); + return writer.toByteArray(); + } +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/irc2/IRC2ScoreInterface.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/irc2/IRC2ScoreInterface.java index 03d5f43a3..e22ea4175 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/irc2/IRC2ScoreInterface.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/irc2/IRC2ScoreInterface.java @@ -1,6 +1,6 @@ package foundation.icon.btp.bts.irc2; -import foundation.icon.btp.irc2.IRC2; +import com.iconloop.score.token.irc2.IRC2; import score.Address; import score.Context; import score.annotation.Optional; @@ -52,4 +52,4 @@ public void Transfer(Address _from, Address _to, BigInteger _value, byte[] _data throw new RuntimeException("not supported EventLog method"); } -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/utils/EnumerableSet.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/utils/EnumerableSet.java new file mode 100644 index 000000000..0c13df3c3 --- /dev/null +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/utils/EnumerableSet.java @@ -0,0 +1,76 @@ +package foundation.icon.btp.bts.utils; + + +import java.util.List; +import score.ArrayDB; +import score.Context; +import score.DictDB; +import scorex.util.ArrayList; + +public class EnumerableSet { + + private final ArrayDB entries; + private final DictDB indexes; + + public EnumerableSet(String varKey, Class valueClass) { + // array of valueClass + this.entries = Context.newArrayDB(varKey + "_es_entries", valueClass); + // value => array index + this.indexes = Context.newDictDB(varKey + "_es_indexes", Integer.class); + } + + public int length() { + return entries.size(); + } + + public V at(int index) { + return entries.get(index); + } + + public boolean contains(V value) { + return indexes.get(value) != null; + } + + public Integer indexOf(V value) { + // returns null if value doesn't exist + Integer result = indexes.get(value); + if (result != null) { + return result - 1; + } + return null; + } + + public void add(V value) { + if (!contains(value)) { + // add new value + entries.add(value); + indexes.set(value, entries.size()); + } + } + + public V remove(V value) { + Integer valueIndex = indexOf(value); + + if (valueIndex != null) { + int lastIndex = entries.size() - 1; + V lastValue = entries.pop(); + indexes.set(value, null); + if (lastIndex != valueIndex) { + entries.set(valueIndex, lastValue); + indexes.set(lastValue, valueIndex + 1); + return lastValue; + } + } + return null; + } + + public List range(int start, int end) { + List result = new ArrayList<>(); + int _end = Math.min(end, length()); + + for (int i = start; i < _end; i++) { + result.add(at(i)); + } + return result; + } +} diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java new file mode 100644 index 000000000..fcf3a2d3f --- /dev/null +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java @@ -0,0 +1,200 @@ +package foundation.icon.btp.bts; + + +import com.iconloop.score.test.Account; +import com.iconloop.score.test.Score; +import com.iconloop.score.test.ServiceManager; +import com.iconloop.score.test.TestBase; +import foundation.icon.btp.lib.BTPAddress; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.function.Executable; +import java.math.BigInteger; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.MockedStatic; +import org.mockito.MockedStatic.Verification; +import org.mockito.Mockito; +import score.Address; +import score.Context; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; + +public class AbstractBTPTokenService extends TestBase { + public static final ServiceManager sm = getServiceManager(); + public Account owner; + public Account nonOwner; + public Score score; + public Account irc2 = Account.newScoreAccount(100); + public Account wrappedIRC2 = Account.newScoreAccount(101); + public BTPTokenService scoreSpy; + public Account bmcMock = Account.newScoreAccount(1); + public String TEST_TOKEN = "Test Token"; + public String ICON = "ICON"; + public String PARA = "PARA"; + public String ETH_ADDR = "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"; + + BTPAddress btpAddress1 = new BTPAddress("network","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + BTPAddress btpAddress2 = new BTPAddress("network2","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + BTPAddress btpAddress3 = new BTPAddress("harmony","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + String[] links = new String[] {btpAddress1.toString(), btpAddress2.toString(), btpAddress3.toString()}; + + static MockedStatic contextMock; + + @BeforeAll + protected static void init() { + contextMock = Mockito.mockStatic(Context.class, Mockito.CALLS_REAL_METHODS); + } + + @BeforeEach + void setup() throws Exception { + + owner = sm.createAccount(100); + nonOwner = sm.createAccount(100); + BTPAddress btpAddress = new BTPAddress("icon", "0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + + byte[] bytes = "OptimizedJar".getBytes(); + + contextMock.when(() -> Context.call(eq(String.class),eq(bmcMock.getAddress()), + eq("getBtpAddress"), any())).thenReturn(btpAddress.toString()); + + score = sm.deploy(owner, BTPTokenService.class, + bmcMock.getAddress(), ICON, 18, bytes); + + BTPTokenService instance = (BTPTokenService) score.getInstance(); + scoreSpy = spy(instance); + score.setInstance(scoreSpy); + } + + public void blacklistMocks() { + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), any(), any(), any(), any()); + + contextMock.when(sendMessage).thenReturn(null); + getLinksMock(); + + } + + public void getLinksMock() { + Verification returnLinks = () -> Context.call(eq(String[].class), + eq(bmcMock.getAddress()), eq("getLinks"), any()); + contextMock.when(returnLinks).thenReturn(links); + } + + protected void icxLimitBTPMessage() { + getLinksMock(); + for (String link: links) { + contextMock.when(() -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), eq(link), any(), + eq(BigInteger.ONE), any())).thenReturn(null); + } + } + + protected void printSn() { + System.out.println("SN is: "+score.call("getSn")); + } + + protected void blackListUser(String net, String[] addr, BigInteger sn) { + blacklistMocks(); + score.invoke(owner, "addBlacklistAddress", net, addr); + byte[] _msg = blacklistSuccessfulResponse(); + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", sn, _msg); + } + + protected void removeBlackListedUser(String net, String[] addr, BigInteger sn) { + blacklistMocks(); + score.invoke(owner, "removeBlacklistAddress", net, addr); + byte[] _msg = blacklistRemoveSuccessfulResponse(); + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", sn, _msg); + } + + protected byte[] blacklistSuccessfulResponse() { + BlacklistResponse response = new BlacklistResponse(); + response.setCode(BlacklistResponse.RC_OK); + BTSMessage message = new BTSMessage(); + message.setData(response.toBytes()); + message.setServiceType(BTSMessage.ADD_TO_BLACKLIST); + return message.toBytes(); + } + + protected byte[] blacklistRemoveSuccessfulResponse() { + BlacklistResponse response = new BlacklistResponse(); + response.setCode(BlacklistResponse.RC_OK); + BTSMessage message = new BTSMessage(); + message.setData(response.toBytes()); + message.setServiceType(BTSMessage.REMOVE_FROM_BLACKLIST); + return message.toBytes(); + } + + protected void tokenLimitBTPMessage() { + getLinksMock(); + for (String link: links) { + contextMock.when(() -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), eq(link), any(), + any(), any())).thenReturn(null); + } + } + + public String generateBTPAddress(String net, String addr) { + BTPAddress btpAddress = new BTPAddress(net, addr); + return btpAddress.toString(); + } + + public Verification sendICX() { + Verification sendICX = () -> Context.getValue(); + return sendICX; + } + + public Verification sendIcxToUser() { + Verification sendICXToUser = () -> Context.transfer(any(), any()); + return sendICXToUser; + } + + public void sendBTPMessageMock() { + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), any(), any(), any(), any()); + contextMock.when(sendMessage).thenReturn(null); + } + + public void register(String name, Address addr) { +// tokenLimitBTPMessage(); + score.invoke(owner, "register", + name, name, 18, BigInteger.valueOf(10), BigInteger.ONE, addr); + } + + public void register() { +// tokenLimitBTPMessage(); + score.invoke(owner, "register", + TEST_TOKEN, "TTK", 18, BigInteger.ONE, BigInteger.ONE, irc2.getAddress()); + } + + public void registerWrapped() { + +// tokenLimitBTPMessage(); + Verification deployWrappedToken = () -> Context.deploy(any(), eq(PARA), + eq(PARA),eq(18)); + contextMock.when(deployWrappedToken).thenReturn(wrappedIRC2.getAddress()); + + score.invoke(owner, "register",PARA, PARA, 18, BigInteger.ZERO, BigInteger.ZERO, + Address.fromString("cx0000000000000000000000000000000000000000")); + } + + public void deposit(BigInteger value) { + contextMock.when(() -> Context.call(eq(BigInteger.class),eq(irc2.getAddress()), eq("balanceOf"), eq(owner.getAddress()))).thenReturn(BigInteger.valueOf(100)); + score.invoke(irc2,"tokenFallback", owner.getAddress(), value, "0".getBytes()); + } + + public void expectErrorMessage(Executable contractCall, String errorMessage) { + AssertionError e = Assertions.assertThrows(AssertionError.class, contractCall); + assertEquals(errorMessage, e.getMessage()); + } + + public void expectErrorMessageIn(Executable contractCall, String errorMessage) { + AssertionError e = Assertions.assertThrows(AssertionError.class, contractCall); + assert e.getMessage().contains(errorMessage); + } +} \ No newline at end of file diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTS.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTS.java index ed21546be..58d4853c0 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTS.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTS.java @@ -1,75 +1,75 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// /* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ -package foundation.icon.btp.bts; +// package foundation.icon.btp.bts; -import foundation.icon.btp.lib.BTPAddress; +// import foundation.icon.btp.lib.BTPAddress; -import java.util.Arrays; -import java.util.Comparator; +// import java.util.Arrays; +// import java.util.Comparator; -import static org.junit.jupiter.api.Assertions.assertEquals; +// import static org.junit.jupiter.api.Assertions.assertEquals; -public class AssertBTS { - static Comparator assetComparator = (o1, o2) -> { - int ret = o1.getCoinName().compareTo(o2.getCoinName()); - if (ret == 0) { - return o1.getAmount().compareTo(o2.getAmount()); - } - return ret; - }; - static Comparator assetTransferDetailComparator = (o1, o2) -> { - int ret = assetComparator.compare(o1, o2); - if (ret == 0) { - return o1.getFee().compareTo(o2.getFee()); - } - return ret; - }; +// public class AssertBTS { +// static Comparator assetComparator = (o1, o2) -> { +// int ret = o1.getCoinName().compareTo(o2.getCoinName()); +// if (ret == 0) { +// return o1.getAmount().compareTo(o2.getAmount()); +// } +// return ret; +// }; +// static Comparator assetTransferDetailComparator = (o1, o2) -> { +// int ret = assetComparator.compare(o1, o2); +// if (ret == 0) { +// return o1.getFee().compareTo(o2.getFee()); +// } +// return ret; +// }; - static void assertEqualsAsset(Asset o1, Asset o2) { - assertEquals(o1.getCoinName(), o2.getCoinName()); - assertEquals(o1.getAmount(), o2.getAmount()); - } +// static void assertEqualsAsset(Asset o1, Asset o2) { +// assertEquals(o1.getCoinName(), o2.getCoinName()); +// assertEquals(o1.getAmount(), o2.getAmount()); +// } - static void assertEqualsAssets(Asset[] o1, Asset[] o2) { - assertEquals(o1.length, o2.length); - Arrays.sort(o1, assetComparator); - Arrays.sort(o2, assetComparator); - for (int i = 0; i < o1.length; i++) { - assertEqualsAsset(o1[i], o2[i]); - } - } +// static void assertEqualsAssets(Asset[] o1, Asset[] o2) { +// assertEquals(o1.length, o2.length); +// Arrays.sort(o1, assetComparator); +// Arrays.sort(o2, assetComparator); +// for (int i = 0; i < o1.length; i++) { +// assertEqualsAsset(o1[i], o2[i]); +// } +// } - static void assertEqualsTransferRequest(TransferTransaction o1, TransferRequest o2) { - assertEquals(o1.getFrom(), o2.getFrom()); - assertEquals(BTPAddress.valueOf(o1.getTo()).account(), o2.getTo()); - assertEqualsAssets(o1.getAssets(), o2.getAssets()); - } +// static void assertEqualsTransferRequest(TransferTransaction o1, TransferRequest o2) { +// assertEquals(o1.getFrom(), o2.getFrom()); +// assertEquals(BTPAddress.valueOf(o1.getTo()).account(), o2.getTo()); +// assertEqualsAssets(o1.getAssets(), o2.getAssets()); +// } - static void assertEqualsAssetTransferDetail(AssetTransferDetail o1, AssetTransferDetail o2) { - assertEqualsAsset(o1, o2); - assertEquals(o1.getFee(), o2.getFee()); - } +// static void assertEqualsAssetTransferDetail(AssetTransferDetail o1, AssetTransferDetail o2) { +// assertEqualsAsset(o1, o2); +// assertEquals(o1.getFee(), o2.getFee()); +// } - static void assertEqualsAssetTransferDetails(AssetTransferDetail[] o1, AssetTransferDetail[] o2) { - assertEquals(o1.length, o2.length); - Arrays.sort(o1, assetTransferDetailComparator); - Arrays.sort(o2, assetTransferDetailComparator); - for (int i = 0; i < o1.length; i++) { - assertEqualsAssetTransferDetail(o1[i], o2[i]); - } - } -} +// static void assertEqualsAssetTransferDetails(AssetTransferDetail[] o1, AssetTransferDetail[] o2) { +// assertEquals(o1.length, o2.length); +// Arrays.sort(o1, assetTransferDetailComparator); +// Arrays.sort(o2, assetTransferDetailComparator); +// for (int i = 0; i < o1.length; i++) { +// assertEqualsAssetTransferDetail(o1[i], o2[i]); +// } +// } +// } diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTSException.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTSException.java index 950d20bd3..3f0a045c8 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTSException.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/AssertBTSException.java @@ -1,41 +1,41 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package foundation.icon.btp.bts; - -import foundation.icon.btp.test.AssertBTPException; -import org.junit.jupiter.api.function.Executable; - -@SuppressWarnings("ThrowableNotThrown") -public class AssertBTSException { - - public static void assertUnknown(Executable executable) { - AssertBTPException.assertBTPException(BTSException.unknown(""), executable); - } - - public static void assertUnauthorized(Executable executable) { - AssertBTPException.assertBTPException(BTSException.unauthorized(), executable); - } - - public static void assertIRC31Failure(Executable executable) { - AssertBTPException.assertBTPException(BTSException.irc31Failure(), executable); - } - - public static void assertIRC31Reverted(Executable executable) { - AssertBTPException.assertBTPException(BTSException.irc31Reverted(), executable); - } - -} +// /* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +// package foundation.icon.btp.bts; + +// import foundation.icon.btp.test.AssertBTPException; +// import org.junit.jupiter.api.function.Executable; + +// @SuppressWarnings("ThrowableNotThrown") +// public class AssertBTSException { + +// public static void assertUnknown(Executable executable) { +// AssertBTPException.assertBTPException(BTSException.unknown(""), executable); +// } + +// public static void assertUnauthorized(Executable executable) { +// AssertBTPException.assertBTPException(BTSException.unauthorized(), executable); +// } + +// public static void assertIRC31Failure(Executable executable) { +// AssertBTPException.assertBTPException(BTSException.irc31Failure(), executable); +// } + +// public static void assertIRC31Reverted(Executable executable) { +// AssertBTPException.assertBTPException(BTSException.irc31Reverted(), executable); +// } + +// } diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTPTokenServiceTest.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTPTokenServiceTest.java index 2f5aab909..dcadfcaf7 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTPTokenServiceTest.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTPTokenServiceTest.java @@ -1,205 +1,207 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package foundation.icon.btp.bts; - -import foundation.icon.btp.lib.BTPAddress; -import foundation.icon.btp.test.BTPIntegrationTest; -import foundation.icon.btp.test.MockBMCIntegrationTest; -import foundation.icon.btp.test.SendMessageEventLog; -import foundation.icon.jsonrpc.Address; -import foundation.icon.jsonrpc.model.TransactionResult; -import foundation.icon.score.client.RevertedException; -import org.junit.jupiter.api.function.Executable; -import score.UserRevertedException; - -import java.math.BigInteger; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class BTPTokenServiceTest implements BTSIntegrationTest { - - static Address btsAddress = btsClient._address(); - static Address owner = Address.of(btsClient._wallet()); - static BTPAddress link = BTPIntegrationTest.Faker.btpLink(); - static String linkNet = BTPIntegrationTest.Faker.btpNetwork(); - static Address testerAddress = Address.of(tester); - static BTPAddress to = new BTPAddress(BTPAddress.PROTOCOL_BTP, linkNet, testerAddress.toString()); - static String nativeCoinName = bts.coinNames()[0]; - static BigInteger nativeValue = BigInteger.valueOf(10); - static String coinName = "coin"; - static BigInteger coinValue = BigInteger.valueOf(10); - static BigInteger coinId; - static long code = 1; - static String msg = "err"; - - static String net = MockBMCIntegrationTest.mockBMC.getNet(); - static BTPAddress fa = new BTPAddress(BTPAddress.PROTOCOL_BTP, net, testerAddress.toString()); - static BTPAddress linkFa = new BTPAddress(BTPAddress.PROTOCOL_BTP, linkNet, testerAddress.toString()); - static BigInteger feeRatio = BigInteger.TEN; - - // static boolean isExistsCoin(String name) { - // return ScoreIntegrationTest.indexOf(bts.coinNames(), name) >= 0; - // } - - // static void register(String name) { - // bts.register(name); - // assertTrue(isExistsCoin(name)); - // } - - // static BigInteger transferBatch(TransferTransaction transaction) { - // BigInteger nativeCoinValue = nativeCoinValue(transaction.getAssets()); - // List assets = coinAssets(transaction.getAssets()); - // BigInteger[] coinIds = coinIds(assets); - // String[] coinNames = coinNames(assets); - // BigInteger[] coinValues = coinValues(assets); - // Address from = new Address(transaction.getFrom()); - // BigInteger[] snContainer = new BigInteger[1]; - // Consumer checker = transferStartEventLogChecker(transaction) - // .andThen(sendMessageEventLogChecker(transaction, snContainer)) - // .andThen(IRC31SupplierTest.transferFromBatchChecker( - // btsAddress, from, btsAddress, coinIds, coinValues)); - // Executable executable = () -> ((BTSScoreClient) bts).transferBatch( - // checker, - // nativeCoinValue, - // coinNames, coinValues, to.toString()); - // ScoreIntegrationTest.balanceCheck(btsAddress, nativeCoinValue, () -> - // IRC31SupplierTest.balanceBatchCheck(btsAddress, coinIds, coinValues, () -> - // balanceBatchCheck(from, transaction.getAssets(), executable, - // BalanceCheckType.lock))); - // return snContainer[0]; - // } - - - // static void handleTransferResponse(TransferTransaction transaction, BigInteger sn) { - // TransferResponse response = new TransferResponse(); - // response.setCode(TransferResponse.RC_OK); - // response.setMessage(TransferResponse.OK_MSG); - // BTSMessage btsMessage = new BTSMessage(); - // btsMessage.setServiceType(BTSMessage.REPONSE_HANDLE_SERVICE); - // btsMessage.setData(response.toBytes()); - - // List assets = coinAssets(transferRequest(transaction).getAssets()); - // BigInteger[] coinIds = coinIds(assets); - // BigInteger[] coinValues = coinValues(assets); - // Address from = new Address(transaction.getFrom()); - // Consumer checker = IRC31SupplierTest.burnBatchChecker( - // btsAddress, btsAddress, coinIds, coinValues) - // .andThen(transferEndEventLogChecker(from, sn, response)); - // balanceBatchCheck(from, transaction.getAssets(), () -> - // ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPMessage( - // checker, - // btsAddress, linkNet, BTPTokenService.SERVICE, sn, btsMessage.toBytes()), - // BalanceCheckType.unlock); - // } -/* - static void handleBTPError(TransferTransaction transaction, BigInteger sn, long code, String msg) { - TransferResponse response = new TransferResponse(); - response.setCode(TransferResponse.RC_ERR); - response.setMessage(("BTPError [code:" + code + ",msg:" + msg)); - BTSMessage btsMessage = new BTSMessage(); - btsMessage.setServiceType(BTSMessage.REPONSE_HANDLE_SERVICE); - btsMessage.setData(response.toBytes()); - - Address from = new Address(transaction.getFrom()); - balanceBatchCheck( - from, - transaction.getAssets(), - () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPError( - transferEndEventLogChecker(from, sn, response), - btsAddress, link.toString(), BTPTokenService.SERVICE, sn, code, msg), - BalanceCheckType.refund); - }*/ - - static Consumer transferStartEventLogChecker(TransferTransaction transaction) { - return BTSIntegrationTest.eventLogChecker(TransferStartEventLog::eventLogs, (el) -> { - System.out.println(el); - assertEquals(transaction.getFrom(), el.getFrom().toString()); - assertEquals(transaction.getTo(), el.getTo()); - AssertBTS.assertEqualsAssetTransferDetails(transaction.getAssets(), el.getAssets()); - }); - } - - static Consumer transferEndEventLogChecker(Address from, BigInteger sn, TransferResponse response) { - return BTSIntegrationTest.eventLogChecker(TransferEndEventLog::eventLogs, (el) -> { - assertEquals(from, el.getFrom()); - assertEquals(sn, el.getSn()); - assertEquals(response.getCode(), el.getCode()); - assertEquals(response.getMessage(), new String(el.getMsg())); - }); - } - - static Consumer sendMessageEventLogChecker(TransferTransaction transaction) { - return sendMessageEventLogChecker(transaction, null); - } - - static Consumer sendMessageEventLogChecker(TransferTransaction transaction, BigInteger[] snContainer) { - return MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { - assertEquals(BTPAddress.valueOf(transaction.getTo()).net(), el.getTo()); - assertEquals(BTPTokenService.SERVICE, el.getSvc()); - if (snContainer != null) { - snContainer[0] = el.getSn(); - } +///* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package foundation.icon.btp.bts; +// +//import foundation.icon.btp.lib.BTPAddress; +//import foundation.icon.btp.test.BTPIntegrationTest; +//import foundation.icon.btp.test.MockBMCIntegrationTest; +//import foundation.icon.btp.test.SendMessageEventLog; +//import foundation.icon.jsonrpc.Address; +//import foundation.icon.jsonrpc.model.TransactionResult; +//import foundation.icon.score.client.RevertedException; +//import foundation.icon.score.test.ScoreIntegrationTest; +//import org.junit.jupiter.api.BeforeAll; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.function.Executable; +//import score.UserRevertedException; +// +//import java.math.BigInteger; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.List; +//import java.util.function.Consumer; +//import java.util.stream.Collectors; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertTrue; +// +//class BTPTokenServiceTest implements BTSIntegrationTest { +// +// static Address btsAddress = btsClient._address(); +// static Address owner = Address.of(btsClient._wallet()); +// static BTPAddress link = BTPIntegrationTest.Faker.btpLink(); +// static String linkNet = BTPIntegrationTest.Faker.btpNetwork(); +// static Address testerAddress = Address.of(tester); +// static BTPAddress to = new BTPAddress(BTPAddress.PROTOCOL_BTP, linkNet, testerAddress.toString()); +// static String nativeCoinName = bts.coinNames()[0]; +// static BigInteger nativeValue = BigInteger.valueOf(10); +// static String coinName = "coin"; +// static BigInteger coinValue = BigInteger.valueOf(10); +// static BigInteger coinId; +// static long code = 1; +// static String msg = "err"; +// +// static String net = MockBMCIntegrationTest.mockBMC.getNet(); +// static BTPAddress fa = new BTPAddress(BTPAddress.PROTOCOL_BTP, net, testerAddress.toString()); +// static BTPAddress linkFa = new BTPAddress(BTPAddress.PROTOCOL_BTP, linkNet, testerAddress.toString()); +// static BigInteger feeRatio = BigInteger.TEN; +// +// // static boolean isExistsCoin(String name) { +// // return ScoreIntegrationTest.indexOf(bts.coinNames(), name) >= 0; +// // } +// +// // static void register(String name) { +// // bts.register(name); +// // assertTrue(isExistsCoin(name)); +// // } +// +// // static BigInteger transferBatch(TransferTransaction transaction) { +// // BigInteger nativeCoinValue = nativeCoinValue(transaction.getAssets()); +// // List assets = coinAssets(transaction.getAssets()); +// // BigInteger[] coinIds = coinIds(assets); +// // String[] coinNames = coinNames(assets); +// // BigInteger[] coinValues = coinValues(assets); +// // Address from = new Address(transaction.getFrom()); +// // BigInteger[] snContainer = new BigInteger[1]; +// // Consumer checker = transferStartEventLogChecker(transaction) +// // .andThen(sendMessageEventLogChecker(transaction, snContainer)) +// // .andThen(IRC31SupplierTest.transferFromBatchChecker( +// // btsAddress, from, btsAddress, coinIds, coinValues)); +// // Executable executable = () -> ((BTSScoreClient) bts).transferBatch( +// // checker, +// // nativeCoinValue, +// // coinNames, coinValues, to.toString()); +// // ScoreIntegrationTest.balanceCheck(btsAddress, nativeCoinValue, () -> +// // IRC31SupplierTest.balanceBatchCheck(btsAddress, coinIds, coinValues, () -> +// // balanceBatchCheck(from, transaction.getAssets(), executable, +// // BalanceCheckType.lock))); +// // return snContainer[0]; +// // } +// +// +// // static void handleTransferResponse(TransferTransaction transaction, BigInteger sn) { +// // TransferResponse response = new TransferResponse(); +// // response.setCode(TransferResponse.RC_OK); +// // response.setMessage(TransferResponse.OK_MSG); +// // BTSMessage btsMessage = new BTSMessage(); +// // btsMessage.setServiceType(BTSMessage.REPONSE_HANDLE_SERVICE); +// // btsMessage.setData(response.toBytes()); +// +// // List assets = coinAssets(transferRequest(transaction).getAssets()); +// // BigInteger[] coinIds = coinIds(assets); +// // BigInteger[] coinValues = coinValues(assets); +// // Address from = new Address(transaction.getFrom()); +// // Consumer checker = IRC31SupplierTest.burnBatchChecker( +// // btsAddress, btsAddress, coinIds, coinValues) +// // .andThen(transferEndEventLogChecker(from, sn, response)); +// // balanceBatchCheck(from, transaction.getAssets(), () -> +// // ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPMessage( +// // checker, +// // btsAddress, linkNet, BTPTokenService.SERVICE, sn, btsMessage.toBytes()), +// // BalanceCheckType.unlock); +// // } +///* +// static void handleBTPError(TransferTransaction transaction, BigInteger sn, long code, String msg) { +// TransferResponse response = new TransferResponse(); +// response.setCode(TransferResponse.RC_ERR); +// response.setMessage(("BTPError [code:" + code + ",msg:" + msg)); +// BTSMessage btsMessage = new BTSMessage(); +// btsMessage.setServiceType(BTSMessage.REPONSE_HANDLE_SERVICE); +// btsMessage.setData(response.toBytes()); +// +// Address from = new Address(transaction.getFrom()); +// balanceBatchCheck( +// from, +// transaction.getAssets(), +// () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPError( +// transferEndEventLogChecker(from, sn, response), +// btsAddress, link.toString(), BTPTokenService.SERVICE, sn, code, msg), +// BalanceCheckType.refund); +// }*/ +// +// static Consumer transferStartEventLogChecker(TransferTransaction transaction) { +// return BTSIntegrationTest.eventLogChecker(TransferStartEventLog::eventLogs, (el) -> { +// System.out.println(el); +// assertEquals(transaction.getFrom(), el.getFrom().toString()); +// assertEquals(transaction.getTo(), el.getTo()); +// AssertBTS.assertEqualsAssetTransferDetails(transaction.getAssets(), el.getAssets()); +// }); +// } +// +// static Consumer transferEndEventLogChecker(Address from, BigInteger sn, TransferResponse response) { +// return BTSIntegrationTest.eventLogChecker(TransferEndEventLog::eventLogs, (el) -> { +// assertEquals(from, el.getFrom()); // assertEquals(sn, el.getSn()); - BTSMessage btsMessage = BTSMessage.fromBytes(el.getMsg()); - assertEquals(BTSMessage.REQUEST_COIN_TRANSFER, btsMessage.getServiceType()); - AssertBTS.assertEqualsTransferRequest(transaction, TransferRequest.fromBytes(btsMessage.getData())); - }); - } - - static void lockedBalanceCheck(Address address, Asset asset, Executable executable) { +// assertEquals(response.getCode(), el.getCode()); +// assertEquals(response.getMessage(), new String(el.getMsg())); +// }); +// } +// +// static Consumer sendMessageEventLogChecker(TransferTransaction transaction) { +// return sendMessageEventLogChecker(transaction, null); +// } +// +// static Consumer sendMessageEventLogChecker(TransferTransaction transaction, BigInteger[] snContainer) { +// return MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { +// assertEquals(BTPAddress.valueOf(transaction.getTo()).net(), el.getTo()); +// assertEquals(BTPTokenService.SERVICE, el.getSvc()); +// if (snContainer != null) { +// snContainer[0] = el.getSn(); +// } +//// assertEquals(sn, el.getSn()); +// BTSMessage btsMessage = BTSMessage.fromBytes(el.getMsg()); +// assertEquals(BTSMessage.REQUEST_COIN_TRANSFER, btsMessage.getServiceType()); +// AssertBTS.assertEqualsTransferRequest(transaction, TransferRequest.fromBytes(btsMessage.getData())); +// }); +// } +// +// static void lockedBalanceCheck(Address address, Asset asset, Executable executable) { // Balance balance = bts.balanceOf(address, asset.getCoinName()); - Map balance = bts.balanceOf(address, asset.getCoinName()); - try { - executable.execute(); - } catch (UserRevertedException | RevertedException e) { - throw e; - } catch (Throwable e) { - throw new RuntimeException(e); - } +// try { +// executable.execute(); +// } catch (UserRevertedException | RevertedException e) { +// throw e; +// } catch (Throwable e) { +// throw new RuntimeException(e); +// } // assertEquals(balance.getLocked().add(asset.getAmount()), // bts.balanceOf(address, asset.getCoinName()).getLocked()); - } - - enum BalanceCheckType { - lock, unlock, refund - } - - static void balanceBatchCheck(Address address, AssetTransferDetail[] assetDetails, Executable executable, BalanceCheckType type) { - List list = Arrays.asList(assetDetails); - String[] coinNames = list.stream() - .map(AssetTransferDetail::getCoinName).toArray(String[]::new); - BigInteger[] values = list.stream() - .map((a) -> a.getAmount().add(a.getFee())).toArray(BigInteger[]::new); - +// } +// +// enum BalanceCheckType { +// lock, unlock, refund +// } +// +// static void balanceBatchCheck(Address address, AssetTransferDetail[] assetDetails, Executable executable, BalanceCheckType type) { +// List list = Arrays.asList(assetDetails); +// String[] coinNames = list.stream() +// .map(AssetTransferDetail::getCoinName).toArray(String[]::new); +// BigInteger[] values = list.stream() +// .map((a) -> a.getAmount().add(a.getFee())).toArray(BigInteger[]::new); +// // Balance[] balances = bts.balanceOfBatch(address, coinNames); - List> balances = bts.balanceOfBatch(address, coinNames); - assertEquals(coinNames.length, balances.size()); - try { - executable.execute(); - } catch (UserRevertedException | RevertedException e) { - throw e; - } catch (Throwable e) { - throw new RuntimeException(e); - } +// assertEquals(coinNames.length, balances.length); +// try { +// executable.execute(); +// } catch (UserRevertedException | RevertedException e) { +// throw e; +// } catch (Throwable e) { +// throw new RuntimeException(e); +// } // Balance[] actual = bts.balanceOfBatch(address, coinNames); // for (int i = 0; i < coinNames.length; i++) { // BigInteger locked = balances[i].getLocked(); @@ -213,89 +215,89 @@ static void balanceBatchCheck(Address address, AssetTransferDetail[] assetDetail // } // assertEquals(locked, actual[i].getLocked()); // } - } - - static TransferRequest transferRequest(TransferTransaction transaction) { - TransferRequest request = new TransferRequest(); - request.setFrom(transaction.getFrom()); - request.setTo(BTPAddress.valueOf(transaction.getTo()).account()); - - AssetTransferDetail[] assetDetails = transaction.getAssets(); - Asset[] assets = new Asset[assetDetails.length]; - for (int i = 0; i < assetDetails.length; i++) { - assets[i] = new Asset(assetDetails[i]); - } - request.setAssets(assets); - return request; - } - - static TransferTransaction transferTransaction(Address from, BTPAddress to, BigInteger feeRatio, Asset... assets) { - TransferTransaction transaction = new TransferTransaction(); - transaction.setFrom(from.toString()); - transaction.setTo(to.toString()); - AssetTransferDetail[] assetDetails = new AssetTransferDetail[assets.length]; - for (int i = 0; i < assets.length; i++) { - Asset asset = assets[i]; - AssetTransferDetail assetDetail = new AssetTransferDetail(); - assetDetail.setCoinName(asset.getCoinName()); - BigInteger fee = asset.getAmount().multiply(feeRatio).divide(BTPTokenService.FEE_DENOMINATOR); - if (feeRatio.compareTo(BigInteger.ZERO) > 0 && fee.compareTo(BigInteger.ZERO) == 0) { - fee = BigInteger.ONE; - } - assetDetail.setFee(fee); - assetDetail.setAmount(asset.getAmount().subtract(assetDetail.getFee())); - assetDetails[i] = assetDetail; - } - transaction.setAssets(assetDetails); - return transaction; - } - - static BigInteger nativeCoinValue(AssetTransferDetail[] assetDetails) { - return Arrays.stream(assetDetails) - .filter((a) -> a.getCoinName().equals(nativeCoinName)) - .map((a) -> a.getAmount().add(a.getFee())) - .findAny().orElse(BigInteger.ZERO); - } - - static BigInteger nativeCoinValue(Asset[] assets) { - return Arrays.stream(assets) - .filter((a) -> a.getCoinName().equals(nativeCoinName)) - .map(Asset::getAmount) - .findAny().orElse(BigInteger.ZERO); - } - - static List coinAssets(AssetTransferDetail[] assetDetails) { - return Arrays.stream(assetDetails) - .filter((a) -> !a.getCoinName().equals(nativeCoinName)) - .map((a) -> { - Asset asset = new Asset(a); - asset.setAmount(a.getAmount().add(a.getFee())); - return asset; - }).collect(Collectors.toList()); - } - - static List coinAssets(Asset[] assets) { - return Arrays.stream(assets) - .filter((a) -> !a.getCoinName().equals(nativeCoinName)) - .collect(Collectors.toList()); - } - - // static BigInteger[] coinIds(List assets) { - // return assets.stream() - // .map((a) -> bts.coinId(a.getCoinName())).toArray(BigInteger[]::new); - // } - - static String[] coinNames(List assets) { - return assets.stream() - .map(Asset::getCoinName).toArray(String[]::new); - } - - static BigInteger[] coinValues(List assets) { - return assets.stream() - .map(Asset::getAmount).toArray(BigInteger[]::new); - } - - static Asset[] feeAssets() { +// } +// +// static TransferRequest transferRequest(TransferTransaction transaction) { +// TransferRequest request = new TransferRequest(); +// request.setFrom(transaction.getFrom()); +// request.setTo(BTPAddress.valueOf(transaction.getTo()).account()); +// +// AssetTransferDetail[] assetDetails = transaction.getAssets(); +// Asset[] assets = new Asset[assetDetails.length]; +// for (int i = 0; i < assetDetails.length; i++) { +// assets[i] = new Asset(assetDetails[i]); +// } +// request.setAssets(assets); +// return request; +// } +// +// static TransferTransaction transferTransaction(Address from, BTPAddress to, BigInteger feeRatio, Asset... assets) { +// TransferTransaction transaction = new TransferTransaction(); +// transaction.setFrom(from.toString()); +// transaction.setTo(to.toString()); +// AssetTransferDetail[] assetDetails = new AssetTransferDetail[assets.length]; +// for (int i = 0; i < assets.length; i++) { +// Asset asset = assets[i]; +// AssetTransferDetail assetDetail = new AssetTransferDetail(); +// assetDetail.setCoinName(asset.getCoinName()); +// BigInteger fee = asset.getAmount().multiply(feeRatio).divide(BTPTokenService.FEE_DENOMINATOR); +// if (feeRatio.compareTo(BigInteger.ZERO) > 0 && fee.compareTo(BigInteger.ZERO) == 0) { +// fee = BigInteger.ONE; +// } +// assetDetail.setFee(fee); +// assetDetail.setAmount(asset.getAmount().subtract(assetDetail.getFee())); +// assetDetails[i] = assetDetail; +// } +// transaction.setAssets(assetDetails); +// return transaction; +// } +// +// static BigInteger nativeCoinValue(AssetTransferDetail[] assetDetails) { +// return Arrays.stream(assetDetails) +// .filter((a) -> a.getCoinName().equals(nativeCoinName)) +// .map((a) -> a.getAmount().add(a.getFee())) +// .findAny().orElse(BigInteger.ZERO); +// } +// +// static BigInteger nativeCoinValue(Asset[] assets) { +// return Arrays.stream(assets) +// .filter((a) -> a.getCoinName().equals(nativeCoinName)) +// .map(Asset::getAmount) +// .findAny().orElse(BigInteger.ZERO); +// } +// +// static List coinAssets(AssetTransferDetail[] assetDetails) { +// return Arrays.stream(assetDetails) +// .filter((a) -> !a.getCoinName().equals(nativeCoinName)) +// .map((a) -> { +// Asset asset = new Asset(a); +// asset.setAmount(a.getAmount().add(a.getFee())); +// return asset; +// }).collect(Collectors.toList()); +// } +// +// static List coinAssets(Asset[] assets) { +// return Arrays.stream(assets) +// .filter((a) -> !a.getCoinName().equals(nativeCoinName)) +// .collect(Collectors.toList()); +// } +// +// // static BigInteger[] coinIds(List assets) { +// // return assets.stream() +// // .map((a) -> bts.coinId(a.getCoinName())).toArray(BigInteger[]::new); +// // } +// +// static String[] coinNames(List assets) { +// return assets.stream() +// .map(Asset::getCoinName).toArray(String[]::new); +// } +// +// static BigInteger[] coinValues(List assets) { +// return assets.stream() +// .map(Asset::getAmount).toArray(BigInteger[]::new); +// } +// +// static Asset[] feeAssets() { // String[] coinNames = bts.coinNames(); // Balance[] balances = bts.balanceOfBatch(btsAddress, coinNames); // List feeAssets = new ArrayList<>(); @@ -307,256 +309,255 @@ static Asset[] feeAssets() { // } // } // return feeAssets.toArray(Asset[]::new); - throw new UnsupportedOperationException(); - } - - // @BeforeAll - // static void beforeAll() { - // if (!isExistsCoin(coinName)) { - // register(coinName); - // coinId = bts.coinId(coinName); - // } - // if (!bts.feeRatio().equals(feeRatio)) { - // bts.setFeeRatio(feeRatio); - // } - // } - - // @Test - // void registerShouldSuccess() { - // register(ScoreIntegrationTest.Faker.faker.name().name()); - // } - - // @Test - // void transferNativeCoinShouldMakeEventLogAndLockBalance() { - // Asset asset = new Asset(nativeCoinName, nativeValue); - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, asset); - // Consumer checker = transferStartEventLogChecker(transaction) - // .andThen(sendMessageEventLogChecker(transaction)); - // ScoreIntegrationTest.balanceCheck(btsAddress, asset.getAmount(), () -> - // lockedBalanceCheck(new Address(transaction.getFrom()), asset, () -> - // ((BTSScoreClient) bts).transferNativeCoin( - // checker, - // asset.getAmount(), - // transaction.getTo()))); - // } - - // @Test - // void transferShouldMakeEventLogAndLockBalance() { - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // Asset asset = new Asset(coinName, coinValue); - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, asset); - // Address from = new Address(transaction.getFrom()); - // Consumer checker = transferStartEventLogChecker(transaction) - // .andThen(sendMessageEventLogChecker(transaction)) - // .andThen(IRC31SupplierTest.transferFromChecker( - // btsAddress, from, btsAddress, coinId, asset.getAmount())); - // IRC31SupplierTest.balanceCheck(btsAddress, coinId, coinValue, () -> - // lockedBalanceCheck(from, asset, () -> - // ((BTSScoreClient) bts).transfer( - // checker, - // asset.getCoinName(), asset.getAmount(), transaction.getTo()) - // ) - // ); - // } - - // @Test - // void transferBatchShouldShouldMakeEventLogAndLockBalance() { - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - - // transferBatch(transaction); - // } - - // @Test - // void handleTransferRequestShouldIRC31MintBatchAndResponse() { - // //mint to tester - // IRC31SupplierTest.mint(testerAddress, coinId, coinValue); - - // //transfer owner to tester - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - // TransferRequest request = transferRequest(transaction); - // List assets = coinAssets(request.getAssets()); - - // BigInteger[] coinIds = coinIds(assets); - // BigInteger[] coinValues = coinValues(assets); - - // BTSMessage btsMessage = new BTSMessage(); - // btsMessage.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); - // btsMessage.setData(request.toBytes()); - - // Address to = new Address(BTPAddress.valueOf(transaction.getTo()).account()); - - // BigInteger sn = BigInteger.ONE; - // Consumer checker = IRC31SupplierTest.mintBatchChecker( - // btsAddress, to, coinIds, coinValues).andThen( - // MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { - // assertEquals(linkNet, el.getTo()); - // assertEquals(BTPTokenService.SERVICE, el.getSvc()); - // assertEquals(sn, el.getSn()); - // BTSMessage btsMsg = BTSMessage.fromBytes(el.getMsg()); - // assertEquals(BTSMessage.REPONSE_HANDLE_SERVICE, btsMsg.getServiceType()); - // TransferResponse response = TransferResponse.fromBytes(btsMsg.getData()); - // assertEquals(TransferResponse.RC_OK, response.getCode()); - // assertEquals(TransferResponse.OK_MSG, response.getMessage()); - // })); - // Executable executable = () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) - // .intercallHandleBTPMessage( - // checker, - // btsAddress, linkNet, BTPTokenService.SERVICE, sn, btsMessage.toBytes()); - // ScoreIntegrationTest.balanceCheck(to, nativeCoinValue(request.getAssets()), () -> - // IRC31SupplierTest.balanceBatchCheck(to, coinIds, coinValues, executable)); - // } - - // @Test - // void handleTransferResponseShouldIRC31BurnBatchAndMakeEventLog() { - // //mint - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // //transferBatch - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - // BigInteger sn = transferBatch(transaction); - - // // - // handleTransferResponse(transaction, sn); - // } - - // @Test - // void handleUnknownResponseShouldMakeEventLog() { - // TransferResponse response = new TransferResponse(); - // response.setCode(TransferResponse.RC_ERR); - // response.setMessage(TransferResponse.ERR_MSG_UNKNOWN_TYPE); - // BTSMessage btsMessage = new BTSMessage(); - // btsMessage.setServiceType(BTSMessage.UNKNOWN_TYPE); - // btsMessage.setData(response.toBytes()); - - // BigInteger sn = BigInteger.ONE; - // ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPMessage( - // BTSIntegrationTest.eventLogChecker(UnknownResponseEventLog::eventLogs, (el) -> { - // assertEquals(linkNet, el.getFrom()); - // assertEquals(sn, el.getSn()); - // }), - // btsAddress, linkNet, BTPTokenService.SERVICE, sn, btsMessage.toBytes()); - // } - - // @Test - // void handleBTPMessageShouldRevert() { - // AssertBTSException.assertUnknown(() -> - // btsBSH.handleBTPMessage(linkNet, BTPTokenService.SERVICE, BigInteger.ONE, new byte[]{})); - // } - - // @Test - // void handleBTPErrorShouldMakeEventLogAndAddRefundableBalance() { - // //mint - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // //transferBatch - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - // BigInteger sn = transferBatch(transaction); - - // //handleBTPError - // handleBTPError(transaction, sn, code, msg); - // } - - // @Test - // void reclaim() { - // //mint - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // //transferBatch - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - // BigInteger sn = transferBatch(transaction); - - // //handleBTPError for make refundable balance - // handleBTPError(transaction, sn, code, msg); - - // //reclaim - // ScoreIntegrationTest.balanceCheck(owner, nativeValue, () -> - // bts.reclaim(nativeCoinName, nativeValue)); - // IRC31SupplierTest.balanceCheck(owner, coinId, coinValue, () -> - // bts.reclaim(coinName, coinValue)); - // } - - // @Test - // void handleBTPErrorShouldRevert() { - // AssertBTSException.assertUnknown(() -> - // btsBSH.handleBTPError(linkNet, - // BTPTokenService.SERVICE, BigInteger.ONE, 0, "")); - // } - - // @Test - // void handleFeeGatheringShouldIRC31Transfer() {//how to clear feeBalances as zero? - // //mint - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // //transferBatch - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - // BigInteger sn = transferBatch(transaction); - - // //handleTransferResponse - // handleTransferResponse(transaction, sn); - - // // - // Asset[] feeAssets = feeAssets(); - // System.out.println(Arrays.toString(feeAssets)); - // BigInteger nativeFee = nativeCoinValue(feeAssets); - // List coinAssets = coinAssets(feeAssets); - // BigInteger[] coinIds = coinIds(coinAssets); - // BigInteger[] coinValues = coinValues(coinAssets); - - // Address faAddr = new Address(fa.account()); - // Executable executable = () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) - // .intercallHandleFeeGathering( - // IRC31SupplierTest.transferFromBatchChecker( - // btsAddress, btsAddress, faAddr, coinIds, coinValues), - // btsAddress, fa.toString(), BTPTokenService.SERVICE); - // ScoreIntegrationTest.balanceCheck(faAddr, nativeFee, () -> - // IRC31SupplierTest.balanceBatchCheck(faAddr, coinIds, coinValues, executable)); - // } - - // @Test - // void handleFeeGatheringShouldTransferStart() { - // //mint - // IRC31SupplierTest.mint(owner, coinId, coinValue); - - // //transferBatch - // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, - // new Asset(nativeCoinName, nativeValue), - // new Asset(coinName, coinValue)); - // BigInteger sn = transferBatch(transaction); - - // //handleTransferResponse - // handleTransferResponse(transaction, sn); - - // // - // Asset[] feeAssets = feeAssets(); - // System.out.println(Arrays.toString(feeAssets)); - - // TransferTransaction feeTransaction = transferTransaction( - // btsAddress, linkFa, BigInteger.ZERO, feeAssets); - // ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) - // .intercallHandleFeeGathering( - // transferStartEventLogChecker(feeTransaction) - // .andThen(sendMessageEventLogChecker(feeTransaction)), - // btsAddress, linkFa.toString(), BTPTokenService.SERVICE); - // } - - // @Test - // void handleFeeGatheringShouldRevert() { - // AssertBTSException.assertUnknown(() -> - // btsBSH.handleFeeGathering(link.toString(), BTPTokenService.SERVICE)); - // } -} \ No newline at end of file +// } +// +// // @BeforeAll +// // static void beforeAll() { +// // if (!isExistsCoin(coinName)) { +// // register(coinName); +// // coinId = bts.coinId(coinName); +// // } +// // if (!bts.feeRatio().equals(feeRatio)) { +// // bts.setFeeRatio(feeRatio); +// // } +// // } +// +// // @Test +// // void registerShouldSuccess() { +// // register(ScoreIntegrationTest.Faker.faker.name().name()); +// // } +// +// // @Test +// // void transferNativeCoinShouldMakeEventLogAndLockBalance() { +// // Asset asset = new Asset(nativeCoinName, nativeValue); +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, asset); +// // Consumer checker = transferStartEventLogChecker(transaction) +// // .andThen(sendMessageEventLogChecker(transaction)); +// // ScoreIntegrationTest.balanceCheck(btsAddress, asset.getAmount(), () -> +// // lockedBalanceCheck(new Address(transaction.getFrom()), asset, () -> +// // ((BTSScoreClient) bts).transferNativeCoin( +// // checker, +// // asset.getAmount(), +// // transaction.getTo()))); +// // } +// +// // @Test +// // void transferShouldMakeEventLogAndLockBalance() { +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // Asset asset = new Asset(coinName, coinValue); +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, asset); +// // Address from = new Address(transaction.getFrom()); +// // Consumer checker = transferStartEventLogChecker(transaction) +// // .andThen(sendMessageEventLogChecker(transaction)) +// // .andThen(IRC31SupplierTest.transferFromChecker( +// // btsAddress, from, btsAddress, coinId, asset.getAmount())); +// // IRC31SupplierTest.balanceCheck(btsAddress, coinId, coinValue, () -> +// // lockedBalanceCheck(from, asset, () -> +// // ((BTSScoreClient) bts).transfer( +// // checker, +// // asset.getCoinName(), asset.getAmount(), transaction.getTo()) +// // ) +// // ); +// // } +// +// // @Test +// // void transferBatchShouldShouldMakeEventLogAndLockBalance() { +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// +// // transferBatch(transaction); +// // } +// +// // @Test +// // void handleTransferRequestShouldIRC31MintBatchAndResponse() { +// // //mint to tester +// // IRC31SupplierTest.mint(testerAddress, coinId, coinValue); +// +// // //transfer owner to tester +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// // TransferRequest request = transferRequest(transaction); +// // List assets = coinAssets(request.getAssets()); +// +// // BigInteger[] coinIds = coinIds(assets); +// // BigInteger[] coinValues = coinValues(assets); +// +// // BTSMessage btsMessage = new BTSMessage(); +// // btsMessage.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); +// // btsMessage.setData(request.toBytes()); +// +// // Address to = new Address(BTPAddress.valueOf(transaction.getTo()).account()); +// +// // BigInteger sn = BigInteger.ONE; +// // Consumer checker = IRC31SupplierTest.mintBatchChecker( +// // btsAddress, to, coinIds, coinValues).andThen( +// // MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { +// // assertEquals(linkNet, el.getTo()); +// // assertEquals(BTPTokenService.SERVICE, el.getSvc()); +// // assertEquals(sn, el.getSn()); +// // BTSMessage btsMsg = BTSMessage.fromBytes(el.getMsg()); +// // assertEquals(BTSMessage.REPONSE_HANDLE_SERVICE, btsMsg.getServiceType()); +// // TransferResponse response = TransferResponse.fromBytes(btsMsg.getData()); +// // assertEquals(TransferResponse.RC_OK, response.getCode()); +// // assertEquals(TransferResponse.OK_MSG, response.getMessage()); +// // })); +// // Executable executable = () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) +// // .intercallHandleBTPMessage( +// // checker, +// // btsAddress, linkNet, BTPTokenService.SERVICE, sn, btsMessage.toBytes()); +// // ScoreIntegrationTest.balanceCheck(to, nativeCoinValue(request.getAssets()), () -> +// // IRC31SupplierTest.balanceBatchCheck(to, coinIds, coinValues, executable)); +// // } +// +// // @Test +// // void handleTransferResponseShouldIRC31BurnBatchAndMakeEventLog() { +// // //mint +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // //transferBatch +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// // BigInteger sn = transferBatch(transaction); +// +// // // +// // handleTransferResponse(transaction, sn); +// // } +// +// // @Test +// // void handleUnknownResponseShouldMakeEventLog() { +// // TransferResponse response = new TransferResponse(); +// // response.setCode(TransferResponse.RC_ERR); +// // response.setMessage(TransferResponse.ERR_MSG_UNKNOWN_TYPE); +// // BTSMessage btsMessage = new BTSMessage(); +// // btsMessage.setServiceType(BTSMessage.UNKNOWN_TYPE); +// // btsMessage.setData(response.toBytes()); +// +// // BigInteger sn = BigInteger.ONE; +// // ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPMessage( +// // BTSIntegrationTest.eventLogChecker(UnknownResponseEventLog::eventLogs, (el) -> { +// // assertEquals(linkNet, el.getFrom()); +// // assertEquals(sn, el.getSn()); +// // }), +// // btsAddress, linkNet, BTPTokenService.SERVICE, sn, btsMessage.toBytes()); +// // } +// +// // @Test +// // void handleBTPMessageShouldRevert() { +// // AssertBTSException.assertUnknown(() -> +// // btsBSH.handleBTPMessage(linkNet, BTPTokenService.SERVICE, BigInteger.ONE, new byte[]{})); +// // } +// +// // @Test +// // void handleBTPErrorShouldMakeEventLogAndAddRefundableBalance() { +// // //mint +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // //transferBatch +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// // BigInteger sn = transferBatch(transaction); +// +// // //handleBTPError +// // handleBTPError(transaction, sn, code, msg); +// // } +// +// // @Test +// // void reclaim() { +// // //mint +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // //transferBatch +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// // BigInteger sn = transferBatch(transaction); +// +// // //handleBTPError for make refundable balance +// // handleBTPError(transaction, sn, code, msg); +// +// // //reclaim +// // ScoreIntegrationTest.balanceCheck(owner, nativeValue, () -> +// // bts.reclaim(nativeCoinName, nativeValue)); +// // IRC31SupplierTest.balanceCheck(owner, coinId, coinValue, () -> +// // bts.reclaim(coinName, coinValue)); +// // } +// +// // @Test +// // void handleBTPErrorShouldRevert() { +// // AssertBTSException.assertUnknown(() -> +// // btsBSH.handleBTPError(linkNet, +// // BTPTokenService.SERVICE, BigInteger.ONE, 0, "")); +// // } +// +// // @Test +// // void handleFeeGatheringShouldIRC31Transfer() {//how to clear feeBalances as zero? +// // //mint +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // //transferBatch +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// // BigInteger sn = transferBatch(transaction); +// +// // //handleTransferResponse +// // handleTransferResponse(transaction, sn); +// +// // // +// // Asset[] feeAssets = feeAssets(); +// // System.out.println(Arrays.toString(feeAssets)); +// // BigInteger nativeFee = nativeCoinValue(feeAssets); +// // List coinAssets = coinAssets(feeAssets); +// // BigInteger[] coinIds = coinIds(coinAssets); +// // BigInteger[] coinValues = coinValues(coinAssets); +// +// // Address faAddr = new Address(fa.account()); +// // Executable executable = () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) +// // .intercallHandleFeeGathering( +// // IRC31SupplierTest.transferFromBatchChecker( +// // btsAddress, btsAddress, faAddr, coinIds, coinValues), +// // btsAddress, fa.toString(), BTPTokenService.SERVICE); +// // ScoreIntegrationTest.balanceCheck(faAddr, nativeFee, () -> +// // IRC31SupplierTest.balanceBatchCheck(faAddr, coinIds, coinValues, executable)); +// // } +// +// // @Test +// // void handleFeeGatheringShouldTransferStart() { +// // //mint +// // IRC31SupplierTest.mint(owner, coinId, coinValue); +// +// // //transferBatch +// // TransferTransaction transaction = transferTransaction(owner, to, feeRatio, +// // new Asset(nativeCoinName, nativeValue), +// // new Asset(coinName, coinValue)); +// // BigInteger sn = transferBatch(transaction); +// +// // //handleTransferResponse +// // handleTransferResponse(transaction, sn); +// +// // // +// // Asset[] feeAssets = feeAssets(); +// // System.out.println(Arrays.toString(feeAssets)); +// +// // TransferTransaction feeTransaction = transferTransaction( +// // btsAddress, linkFa, BigInteger.ZERO, feeAssets); +// // ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) +// // .intercallHandleFeeGathering( +// // transferStartEventLogChecker(feeTransaction) +// // .andThen(sendMessageEventLogChecker(feeTransaction)), +// // btsAddress, linkFa.toString(), BTPTokenService.SERVICE); +// // } +// +// // @Test +// // void handleFeeGatheringShouldRevert() { +// // AssertBTSException.assertUnknown(() -> +// // btsBSH.handleFeeGathering(link.toString(), BTPTokenService.SERVICE)); +// // } +//} \ No newline at end of file diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSIntegrationTest.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSIntegrationTest.java index a4de9c552..962254495 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSIntegrationTest.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSIntegrationTest.java @@ -1,64 +1,64 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// /* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ -package foundation.icon.btp.bts; +// package foundation.icon.btp.bts; -import foundation.icon.btp.lib.BSH; -import foundation.icon.btp.lib.BSHScoreClient; -import foundation.icon.btp.lib.OwnerManager; -import foundation.icon.btp.lib.OwnerManagerScoreClient; -import foundation.icon.btp.test.BTPIntegrationTest; -import foundation.icon.btp.test.MockBMCIntegrationTest; -import foundation.icon.icx.Wallet; -import foundation.icon.jsonrpc.model.TransactionResult; -import foundation.icon.score.client.DefaultScoreClient; -import foundation.icon.score.client.ScoreClient; -import foundation.icon.score.test.ScoreIntegrationTest; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.TestMethodOrder; +// import foundation.icon.btp.lib.BSH; +// import foundation.icon.btp.lib.BSHScoreClient; +// import foundation.icon.btp.lib.OwnerManager; +// import foundation.icon.btp.lib.OwnerManagerScoreClient; +// import foundation.icon.btp.test.BTPIntegrationTest; +// import foundation.icon.btp.test.MockBMCIntegrationTest; +// import foundation.icon.icx.Wallet; +// import foundation.icon.jsonrpc.model.TransactionResult; +// import foundation.icon.score.client.DefaultScoreClient; +// import foundation.icon.score.client.ScoreClient; +// import foundation.icon.score.test.ScoreIntegrationTest; +// import org.junit.jupiter.api.MethodOrderer; +// import org.junit.jupiter.api.TestMethodOrder; -import java.util.Map; -import java.util.function.Consumer; +// import java.util.Map; +// import java.util.function.Consumer; -@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class) -public interface BTSIntegrationTest extends BTPIntegrationTest { +// @TestMethodOrder(value = MethodOrderer.OrderAnnotation.class) +// public interface BTSIntegrationTest extends BTPIntegrationTest { - DefaultScoreClient btsClient = DefaultScoreClient.of( - System.getProperties(), Map.of( - "_bmc", MockBMCIntegrationTest.mockBMCClient._address())); +// DefaultScoreClient btsClient = DefaultScoreClient.of( +// System.getProperties(), Map.of( +// "_bmc", MockBMCIntegrationTest.mockBMCClient._address())); - @ScoreClient - BTS bts = new BTSScoreClient(btsClient); +// @ScoreClient +// BTS bts = new BTSScoreClient(btsClient); - @ScoreClient - BSH btsBSH = new BSHScoreClient(btsClient); +// @ScoreClient +// BSH btsBSH = new BSHScoreClient(btsClient); - @ScoreClient - OwnerManager btsOwnerManager = new OwnerManagerScoreClient(btsClient); +// @ScoreClient +// OwnerManager btsOwnerManager = new OwnerManagerScoreClient(btsClient); - Wallet tester = ScoreIntegrationTest.getOrGenerateWallet("tester.", System.getProperties()); - DefaultScoreClient btsClientWithTester = new DefaultScoreClient( - btsClient.endpoint(), btsClient._nid(), tester, btsClient._address()); - BTS btsWithTester = new BTSScoreClient(btsClientWithTester); - OwnerManager btsOwnerManagerWithTester = new OwnerManagerScoreClient(btsClientWithTester); +// Wallet tester = ScoreIntegrationTest.getOrGenerateWallet("tester.", System.getProperties()); +// DefaultScoreClient btsClientWithTester = new DefaultScoreClient( +// btsClient.endpoint(), btsClient._nid(), tester, btsClient._address()); +// BTS btsWithTester = new BTSScoreClient(btsClientWithTester); +// OwnerManager btsOwnerManagerWithTester = new OwnerManagerScoreClient(btsClientWithTester); - static Consumer eventLogChecker( - EventLogsSupplier supplier, Consumer consumer) { - return ScoreIntegrationTest.eventLogChecker( - btsClient._address(), supplier, consumer); - } +// static Consumer eventLogChecker( +// EventLogsSupplier supplier, Consumer consumer) { +// return ScoreIntegrationTest.eventLogChecker( +// btsClient._address(), supplier, consumer); +// } -} +// } diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java new file mode 100644 index 000000000..590a6a976 --- /dev/null +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java @@ -0,0 +1,824 @@ +package foundation.icon.btp.bts; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import com.iconloop.score.test.Account; +import foundation.icon.btp.lib.BTPAddress; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.function.Executable; +import org.mockito.MockedStatic.Verification; +import score.Address; +import score.Context; + +@TestMethodOrder(OrderAnnotation.class) +public class BTSTest extends AbstractBTPTokenService { + + @Order(1) + @Test + public void name() { + assertEquals("BTP Token Service", score.call("name")); + assertEquals(BigInteger.ZERO, score.call(("getSn"))); + } + + @Order(2) + @Test + public void addToLockListTest() { + + assertEquals(BigInteger.ZERO, score.call(("getSn"))); + + // non-owner tries + String[] addr = new String[] {"Hell "}; + Executable call = () -> score.invoke(nonOwner, "addBlacklistAddress", "hello world ",addr); + expectErrorMessage(call, "require owner access"); + + blacklistMocks(); + call = () -> score.invoke(owner, "addBlacklistAddress", "networking", addr); + expectErrorMessage(call, "Invalid link"); + + String[] addr1 = new String[] {"all too well"}; + score.invoke(owner, "addBlacklistAddress", "network", addr1); + + byte[] _msg = blacklistSuccessfulResponse(); + + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.ONE, _msg); + + + String[] addr2 = new String[] {" all too well "}; + call = () -> score.invoke(owner, "addBlacklistAddress", "network", addr2); + expectErrorMessage(call, "User already blacklisted"); + + List actual = (List) score.call("getBlackListedUsers", "network", 0, 10); + List expected = List.of("all too well"); + assertEquals(expected, actual); + + String[] addr3 = new String[] {" you belong with me "}; + score.invoke(owner, "addBlacklistAddress", "network", addr3); + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.TWO, _msg); + + expected = List.of("all too well", "you belong with me"); + actual = (List) score.call("getBlackListedUsers", "network", 0, 10); + assertEquals(expected, actual); + + assertEquals(true, score.call("isUserBlackListed", " you belong with me ", "network")); + assertEquals(false, score.call("isUserBlackListed", " yu belong with me ", "network")); + + assertEquals(2, score.call("blackListedUsersCount", "network")); + + assertEquals( BigInteger.valueOf(2), score.call("getSn")); + } + + @Order(3) + @Test + public void removeFromLocklist() { + assertEquals(BigInteger.ZERO, score.call(("getSn"))); + + blacklistMocks(); + + String[] addr1 = new String[] {"all too well"}; + score.invoke(owner, "addBlacklistAddress", "network", addr1); + String[] addr2 = new String[] {" you belong with me "}; + score.invoke(owner, "addBlacklistAddress", "network", addr2); + byte[] _msg = blacklistSuccessfulResponse(); + + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.ONE, _msg); + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.TWO, _msg); + + Executable call = () -> score.invoke(owner, "removeBlacklistAddress", "icx",new String[]{" hell "}); + expectErrorMessage(call, "Invalid link"); + + // non-owner tries + call = () -> score.invoke(nonOwner, "removeBlacklistAddress", "harmony",new String[]{"hell"}); + expectErrorMessage(call, "require owner access"); + + // try to remove non blacklisted + call = () -> score.invoke(owner, "removeBlacklistAddress", "harmony",new String[]{"hell"}); + expectErrorMessage(call, "User not in blacklist"); + + // remove legit user + score.invoke(owner, "removeBlacklistAddress", "network", addr1); + + _msg = blacklistRemoveSuccessfulResponse(); + + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.valueOf(3), _msg); + List expected = List.of("you belong with me"); + List actual = (List) score.call("getBlackListedUsers", "network", 0, 1); + assertEquals(expected, actual); + + assertEquals(1, score.call("blackListedUsersCount", "network")); + assertEquals(false, score.call("isUserBlackListed", " all too well ", "network")); + + + assertEquals(BigInteger.valueOf(3), score.call(("getSn"))); + } + + + @Test + @Order(4) + @DisplayName("register wrapped coin") + public void registerWrappedToken() { + tokenLimitBTPMessage(); + + Verification deployWrappedToken = () -> Context.deploy(any(), eq(PARA), + eq(PARA),eq(18)); + contextMock.when(deployWrappedToken).thenReturn(wrappedIRC2.getAddress()); + + score.invoke(owner, "register",PARA, PARA, 18, BigInteger.ZERO, BigInteger.ZERO, + Address.fromString("cx0000000000000000000000000000000000000000")); + + assertEquals(1, score.call("getRegisteredTokensCount")); + + List registered = List.of(PARA); + assertEquals(registered, score.call("coinNames")); + } + + @Test + @Order(5) + public void registerIRC2Token() { + // non owner tries to call (Scenario 2) + Executable call = () -> score.invoke(nonOwner, "register", + TEST_TOKEN, "TTK", 18, ICX.divide(BigInteger.TEN), ICX, irc2.getAddress()); + expectErrorMessage(call, "require owner access"); + + // owner registers new coin (Scenario 1) + score.invoke(owner, "register", + TEST_TOKEN, "TTK", 18, ICX.divide(BigInteger.TEN), ICX, irc2.getAddress()); + + // owner registers coin that exists (Scenario 3) + call = () -> score.invoke(owner, "register", + TEST_TOKEN, "TTK", 18, ICX.divide(BigInteger.TEN), ICX, irc2.getAddress()); + expectErrorMessage(call, "already existed"); + + List expected = List.of(TEST_TOKEN); + assertEquals(expected, score.call("coinNames")); + + assertEquals(null, score.call("getTokenLimit", TEST_TOKEN)); + + assertEquals(1, score.call("getRegisteredTokensCount")); + } + + @Test + @Order(6) + public void operationsOnUnRegisteredToken() { + Account IRC2Token = Account.newScoreAccount(10); + Executable call = () -> score.invoke(IRC2Token,"tokenFallback", owner.getAddress(), + BigInteger.valueOf(10), "0".getBytes()); + expectErrorMessage(call,"Token not registered"); + } + + @Test + @Order(7) + public void operationOnRegisteredTokens() { + register(); + + contextMock.when(() -> Context.call(eq(BigInteger.class),eq(irc2.getAddress()), eq("balanceOf"), + eq(owner.getAddress()))).thenReturn(BigInteger.valueOf(100)); + score.invoke(irc2,"tokenFallback", owner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); + Map balance = (Map) score.call("balanceOf", owner.getAddress(), TEST_TOKEN); + assertEquals(balance.get("usable"), BigInteger.valueOf(10)); + } + + @Test + @Order(8) + public void reclaimDepositedTokens() { + // for IRC2 tokens + register(); + Executable call = () -> score.invoke(owner, "reclaim", "ABC", BigInteger.valueOf(0)); + expectErrorMessage(call, "_value must be positive"); + call = () -> score.invoke(owner, "reclaim", "ABC", BigInteger.valueOf(-10)); + expectErrorMessage(call, "_value must be positive"); + call = () -> score.invoke(owner, "reclaim", "ABC", BigInteger.valueOf(10)); + expectErrorMessage(call, "invalid value"); + + deposit(BigInteger.valueOf(10)); + // locked = 10 + contextMock.when(() -> Context.call(eq(irc2.getAddress()), eq("transfer"), + eq(owner.getAddress()), eq(BigInteger.valueOf(5)), any())).thenReturn(null); + score.invoke(owner, "reclaim", TEST_TOKEN, BigInteger.valueOf(5)); + + Map balance = (Map) score.call("balanceOf", owner.getAddress(), TEST_TOKEN); + assertEquals(BigInteger.valueOf(5), balance.get("refundable")); + assertEquals(BigInteger.valueOf(0), balance.get("locked")); + assertEquals(BigInteger.valueOf(0), balance.get("usable")); + + // wrapped IRC2 tradable is not directly transferred + + } + + @Test + @Order(9) + public void transferNativeCoin() { + sendBTPMessageMock(); + String btpAddress = generateBTPAddress("network", owner.getAddress().toString()); + Account user = sm.createAccount(1000); + // general condition + contextMock.when(sendICX()).thenReturn(BigInteger.valueOf(100)); + score.invoke(user, "transferNativeCoin", btpAddress); + + // blacklist reciever + blacklistMocks(); + score.invoke(owner, "addRestriction"); + String addr = generateBTPAddress("network", user.getAddress().toString()); + String[] finalAdddr = new String[]{user.getAddress().toString()}; + blackListUser("network", finalAdddr, BigInteger.TWO); + System.out.println(score.call("getBlackListedUsers", "network", 0, 10)); + +// score.invoke(owner, "addBlacklistAddress", "network", user.getAddress().toString()); + + Executable call = () -> + score.invoke(owner, "transferNativeCoin", addr); + expectErrorMessage(call, "_to user is Blacklisted"); + + // blacklist sender + String newAddr = generateBTPAddress("network", owner.getAddress().toString()); + call = () -> score.invoke(user, "transferNativeCoin", newAddr); + expectErrorMessage(call, "_from user is Blacklisted"); + + // remove blacklisted user + removeBlackListedUser("network", finalAdddr, BigInteger.valueOf(3)); + + score.invoke(owner, "setTokenLimit", new String[]{ICON}, + new BigInteger[]{BigInteger.valueOf(90)}); + + call = () -> score.invoke(owner, "transferNativeCoin", addr); + expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); + + score.invoke(owner, "setTokenLimit", new String[]{ICON}, + new BigInteger[]{BigInteger.valueOf(100)}); + + score.invoke(owner, "transferNativeCoin", addr); + + contextMock.when(sendICX()).thenReturn(BigInteger.valueOf(99)); + score.invoke(owner, "transferNativeCoin", addr); + } + + @Test + @Order(10) + public void transfer() { + String btpaddr = generateBTPAddress("harmony",ETH_ADDR); + + Executable call = () -> score.invoke(nonOwner, "transfer", "Token1", + BigInteger.valueOf(-1), btpaddr); + expectErrorMessage(call, "Invalid amount"); + + call = () -> score.invoke(nonOwner, "transfer", "Token1", + BigInteger.ZERO, btpaddr); + expectErrorMessage(call, "Invalid amount"); + + call = () -> score.invoke(nonOwner, "transfer", "Token1", + BigInteger.valueOf(10), btpaddr); + expectErrorMessage(call, "Not supported Token"); + + register(); + + // mock message to bmc + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), eq("harmony"), eq("bts"), eq(BigInteger.ONE), any()); + contextMock.when(sendMessage).thenReturn(null); + + score.invoke(irc2,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); + score.invoke(nonOwner, "transfer", TEST_TOKEN, BigInteger.valueOf(10), btpaddr); + + Map balance = (Map) score.call("balanceOf", nonOwner.getAddress(), TEST_TOKEN); + // currently, still in locked state + // will be updated once response comes from relayer via bmc + assertEquals(BigInteger.valueOf(10), balance.get("locked")); + } + + @Test + @Order(11) + public void transferBatch() { + String[] coinNames = new String[]{"Token1","Token2","Token3","Token4", PARA}; + BigInteger val = BigInteger.valueOf(10); + BigInteger[] values = new BigInteger[]{val,val, val, val, val}; + String destination = generateBTPAddress("harmony", ETH_ADDR); + + // none of them registered + // user has not deposited any of them + Executable call = () -> score.invoke(nonOwner, "transferBatch", coinNames, values, destination); + expectErrorMessage(call, "Not supported Token"); + + BigInteger[] wrongValues = new BigInteger[]{val,val, val}; + + call = () -> score.invoke(nonOwner, "transferBatch", coinNames, wrongValues, destination); + expectErrorMessage(call, "Invalid arguments"); + + // add native coin as well in batch + contextMock.when(sendICX()).thenReturn(BigInteger.valueOf(100)); + + // register tokens + Account token1 = Account.newScoreAccount(10); + Account token2 = Account.newScoreAccount(11); + Account token3 = Account.newScoreAccount(12); + Account token4 = Account.newScoreAccount(13); + + // register irc2 token + register(coinNames[0], token1.getAddress()); + register(coinNames[1], token2.getAddress()); + register(coinNames[2], token3.getAddress()); + register(coinNames[3], token4.getAddress()); + + // register wrapped token "PARA" + registerWrapped(); + + assertEquals(BigInteger.ZERO, score.call("getSn")); + + score.invoke(token1,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(8), "0".getBytes()); + score.invoke(token2,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); + score.invoke(token3,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); + score.invoke(token4,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); + + // PARA wrapped transferFrom mock + Verification transferFromMock = () -> Context.call(eq(Boolean.class), any(), eq("transferFrom"), + eq(nonOwner.getAddress()), eq(score.getAddress()), eq(BigInteger.valueOf(10)), any()); + contextMock.when(transferFromMock).thenReturn(true); + + call = () -> score.invoke(nonOwner, "transferBatch", coinNames, values, destination); + expectErrorMessageIn(call, "InSufficient Usable Balance"); + + score.invoke(token1,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); + + // mock message to bmc + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), eq("harmony"), eq("bts"), eq(BigInteger.ONE), any()); + contextMock.when(sendMessage).thenReturn(null); + + score.invoke(nonOwner, "transferBatch", coinNames, values, destination); + verify(scoreSpy).TransferStart(eq(nonOwner.getAddress()), eq(destination), eq(BigInteger.ONE), any()); + + Map bal = (Map) score.call("balanceOf", nonOwner.getAddress(), coinNames[0]); + assertEquals(bal.get("usable"), BigInteger.valueOf(8)); + assertEquals(bal.get("locked"), BigInteger.valueOf(10)); + + // PARA wrapped token balance mock + contextMock.when(() -> Context.call(eq(BigInteger.class),eq(wrappedIRC2.getAddress()), eq("balanceOf"), + eq(nonOwner.getAddress()))).thenReturn(BigInteger.valueOf(50)); + + contextMock.when(() -> Context.call(eq(BigInteger.class),eq(wrappedIRC2.getAddress()), eq("allowance"), + eq(nonOwner.getAddress()), eq(score.getAddress()))).thenReturn(BigInteger.valueOf(40)); + + bal = (Map) score.call("balanceOf", nonOwner.getAddress(), coinNames[4]); + // usable is min of balanceOf and allowed + assertEquals(bal.get("usable"), BigInteger.valueOf(40)); + assertEquals(bal.get("locked"), BigInteger.valueOf(10)); + + // message service number 1 + TransferTransaction txn = (TransferTransaction) score.call("getTransaction", BigInteger.ONE); + assertEquals(txn.getFrom(), nonOwner.getAddress().toString()); + assertEquals(txn.getTo(), destination); + assertEquals(txn.getAssets().length,6); + + // this goes to bmc -> relayer -> solidity -> relayer -> bmc -> here again + } + + @Test + @Order(12) + public void handleBTPMessage1() { + // solidity -> relayer -> bmc -> bts -> transfer/mint on icon side + Executable call = () -> score.invoke(owner, "handleBTPMessage", + "from","svc",BigInteger.ONE, "ehehehe".getBytes()); + expectErrorMessage(call, "Only BMC"); + + // irc2, wrapped and native-coin + Asset asset1 = new Asset(TEST_TOKEN, BigInteger.valueOf(50)); + Asset asset2 = new Asset(PARA, BigInteger.valueOf(30)); + Asset asset3 = new Asset(ICON, BigInteger.valueOf(20)); + + // TransferRequest Message + TransferRequest request = new TransferRequest(); + request.setFrom(bmcMock.getAddress().toString()); + request.setTo(nonOwner.getAddress().toString()); + request.setAssets(new Asset[]{asset1, asset2, asset3}); + + BTSMessage message = new BTSMessage(); + message.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); + message.setData(request.toBytes()); + + byte[] _msg = message.toBytes(); + + // ICON preregistered, register remaining two + Account token1 = Account.newScoreAccount(5); + register(TEST_TOKEN, token1.getAddress()); + + assertEquals(BigInteger.ZERO, score.call("getSn")); + + // wrapped token not registered yet + call = () -> score.invoke(bmcMock, "handleBTPMessage", + "from","svc",BigInteger.valueOf(3), _msg); + expectErrorMessage(call, "Invalid Token"); + + // register wrapped token + registerWrapped(); + + assertEquals(BigInteger.ZERO, score.call("getSn")); + + // native-coin + contextMock.when(()->Context.transfer(any(Address.class), + eq(BigInteger.valueOf(20)))).then(invocationOnMock -> null); + // wrapped coin + contextMock.when(() ->Context.call(any(), eq("mint"), + eq(nonOwner.getAddress()), eq(BigInteger.valueOf(30)))).thenReturn(null); + // irc2 transfer + contextMock.when(() ->Context.call(eq(token1.getAddress()), eq("transfer"), + eq(nonOwner.getAddress()), eq(BigInteger.valueOf(50)), any())).thenReturn(null); + + // mock bmc message + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), eq("fromString"), eq("bts"), eq(BigInteger.ONE), any()); + contextMock.when(sendMessage).thenReturn(null); + + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.ONE, _msg); + verify(scoreSpy).TransferReceived(eq("fromString"), eq(nonOwner.getAddress()), eq(BigInteger.ONE), any()); + } + + @Test + @Order(13) + public void handleBTPMessage2() { + + // request plus response + String[] coinNames = new String[]{"Token1","Token2",PARA}; + BigInteger val = BigInteger.valueOf(10); + BigInteger val1 = BigInteger.valueOf(10000); + BigInteger[] values = new BigInteger[]{val, val1, val}; + String destination = generateBTPAddress("0x1.bsc", ETH_ADDR); + + // add native coin as well in batch + contextMock.when(sendICX()).thenReturn(BigInteger.valueOf(50)); + + Account token1 = Account.newScoreAccount(50); + Account token2 = Account.newScoreAccount(51); + + register("Token1", token1.getAddress()); + register("Token2", token2.getAddress()); + registerWrapped(); + + // transfer 50 of token 1 + score.invoke(token1,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(50), "0".getBytes()); + score.invoke(token2,"tokenFallback", nonOwner.getAddress(), val1, "0".getBytes()); + Verification transferFromMock = () -> Context.call(eq(Boolean.class), any(), eq("transferFrom"), + eq(nonOwner.getAddress()), eq(score.getAddress()), eq(BigInteger.valueOf(10)), any()); + contextMock.when(transferFromMock).thenReturn(true); + + sendBTPMessageMock(); + score.invoke(nonOwner, "transferBatch", coinNames, values, destination); + verify(scoreSpy).TransferStart(any(), any(), any(), any()); + assertEquals(BigInteger.ONE, score.call("getSn")); + + // mock allowance and balanceOf for wrapped token + + contextMock.when(() -> Context.call(eq(BigInteger.class),eq(wrappedIRC2.getAddress()), eq("balanceOf"), + eq(nonOwner.getAddress()))).thenReturn(BigInteger.valueOf(100)); + + contextMock.when(() -> Context.call(eq(BigInteger.class),eq(wrappedIRC2.getAddress()), eq("allowance"), + eq(nonOwner.getAddress()), eq(score.getAddress()))).thenReturn(BigInteger.valueOf(1000)); + + List> balances = (List>) score.call( + "balanceOfBatch", nonOwner.getAddress(), new String[]{"Token1","Token2", PARA, ICON}); + + // Token1 + assertEquals(balances.get(0).get("locked"), BigInteger.valueOf(10)); + assertEquals(balances.get(0).get("usable"), BigInteger.valueOf(40)); + // Token 2 + assertEquals(balances.get(1).get("locked"), val1); + // PARA + assertEquals(balances.get(2).get("locked"), BigInteger.valueOf(10)); + // ICON + assertEquals(balances.get(3).get("locked"), BigInteger.valueOf(50)); + + + // BTS -> BMC -> RELAYER -> SOLIDITY -> RELAYER -> BMC -> BTS + + assertEquals(BigInteger.ONE, score.call(("getSn"))); + // successful case + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_OK); + + BTSMessage message = new BTSMessage(); + message.setServiceType(BTSMessage.REPONSE_HANDLE_SERVICE); + message.setData(response.toBytes()); + + byte[] _msg = message.toBytes(); + + contextMock.when(() ->Context.call(eq(wrappedIRC2.getAddress()), eq("burn"), + eq(BigInteger.valueOf(10)))).thenReturn(null); + + score.invoke(bmcMock, "handleBTPMessage", + "fromString","bmc",BigInteger.ONE, _msg); + verify(scoreSpy).TransferEnd(eq(nonOwner.getAddress()), eq(BigInteger.ONE), eq(TransferResponse.RC_OK), any()); + assertEquals(null, score.call("getTransaction", BigInteger.ONE)); + + balances = (List>) score.call( + "balanceOfBatch", nonOwner.getAddress(), new String[]{"Token1","Token2", PARA, ICON}); + + // Token1 + assertEquals(balances.get(0).get("locked"), BigInteger.ZERO); + assertEquals(balances.get(0).get("usable"), BigInteger.valueOf(40)); + // Token 2 + assertEquals(balances.get(1).get("locked"), BigInteger.ZERO); + // PARA + assertEquals(balances.get(2).get("locked"), BigInteger.ZERO); + // ICON + assertEquals(balances.get(3).get("locked"), BigInteger.ZERO); + + // fee for wrapped and native-coin set to zero + assertEquals(BigInteger.ONE,score.call("getAccumulatedFees", coinNames[0])); + assertEquals(BigInteger.valueOf(11),score.call("getAccumulatedFees", coinNames[1])); + assertEquals(BigInteger.ZERO,score.call("getAccumulatedFees", coinNames[2])); + assertEquals(BigInteger.ZERO,score.call("getAccumulatedFees", ICON)); + } + + @Test + @Order(14) + @DisplayName("unknown type") + void handleBTPMessage3() { + BTSMessage message = new BTSMessage(); + message.setServiceType(BTSMessage.UNKNOWN_TYPE); + message.setData("a for apple".getBytes()); + + score.invoke(bmcMock, "handleBTPMessage", + "from","bmc",BigInteger.ONE, message.toBytes()); + verify(scoreSpy).UnknownResponse(any(), eq(BigInteger.ONE)); + } + + @Test + @Order(15) + @DisplayName("not handled cases") + void handleBTPMessage4() { + BTSMessage message = new BTSMessage(); + message.setServiceType(100); + message.setData("a for apple".getBytes()); + + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), eq("fromString"), eq("bts"), eq(BigInteger.ONE), any()); + contextMock.when(sendMessage).thenReturn(null); + + score.invoke(bmcMock, "handleBTPMessage", + "fromString","bmc",BigInteger.ONE, message.toBytes()); + } +// +// @Test +// @Order(16) +// @DisplayName("change limit, check restrictions, check fees") +// public void changeLimit() { +// String TOKEN1 = "Token1"; +// BigInteger TWO_HUNDRED_ICX = BigInteger.valueOf(200).multiply(ICX); +// BigInteger FIFTY_ICX = BigInteger.valueOf(50).multiply(ICX); +// Address user1 = sm.createAccount(20).getAddress(); +// +// Account token1 = Account.newScoreAccount(50); +// +// Executable call; +// +// score.invoke(owner, "register", +// TOKEN1, TOKEN1, 18, BigInteger.valueOf(10), +// ICX, token1.getAddress()); +// +// assertEquals(BigInteger.ZERO, score.call("getTokenLimit", TOKEN1)); +// +// tokenLimitBTPMessage(); +// score.invoke(owner, "changeLimit", TOKEN1, BigInteger.valueOf(200)); +// +// assertEquals(BigInteger.valueOf(200), score.call("getTokenLimit", TOKEN1)); +// +// call = () -> score.invoke(owner, "changeLimit", TOKEN1, +// TWO_HUNDRED_ICX.negate()); +// expectErrorMessage(call, "Invalid value"); +// call = () -> score.invoke(owner, "changeLimit", "TokenBSH", +// TWO_HUNDRED_ICX); +// expectErrorMessage(call, "Not registered"); +// +// score.invoke(owner, "changeLimit", TOKEN1, TWO_HUNDRED_ICX); +// +// assertEquals(TWO_HUNDRED_ICX, score.call("getTokenLimit", TOKEN1)); +// +// // send from Harmony to ICON +// // reciever adddress is blacklisted in icon +// +// Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), +// eq("sendMessage"), any(), any(), any(), any()); +// +// Verification returnLinks = () -> Context.call(eq(String[].class), +// eq(bmcMock.getAddress()), eq("getLinks"), any()); +// +// // these 3 BTP addresses are currently supported by ICON Bridge +// BTPAddress btpAddress1 = new BTPAddress("icon","cx0E636c8cF214a9d702C5E4a6D8c020be217766D3"); +// BTPAddress btpAddress2 = new BTPAddress("network","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); +// BTPAddress btpAddress3 = new BTPAddress("harmony","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); +// String[] links = new String[] {btpAddress1.toString(), btpAddress2.toString(), btpAddress3.toString()}; +// +// contextMock.when(sendMessage).thenReturn(null); +// contextMock.when(returnLinks).thenReturn(links); +// +// // blacklist user1 in icon +// score.invoke(owner, "addBlacklistAddress", "icon", user1.toString()); +// +// // handleRequest of coinTransfer coming from harmony +// // fee for this transfer will be handled in harmony side +// +// // value within limit +// Asset asset1 = new Asset(TOKEN1, FIFTY_ICX); +// +// // TransferRequest Message +// TransferRequest request = new TransferRequest(); +// request.setFrom(ETH_ADDR); +// +// // user1 is blacklisted +// request.setTo(user1.toString()); +// request.setAssets(new Asset[]{asset1}); +// +// BTSMessage message = new BTSMessage(); +// message.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); +// message.setData(request.toBytes()); +// +// byte[] _msg = message.toBytes(); +// +// contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), +// eq(user1), eq(FIFTY_ICX), any())).thenReturn(null); +// +// score.invoke(owner, "addRestriction"); +// +// call = () -> score.invoke(bmcMock, "handleBTPMessage", +// "fromString", "svc", BigInteger.ONE, _msg); +// expectErrorMessage(call, "_to user is Blacklisted"); +// +// score.invoke(owner, "removeBlacklistAddress", "icon", user1.toString()); +// score.invoke(bmcMock, "handleBTPMessage", +// "fromString", "svc", BigInteger.ONE, _msg); +// +// // another transfer, but over the limit +// // user not blacklisted +// +// Asset asset2 = new Asset(TOKEN1, FIFTY_ICX.add(TWO_HUNDRED_ICX)); +// // TransferRequest Message +// TransferRequest request2 = new TransferRequest(); +// request2.setFrom(ETH_ADDR); +// request2.setTo(user1.toString()); +// request2.setAssets(new Asset[]{asset2}); +// +// BTSMessage message2 = new BTSMessage(); +// message2.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); +// message2.setData(request2.toBytes()); +// +// byte[] _msg2 = message2.toBytes(); +// contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), +// eq(user1), eq(FIFTY_ICX.add(TWO_HUNDRED_ICX)), any())).thenReturn(null); +// +// call = () -> score.invoke(bmcMock, "handleBTPMessage", +// "fromString", "svc", BigInteger.TWO, _msg2); +// expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); +// +// // limit not set +// // any amount should be able to be transferred off chain +// String TOKEN2 = "Token 2"; +// BigInteger MILLION = BigInteger.valueOf(10_000_000L).multiply(ICX); +// score.invoke(owner, "register", +// TOKEN2, TOKEN2, 18, BigInteger.valueOf(10), +// ICX, token1.getAddress()); +// +// assertEquals(BigInteger.ZERO, score.call("getTokenLimit", TOKEN2)); +// +// Asset asset3 = new Asset(TOKEN2, MILLION); +// +// +// // TransferRequest Message +// TransferRequest request3 = new TransferRequest(); +// request3.setFrom(ETH_ADDR); +// +// // user1 is blacklisted +// request3.setTo(user1.toString()); +// request3.setAssets(new Asset[]{asset3}); +// +// BTSMessage message3 = new BTSMessage(); +// message3.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); +// message3.setData(request3.toBytes()); +// +// byte[] _msg3 = message3.toBytes(); +// +// contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), +// eq(user1), eq(MILLION), any())).thenReturn(null); +// +// // change token limit +// score.invoke(owner, "changeLimit", TOKEN2, TWO_HUNDRED_ICX); +// call = () -> score.invoke(bmcMock, "handleBTPMessage", +// "fromString", "svc", BigInteger.valueOf(3), _msg3); +// expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); +// +// // disable restrictions +// score.invoke(owner, "disableRestrictions"); +// score.invoke(bmcMock, "handleBTPMessage", +// "fromString", "svc", BigInteger.valueOf(3), _msg3); +// } + + @Test + @Order(17) + @DisplayName("add and remove owner") + public void ownerTests() { + String expectedErrorMessage = "caller is not owner"; + Account user1 = sm.createAccount(10); + Account user2 = sm.createAccount(10); + + // Scenario 17: Non-Owner tries to add a new Owner + Executable call = () -> score.invoke(nonOwner, "addOwner", owner.getAddress()); + expectErrorMessage(call, expectedErrorMessage); + + // owner tries to add themselves + call = () -> score.invoke(owner, "addOwner", owner.getAddress()); + expectErrorMessage(call, "given address is score owner"); + + // Scenario 18: Current Owner adds a new Owner + score.invoke(owner, "addOwner", user1.getAddress()); + assertEquals(true, score.call("isOwner", user1.getAddress())); + Address[] owners = (Address[]) score.call("getOwners"); + assertEquals(owner.getAddress(), owners[0]); + assertEquals(user1.getAddress(), owners[1]); + + // Scenario 19: After adding a new Owner, owner registers a new coin + Address rndmAddr = Account.newScoreAccount(13).getAddress(); + register("Random Token", rndmAddr); + assertEquals(rndmAddr, score.call("coinId", "Random Token")); + + // Scenario 20: New Owner registers a new coin + rndmAddr = Account.newScoreAccount(14).getAddress(); + register("wBTC", rndmAddr); + assertEquals(rndmAddr, score.call("coinId", "wBTC")); + + // newly added owner tries to add owner + score.invoke(user1, "addOwner", user2.getAddress()); + assertEquals(true, score.call("isOwner", user2.getAddress())); + + //Scenario 30: Current Owner removes another Owner + score.invoke(user2, "removeOwner", user1.getAddress()); + assertEquals(false, score.call("isOwner", user1.getAddress())); + + // owner tries to add itself again + call = () -> score.invoke(user2, "addOwner", user2.getAddress()); + expectErrorMessage(call,"already exists owner" ); + + // Scenario 31: The last Owner removes him/herself + score.invoke(user2, "removeOwner", user2.getAddress()); + assertEquals(false, score.call("isOwner", user2.getAddress())); + } + + @Test + @Order(18) + public void setFeeRatio() { + registerWrapped(); + + // Scenario 10: None-ownership role client updates a new fee ratio + // Scenario 13: Non-ownership role client updates a new fixed_fee + Executable call = () -> score.invoke(nonOwner, "setFeeRatio", + PARA, BigInteger.valueOf(10), ICX); + expectErrorMessage(call, "require owner access" ); + + call = () -> score.invoke(owner, "setFeeRatio", + PARA, BigInteger.valueOf(10).negate(), ICX.negate()); + expectErrorMessage(call, "The feeNumerator should be less " + + "than FEE_DENOMINATOR and feeNumerator should be greater than 1"); + + // Scenario 11: Fee_numerator is set higher than fee_denominator + call = () -> score.invoke(owner, "setFeeRatio", + PARA, ICX, ICX.negate()); + expectErrorMessage(call, "The feeNumerator should be less " + + "than FEE_DENOMINATOR and feeNumerator should be greater than 1"); + + call = () -> score.invoke(owner, "setFeeRatio", + PARA, BigInteger.valueOf(10), ICX.negate()); + expectErrorMessage(call, "Fixed fee cannot be less than zero"); + + call = () -> score.invoke(owner, "setFeeRatio", + "LAMB", BigInteger.valueOf(100), ICX); + expectErrorMessage(call, "Not supported Coin"); + + // Scenario 9: Contract’s owner updates a new fee ratio + // Scenario 12: Contract’s owner updates fixed fee + score.invoke(owner, "setFeeRatio", + PARA, BigInteger.valueOf(100), ICX); + } + + @Test + @Order(19) + public void coinDetails() { + // Scenario 15: Query a valid supporting coin + registerWrapped(); + assertEquals(wrappedIRC2.getAddress(), score.call("coinId", PARA)); + + // Scenario 16: Query an invalid supporting coin + assertEquals(null, score.call("coinId", "DUM")); + + } +} \ No newline at end of file diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/OwnershipTest.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/OwnershipTest.java index dd21707f7..a003728da 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/OwnershipTest.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/OwnershipTest.java @@ -1,118 +1,118 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package foundation.icon.btp.bts; - -import foundation.icon.jsonrpc.Address; -import foundation.icon.score.test.ScoreIntegrationTest; -import org.junit.jupiter.api.TestInfo; - -import java.math.BigInteger; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class OwnershipTest implements BTSIntegrationTest { - static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); - static String string = ""; - static BigInteger bigInteger = BigInteger.ONE; - - static boolean isExistsOwner(Address address) { - return btsOwnerManager.isOwner(address); - } - - static void addOwner(Address address) { - btsOwnerManager.addOwner(address); - assertTrue(isExistsOwner(address)); - } - - static void removeOwner(Address address) { - btsOwnerManager.removeOwner(address); - assertFalse(isExistsOwner(address)); - } - - static void clearOwner(Address address) { - if (isExistsOwner(address)) { - System.out.println("clear owner address:"+address); - removeOwner(address); - } - } - - @Override - public void clearIfExists(TestInfo testInfo) { - String testMethod = testInfo.getTestMethod().orElseThrow().getName(); - if (!testMethod.endsWith("RevertUnauthorized")) { - clearOwner(address); - } - } - - // @Test - // void addOwnerShouldSuccess() { - // addOwner(address); - // } - - // static void assertAlreadyExists(Executable executable) { - // AssertBTSException.assertUnknown(executable); - // } - - // @Test - // void addOwnerShouldRevertAlreadyExists() { - // addOwner(address); - - // assertAlreadyExists(() -> addOwner(address)); - // } - - // @Test - // void removeOwnerShouldSuccess() { - // addOwner(address); - - // removeOwner(address); - // } - - // static void assertNotExists(Executable executable) { - // AssertBTSException.assertUnknown(executable); - // } - - // @Test - // void removeOwnerShouldRevertNotExists() { - // assertNotExists(() -> removeOwner(address)); - // } - - // static void assertUnauthorized(Executable executable) { - // AssertBTSException.assertUnauthorized(executable); - // } - - // @Test - // void addOwnerShouldRevertUnauthorized() { - // assertUnauthorized(() -> btsOwnerManagerWithTester.addOwner(address)); - // } - - // @Test - // void removeOwnerShouldRevertUnauthorized() { - // assertUnauthorized(() -> btsOwnerManagerWithTester.removeOwner(address)); - // } - - // @Test - // void registerShouldRevertUnauthorized() { - // assertUnauthorized(() -> btsWithTester.register(string)); - // } - - // @Test - // void setFeeRateShouldRevertUnauthorized() { - // assertUnauthorized(() -> btsWithTester.setFeeRatio(bigInteger)); - // } - -} +// /* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +// package foundation.icon.btp.bts; + +// import foundation.icon.jsonrpc.Address; +// import foundation.icon.score.test.ScoreIntegrationTest; +// import org.junit.jupiter.api.TestInfo; + +// import java.math.BigInteger; + +// import static org.junit.jupiter.api.Assertions.assertFalse; +// import static org.junit.jupiter.api.Assertions.assertTrue; + +// public class OwnershipTest implements BTSIntegrationTest { +// static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); +// static String string = ""; +// static BigInteger bigInteger = BigInteger.ONE; + +// static boolean isExistsOwner(Address address) { +// return btsOwnerManager.isOwner(address); +// } + +// static void addOwner(Address address) { +// btsOwnerManager.addOwner(address); +// assertTrue(isExistsOwner(address)); +// } + +// static void removeOwner(Address address) { +// btsOwnerManager.removeOwner(address); +// assertFalse(isExistsOwner(address)); +// } + +// static void clearOwner(Address address) { +// if (isExistsOwner(address)) { +// System.out.println("clear owner address:"+address); +// removeOwner(address); +// } +// } + +// @Override +// public void clearIfExists(TestInfo testInfo) { +// String testMethod = testInfo.getTestMethod().orElseThrow().getName(); +// if (!testMethod.endsWith("RevertUnauthorized")) { +// clearOwner(address); +// } +// } + +// // @Test +// // void addOwnerShouldSuccess() { +// // addOwner(address); +// // } + +// // static void assertAlreadyExists(Executable executable) { +// // AssertBTSException.assertUnknown(executable); +// // } + +// // @Test +// // void addOwnerShouldRevertAlreadyExists() { +// // addOwner(address); + +// // assertAlreadyExists(() -> addOwner(address)); +// // } + +// // @Test +// // void removeOwnerShouldSuccess() { +// // addOwner(address); + +// // removeOwner(address); +// // } + +// // static void assertNotExists(Executable executable) { +// // AssertBTSException.assertUnknown(executable); +// // } + +// // @Test +// // void removeOwnerShouldRevertNotExists() { +// // assertNotExists(() -> removeOwner(address)); +// // } + +// // static void assertUnauthorized(Executable executable) { +// // AssertBTSException.assertUnauthorized(executable); +// // } + +// // @Test +// // void addOwnerShouldRevertUnauthorized() { +// // assertUnauthorized(() -> btsOwnerManagerWithTester.addOwner(address)); +// // } + +// // @Test +// // void removeOwnerShouldRevertUnauthorized() { +// // assertUnauthorized(() -> btsOwnerManagerWithTester.removeOwner(address)); +// // } + +// // @Test +// // void registerShouldRevertUnauthorized() { +// // assertUnauthorized(() -> btsWithTester.register(string)); +// // } + +// // @Test +// // void setFeeRateShouldRevertUnauthorized() { +// // assertUnauthorized(() -> btsWithTester.setFeeRatio(bigInteger)); +// // } + +// } diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferEndEventLog.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferEndEventLog.java index 42ebb6c17..75cbc5ea4 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferEndEventLog.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferEndEventLog.java @@ -1,78 +1,78 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// /* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ -package foundation.icon.btp.bts; +// package foundation.icon.btp.bts; -import foundation.icon.jsonrpc.Address; -import foundation.icon.jsonrpc.IconJsonModule; -import foundation.icon.jsonrpc.model.TransactionResult; -import foundation.icon.score.test.ScoreIntegrationTest; -import foundation.icon.score.util.StringUtil; +// import foundation.icon.jsonrpc.Address; +// import foundation.icon.jsonrpc.IconJsonModule; +// import foundation.icon.jsonrpc.model.TransactionResult; +// import foundation.icon.score.test.ScoreIntegrationTest; +// import foundation.icon.score.util.StringUtil; -import java.math.BigInteger; -import java.util.List; -import java.util.function.Predicate; +// import java.math.BigInteger; +// import java.util.List; +// import java.util.function.Predicate; -public class TransferEndEventLog { - static final String SIGNATURE = "TransferEnd(Address,int,int,bytes)"; - private Address from; - private BigInteger sn; - private BigInteger code; - private byte[] msg; +// public class TransferEndEventLog { +// static final String SIGNATURE = "TransferEnd(Address,int,int,bytes)"; +// private Address from; +// private BigInteger sn; +// private BigInteger code; +// private byte[] msg; - public TransferEndEventLog(TransactionResult.EventLog el) { - from = new Address(el.getIndexed().get(1)); - sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); - code = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); - msg = IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(2)); - } +// public TransferEndEventLog(TransactionResult.EventLog el) { +// from = new Address(el.getIndexed().get(1)); +// sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); +// code = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); +// msg = IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(2)); +// } - public Address getFrom() { - return from; - } +// public Address getFrom() { +// return from; +// } - public BigInteger getSn() { - return sn; - } +// public BigInteger getSn() { +// return sn; +// } - public BigInteger getCode() { - return code; - } +// public BigInteger getCode() { +// return code; +// } - public byte[] getMsg() { - return msg; - } +// public byte[] getMsg() { +// return msg; +// } - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("TransferEndEventLog{"); - sb.append("from=").append(from); - sb.append(", sn=").append(sn); - sb.append(", code=").append(code); - sb.append(", msg=").append(StringUtil.toString(msg)); - sb.append('}'); - return sb.toString(); - } +// @Override +// public String toString() { +// final StringBuilder sb = new StringBuilder("TransferEndEventLog{"); +// sb.append("from=").append(from); +// sb.append(", sn=").append(sn); +// sb.append(", code=").append(code); +// sb.append(", msg=").append(StringUtil.toString(msg)); +// sb.append('}'); +// return sb.toString(); +// } - public static List eventLogs( - TransactionResult txr, Address address, Predicate filter) { - return ScoreIntegrationTest.eventLogs(txr, - TransferEndEventLog.SIGNATURE, - address, - TransferEndEventLog::new, - filter); - } -} +// public static List eventLogs( +// TransactionResult txr, Address address, Predicate filter) { +// return ScoreIntegrationTest.eventLogs(txr, +// TransferEndEventLog.SIGNATURE, +// address, +// TransferEndEventLog::new, +// filter); +// } +// } diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferStartEventLog.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferStartEventLog.java index c9e6853d4..27bfdaa90 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferStartEventLog.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/TransferStartEventLog.java @@ -1,92 +1,92 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// /* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ -package foundation.icon.btp.bts; +// package foundation.icon.btp.bts; -import foundation.icon.jsonrpc.Address; -import foundation.icon.jsonrpc.IconJsonModule; -import foundation.icon.jsonrpc.model.TransactionResult; -import foundation.icon.score.test.ScoreIntegrationTest; -import foundation.icon.score.util.StringUtil; -import score.Context; -import score.ObjectReader; +// import foundation.icon.jsonrpc.Address; +// import foundation.icon.jsonrpc.IconJsonModule; +// import foundation.icon.jsonrpc.model.TransactionResult; +// import foundation.icon.score.test.ScoreIntegrationTest; +// import foundation.icon.score.util.StringUtil; +// import score.Context; +// import score.ObjectReader; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; +// import java.math.BigInteger; +// import java.util.ArrayList; +// import java.util.List; +// import java.util.function.Predicate; -public class TransferStartEventLog { - static final String SIGNATURE = "TransferStart(Address,str,int,bytes)"; - private Address from; - private String to; - private BigInteger sn; - private AssetTransferDetail[] assets; +// public class TransferStartEventLog { +// static final String SIGNATURE = "TransferStart(Address,str,int,bytes)"; +// private Address from; +// private String to; +// private BigInteger sn; +// private AssetTransferDetail[] assets; - public TransferStartEventLog(TransactionResult.EventLog el) { - from = new Address(el.getIndexed().get(1)); - to = el.getData().get(0); - sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); - assets = toAssetTransferDetailArray(IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(2))); - } +// public TransferStartEventLog(TransactionResult.EventLog el) { +// from = new Address(el.getIndexed().get(1)); +// to = el.getData().get(0); +// sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); +// assets = toAssetTransferDetailArray(IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(2))); +// } - public static AssetTransferDetail[] toAssetTransferDetailArray(byte[] bytes) { - ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); - reader.beginList(); - List list = new ArrayList<>(); - while (reader.hasNext()) { - list.add(reader.read(AssetTransferDetail.class)); - } - reader.end(); - return list.toArray(new AssetTransferDetail[]{}); - } +// public static AssetTransferDetail[] toAssetTransferDetailArray(byte[] bytes) { +// ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); +// reader.beginList(); +// List list = new ArrayList<>(); +// while (reader.hasNext()) { +// list.add(reader.read(AssetTransferDetail.class)); +// } +// reader.end(); +// return list.toArray(new AssetTransferDetail[]{}); +// } - public Address getFrom() { - return from; - } +// public Address getFrom() { +// return from; +// } - public String getTo() { - return to; - } +// public String getTo() { +// return to; +// } - public BigInteger getSn() { - return sn; - } +// public BigInteger getSn() { +// return sn; +// } - public AssetTransferDetail[] getAssets() { - return assets; - } +// public AssetTransferDetail[] getAssets() { +// return assets; +// } - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("TransferStartEventLog{"); - sb.append("from=").append(from); - sb.append(", to='").append(to).append('\''); - sb.append(", sn=").append(sn); - sb.append(", assets=").append(StringUtil.toString(assets)); - sb.append('}'); - return sb.toString(); - } +// @Override +// public String toString() { +// final StringBuilder sb = new StringBuilder("TransferStartEventLog{"); +// sb.append("from=").append(from); +// sb.append(", to='").append(to).append('\''); +// sb.append(", sn=").append(sn); +// sb.append(", assets=").append(StringUtil.toString(assets)); +// sb.append('}'); +// return sb.toString(); +// } - public static List eventLogs( - TransactionResult txr, Address address, Predicate filter) { - return ScoreIntegrationTest.eventLogs(txr, - TransferStartEventLog.SIGNATURE, - address, - TransferStartEventLog::new, - filter); - } -} +// public static List eventLogs( +// TransactionResult txr, Address address, Predicate filter) { +// return ScoreIntegrationTest.eventLogs(txr, +// TransferStartEventLog.SIGNATURE, +// address, +// TransferStartEventLog::new, +// filter); +// } +// } diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/UnknownResponseEventLog.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/UnknownResponseEventLog.java index 7ce34e87f..9124129c7 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/UnknownResponseEventLog.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/UnknownResponseEventLog.java @@ -1,63 +1,67 @@ -/* - * Copyright 2021 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package foundation.icon.btp.bts; - -import foundation.icon.jsonrpc.Address; -import foundation.icon.jsonrpc.IconJsonModule; -import foundation.icon.jsonrpc.model.TransactionResult; -import foundation.icon.score.test.ScoreIntegrationTest; - -import java.math.BigInteger; -import java.util.List; -import java.util.function.Predicate; - -public class UnknownResponseEventLog { - static final String SIGNATURE = "UnknownResponse(str,int)"; - private String from; - private BigInteger sn; - - public UnknownResponseEventLog(TransactionResult.EventLog el) { - from = el.getIndexed().get(1); - sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); - } - - public String getFrom() { - return from; - } - - public BigInteger getSn() { - return sn; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("UnknownResponseEventLog{"); - sb.append("from='").append(from).append('\''); - sb.append(", sn=").append(sn); - sb.append('}'); - return sb.toString(); - } - - public static List eventLogs( - TransactionResult txr, Address address, Predicate filter) { - return ScoreIntegrationTest.eventLogs(txr, - UnknownResponseEventLog.SIGNATURE, - address, - UnknownResponseEventLog::new, - filter); - } -} +///* +// * Copyright 2021 ICON Foundation +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package foundation.icon.btp.bts; +// +//import foundation.icon.jsonrpc.Address; +//import foundation.icon.jsonrpc.IconJsonModule; +//import foundation.icon.jsonrpc.model.TransactionResult; +//import foundation.icon.score.test.ScoreIntegrationTest; +//import foundation.icon.score.util.StringUtil; +//import score.Context; +//import score.ObjectReader; +// +//import java.math.BigInteger; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.function.Predicate; +// +//public class UnknownResponseEventLog { +// static final String SIGNATURE = "UnknownResponse(str,int)"; +// private String from; +// private BigInteger sn; +// +// public UnknownResponseEventLog(TransactionResult.EventLog el) { +// from = el.getIndexed().get(1); +// sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); +// } +// +// public String getFrom() { +// return from; +// } +// +// public BigInteger getSn() { +// return sn; +// } +// +// @Override +// public String toString() { +// final StringBuilder sb = new StringBuilder("UnknownResponseEventLog{"); +// sb.append("from='").append(from).append('\''); +// sb.append(", sn=").append(sn); +// sb.append('}'); +// return sb.toString(); +// } +// +// public static List eventLogs( +// TransactionResult txr, Address address, Predicate filter) { +// return ScoreIntegrationTest.eventLogs(txr, +// UnknownResponseEventLog.SIGNATURE, +// address, +// UnknownResponseEventLog::new, +// filter); +// } +//} \ No newline at end of file From 70202f6a6ae26b76f6273df49935b227d7f2e2a5 Mon Sep 17 00:00:00 2001 From: Manish Date: Thu, 4 Aug 2022 13:50:59 +0545 Subject: [PATCH 4/6] icon-bsc deploy script should omit bsr-#45 --- devnet/docker/icon-bsc/scripts/provision.sh | 4 ++-- devnet/docker/icon-bsc/scripts/token.javascore.sh | 4 ++-- docker/javascore/Dockerfile | 4 ++-- docker/javascore/build.sh | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/devnet/docker/icon-bsc/scripts/provision.sh b/devnet/docker/icon-bsc/scripts/provision.sh index bbdc2ab04..cea850636 100644 --- a/devnet/docker/icon-bsc/scripts/provision.sh +++ b/devnet/docker/icon-bsc/scripts/provision.sh @@ -22,7 +22,7 @@ provision() { #deploy icon deploy_javascore_bmc - deploy_javascore_bsr + #deploy_javascore_bsr deploy_javascore_bts deploy_javascore_irc2 deploy_javascore_eth @@ -43,7 +43,7 @@ provision() { configure_javascore_bmc_setFeeAggregator configure_javascore_add_bts configure_javascore_add_bts_owner - configure_javascore_set_bsr + #configure_javascore_set_bsr configure_javascore_bts_setICXFee #configure bsc echo "CONFIGURE BSC" diff --git a/devnet/docker/icon-bsc/scripts/token.javascore.sh b/devnet/docker/icon-bsc/scripts/token.javascore.sh index be105a753..e844699f1 100644 --- a/devnet/docker/icon-bsc/scripts/token.javascore.sh +++ b/devnet/docker/icon-bsc/scripts/token.javascore.sh @@ -33,12 +33,12 @@ deploy_javascore_bsr() { deploy_javascore_bts() { echo "deploying javascore bts" cd $CONFIG_DIR - local irc2Tradeable_score=$(xxd -p $CONTRACTS_DIR/javascore/irc2Tradeable.jar | tr -d '\n') goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/bts.jar \ --content_type application/java \ --param _name="ICX" \ --param _bmc=$(cat btp.icon.bmc) \ - --param _serializedIrc2="$irc2Tradeable_score" | jq -r . > tx.icon.bts + --param _serializedIrc2=$(xxd -p $CONTRACTS_DIR/javascore/irc2Tradeable.jar | tr -d '\n') | jq -r . > tx.icon.bts + sleep 2 extract_scoreAddress tx.icon.bts btp.icon.bts } diff --git a/docker/javascore/Dockerfile b/docker/javascore/Dockerfile index cc6ea7307..dcb3fcd74 100644 --- a/docker/javascore/Dockerfile +++ b/docker/javascore/Dockerfile @@ -17,14 +17,14 @@ WORKDIR ${JAVASCORE_SRC} RUN gradle clean RUN gradle bmc:optimizedJar -RUN gradle bsr:optimizedJar +#RUN gradle bsr:optimizedJar RUN gradle bts:optimizedJar #copy jars to dist WORKDIR ${ROOT} RUN mkdir /dist RUN cp ${JAVASCORE_SRC}/bmc/build/libs/bmc-0.1.0-optimized.jar ${DIST_DIR}/bmc.jar -RUN cp ${JAVASCORE_SRC}/bsr/build/libs/restrictions-0.1.0-optimized.jar ${DIST_DIR}/bsr.jar +#RUN cp ${JAVASCORE_SRC}/bsr/build/libs/restrictions-0.1.0-optimized.jar ${DIST_DIR}/bsr.jar RUN cp ${JAVASCORE_SRC}/bts/build/libs/bts-0.1.0-optimized.jar ${DIST_DIR}/bts.jar RUN cp ${JAVASCORE_SRC}/lib/irc2Tradeable-0.1.0-optimized.jar ${DIST_DIR}/irc2Tradeable.jar diff --git a/docker/javascore/build.sh b/docker/javascore/build.sh index ebcad8afd..67a5f6d30 100755 --- a/docker/javascore/build.sh +++ b/docker/javascore/build.sh @@ -16,7 +16,7 @@ build_image() { docker cp javascore-dist:/dist/bmc.jar ${DIST_DIR} docker cp javascore-dist:/dist/irc2.jar ${DIST_DIR} docker cp javascore-dist:/dist/irc2Tradeable.jar ${DIST_DIR} - docker cp javascore-dist:/dist/bsr.jar ${DIST_DIR} + #docker cp javascore-dist:/dist/bsr.jar ${DIST_DIR} docker cp javascore-dist:/dist/bts.jar ${DIST_DIR} docker rm -f javascore-dist From 61652ddc3c9cf2ed88d70fae187143b3cd385375 Mon Sep 17 00:00:00 2001 From: izyak Date: Thu, 4 Aug 2022 15:30:11 +0545 Subject: [PATCH 5/6] Update unit tests --- .../icon/btp/bts/AbstractBTPTokenService.java | 8 +- .../java/foundation/icon/btp/bts/BTSTest.java | 333 ++++++++++-------- 2 files changed, 181 insertions(+), 160 deletions(-) diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java index fcf3a2d3f..30900675d 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/AbstractBTPTokenService.java @@ -37,7 +37,7 @@ public class AbstractBTPTokenService extends TestBase { public String ETH_ADDR = "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"; BTPAddress btpAddress1 = new BTPAddress("network","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); - BTPAddress btpAddress2 = new BTPAddress("network2","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + BTPAddress btpAddress2 = new BTPAddress("icon","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); BTPAddress btpAddress3 = new BTPAddress("harmony","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); String[] links = new String[] {btpAddress1.toString(), btpAddress2.toString(), btpAddress3.toString()}; @@ -133,8 +133,9 @@ protected byte[] blacklistRemoveSuccessfulResponse() { protected void tokenLimitBTPMessage() { getLinksMock(); for (String link: links) { + BTPAddress addr = BTPAddress.valueOf(link); contextMock.when(() -> Context.call(eq(bmcMock.getAddress()), - eq("sendMessage"), eq(link), any(), + eq("sendMessage"), eq(addr.net()), any(), any(), any())).thenReturn(null); } } @@ -161,20 +162,17 @@ public void sendBTPMessageMock() { } public void register(String name, Address addr) { -// tokenLimitBTPMessage(); score.invoke(owner, "register", name, name, 18, BigInteger.valueOf(10), BigInteger.ONE, addr); } public void register() { -// tokenLimitBTPMessage(); score.invoke(owner, "register", TEST_TOKEN, "TTK", 18, BigInteger.ONE, BigInteger.ONE, irc2.getAddress()); } public void registerWrapped() { -// tokenLimitBTPMessage(); Verification deployWrappedToken = () -> Context.deploy(any(), eq(PARA), eq(PARA),eq(18)); contextMock.when(deployWrappedToken).thenReturn(wrappedIRC2.getAddress()); diff --git a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java index 590a6a976..1a516456c 100644 --- a/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java +++ b/javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java @@ -293,6 +293,12 @@ public void transfer() { score.invoke(irc2,"tokenFallback", nonOwner.getAddress(), BigInteger.valueOf(10), "0".getBytes()); score.invoke(nonOwner, "transfer", TEST_TOKEN, BigInteger.valueOf(10), btpaddr); + contextMock.when(() -> Context.call( + eq(BigInteger.class), + eq(irc2.getAddress()), + eq("balanceOf"), + eq(nonOwner.getAddress()) + )).thenReturn(BigInteger.valueOf(100)); Map balance = (Map) score.call("balanceOf", nonOwner.getAddress(), TEST_TOKEN); // currently, still in locked state // will be updated once response comes from relayer via bmc @@ -360,6 +366,13 @@ public void transferBatch() { score.invoke(nonOwner, "transferBatch", coinNames, values, destination); verify(scoreSpy).TransferStart(eq(nonOwner.getAddress()), eq(destination), eq(BigInteger.ONE), any()); + contextMock.when(() -> Context.call( + eq(BigInteger.class), + any(), + eq("balanceOf"), + eq(nonOwner.getAddress()) + )).thenReturn(BigInteger.valueOf(100)); + Map bal = (Map) score.call("balanceOf", nonOwner.getAddress(), coinNames[0]); assertEquals(bal.get("usable"), BigInteger.valueOf(8)); assertEquals(bal.get("locked"), BigInteger.valueOf(10)); @@ -487,6 +500,11 @@ public void handleBTPMessage2() { contextMock.when(() -> Context.call(eq(BigInteger.class),eq(wrappedIRC2.getAddress()), eq("allowance"), eq(nonOwner.getAddress()), eq(score.getAddress()))).thenReturn(BigInteger.valueOf(1000)); + contextMock.when(() -> Context.call(eq(BigInteger.class), eq(token1.getAddress()), + eq("balanceOf"), eq(nonOwner.getAddress()))).thenReturn(BigInteger.valueOf(100)); + contextMock.when(() -> Context.call(eq(BigInteger.class), eq(token2.getAddress()), + eq("balanceOf"),eq(nonOwner.getAddress()))).thenReturn(BigInteger.valueOf(100)); + List> balances = (List>) score.call( "balanceOfBatch", nonOwner.getAddress(), new String[]{"Token1","Token2", PARA, ICON}); @@ -570,159 +588,164 @@ void handleBTPMessage4() { score.invoke(bmcMock, "handleBTPMessage", "fromString","bmc",BigInteger.ONE, message.toBytes()); } + + @Test + @Order(16) + @DisplayName("change limit, check restrictions, check fees") + public void changeLimit() { + String TOKEN1 = "Token1"; + BigInteger TWO_HUNDRED_ICX = BigInteger.valueOf(200).multiply(ICX); + BigInteger FIFTY_ICX = BigInteger.valueOf(50).multiply(ICX); + Address user1 = sm.createAccount(20).getAddress(); + + Account token1 = Account.newScoreAccount(50); + + Executable call; + + score.invoke(owner, "register", + TOKEN1, TOKEN1, 18, BigInteger.valueOf(10), + ICX, token1.getAddress()); + + assertEquals(null, score.call("getTokenLimit", TOKEN1)); + + tokenLimitBTPMessage(); + score.invoke(owner, "setTokenLimit", new String[]{TOKEN1}, new BigInteger[]{BigInteger.valueOf(200)}); + + assertEquals(BigInteger.valueOf(200), score.call("getTokenLimit", TOKEN1)); // -// @Test -// @Order(16) -// @DisplayName("change limit, check restrictions, check fees") -// public void changeLimit() { -// String TOKEN1 = "Token1"; -// BigInteger TWO_HUNDRED_ICX = BigInteger.valueOf(200).multiply(ICX); -// BigInteger FIFTY_ICX = BigInteger.valueOf(50).multiply(ICX); -// Address user1 = sm.createAccount(20).getAddress(); -// -// Account token1 = Account.newScoreAccount(50); -// -// Executable call; -// -// score.invoke(owner, "register", -// TOKEN1, TOKEN1, 18, BigInteger.valueOf(10), -// ICX, token1.getAddress()); -// -// assertEquals(BigInteger.ZERO, score.call("getTokenLimit", TOKEN1)); -// -// tokenLimitBTPMessage(); -// score.invoke(owner, "changeLimit", TOKEN1, BigInteger.valueOf(200)); -// -// assertEquals(BigInteger.valueOf(200), score.call("getTokenLimit", TOKEN1)); -// -// call = () -> score.invoke(owner, "changeLimit", TOKEN1, -// TWO_HUNDRED_ICX.negate()); -// expectErrorMessage(call, "Invalid value"); -// call = () -> score.invoke(owner, "changeLimit", "TokenBSH", -// TWO_HUNDRED_ICX); -// expectErrorMessage(call, "Not registered"); -// -// score.invoke(owner, "changeLimit", TOKEN1, TWO_HUNDRED_ICX); -// -// assertEquals(TWO_HUNDRED_ICX, score.call("getTokenLimit", TOKEN1)); -// -// // send from Harmony to ICON -// // reciever adddress is blacklisted in icon -// -// Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), -// eq("sendMessage"), any(), any(), any(), any()); -// -// Verification returnLinks = () -> Context.call(eq(String[].class), -// eq(bmcMock.getAddress()), eq("getLinks"), any()); -// -// // these 3 BTP addresses are currently supported by ICON Bridge -// BTPAddress btpAddress1 = new BTPAddress("icon","cx0E636c8cF214a9d702C5E4a6D8c020be217766D3"); -// BTPAddress btpAddress2 = new BTPAddress("network","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); -// BTPAddress btpAddress3 = new BTPAddress("harmony","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); -// String[] links = new String[] {btpAddress1.toString(), btpAddress2.toString(), btpAddress3.toString()}; -// -// contextMock.when(sendMessage).thenReturn(null); -// contextMock.when(returnLinks).thenReturn(links); -// -// // blacklist user1 in icon -// score.invoke(owner, "addBlacklistAddress", "icon", user1.toString()); -// -// // handleRequest of coinTransfer coming from harmony -// // fee for this transfer will be handled in harmony side -// -// // value within limit -// Asset asset1 = new Asset(TOKEN1, FIFTY_ICX); -// -// // TransferRequest Message -// TransferRequest request = new TransferRequest(); -// request.setFrom(ETH_ADDR); -// -// // user1 is blacklisted -// request.setTo(user1.toString()); -// request.setAssets(new Asset[]{asset1}); -// -// BTSMessage message = new BTSMessage(); -// message.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); -// message.setData(request.toBytes()); -// -// byte[] _msg = message.toBytes(); -// -// contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), -// eq(user1), eq(FIFTY_ICX), any())).thenReturn(null); -// -// score.invoke(owner, "addRestriction"); -// -// call = () -> score.invoke(bmcMock, "handleBTPMessage", -// "fromString", "svc", BigInteger.ONE, _msg); -// expectErrorMessage(call, "_to user is Blacklisted"); -// -// score.invoke(owner, "removeBlacklistAddress", "icon", user1.toString()); -// score.invoke(bmcMock, "handleBTPMessage", -// "fromString", "svc", BigInteger.ONE, _msg); -// -// // another transfer, but over the limit -// // user not blacklisted -// -// Asset asset2 = new Asset(TOKEN1, FIFTY_ICX.add(TWO_HUNDRED_ICX)); -// // TransferRequest Message -// TransferRequest request2 = new TransferRequest(); -// request2.setFrom(ETH_ADDR); -// request2.setTo(user1.toString()); -// request2.setAssets(new Asset[]{asset2}); -// -// BTSMessage message2 = new BTSMessage(); -// message2.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); -// message2.setData(request2.toBytes()); -// -// byte[] _msg2 = message2.toBytes(); -// contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), -// eq(user1), eq(FIFTY_ICX.add(TWO_HUNDRED_ICX)), any())).thenReturn(null); -// -// call = () -> score.invoke(bmcMock, "handleBTPMessage", -// "fromString", "svc", BigInteger.TWO, _msg2); -// expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); -// -// // limit not set -// // any amount should be able to be transferred off chain -// String TOKEN2 = "Token 2"; -// BigInteger MILLION = BigInteger.valueOf(10_000_000L).multiply(ICX); -// score.invoke(owner, "register", -// TOKEN2, TOKEN2, 18, BigInteger.valueOf(10), -// ICX, token1.getAddress()); -// -// assertEquals(BigInteger.ZERO, score.call("getTokenLimit", TOKEN2)); -// -// Asset asset3 = new Asset(TOKEN2, MILLION); -// -// -// // TransferRequest Message -// TransferRequest request3 = new TransferRequest(); -// request3.setFrom(ETH_ADDR); -// -// // user1 is blacklisted -// request3.setTo(user1.toString()); -// request3.setAssets(new Asset[]{asset3}); -// -// BTSMessage message3 = new BTSMessage(); -// message3.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); -// message3.setData(request3.toBytes()); -// -// byte[] _msg3 = message3.toBytes(); -// -// contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), -// eq(user1), eq(MILLION), any())).thenReturn(null); -// -// // change token limit -// score.invoke(owner, "changeLimit", TOKEN2, TWO_HUNDRED_ICX); -// call = () -> score.invoke(bmcMock, "handleBTPMessage", -// "fromString", "svc", BigInteger.valueOf(3), _msg3); -// expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); -// -// // disable restrictions -// score.invoke(owner, "disableRestrictions"); -// score.invoke(bmcMock, "handleBTPMessage", -// "fromString", "svc", BigInteger.valueOf(3), _msg3); -// } + call = () -> score.invoke(owner, "setTokenLimit", new String[]{TOKEN1}, + new BigInteger[]{TWO_HUNDRED_ICX.negate()}); + expectErrorMessage(call, "Invalid value"); + call = () -> score.invoke(owner, "setTokenLimit", new String[]{"TokenBSH"}, + new BigInteger[]{TWO_HUNDRED_ICX}); + expectErrorMessage(call, "Not registered"); + + score.invoke(owner, "setTokenLimit", new String[]{TOKEN1}, new BigInteger[]{TWO_HUNDRED_ICX}); + + assertEquals(TWO_HUNDRED_ICX, score.call("getTokenLimit", TOKEN1)); + +// send from Harmony to ICON +// reciever adddress is blacklisted in icon + + Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), + eq("sendMessage"), any(), any(), any(), any()); + + Verification returnLinks = () -> Context.call(eq(String[].class), + eq(bmcMock.getAddress()), eq("getLinks"), any()); + + // these 3 BTP addresses are currently supported by ICON Bridge + BTPAddress btpAddress1 = new BTPAddress("icon","cx0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + BTPAddress btpAddress2 = new BTPAddress("network","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + BTPAddress btpAddress3 = new BTPAddress("harmony","0x0E636c8cF214a9d702C5E4a6D8c020be217766D3"); + String[] links = new String[] {btpAddress1.toString(), btpAddress2.toString(), btpAddress3.toString()}; + + contextMock.when(sendMessage).thenReturn(null); + contextMock.when(returnLinks).thenReturn(links); + + // blacklist user1 in icon + printSn(); + blackListUser("icon", new String[]{user1.toString()}, BigInteger.valueOf(3)); + + // handleRequest of coinTransfer coming from harmony + // fee for this transfer will be handled in harmony side + + // value within limit + Asset asset1 = new Asset(TOKEN1, FIFTY_ICX); + + // TransferRequest Message + TransferRequest request = new TransferRequest(); + request.setFrom(ETH_ADDR); + + // user1 is blacklisted + request.setTo(user1.toString()); + request.setAssets(new Asset[]{asset1}); + + BTSMessage message = new BTSMessage(); + message.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); + message.setData(request.toBytes()); + + byte[] _msg = message.toBytes(); + + contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), + eq(user1), eq(FIFTY_ICX), any())).thenReturn(null); + + score.invoke(owner, "addRestriction"); + + call = () -> score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.ONE, _msg); + expectErrorMessage(call, "_to user is Blacklisted"); + + removeBlackListedUser("icon", new String[]{user1.toString()}, BigInteger.valueOf(4) ); + + System.out.println("FUCK YOU"); + // check this + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.valueOf(5), _msg); + + // another transfer, but over the limit + // user not blacklisted + + Asset asset2 = new Asset(TOKEN1, FIFTY_ICX.add(TWO_HUNDRED_ICX)); + // TransferRequest Message + TransferRequest request2 = new TransferRequest(); + request2.setFrom(ETH_ADDR); + request2.setTo(user1.toString()); + request2.setAssets(new Asset[]{asset2}); + + BTSMessage message2 = new BTSMessage(); + message2.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); + message2.setData(request2.toBytes()); + + byte[] _msg2 = message2.toBytes(); + contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), + eq(user1), eq(FIFTY_ICX.add(TWO_HUNDRED_ICX)), any())).thenReturn(null); + + call = () -> score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.valueOf(6), _msg2); + expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); + + // limit not set + // any amount should be able to be transferred off chain + String TOKEN2 = "Token 2"; + BigInteger MILLION = BigInteger.valueOf(10_000_000L).multiply(ICX); + score.invoke(owner, "register", + TOKEN2, TOKEN2, 18, BigInteger.valueOf(10), + ICX, token1.getAddress()); + + assertEquals(null, score.call("getTokenLimit", TOKEN2)); + + Asset asset3 = new Asset(TOKEN2, MILLION); + + + // TransferRequest Message + TransferRequest request3 = new TransferRequest(); + request3.setFrom(ETH_ADDR); + + // user1 is blacklisted + request3.setTo(user1.toString()); + request3.setAssets(new Asset[]{asset3}); + + BTSMessage message3 = new BTSMessage(); + message3.setServiceType(BTSMessage.REQUEST_COIN_TRANSFER); + message3.setData(request3.toBytes()); + + byte[] _msg3 = message3.toBytes(); + + contextMock.when(() -> Context.call(eq(token1.getAddress()), eq("transfer"), + eq(user1), eq(MILLION), any())).thenReturn(null); + + + // change token limit + score.invoke(owner, "setTokenLimit", new String[]{TOKEN2}, new BigInteger[]{TWO_HUNDRED_ICX}); + call = () -> score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.valueOf(6), _msg3); + expectErrorMessage(call, "Transfer amount exceeds the transaction limit"); + + // disable restrictions + score.invoke(owner, "disableRestrictions"); + score.invoke(bmcMock, "handleBTPMessage", + "fromString", "svc", BigInteger.valueOf(7), _msg3); + } @Test @Order(17) @@ -787,18 +810,18 @@ public void setFeeRatio() { call = () -> score.invoke(owner, "setFeeRatio", PARA, BigInteger.valueOf(10).negate(), ICX.negate()); - expectErrorMessage(call, "The feeNumerator should be less " + expectErrorMessageIn(call, "The feeNumerator should be less " + "than FEE_DENOMINATOR and feeNumerator should be greater than 1"); // Scenario 11: Fee_numerator is set higher than fee_denominator call = () -> score.invoke(owner, "setFeeRatio", PARA, ICX, ICX.negate()); - expectErrorMessage(call, "The feeNumerator should be less " + expectErrorMessageIn(call, "The feeNumerator should be less " + "than FEE_DENOMINATOR and feeNumerator should be greater than 1"); call = () -> score.invoke(owner, "setFeeRatio", PARA, BigInteger.valueOf(10), ICX.negate()); - expectErrorMessage(call, "Fixed fee cannot be less than zero"); + expectErrorMessageIn(call, "Fixed fee cannot be less than zero"); call = () -> score.invoke(owner, "setFeeRatio", "LAMB", BigInteger.valueOf(100), ICX); From 1d1428830deaea6950b9a89bf233a73664f502f9 Mon Sep 17 00:00:00 2001 From: izyak Date: Thu, 4 Aug 2022 15:38:33 +0545 Subject: [PATCH 6/6] Remove integration tests module --- javascore/settings.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascore/settings.gradle b/javascore/settings.gradle index 2d289d709..8116db1ea 100644 --- a/javascore/settings.gradle +++ b/javascore/settings.gradle @@ -3,6 +3,5 @@ rootProject.name = 'javascore-btp' include ( 'lib', 'bmc', - 'bts', - 'integration-tests' + 'bts' ) \ No newline at end of file