diff --git a/.env.example b/.env.example index ca155c69..04c36efb 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,10 @@ -DEPLOYER_PK="" \ No newline at end of file +FIL_SOL_DOCKER_IMG="fil-sol-dev-env:latest-amd64" + +#Master account +ETH_PK="" + +#Generated F3 account on Calibnet +#IMPORTANT: do not add real funds !!! +F3_ADDR="t3wybme2dab6l3h4zuuzxxteztmtq7jyh6qgrko3urkvhjfqdr33vsus4x5s2ccpuhhc5xscpx3bcpufsf6vzq" +F3_PK="137412325dfbf9757df5d14c645aacffc4803c12798e0b2cdd154772f5799c47" +F3_ID="111415" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2894955e..56bcd0ce 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,7 @@ yarn.lock typechain-types .openzeppelin/ -.env \ No newline at end of file +.env +.vscode + +lib-dev/dev-env/.internal \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index ac90cc6e..2c05f1f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,4 @@ url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable [submodule "lib/forge-std"] path = lib/forge-std - url = https://github.com/foundry-rs/forge-std + url = https://github.com/foundry-rs/forge-std \ No newline at end of file diff --git a/Makefile b/Makefile index 905af9c0..18a3184d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ +hh_localnet_setup_lock := "localnet-setup-running.lock" + ################ BUILD ################ solc := "./bin/solc" @@ -131,6 +133,26 @@ test_deserialize: build test_address: build cd testing && cargo test address -- --nocapture +################ TESTS HARDHAT ################ + +start_localnet: + ./lib-dev/dev-env/1_clean-start-localnet.sh + +restart_localnet: + ./lib-dev/dev-env/2_restart-localnet.sh + +simple_start_localnet: + ./lib-dev/dev-env/3_clean-simple-start.sh + +kill_localnet: + ./lib-dev/dev-env/4_kill-lotus-ps.sh + +test_hh_localnet: + ./lib-dev/dev-env/5_test-run-localnet.sh + +test_hh_calibnet: + export HH_NETWORK=calibnet && npx hardhat test + ################ TESTS SECURITY ################ security_account_api: @@ -205,3 +227,5 @@ install-opencl: sudo apt-get update sudo apt-get install ocl-icd-opencl-dev +deps_install: install_solc_linux + yarn install \ No newline at end of file diff --git a/contracts/v0.8/DataCapAPI.sol b/contracts/v0.8/DataCapAPI.sol index e957fec4..5fd440e1 100644 --- a/contracts/v0.8/DataCapAPI.sol +++ b/contracts/v0.8/DataCapAPI.sol @@ -75,8 +75,7 @@ library DataCapAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Return the DataCap token balance for the wallet address. @@ -92,8 +91,7 @@ library DataCapAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Return the allowance between owner and operator address. @@ -109,8 +107,7 @@ library DataCapAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Transfers data cap tokens to an address. @@ -178,8 +175,7 @@ library DataCapAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Decrease the DataCap token allowance that an operator controls of the owner's balance by the requested amount. @@ -202,8 +198,7 @@ library DataCapAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Revoke the DataCap token allowance from the operator and set the operator's allowance in behave of owner/caller address to 0. @@ -226,8 +221,7 @@ library DataCapAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Burn an amount of DataCap token from the owner/caller address, decreasing total token supply. @@ -243,8 +237,7 @@ library DataCapAPI { return (0, result.deserializeArrayBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Burn an amount of DataCap token from the specified address (owner address), decrease the allowance of operator/caller, and decrease total token supply. @@ -260,7 +253,9 @@ library DataCapAPI { return (0, result.deserializeBurnFromReturn()); } - DataCapTypes.BurnFromReturn memory empty_res; - return (exit_code, empty_res); + return ( + exit_code, + DataCapTypes.BurnFromReturn({balance: CommonTypes.BigInt({val: hex"00", neg: false}), allowance: CommonTypes.BigInt({val: hex"00", neg: false})}) + ); } } diff --git a/contracts/v0.8/MarketAPI.sol b/contracts/v0.8/MarketAPI.sol index be926e2e..148d1e32 100644 --- a/contracts/v0.8/MarketAPI.sol +++ b/contracts/v0.8/MarketAPI.sol @@ -79,8 +79,7 @@ library MarketAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Return the escrow balance and locked amount for an address. @@ -96,8 +95,10 @@ library MarketAPI { return (0, result.deserializeGetBalanceReturn()); } - MarketTypes.GetBalanceReturn memory empty_res; - return (exit_code, empty_res); + return ( + exit_code, + MarketTypes.GetBalanceReturn({balance: CommonTypes.BigInt({val: hex"00", neg: false}), locked: CommonTypes.BigInt({val: hex"00", neg: false})}) + ); } /// @notice This will be available after the deal is published (whether or not is is activated) and up until some undefined period after it is terminated. @@ -118,8 +119,7 @@ library MarketAPI { return (0, result.deserializeGetDealDataCommitmentReturn()); } - MarketTypes.GetDealDataCommitmentReturn memory empty_res; - return (exit_code, empty_res); + return (exit_code, MarketTypes.GetDealDataCommitmentReturn({data: hex"", size: 0})); } /// @notice Returns the client for the specified deal @@ -135,8 +135,7 @@ library MarketAPI { return (0, result.deserializeUint64()); } - uint64 empty_res; - return (exit_code, empty_res); + return (exit_code, uint64(0)); } /// @notice Returns the provider for a specified deal @@ -157,8 +156,7 @@ library MarketAPI { return (0, result.deserializeUint64()); } - uint64 empty_res; - return (exit_code, empty_res); + return (exit_code, uint64(0)); } /// @notice Returns the label of a storage deal @@ -174,8 +172,7 @@ library MarketAPI { return (0, result.deserializeDealLabel()); } - CommonTypes.DealLabel memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.DealLabel({data: hex"", isString: false})); } /// @notice Returns the start epoch and duration(in epochs) of a deal proposal. @@ -191,8 +188,7 @@ library MarketAPI { return (0, result.deserializeGetDealTermReturn()); } - MarketTypes.GetDealTermReturn memory empty_res; - return (exit_code, empty_res); + return (exit_code, MarketTypes.GetDealTermReturn({start: CommonTypes.ChainEpoch.wrap(0), duration: CommonTypes.ChainEpoch.wrap(0)})); } /// @notice Returns the total price that will be paid from the client to the provider for this deal. @@ -213,8 +209,7 @@ library MarketAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice get the client collateral requirement for a deal @@ -235,8 +230,7 @@ library MarketAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Returns the provider's collateral requirement for a deal @@ -257,8 +251,7 @@ library MarketAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Returns the verified flag for a deal @@ -280,8 +273,7 @@ library MarketAPI { return (0, result.deserializeBool()); } - bool empty_res; - return (exit_code, empty_res); + return (exit_code, false); } /// @notice Fetches activation state for a deal. @@ -301,8 +293,7 @@ library MarketAPI { return (0, result.deserializeGetDealActivationReturn()); } - MarketTypes.GetDealActivationReturn memory empty_res; - return (exit_code, empty_res); + return (exit_code, MarketTypes.GetDealActivationReturn({activated: CommonTypes.ChainEpoch.wrap(0), terminated: CommonTypes.ChainEpoch.wrap(0)})); } /// @notice Publish a new set of storage deals (not yet included in a sector). diff --git a/contracts/v0.8/MinerAPI.sol b/contracts/v0.8/MinerAPI.sol index 7bd7ce13..d2f0535f 100644 --- a/contracts/v0.8/MinerAPI.sol +++ b/contracts/v0.8/MinerAPI.sol @@ -50,8 +50,7 @@ library MinerAPI { return (0, result.deserializeGetOwnerReturn()); } - MinerTypes.GetOwnerReturn memory empty_res; - return (exit_code, empty_res); + return (exit_code, MinerTypes.GetOwnerReturn({owner: CommonTypes.FilAddress({data: hex""}), proposed: CommonTypes.FilAddress({data: hex""})})); } /// @notice Proposes or confirms a change of owner address. @@ -96,8 +95,7 @@ library MinerAPI { return (0, result.deserializeBool()); } - bool empty_res; - return (exit_code, empty_res); + return (exit_code, false); } /// @dev For more information about sector sizes, please refer to https://spec.filecoin.io/systems/filecoin_mining/sector/#section-systems.filecoin_mining.sector @@ -113,8 +111,7 @@ library MinerAPI { return (0, result.deserializeUint64()); } - uint64 empty_res; - return (exit_code, empty_res); + return (exit_code, uint64(0)); } /// @notice This is calculated as actor balance - (vesting funds + pre-commit deposit + initial pledge requirement + fee debt) @@ -136,8 +133,7 @@ library MinerAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Returns specified miner's vesting funds @@ -150,8 +146,7 @@ library MinerAPI { if (exit_code == 0) { return (0, result.deserializeGetVestingFundsReturn()); } - MinerTypes.VestingFunds[] memory empty_res; - return (exit_code, empty_res); + return (exit_code, new MinerTypes.VestingFunds[](0)); } /// @notice Proposes or confirms a change of beneficiary address. @@ -298,8 +293,7 @@ library MinerAPI { return (0, result.deserializeArrayFilAddress()); } - CommonTypes.FilAddress memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.FilAddress({data: hex""})); } /// @notice Returns miner's multiaddresses @@ -314,8 +308,7 @@ library MinerAPI { return (0, result.deserializeGetMultiaddrsReturn()); } - CommonTypes.FilAddress[] memory empty_res; - return (exit_code, empty_res); + return (exit_code, new CommonTypes.FilAddress[](0)); } /// @notice Withdraws balance for a specified miner @@ -339,7 +332,6 @@ library MinerAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } } diff --git a/contracts/v0.8/PowerAPI.sol b/contracts/v0.8/PowerAPI.sol index 5080ce0c..4475fb6e 100644 --- a/contracts/v0.8/PowerAPI.sol +++ b/contracts/v0.8/PowerAPI.sol @@ -55,8 +55,10 @@ library PowerAPI { return (0, result.deserializeCreateMinerReturn()); } - PowerTypes.CreateMinerReturn memory empty_res; - return (exit_code, empty_res); + return ( + exit_code, + PowerTypes.CreateMinerReturn({id_address: CommonTypes.FilAddress({data: hex""}), robust_address: CommonTypes.FilAddress({data: hex""})}) + ); } /// @notice Returns the total number of miners created, regardless of whether or not they have any pledged storage. @@ -71,8 +73,7 @@ library PowerAPI { return (0, result.deserializeUint64()); } - uint64 empty_res; - return (exit_code, empty_res); + return (exit_code, uint64(0)); } /// @notice Returns the total number of miners that have more than the consensus minimum amount of storage active. @@ -92,8 +93,7 @@ library PowerAPI { return (0, result.deserializeInt64()); } - int64 empty_res; - return (exit_code, empty_res); + return (exit_code, int64(0)); } /// @notice Returns the total raw power of the network. @@ -108,8 +108,7 @@ library PowerAPI { return (0, result.deserializeBytesBigInt()); } - CommonTypes.BigInt memory empty_res; - return (exit_code, empty_res); + return (exit_code, CommonTypes.BigInt({val: hex"00", neg: false})); } /// @notice Returns the raw power claimed by the specified miner, and whether the miner has more than the consensus minimum amount of storage active. @@ -125,7 +124,6 @@ library PowerAPI { return (0, result.deserializeMinerRawPowerReturn()); } - PowerTypes.MinerRawPowerReturn memory empty_res; - return (exit_code, empty_res); + return (exit_code, PowerTypes.MinerRawPowerReturn({raw_byte_power: CommonTypes.BigInt({val: hex"00", neg: false}), meets_consensus_minimum: false})); } } diff --git a/contracts/v0.8/PrecompilesAPI.sol b/contracts/v0.8/PrecompilesAPI.sol index a7bcbb76..098407a9 100644 --- a/contracts/v0.8/PrecompilesAPI.sol +++ b/contracts/v0.8/PrecompilesAPI.sol @@ -39,9 +39,7 @@ library PrecompilesAPI { revert FailToCallActor(); } - uint256 actor_id = abi.decode(raw_response, (uint256)); - - return uint64(actor_id); + return uint64(abi.decode(raw_response, (uint256))); } /// @notice Returns the actor id from an eth address @@ -55,9 +53,7 @@ library PrecompilesAPI { revert FailToCallActor(); } - uint256 actor_id = abi.decode(raw_response, (uint256)); - - return uint64(actor_id); + return uint64(abi.decode(raw_response, (uint256))); } /// @notice Returns the actor's delegated address (f4) from an actor id diff --git a/contracts/v0.8/cbor/FilecoinCbor.sol b/contracts/v0.8/cbor/FilecoinCbor.sol index e3b7fefe..3c8ab44d 100644 --- a/contracts/v0.8/cbor/FilecoinCbor.sol +++ b/contracts/v0.8/cbor/FilecoinCbor.sol @@ -50,7 +50,7 @@ library FilecoinCBOR { buf.buf.appendUint8(uint8(((MAJOR_TYPE_TAG << 5) | PAYLOAD_LEN_8_BITS))); buf.buf.appendUint8(TAG_TYPE_CID_CODE); // See https://ipld.io/specs/codecs/dag-cbor/spec/#links for explanation on 0x00 prefix. - buf.writeBytes(bytes.concat(hex'00', value)); + buf.writeBytes(bytes.concat(hex"00", value)); } function readCid(bytes memory cborData, uint byteIdx) internal pure returns (CommonTypes.Cid memory, uint) { @@ -70,7 +70,7 @@ library FilecoinCBOR { CommonTypes.Cid memory ret; ret.data = new bytes(raw.length - 1); for (uint256 i = 1; i < raw.length; i++) { - ret.data[i-1] = raw[i]; + ret.data[i - 1] = raw[i]; } return (ret, byteIdx); diff --git a/contracts/v0.8/helpers/_BasicProxy.sol b/contracts/v0.8/helpers/_BasicProxy.sol new file mode 100644 index 00000000..5f958bb7 --- /dev/null +++ b/contracts/v0.8/helpers/_BasicProxy.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.8.17; + +contract _BasicProxy { + //note: constants are not in storage + uint constant S_OFFSET = 5; + //storage: + uint[S_OFFSET] _notUsed; + address public delegate; + bool public called; + + function upgradeDelegate(address newDelegateAddress) public { + delegate = newDelegateAddress; + } + + fallback() external { + called = true; + assembly { + let _target := sload(S_OFFSET) + calldatacopy(0x0, 0x0, calldatasize()) + let result := delegatecall(gas(), _target, 0x0, calldatasize(), 0x0, 0) + returndatacopy(0x0, 0x0, returndatasize()) + switch result + case 0 { + revert(0, 0) + } + default { + return(0, returndatasize()) + } + } + } +} + +contract _BasicProxyFactory { + address[] public proxies; + bool[] public proxyOccupied; + + address public verifRegProxy; + address public dataCapProxy; + + constructor() { + verifRegProxy = address(new _BasicProxy()); + dataCapProxy = address(new _BasicProxy()); + + for (uint i = 0; i < 10; ++i) { + deployProxy(); + } + } + + function deployProxy() public { + _BasicProxy bp = new _BasicProxy(); + + proxies.push(address(bp)); + proxyOccupied.push(false); + } + + function getFirstAvailableProxy() public view returns (address, uint) { + for (uint i = 0; i < proxies.length; ++i) { + if (proxyOccupied[i] == false) { + return (proxies[i], i); + } + } + + return (address(0), 0); + } + + function occupyProxy(uint i) public { + require(i < proxies.length, "ERR: Proxy index out of bounds!"); + // require(proxyOccupied[i] == false, "ERR: Proxy already occupied!"); + proxyOccupied[i] = true; + } + + function getProxyCount() public view returns (uint) { + return proxies.length; + } +} diff --git a/contracts/v0.8/tests/bigints2.test.sol b/contracts/v0.8/tests/bigints2.test.sol new file mode 100644 index 00000000..5bc4b263 --- /dev/null +++ b/contracts/v0.8/tests/bigints2.test.sol @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.17; + +import {Test} from "forge-std/Test.sol"; +import {FilAddresses} from "contracts/v0.8/utils/FilAddresses.sol"; +import {FilAddressIdConverter} from "contracts/v0.8/utils/FilAddressIdConverter.sol"; +import {CommonTypes} from "contracts/v0.8/types/CommonTypes.sol"; + +import "../cbor/FilecoinCbor.sol"; +import "../../vendor/solidity-BigNumber/src/BigNumbers.sol"; + +import "../utils/BigInts.sol"; + +contract BigIntsTest2 is Test { + using BigInts for CommonTypes.BigInt; + + function test_to_uint256() public view { + CommonTypes.BigInt memory value; + uint256 converted; + bool isOverflow; + + value = CommonTypes.BigInt(hex"ff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 255, "'0xff' should be '255'"); + + value = CommonTypes.BigInt(hex"ffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 65535, "'0xffff' should be '65535'"); + + value = CommonTypes.BigInt(hex"ffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 16777215, "'0xffffff' should be '16777215'"); + + value = CommonTypes.BigInt(hex"ffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 4294967295, "'0xffffffff' should be '4294967295'"); + + value = CommonTypes.BigInt(hex"ffffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 1099511627775, "'0xffffffffff' should be '1099511627775'"); + + value = CommonTypes.BigInt(hex"ffffffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 281474976710655, "'0xffffffffffff' should be '281474976710655'"); + + value = CommonTypes.BigInt(hex"ffffffffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 72057594037927935, "'0xffffffffffffff' should be '72057594037927935'"); + + value = CommonTypes.BigInt(hex"ffffffffffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 18446744073709551615, "'0xffffffffffffffff' should be '18446744073709551615'"); + + value = CommonTypes.BigInt(hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require( + converted == 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "'(2 ** 256) - 1' should be '115792089237316195423570985008687907853269984665640564039457584007913129639935'" + ); + + value = CommonTypes.BigInt(hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", false); + (converted, isOverflow) = value.toUint256(); + require(isOverflow, "it should not be valid"); + require(converted == 0, "overflow should have happened"); + + value = CommonTypes.BigInt(hex"00", false); + (converted, isOverflow) = value.toUint256(); + require(!isOverflow, "it should be valid"); + require(converted == 0, "'0x00' should be '0'"); + } + + function test_to_int256_positive() public view { + CommonTypes.BigInt memory value; + int256 converted; + bool isOverflow; + + value = CommonTypes.BigInt(hex"ff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 255, "'0xff' should be '255'"); + + value = CommonTypes.BigInt(hex"ffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 65535, "'0xffff' should be '65535'"); + + value = CommonTypes.BigInt(hex"ffffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 16777215, "'0xffffff' should be '16777215'"); + + value = CommonTypes.BigInt(hex"ffffffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 4294967295, "'0xffffffff' should be '4294967295'"); + + value = CommonTypes.BigInt(hex"ffffffffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 1099511627775, "'0xffffffffff' should be '1099511627775'"); + + value = CommonTypes.BigInt(hex"ffffffffffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 281474976710655, "'0xffffffffffff' should be '281474976710655'"); + + value = CommonTypes.BigInt(hex"ffffffffffffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 72057594037927935, "'0xffffffffffffff' should be '72057594037927935'"); + + value = CommonTypes.BigInt(hex"ffffffffffffffff", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 18446744073709551615, "'0xffffffffffffffff' should be '18446744073709551615'"); + + value = CommonTypes.BigInt(hex"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require( + converted == 57896044618658097711785492504343953926634992332820282019728792003956564819967, + "'(2 ** 256) / 2 - 1' should be '57896044618658097711785492504343953926634992332820282019728792003956564819967'" + ); + + value = CommonTypes.BigInt(hex"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", false); + (converted, isOverflow) = value.toInt256(); + require(isOverflow, "it should not be valid"); + require(converted == 0, "overflow should have happened"); + + value = CommonTypes.BigInt(hex"00", false); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == 0, "'0x00' should be '0'"); + } + + function test_to_int256_negative() public view { + CommonTypes.BigInt memory value; + int256 converted; + bool isOverflow; + + value = CommonTypes.BigInt(hex"ff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -255, "'0xff' should be '-255'"); + + value = CommonTypes.BigInt(hex"ffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -65535, "'0xffff' should be '-65535'"); + + value = CommonTypes.BigInt(hex"ffffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -16777215, "'0xffffff' should be '-16777215'"); + + value = CommonTypes.BigInt(hex"ffffffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -4294967295, "'0xffffffff' should be '-4294967295'"); + + value = CommonTypes.BigInt(hex"ffffffffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -1099511627775, "'0xffffffffff' should be '-1099511627775'"); + + value = CommonTypes.BigInt(hex"ffffffffffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -281474976710655, "'0xffffffffffff' should be '-281474976710655'"); + + value = CommonTypes.BigInt(hex"ffffffffffffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -72057594037927935, "'0xffffffffffffff' should be '-72057594037927935'"); + + value = CommonTypes.BigInt(hex"ffffffffffffffff", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require(converted == -18446744073709551615, "'0xffffffffffffffff' should be '-18446744073709551615'"); + + value = CommonTypes.BigInt(hex"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", true); + (converted, isOverflow) = value.toInt256(); + require(!isOverflow, "it should be valid"); + require( + converted == -57896044618658097711785492504343953926634992332820282019728792003956564819967, + "'(2 ** 256) / 2 - 1' should be '-57896044618658097711785492504343953926634992332820282019728792003956564819967'" + ); + + value = CommonTypes.BigInt(hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", true); + (converted, isOverflow) = value.toInt256(); + require(isOverflow, "it should not be valid"); + require(converted == 0, "overflow should have happened"); + } + + function test_from_uint256() public view { + CommonTypes.BigInt memory converted; + + converted = BigInts.fromUint256(255); + require(keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000000000000000ff"), "'255' should be '0xff'"); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(65535); + require(keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000000000000000ffff"), "'65535' should be '0xffff'"); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(16777215); + require( + keccak256(converted.val) == keccak256(hex"0000000000000000000000000000000000000000000000000000000000ffffff"), + "'16777215' should be '0xffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(4294967295); + require( + keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000000000ffffffff"), + "'4294967295' should be '0xffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(1099511627775); + require( + keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000000000ffffffffff"), + "'1099511627775' should be '0xffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(281474976710655); + require( + keccak256(converted.val) == keccak256(hex"0000000000000000000000000000000000000000000000000000ffffffffffff"), + "'281474976710655' should be '0xffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(72057594037927935); + require( + keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000ffffffffffffff"), + "'72057594037927935' should be '0xffffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(18446744073709551615); + require( + keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000ffffffffffffffff"), + "'18446744073709551615' should be '0xffffffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromUint256(115792089237316195423570985008687907853269984665640564039457584007913129639935); + require( + keccak256(converted.val) == keccak256(hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + "'115792089237316195423570985008687907853269984665640564039457584007913129639935' should be '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + } + + function test_from_int256_positive() public view { + CommonTypes.BigInt memory converted; + + converted = BigInts.fromInt256(255); + require(keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000000000000000ff"), "'255' should be '0xff'"); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(65535); + require(keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000000000000000ffff"), "'65535' should be '0xffff'"); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(16777215); + require( + keccak256(converted.val) == keccak256(hex"0000000000000000000000000000000000000000000000000000000000ffffff"), + "'16777215' should be '0xffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(4294967295); + require( + keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000000000ffffffff"), + "'4294967295' should be '0xffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(1099511627775); + require( + keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000000000ffffffffff"), + "'1099511627775' should be '0xffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(281474976710655); + require( + keccak256(converted.val) == keccak256(hex"0000000000000000000000000000000000000000000000000000ffffffffffff"), + "'281474976710655' should be '0xffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(72057594037927935); + require( + keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000ffffffffffffff"), + "'72057594037927935' should be '0xffffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(18446744073709551615); + require( + keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000ffffffffffffffff"), + "'18446744073709551615' should be '0xffffffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + + converted = BigInts.fromInt256(57896044618658097711785492504343953926634992332820282019728792003956564819967); + require( + keccak256(converted.val) == keccak256(hex"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + "'57896044618658097711785492504343953926634992332820282019728792003956564819967' should be '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'" + ); + require(converted.neg == false, "'neg flag should be false'"); + } + + function test_from_int256_negative() public view { + CommonTypes.BigInt memory converted; + + converted = BigInts.fromInt256(-255); + require(keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000000000000000ff"), "'255' should be '0xff'"); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-65535); + require(keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000000000000000ffff"), "'65535' should be '0xffff'"); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-16777215); + require( + keccak256(converted.val) == keccak256(hex"0000000000000000000000000000000000000000000000000000000000ffffff"), + "'16777215' should be '0xffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-4294967295); + require( + keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000000000ffffffff"), + "'4294967295' should be '0xffffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-1099511627775); + require( + keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000000000ffffffffff"), + "'1099511627775' should be '0xffffffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-281474976710655); + require( + keccak256(converted.val) == keccak256(hex"0000000000000000000000000000000000000000000000000000ffffffffffff"), + "'281474976710655' should be '0xffffffffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-72057594037927935); + require( + keccak256(converted.val) == keccak256(hex"00000000000000000000000000000000000000000000000000ffffffffffffff"), + "'72057594037927935' should be '0xffffffffffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-18446744073709551615); + require( + keccak256(converted.val) == keccak256(hex"000000000000000000000000000000000000000000000000ffffffffffffffff"), + "'18446744073709551615' should be '0xffffffffffffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + + converted = BigInts.fromInt256(-57896044618658097711785492504343953926634992332820282019728792003956564819967); + require( + keccak256(converted.val) == keccak256(hex"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + "'57896044618658097711785492504343953926634992332820282019728792003956564819967' should be '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'" + ); + require(converted.neg == true, "'neg flag should be false'"); + } + + function test_zero_to_uint256() public { + CommonTypes.BigInt memory value = CommonTypes.BigInt({val: hex"00", neg: false}); + + (uint256 converted, bool isOverflow) = value.toUint256(); + + require(converted == 0, "converted value is not 0!"); + } + + function test_zero_to_int256() public { + CommonTypes.BigInt memory value = CommonTypes.BigInt({val: hex"00", neg: false}); + + (int256 converted, bool isOverflow) = value.toInt256(); + + require(converted == 0, "converted value is not 0!"); + } + + function test_empty_val_to_uint256() public { + CommonTypes.BigInt memory value = CommonTypes.BigInt({val: hex"", neg: false}); + + (uint256 converted, bool isOverflow) = value.toUint256(); + + require(converted == 0, "converted value is not 0!"); + } + + function test_empty_val_to_int256() public { + CommonTypes.BigInt memory value = CommonTypes.BigInt({val: hex"", neg: false}); + + (int256 converted, bool isOverflow) = value.toInt256(); + + require(converted == 0, "converted value is not 0!"); + } +} diff --git a/contracts/v0.8/tests/datacap.test.sol b/contracts/v0.8/tests/datacap.test.sol index d5aaf625..3a457bad 100644 --- a/contracts/v0.8/tests/datacap.test.sol +++ b/contracts/v0.8/tests/datacap.test.sol @@ -30,6 +30,11 @@ import "../utils/Errors.sol"; /// @notice It imports the library and create a callable method for each method in the library /// @author Zondax AG contract DataCapApiTest { + address _n; + address _n2; + function dummy() public view returns (uint) { + return 1301; + } function name() public view returns (string memory) { (int256 exit_code, string memory result) = DataCapAPI.name(); diff --git a/contracts/v0.8/tests/miner.test.sol b/contracts/v0.8/tests/miner.test.sol index df551722..efbfc892 100644 --- a/contracts/v0.8/tests/miner.test.sol +++ b/contracts/v0.8/tests/miner.test.sol @@ -22,6 +22,7 @@ pragma solidity ^0.8.17; import "../MinerAPI.sol"; import "../types/MinerTypes.sol"; import "../utils/Errors.sol"; +import "../cbor/FilecoinCbor.sol"; /// @notice This file is meant to serve as a deployable contract of the miner actor API, as the library by itself is not. /// @notice It imports the library and create a callable method for each method in the library @@ -138,4 +139,8 @@ contract MinerApiTest { return result; } + + function encodeFilAddress(CommonTypes.FilAddress memory filAddr) public view returns (bytes memory) { + return FilecoinCBOR.serializeAddress(filAddr); + } } diff --git a/contracts/v0.8/tests/send.test.sol b/contracts/v0.8/tests/send.test.sol index 0a434834..c3c27b7c 100644 --- a/contracts/v0.8/tests/send.test.sol +++ b/contracts/v0.8/tests/send.test.sol @@ -41,4 +41,15 @@ contract SendApiTest { return exit_code; } + + function send_with_actor_id(CommonTypes.FilActorId target, uint256 amount) public returns (int256) { + return send(target, amount); + } + + function send_with_address(CommonTypes.FilAddress memory target, uint256 amount) public returns (int256) { + return send(target, amount); + } + + receive() external payable {} + fallback() external payable {} } diff --git a/contracts/v0.8/types/CommonTypes.sol b/contracts/v0.8/types/CommonTypes.sol index c4e1a4f6..a1533af1 100644 --- a/contracts/v0.8/types/CommonTypes.sol +++ b/contracts/v0.8/types/CommonTypes.sol @@ -19,13 +19,12 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.17; - /// @title Filecoin actors' common types for Solidity. /// @author Zondax AG library CommonTypes { /// @dev Protocol byte values /// @notice These constants represent the byte value for each protocol. - /// For more information see the Filecoin documentation: + /// For more information see the Filecoin documentation: /// https://docs.filecoin.io/smart-contracts/filecoin-evm-runtime/address-types bytes1 constant PROTOCOL_ID = hex"00"; bytes1 constant PROTOCOL_SECP256K1 = hex"01"; @@ -39,7 +38,7 @@ library CommonTypes { /// @dev Protocols address lengths /// @notice These constants represent the address lengths for each protocol. - /// For more information see the Filecoin specification: + /// For more information see the Filecoin specification: /// https://spec.filecoin.io/#section-appendix uint256 constant MIN_PROTOCOL_ID_ADDRESS_LENGTH = 1; uint256 constant MAX_PROTOCOL_ID_ADDRESS_LENGTH = 11; diff --git a/contracts/v0.8/types/DataCapTypes.sol b/contracts/v0.8/types/DataCapTypes.sol index d78415f7..1cd1b4ab 100644 --- a/contracts/v0.8/types/DataCapTypes.sol +++ b/contracts/v0.8/types/DataCapTypes.sol @@ -50,18 +50,18 @@ library DataCapTypes { /// @param amount a non-negative amount to transfer. /// @param operator_data Arbitrary data to pass on via the receiver hook. struct TransferParams { + bytes operator_data; CommonTypes.FilAddress to; CommonTypes.BigInt amount; - bytes operator_data; } /// @param from_balance the balance of from_address. /// @param to_balance the balance of to_address. /// @param recipient_data data returned from receive hook. struct TransferReturn { + bytes recipient_data; CommonTypes.BigInt from_balance; CommonTypes.BigInt to_balance; - bytes recipient_data; } /// @param from the address to send DataCap Token. @@ -69,10 +69,10 @@ library DataCapTypes { /// @param amount a non-negative amount to transfer. /// @param operator_data arbitrary data to pass on via the receiver hook. struct TransferFromParams { + bytes operator_data; CommonTypes.FilAddress from; CommonTypes.FilAddress to; CommonTypes.BigInt amount; - bytes operator_data; } /// @param from_balance the balance of from_address. @@ -80,10 +80,10 @@ library DataCapTypes { /// @param allowance the remaining allowance of owner address. /// @param recipient_data data returned from receive hook. struct TransferFromReturn { + bytes recipient_data; CommonTypes.BigInt from_balance; CommonTypes.BigInt to_balance; CommonTypes.BigInt allowance; - bytes recipient_data; } /// @param operator the wallet address of the operator. diff --git a/contracts/v0.8/types/MarketTypes.sol b/contracts/v0.8/types/MarketTypes.sol index 0d0dcd9f..3eb00186 100644 --- a/contracts/v0.8/types/MarketTypes.sol +++ b/contracts/v0.8/types/MarketTypes.sol @@ -84,8 +84,8 @@ library MarketTypes { /// @param ids returned storage deal IDs. /// @param valid_deals represent all the valid deals. struct PublishStorageDealsReturn { - uint64[] ids; bytes valid_deals; + uint64[] ids; } /// @param piece_cid PieceCID. diff --git a/contracts/v0.8/types/PowerTypes.sol b/contracts/v0.8/types/PowerTypes.sol index 1110d2d5..fe772d9c 100644 --- a/contracts/v0.8/types/PowerTypes.sol +++ b/contracts/v0.8/types/PowerTypes.sol @@ -40,9 +40,9 @@ library PowerTypes { struct CreateMinerParams { CommonTypes.FilAddress owner; CommonTypes.FilAddress worker; - RegisteredPoStProof window_post_proof_type; CommonTypes.FilAddress peer; CommonTypes.FilAddress[] multiaddrs; + RegisteredPoStProof window_post_proof_type; } /// @param id_address the canonical ID-based address for the actor. diff --git a/contracts/v0.8/types/VerifRegTypes.sol b/contracts/v0.8/types/VerifRegTypes.sol index b51dc5ee..05b47368 100644 --- a/contracts/v0.8/types/VerifRegTypes.sol +++ b/contracts/v0.8/types/VerifRegTypes.sol @@ -101,13 +101,13 @@ library VerifRegTypes { /// @param term_start the epoch at which the piece was committed. /// @param sector ID of the provider's sector in which the data is committed. struct Claim { + bytes data; CommonTypes.FilActorId provider; CommonTypes.FilActorId client; - bytes data; - uint64 size; CommonTypes.ChainEpoch term_min; CommonTypes.ChainEpoch term_max; CommonTypes.ChainEpoch term_start; CommonTypes.FilActorId sector; + uint64 size; } } diff --git a/contracts/v0.8/utils/Actor.sol b/contracts/v0.8/utils/Actor.sol index 9336bf17..d1183909 100644 --- a/contracts/v0.8/utils/Actor.sol +++ b/contracts/v0.8/utils/Actor.sol @@ -190,7 +190,7 @@ library Actor { /// @param method_num id of the method from the actor to call /// @param codec how the request data passed as argument is encoded /// @param raw_request encoded arguments to be passed in the call - /// @dev it requires the id to be bigger than 99, as singleton actors are smaller than that + /// @dev it requires the id to be smaller than 99, as singleton actors are smaller than that function callNonSingletonByIDReadOnly( CommonTypes.FilActorId target, uint256 method_num, diff --git a/contracts/v0.8/utils/BigInts.sol b/contracts/v0.8/utils/BigInts.sol index e7be841a..59a514d7 100644 --- a/contracts/v0.8/utils/BigInts.sol +++ b/contracts/v0.8/utils/BigInts.sol @@ -51,12 +51,15 @@ library BigInts { /// @notice allow to get a uint256 from a BigInt value. /// @notice If the value is negative, it will generate an error. /// @param value BigInt number - /// @return a uint256 value and flog that indicates whether it was possible to convert or not (the value overflows uint256 type) + /// @return a uint256 value and a flag that indicates whether it was possible to convert the arg. value + /// (returns true if the arg. value overflows uint256 type) function toUint256(CommonTypes.BigInt memory value) internal view returns (uint256, bool) { if (value.neg) { revert NegativeValueNotAllowed(); } + if (value.val.length == 0) value.val = hex"00"; + BigNumber memory max = BigNumbers.init(MAX_UINT, false); BigNumber memory bigNumValue = BigNumbers.init(value.val, value.neg); if (BigNumbers.gt(bigNumValue, max)) { @@ -69,8 +72,11 @@ library BigInts { /// @notice allow to get a int256 from a BigInt value. /// @notice If the value is grater than what a int256 can store, it will generate an error. /// @param value BigInt number - /// @return a int256 value and flog that indicates whether it was possible to convert or not (the value overflows int256 type) + /// @return a int256 value and a flag that indicates whether it was possible to convert or not + /// (returns true if the arg. value overflows int256 type) function toInt256(CommonTypes.BigInt memory value) internal view returns (int256, bool) { + if (value.val.length == 0) value.val = hex"00"; + BigNumber memory max = BigNumbers.init(MAX_INT, false); BigNumber memory bigNumValue = BigNumbers.init(value.val, false); if (BigNumbers.gt(bigNumValue, max)) { diff --git a/hardhat.config.ts b/hardhat.config.ts index 818809b9..25ddd338 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -17,7 +17,27 @@ try { extractedSolcVersion = tomlData.split(`solc`)[1].split("=")[1].split("\n")[0].replaceAll(" ", "").replaceAll('"', "").replaceAll("'", "") } catch { - console.log({ error: "Solc version in foundry.toml not set" }) + console.log({ error: "Solc version in foundry.toml not set (Hardhat needs to be run from projects root)" }) + process.exit(1) +} + +const HH_NETWORK = process.env.HH_NETWORK != undefined ? process.env.HH_NETWORK : "localnet" +const SUPPORTED_NETWORKS = { + localnet: { + url: "http://127.0.0.1:1234/rpc/v1", + chainId: 31415926, + gas: 1_000_000_000, + blockGasLimit: 1_000_000_000, + }, + calibnet: { + url: "https://calibration.filfox.info/rpc/v1", + chainId: 314159, + accounts: [process.env.ETH_PK], + }, +} + +if (HH_NETWORK === undefined || SUPPORTED_NETWORKS[HH_NETWORK] == null) { + console.log({ error: `HH_NETWORK env var (val:${HH_NETWORK}) not supported! (Must be: ${Object.keys(SUPPORTED_NETWORKS).join(" | ")})` }) process.exit(1) } @@ -31,27 +51,13 @@ const config: HardhatUserConfig = { }, }, }, - networks: { - hardhat: { - blockGasLimit: 1000000000000000, - }, - localnet: { - url: "http://127.0.0.1:1234/rpc/v1", - chainId: 31415926, - gas: 1_000_000_000, - blockGasLimit: 1_000_000_000, - }, - calibnet: { - url: "https://api.calibration.node.glif.io/rpc/v1", - chainId: 314159, - accounts: [process.env.DEPLOYER_PK], - }, - }, + defaultNetwork: HH_NETWORK, + networks: SUPPORTED_NETWORKS, mocha: { - timeout: 100000000, + timeout: 1000000000, }, paths: { - tests: "./hh-test", + tests: `./hh-test/${HH_NETWORK}`, }, } diff --git a/hh-test/_deployProxies.ts b/hh-test/_deployProxies.ts new file mode 100644 index 00000000..5cbc9c8c --- /dev/null +++ b/hh-test/_deployProxies.ts @@ -0,0 +1,47 @@ +import { ethers } from "hardhat" +import { writeFileSync } from "fs" +import * as utils from "./utils" +import { CommonTypes, DataCapTypes } from "../typechain-types/contracts/v0.8/tests/datacap.test.sol/DataCapApiTest" + +import "dotenv/config" +import { execSync } from "child_process" + +const main = async () => { + const [deployer, userWithDataCapAddr] = utils.generate_and_fund_f410_accounts(2, 100) + const [verifier, verifier2] = await utils.generate_f3_accounts(2) + + utils.lotus.sendFunds(verifier.fil.address, 10) + utils.lotus.sendFunds(verifier2.fil.address, 10) + await utils.defaultTxDelay() + + const proxyFact = await utils.deployContract(deployer, "_BasicProxyFactory") + + const proxyCount = await proxyFact.eth.contract.getProxyCount() + + const verifRegProxyAddr = await proxyFact.eth.contract.verifRegProxy() + const dataCapProxyAddr = await proxyFact.eth.contract.dataCapProxy() + + const filePathPrefix = `/var/lib/fil-sol/lib-dev/dev-env/.internal` + + writeFileSync(`${filePathPrefix}/proxyFactory.addr`, proxyFact.eth.address) + writeFileSync(`${filePathPrefix}/verifRegProxy.addr`, verifRegProxyAddr) + writeFileSync(`${filePathPrefix}/dataCapProxy.addr`, dataCapProxyAddr) + writeFileSync(`${filePathPrefix}/userWithDataCap.addr`, userWithDataCapAddr.fil.address) + + await utils.lotus.restart({ LOTUS_FEVM_ENABLEETHRPC: false }) + + execSync(`echo "RPC_RESTARTED" >> /var/lib/fil-sol/log.txt`) + + utils.lotus.registerVerifier(utils.ethAddressToFilAddress(verifRegProxyAddr), 16000000) + utils.lotus.registerVerifier(verifier.fil.address, 16000000) + utils.lotus.registerVerifier(verifier2.fil.address, 16000000) + utils.lotus.grantDatacap(verifier.fil.address, utils.ethAddressToFilAddress(dataCapProxyAddr), 1000) + // utils.lotus.grantDatacap(verifier2.fil.address, userWithDataCapAddr.fil.address, 1000) + + utils.lotus.kill() +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/hh-test/calibnet/_common.ts b/hh-test/calibnet/_common.ts new file mode 100644 index 00000000..5aa4f225 --- /dev/null +++ b/hh-test/calibnet/_common.ts @@ -0,0 +1,63 @@ +import { MarketTypes, CommonTypes } from "../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +export const getActualDealInfo = async (market: any, dealID: number) => { + const dealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await market.eth.contract.get_deal_data_commitment(dealID) + const dealClientId = await market.eth.contract.get_deal_client(dealID) + const dealProviderId = await market.eth.contract.get_deal_provider(dealID) + const dealLabel: CommonTypes.DealLabelStruct = await market.eth.contract.get_deal_label(dealID) + const dealTerm: MarketTypes.GetDealTermReturnStruct = await market.eth.contract.get_deal_term(dealID) + const dealTotalPrice: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_total_price(dealID) + const dealClientCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_client_collateral(dealID) + const dealProviderCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_provider_collateral(dealID) + + const dealVerified = await market.eth.contract.get_deal_verified(dealID) + const dealActivation: MarketTypes.GetDealActivationReturnStruct = await market.eth.contract.get_deal_activation(dealID) + + return { + dealCommitment, + dealClientId, + dealProviderId, + dealLabel, + dealTerm, + dealTotalPrice, + dealClientCollateral, + dealProviderCollateral, + dealVerified, + dealActivation, + } +} + +export const CHECKING_DEAL_IDS = [193771, 193630] + +export const EXPECTED_DEAL_INFO = { + 193771: { + dealCommitment: { data: "0x000181e20392202059a55142771123075b29b33b79cd6b03b4c4b00f6b6b49e7d541c476fcd00c3a", size: BigInt(536870912) }, + dealClientId: BigInt(35150), + dealProviderId: BigInt(17840), + dealLabel: { + data: "0x62616679626569636c7067676634356e696d6c6b7a716e6e77746466706c36646137696a7134716d6973356469656d646733733679656a366c3434", + isString: true, + }, + dealTerm: { start: BigInt(1589409), end: BigInt(3542400) }, + dealTotalPrice: { val: "0x", neg: false }, + dealClientCollateral: { val: "0x", neg: false }, + dealProviderCollateral: { val: "0x", neg: false }, + dealVerified: true, + dealActivation: { activated: BigInt(1584851), terminated: BigInt(0) }, + }, + 193630: { + dealCommitment: { data: "0x000181e2039220206e4100083d6843845e8e3d2f8f785a7f1a2b5d3300d2645ffe1d99a811cbdf08", size: BigInt(34359738368) }, + dealClientId: BigInt(1206), + dealProviderId: BigInt(95029), + dealLabel: { + data: "0x6d4158436735414967586a7965624f39443365764a4e4c4978354576374548506e4f56466564306d48335a6856544852506a5938", + isString: true, + }, + dealTerm: { start: BigInt(1647431), duration: BigInt(1468800) }, + dealTotalPrice: { val: "0x", neg: false }, + dealClientCollateral: { val: "0x", neg: false }, + dealProviderCollateral: { val: "0x", neg: false }, + dealVerified: false, + dealActivation: { activated: BigInt(1578570), terminated: BigInt(0) }, + }, +} diff --git a/hh-test/calibnet/e2e/account.t.ts b/hh-test/calibnet/e2e/account.t.ts new file mode 100644 index 00000000..60f9f1c7 --- /dev/null +++ b/hh-test/calibnet/e2e/account.t.ts @@ -0,0 +1,85 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import { CommonTypes, AccountTypes } from "../../../typechain-types/contracts/v0.8/tests/account.test.sol/AccountApiTest" + +import * as utils from "../../utils" + +describe("Account Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + const message = + "8eabea2a4001061ac4c9fe3c517725b8829b159149a863b2a2320cc628d026a871d3cb34947371f384a9eb49ff9bd56a019fa70e10c06ac5ca93df3c1d6f54d540c57cbe2f5209cafdc12146d5d59172dd4d8359015e10584fa6327de0ce5a6a" + + //note: the following code snippet is the way the `signature` variable value was generated + // const user = utils.lotus.importDefaultWallets() + // const _signature = utils.lotus.signMessage(user.fil.address, message) + + const signature = + "02b179ef5bdedaffffdd5a8c245c7e3a9629329b7870ee56eff12dbbc5479cebaae70233d708b37d1ff57ccab813c22aa80a9ea5d8fd8df5364d3dc71e024e1ffd872d87c2c60cab54966aac4f6b1fee4b0b3c663dde7d6d525a26f57e569452c5" + + // expect(_signature).to.eq(signature) + + dbg(`Deploying contracts... (account)`) + + const account = await utils.deployContract(deployer, "AccountApiTest") + + const slicedSignature = signature.slice(2) + + const hexSig = `0x${slicedSignature}` + const bytes = [] + for (let c = 0; c < hexSig.length; c += 2) { + bytes.push(parseInt(hexSig.substr(c, 2), 16)) + } + + const _sig = Uint8Array.from([...bytes.slice(1)]) + + const target = BigInt(process.env.F3_ID) + + const params: AccountTypes.AuthenticateMessageParamsStruct = { + signature: _sig, + message: utils.hexToBytes(message), + } + + dbg(`Authenticating message...`) + + //note: no additional checks performed + // it will revert if the signature/message is incorrect + await account.eth.contract.authenticate_message(target, params) + + dbg(`Calling universal hook...`) + + const universalReceiverParams: CommonTypes.UniversalReceiverParamsStruct = { + type_: BigInt(0), + payload: Uint8Array.from([1, 2, 3]), + } + + //note: no additional checks performed (reverts on error) + await account.eth.contract.universal_receiver_hook(target, universalReceiverParams) + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/e2e/address.t.ts b/hh-test/calibnet/e2e/address.t.ts new file mode 100644 index 00000000..bc3e72a6 --- /dev/null +++ b/hh-test/calibnet/e2e/address.t.ts @@ -0,0 +1,41 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Address Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (AddressTest)`) + + const addressSC = await utils.deployContract(deployer, "AddressTest") + + //note: no additional checks performed (reverts on error) + await addressSC.eth.contract.actorid_conversion() + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/e2e/bigints.t.ts b/hh-test/calibnet/e2e/bigints.t.ts new file mode 100644 index 00000000..ba928f39 --- /dev/null +++ b/hh-test/calibnet/e2e/bigints.t.ts @@ -0,0 +1,50 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("BigInt Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (BigIntsTest)`) + + const bigIntSC = await utils.deployContract(deployer, "BigIntsTest") + + //note: additional checks performed inside contracts (all revert on error) + await bigIntSC.eth.contract.to_uint256() + + await bigIntSC.eth.contract.to_int256_negative() + + await bigIntSC.eth.contract.to_int256_positive() + + await bigIntSC.eth.contract.from_uint256() + + await bigIntSC.eth.contract.from_int256_positive() + + await bigIntSC.eth.contract.from_int256_negative() +} diff --git a/hh-test/calibnet/e2e/cborDecode.t.ts b/hh-test/calibnet/e2e/cborDecode.t.ts new file mode 100644 index 00000000..43a7dc76 --- /dev/null +++ b/hh-test/calibnet/e2e/cborDecode.t.ts @@ -0,0 +1,56 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("CborDecode Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (CborDecodeTest)`) + + const cborDecode = await utils.deployContract(deployer, "CborDecodeTest") + + //note: additional checks performed inside contracts (all revert on error) + await cborDecode.eth.contract.decodeFixedArray() + + await cborDecode.eth.contract.decodeFalse() + + await cborDecode.eth.contract.decodeTrue() + + await cborDecode.eth.contract.decodeNull() + + await cborDecode.eth.contract.decodeInteger() + + await cborDecode.eth.contract.decodeString() + + await cborDecode.eth.contract.decodeStringWithWeirdChar() + + await cborDecode.eth.contract.decodeArrayU8() + + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/e2e/datacap.t.ts b/hh-test/calibnet/e2e/datacap.t.ts new file mode 100644 index 00000000..3706f380 --- /dev/null +++ b/hh-test/calibnet/e2e/datacap.t.ts @@ -0,0 +1,95 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" +import { CommonTypes, DataCapTypes } from "../../../typechain-types/contracts/v0.8/tests/datacap.test.sol/DataCapApiTest" + +describe("Datacap Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (DataCapApiTest)`) + + const DataCapFactory = await ethers.getContractFactory("DataCapApiTest") + const datacap = await utils.deployContract(deployer, "DataCapApiTest") + + dbg(JSON.stringify({ datacapAddr: utils.ethAddressToFilAddress(await datacap.eth.contract.getAddress()) })) + + dbg("calling 'name'") + + const expectedName = "DataCap" + const actualName = await datacap.eth.contract.name() + + expect(actualName).to.eq(expectedName) + + dbg("calling 'symbol'") + + const expectedSymbol = "DCAP" + const actualSymbol = await datacap.eth.contract.symbol() + + expect(actualSymbol).to.eq(expectedSymbol) + + const expectedTotalSupply = { + val: "0x0393110ee5ed51c00000", + neg: false, + } + + dbg("calling 'total_supply'") + + const actualTotalSupply = await datacap.eth.contract.total_supply() + + //note: totalSupply changes over time, cannot be dynamically determined + // expect(actualTotalSupply.val).to.eq(expectedTotalSupply.val) + // expect(actualTotalSupply.neg).to.eq(expectedTotalSupply.neg) + + dbg("calling 'balance'") + + const addr: CommonTypes.FilAddressStruct = { data: Uint8Array.from([0, 66]) } + const expectedBalance: CommonTypes.BigIntStruct = { val: "0x", neg: false } + const actualBalance: CommonTypes.BigIntStruct = await datacap.eth.contract.balance(addr) + + expect(actualBalance.neg).to.eq(expectedBalance.neg) + expect(actualBalance.val).to.eq(expectedBalance.val) + + dbg("calling 'allowance'") + + const targetByteAddr = utils.filAddressToBytes(process.env.F3_ADDR) + + const allowanceParams: DataCapTypes.GetAllowanceParamsStruct = { + owner: { + data: targetByteAddr, + }, + operator: { + data: targetByteAddr, + }, + } + const expectedAllowance: CommonTypes.BigIntStruct = { val: "0x", neg: false } + const actualAllowance: CommonTypes.BigIntStruct = await datacap.eth.contract.allowance(allowanceParams) + + expect(actualAllowance.neg).to.eq(expectedAllowance.neg) + expect(actualAllowance.val).to.eq(expectedAllowance.val) +} diff --git a/hh-test/calibnet/e2e/deserializeParams.t.ts b/hh-test/calibnet/e2e/deserializeParams.t.ts new file mode 100644 index 00000000..a2697baa --- /dev/null +++ b/hh-test/calibnet/e2e/deserializeParams.t.ts @@ -0,0 +1,42 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Deserialize Params Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (DeserializeParamsTest)`) + + const deserializeParamsSC = await utils.deployContract(deployer, "DeserializeParamsTest") + + //note: additional checks performed inside contracts (all revert on error) + await deserializeParamsSC.eth.contract.deserializeGetVestingFundsReturn() + + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/e2e/leb128.t.ts b/hh-test/calibnet/e2e/leb128.t.ts new file mode 100644 index 00000000..237deb50 --- /dev/null +++ b/hh-test/calibnet/e2e/leb128.t.ts @@ -0,0 +1,49 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Leb128 Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + // await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (Leb128Generated[1..15]Test)`) + + const startingNonce = await deployer.eth.signer.getNonce() + + const leb128_SC = [] + for (let i = 1; i < 15; ++i) { + const cName = `Leb128Generated${i}Test` + const sc = await utils.deployContract(deployer, cName) + leb128_SC.push(sc) + } + + for (const l128_SC of leb128_SC) { + //note: additional checks performed inside contracts (all revert on error) + await l128_SC.eth.contract.unsiged_integer_leb128_encoding_generated() + } +} diff --git a/hh-test/calibnet/e2e/market.t.ts b/hh-test/calibnet/e2e/market.t.ts new file mode 100644 index 00000000..679e161b --- /dev/null +++ b/hh-test/calibnet/e2e/market.t.ts @@ -0,0 +1,116 @@ +import { ethers } from "hardhat" +import { expect } from "chai" + +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +import * as utils from "../../utils" + +import { CHECKING_DEAL_IDS, EXPECTED_DEAL_INFO, getActualDealInfo } from "../_common" + +describe("Market Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + let market, deployer + + before(async () => { + const { deployer: _deployer } = await utils.performGeneralSetupOnCalibnet() + deployer = _deployer + market = await utils.deployContract(deployer, "MarketApiTest") + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: State changes on Calibnet", async () => { + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + it("Test 2: Reading from Calibnet", async () => { + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) +const test1 = async (testName: string, { deployer, market }) => { + const dbg = utils.initDbg(testName) + + let amount = BigInt(10 ** 18) + + const targetByteAddr = utils.filAddressToBytes(market.fil.address) + + dbg(`Adding funds to balance...`) + + const previousBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + await market.eth.contract.add_balance({ data: targetByteAddr }, amount, { gasLimit: 1_000_000_000, value: amount }) + await utils.defaultTxDelay() + + let previousBalanceBigInt = BigInt(previousBalance.balance.val as string) + + let expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt + amount), neg: false } + + let actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + dbg(`Withdrawing funds from balance...`) + + amount = BigInt(100) + const tokenAmount = { val: Uint8Array.from([100]), neg: false } + + const withdrawalParams: MarketTypes.WithdrawBalanceParamsStruct = { + provider_or_client: { data: targetByteAddr }, + tokenAmount, + } + + await market.eth.contract.withdraw_balance(withdrawalParams, { gasLimit: 1_000_000_000 }) + await utils.defaultTxDelay() + + previousBalanceBigInt = BigInt(actualClientBalance.balance.val as string) + + expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt - amount), neg: false } + + actualClientBalance = await market.eth.contract.get_balance({ data: targetByteAddr }) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) +} + +const test2 = async (testName: string, { deployer, market }) => { + const dbg = utils.initDbg(testName) + + for (const dealID of CHECKING_DEAL_IDS) { + const actualDealInfo = await getActualDealInfo(market, dealID) + const expectedDealInfo = EXPECTED_DEAL_INFO[dealID] + + expect(actualDealInfo.dealCommitment.data).to.eq(expectedDealInfo.dealCommitment.data) + expect(actualDealInfo.dealCommitment.size).to.eq(expectedDealInfo.dealCommitment.size) + + expect(actualDealInfo.dealClientId).to.eq(expectedDealInfo.dealClientId) + expect(actualDealInfo.dealProviderId).to.eq(expectedDealInfo.dealProviderId) + + expect(actualDealInfo.dealLabel.data).to.eq(expectedDealInfo.dealLabel.data) + expect(actualDealInfo.dealLabel.isString).to.eq(expectedDealInfo.dealLabel.isString) + + expect(actualDealInfo.dealTotalPrice.val).to.eq(expectedDealInfo.dealTotalPrice.val) + expect(actualDealInfo.dealTotalPrice.neg).to.eq(expectedDealInfo.dealTotalPrice.neg) + + expect(actualDealInfo.dealClientCollateral.val).to.eq(expectedDealInfo.dealClientCollateral.val) + expect(actualDealInfo.dealClientCollateral.neg).to.eq(expectedDealInfo.dealClientCollateral.neg) + expect(actualDealInfo.dealProviderCollateral.val).to.eq(expectedDealInfo.dealProviderCollateral.val) + expect(actualDealInfo.dealProviderCollateral.neg).to.eq(expectedDealInfo.dealProviderCollateral.neg) + + expect(actualDealInfo.dealVerified).to.eq(expectedDealInfo.dealVerified) + expect(actualDealInfo.dealActivation.activated).to.eq(expectedDealInfo.dealActivation.activated) + expect(actualDealInfo.dealActivation.terminated).to.eq(expectedDealInfo.dealActivation.terminated) + } +} diff --git a/hh-test/calibnet/e2e/marketCbor.t.ts b/hh-test/calibnet/e2e/marketCbor.t.ts new file mode 100644 index 00000000..9c416020 --- /dev/null +++ b/hh-test/calibnet/e2e/marketCbor.t.ts @@ -0,0 +1,41 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Market Cbot Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (MarketCBORTest)`) + + const marketCBOR_SC = await utils.deployContract(deployer, "MarketCBORTest") + + //note: additional checks performed inside contracts (all revert on error) + await marketCBOR_SC.eth.contract.testDealProposalSerDes() + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/e2e/precompiles.t.ts b/hh-test/calibnet/e2e/precompiles.t.ts new file mode 100644 index 00000000..bc35bcab --- /dev/null +++ b/hh-test/calibnet/e2e/precompiles.t.ts @@ -0,0 +1,72 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +import { CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/precompiles.test.sol/PrecompilesApiTest" + +describe("Precompiles Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (PrecompilesApiTest)`) + + const precompilesSC = await utils.deployContract(deployer, "PrecompilesApiTest") + + const targetF3Addr = "t3wybme2dab6l3h4zuuzxxteztmtq7jyh6qgrko3urkvhjfqdr33vsus4x5s2ccpuhhc5xscpx3bcpufsf6vzq" + const actorId = BigInt(112484) + + //resolving address + const addr: CommonTypes.FilAddressStruct = { + data: utils.filAddressToBytes(targetF3Addr), + } + + const expectedResolvedAddr = actorId + const actualResolvedAddr = await precompilesSC.eth.contract.resolve_address(addr) + + expect(actualResolvedAddr).to.eq(expectedResolvedAddr) + + //resolving empty delegated address (F3 addr type) + const expectedDelegatedAddr = "0x" + const actualDelegatedAddr = await precompilesSC.eth.contract.lookup_delegated_address(actorId) + + expect(actualDelegatedAddr).to.eq(expectedDelegatedAddr) + + //resolving non-empty delegated address + const ethTargetAddr = "0x5E2E67CC130D09438117D404AAAc53e49997DC4F" + + const targetAddr = utils.ethAddressToFilAddress(ethTargetAddr) + const actorId2 = BigInt(114105) + const expectedDelegatedAddr2 = utils.bytesToHex(utils.filAddressToBytes(targetAddr)) + const actualDelegatedAddr2 = await precompilesSC.eth.contract.lookup_delegated_address(actorId2) + + expect(actualDelegatedAddr2).to.eq(expectedDelegatedAddr2) + + //resolving eth address + const expectedIdForEthAddress = actorId2 + const actualIdForEthAddr = await precompilesSC.eth.contract.resolve_eth_address(ethTargetAddr) + + expect(actualIdForEthAddr).to.eq(expectedIdForEthAddress) +} diff --git a/hh-test/calibnet/e2e/send.t.ts b/hh-test/calibnet/e2e/send.t.ts new file mode 100644 index 00000000..9e88dbc4 --- /dev/null +++ b/hh-test/calibnet/e2e/send.t.ts @@ -0,0 +1,62 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" +import { CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/send.test.sol/SendApiTest" + +describe("Send Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (SendApiTest)`) + + const sendSC = await utils.deployContract(deployer, "SendApiTest") + + //send some tokens to the smart contract + await deployer.eth.signer.sendTransaction({ + to: sendSC.eth.address, + value: 100, + }) + await utils.defaultTxDelay() + + //note: additional checks performed inside contracts (all revert on error) + //calling `send` + dbg("calling `send`") + const target = 0x65 + const amount = 10 + await sendSC.eth.contract.send_with_actor_id(target, amount) + await utils.defaultTxDelay() + + //calling `send (address)` + dbg("calling `send (address)`") + const target2: CommonTypes.FilAddressStruct = { + data: Uint8Array.from([0x00, 0x65]), + } + const amount2 = 10 + await sendSC.eth.contract.send_with_address(target2, amount2) + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/e2e/verifreg.t.ts b/hh-test/calibnet/e2e/verifreg.t.ts new file mode 100644 index 00000000..5862746f --- /dev/null +++ b/hh-test/calibnet/e2e/verifreg.t.ts @@ -0,0 +1,68 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import { VerifRegTypes, CommonTypes, VerifRegApiTest } from "../../../typechain-types/contracts/v0.8/tests/verifreg.test.sol/VerifRegApiTest" + +import * as utils from "../../utils" + +describe("Verifreg Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetupOnCalibnet() + + dbg(`Deploying contracts... (verifreg)`) + + const VerifRegFactory = await ethers.getContractFactory("VerifRegApiTest") + const verifreg = await utils.deployContract(deployer, "VerifRegApiTest") + + const storageProviderId = 1000 + const claim_ids = [0, 1] + const getClaimsParams: VerifRegTypes.GetClaimsParamsStruct = { + provider: storageProviderId, + claim_ids, + } + const res: VerifRegTypes.GetClaimsReturnStruct = await verifreg.eth.contract.get_claims(getClaimsParams) + + const removeAllocationParams: VerifRegTypes.RemoveExpiredAllocationsParamsStruct = { + client: BigInt(0x65), + allocation_ids: [], + } + + dbg(`Removing allocations...`) + + await verifreg.eth.contract.remove_expired_allocations(removeAllocationParams) + await utils.defaultTxDelay() + + const removeClaimParams: VerifRegTypes.RemoveExpiredClaimsParamsStruct = { + provider: BigInt(0x66), + claim_ids, + } + + dbg(`Removing expired claims...`) + + await verifreg.eth.contract.remove_expired_claims(removeClaimParams) + await utils.defaultTxDelay() +} diff --git a/hh-test/calibnet/upgradeable/market.beacon.t.ts b/hh-test/calibnet/upgradeable/market.beacon.t.ts new file mode 100644 index 00000000..1f55d9a7 --- /dev/null +++ b/hh-test/calibnet/upgradeable/market.beacon.t.ts @@ -0,0 +1,160 @@ +import { ethers, upgrades, network } from "hardhat" +import { expect } from "chai" + +import * as utils from "../../utils" + +import { CHECKING_DEAL_IDS, EXPECTED_DEAL_INFO, getActualDealInfo } from "../_common" + +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +describe("Market Tests (Beacon)", () => { + const DBG_TESTS = {} + let currentTestName: string + let market, beacon, deployer + + before(async () => { + utils.removeProxyArtifacts() + const { deployer: _deployer } = await utils.performGeneralSetupOnCalibnet() + deployer = _deployer + + const MarketContractFactory = (await ethers.getContractFactory("MarketApiUpgradeableTest", deployer.eth.signer)) as any + + beacon = await upgrades.deployBeacon(MarketContractFactory) + await utils.defaultTxDelay() + + const instance = await upgrades.deployBeaconProxy(beacon, MarketContractFactory, []) + await utils.defaultTxDelay() + + market = { eth: { contract: instance, address: await instance.getAddress() }, fil: { address: "" } } + market.fil = { address: utils.ethAddressToFilAddress(market.eth.address) } + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: State changes on Calibnet (Before Upgrade)", async () => { + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 2: Reading from Calibnet (Before Upgrade)", async () => { + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 1: State changes on Calibnet (After Upgrade)", async () => { + await _upgradeProxy({ market, beacon, deployer }) + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 2: Reading from Calibnet (After Upgrade)", async () => { + await _upgradeProxy({ market, beacon, deployer }) + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName, { deployer, market }) => { + const dbg = utils.initDbg(testName) + + let amount = BigInt(10 ** 18) + + const targetByteAddr = utils.filAddressToBytes(market.fil.address) + + //adding funds to balance + + const previousBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + await market.eth.contract.add_balance({ data: targetByteAddr }, amount, { gasLimit: 1_000_000_000, value: amount }) + await utils.defaultTxDelay() + + let previousBalanceBigInt = BigInt(previousBalance.balance.val as string) + + let expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt + amount), neg: false } + + let actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + dbg("Adding Balance: " + JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + //withdrawing funds from balance + + const balanceBeforeWithdrawal = actualClientBalance.balance + + amount = BigInt(64) + const tokenAmount = { val: Uint8Array.from([64]), neg: false } + dbg(JSON.stringify({ amount, tokenAmount })) + + const withdrawalParams: MarketTypes.WithdrawBalanceParamsStruct = { + provider_or_client: { data: targetByteAddr }, + tokenAmount, + } + + const withdrawTx = await market.eth.contract.withdraw_balance(withdrawalParams, { gasLimit: 1_000_000_000 }) + dbg("withdrawTx: " + JSON.stringify({ withdrawTx })) + + await utils.defaultTxDelay() + + previousBalanceBigInt = BigInt(balanceBeforeWithdrawal.val as string) + + expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt - amount), neg: false } + + actualClientBalance = await market.eth.contract.get_balance({ data: targetByteAddr }) + + dbg("Withdrawing Balance: " + JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) +} + +const test2 = async (testName, { deployer, market }) => { + for (const dealID of CHECKING_DEAL_IDS) { + const actualDealInfo = await getActualDealInfo(market, dealID) + const expectedDealInfo = EXPECTED_DEAL_INFO[dealID] + + expect(actualDealInfo.dealCommitment.data).to.eq(expectedDealInfo.dealCommitment.data) + expect(actualDealInfo.dealCommitment.size).to.eq(expectedDealInfo.dealCommitment.size) + + expect(actualDealInfo.dealClientId).to.eq(expectedDealInfo.dealClientId) + expect(actualDealInfo.dealProviderId).to.eq(expectedDealInfo.dealProviderId) + + expect(actualDealInfo.dealLabel.data).to.eq(expectedDealInfo.dealLabel.data) + expect(actualDealInfo.dealLabel.isString).to.eq(expectedDealInfo.dealLabel.isString) + + expect(actualDealInfo.dealTotalPrice.val).to.eq(expectedDealInfo.dealTotalPrice.val) + expect(actualDealInfo.dealTotalPrice.neg).to.eq(expectedDealInfo.dealTotalPrice.neg) + + expect(actualDealInfo.dealClientCollateral.val).to.eq(expectedDealInfo.dealClientCollateral.val) + expect(actualDealInfo.dealClientCollateral.neg).to.eq(expectedDealInfo.dealClientCollateral.neg) + expect(actualDealInfo.dealProviderCollateral.val).to.eq(expectedDealInfo.dealProviderCollateral.val) + expect(actualDealInfo.dealProviderCollateral.neg).to.eq(expectedDealInfo.dealProviderCollateral.neg) + + expect(actualDealInfo.dealVerified).to.eq(expectedDealInfo.dealVerified) + expect(actualDealInfo.dealActivation.activated).to.eq(expectedDealInfo.dealActivation.activated) + expect(actualDealInfo.dealActivation.terminated).to.eq(expectedDealInfo.dealActivation.terminated) + } +} + +let proxyUpgraded = false +const _upgradeProxy = async ({ market, beacon, deployer }) => { + if (proxyUpgraded) return + + const MarketContractFactoryV2 = (await ethers.getContractFactory("MarketApiUpgradeableV2Test", deployer.eth.signer)) as any + await upgrades.upgradeBeacon(beacon, MarketContractFactoryV2) + await utils.defaultTxDelay() + + market.eth.contract = MarketContractFactoryV2.attach(market.eth.address) + + proxyUpgraded = true +} diff --git a/hh-test/calibnet/upgradeable/market.transparent.t.ts b/hh-test/calibnet/upgradeable/market.transparent.t.ts new file mode 100644 index 00000000..e32c8a17 --- /dev/null +++ b/hh-test/calibnet/upgradeable/market.transparent.t.ts @@ -0,0 +1,160 @@ +import { ethers, upgrades, network } from "hardhat" +import { expect } from "chai" + +import * as utils from "../../utils" + +import { CHECKING_DEAL_IDS, EXPECTED_DEAL_INFO, getActualDealInfo } from "../_common" + +import { MarketApiUpgradeableTest } from "../../../typechain-types" +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +describe("Market Tests (Transparent)", () => { + const DBG_TESTS = {} + let currentTestName: string + let market, deployer + + before(async () => { + utils.removeProxyArtifacts() + const { deployer: _deployer } = await utils.performGeneralSetupOnCalibnet() + deployer = _deployer + + const MarketContractFactory = (await ethers.getContractFactory("MarketApiUpgradeableTest", deployer.eth.signer)) as any + const marketContract: MarketApiUpgradeableTest = (await upgrades.deployProxy(MarketContractFactory, [], { + unsafeAllow: ["delegatecall"], + })) as unknown as MarketApiUpgradeableTest + + await utils.defaultTxDelay() + + market = { eth: { contract: marketContract, address: await marketContract.getAddress() }, fil: { address: "" } } + market.fil = { address: utils.ethAddressToFilAddress(market.eth.address) } + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: State changes on Calibnet (Before Upgrade)", async () => { + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 2: Reading from Calibnet (Before Upgrade)", async () => { + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 1: State changes on Calibnet (After Upgrade)", async () => { + await _upgradeProxy({ market, deployer }) + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 2: Reading from Calibnet (After Upgrade)", async () => { + await _upgradeProxy({ market, deployer }) + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName, { deployer, market }) => { + const dbg = utils.initDbg(testName) + + let amount = BigInt(10 ** 18) + + const targetByteAddr = utils.filAddressToBytes(market.fil.address) + + //adding funds to balance + + const previousBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + await market.eth.contract.add_balance({ data: targetByteAddr }, amount, { gasLimit: 1_000_000_000, value: amount }) + await utils.defaultTxDelay() + + let previousBalanceBigInt = BigInt(previousBalance.balance.val as string) + + let expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt + amount), neg: false } + + let actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + dbg("Adding Balance: " + JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + //withdrawing funds from balance + + const balanceBeforeWithdrawal = actualClientBalance.balance + + amount = BigInt(64) + const tokenAmount = { val: Uint8Array.from([64]), neg: false } + dbg(JSON.stringify({ amount, tokenAmount })) + + const withdrawalParams: MarketTypes.WithdrawBalanceParamsStruct = { + provider_or_client: { data: targetByteAddr }, + tokenAmount, + } + + const withdrawTx = await market.eth.contract.withdraw_balance(withdrawalParams, { gasLimit: 1_000_000_000 }) + dbg("withdrawTx: " + JSON.stringify({ withdrawTx })) + + await utils.defaultTxDelay() + + previousBalanceBigInt = BigInt(balanceBeforeWithdrawal.val as string) + + expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt - amount), neg: false } + + actualClientBalance = await market.eth.contract.get_balance({ data: targetByteAddr }) + + dbg("Withdrawing Balance: " + JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) +} + +const test2 = async (testName, { deployer, market }) => { + for (const dealID of CHECKING_DEAL_IDS) { + const actualDealInfo = await getActualDealInfo(market, dealID) + const expectedDealInfo = EXPECTED_DEAL_INFO[dealID] + + expect(actualDealInfo.dealCommitment.data).to.eq(expectedDealInfo.dealCommitment.data) + expect(actualDealInfo.dealCommitment.size).to.eq(expectedDealInfo.dealCommitment.size) + + expect(actualDealInfo.dealClientId).to.eq(expectedDealInfo.dealClientId) + expect(actualDealInfo.dealProviderId).to.eq(expectedDealInfo.dealProviderId) + + expect(actualDealInfo.dealLabel.data).to.eq(expectedDealInfo.dealLabel.data) + expect(actualDealInfo.dealLabel.isString).to.eq(expectedDealInfo.dealLabel.isString) + + expect(actualDealInfo.dealTotalPrice.val).to.eq(expectedDealInfo.dealTotalPrice.val) + expect(actualDealInfo.dealTotalPrice.neg).to.eq(expectedDealInfo.dealTotalPrice.neg) + + expect(actualDealInfo.dealClientCollateral.val).to.eq(expectedDealInfo.dealClientCollateral.val) + expect(actualDealInfo.dealClientCollateral.neg).to.eq(expectedDealInfo.dealClientCollateral.neg) + expect(actualDealInfo.dealProviderCollateral.val).to.eq(expectedDealInfo.dealProviderCollateral.val) + expect(actualDealInfo.dealProviderCollateral.neg).to.eq(expectedDealInfo.dealProviderCollateral.neg) + + expect(actualDealInfo.dealVerified).to.eq(expectedDealInfo.dealVerified) + expect(actualDealInfo.dealActivation.activated).to.eq(expectedDealInfo.dealActivation.activated) + expect(actualDealInfo.dealActivation.terminated).to.eq(expectedDealInfo.dealActivation.terminated) + } +} + +let proxyUpgraded = false +const _upgradeProxy = async ({ market, deployer }) => { + if (proxyUpgraded) return + + const MarketContractFactoryV2 = (await ethers.getContractFactory("MarketApiUpgradeableV2Test", deployer.eth.signer)) as any + await upgrades.upgradeProxy(market.eth.contract, MarketContractFactoryV2, { + unsafeAllow: ["delegatecall"], + }) + await utils.defaultTxDelay() + + proxyUpgraded = true +} diff --git a/hh-test/calibnet/upgradeable/market.uups.t.ts b/hh-test/calibnet/upgradeable/market.uups.t.ts new file mode 100644 index 00000000..cfdb496a --- /dev/null +++ b/hh-test/calibnet/upgradeable/market.uups.t.ts @@ -0,0 +1,163 @@ +import { ethers, upgrades, network } from "hardhat" +import { expect } from "chai" + +import * as utils from "../../utils" + +import { CHECKING_DEAL_IDS, EXPECTED_DEAL_INFO, getActualDealInfo } from "../_common" + +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +describe("Market Tests (UUPS)", () => { + const DBG_TESTS = {} + let currentTestName: string + let market, beacon, deployer + + before(async () => { + utils.removeProxyArtifacts() + const { deployer: _deployer } = await utils.performGeneralSetupOnCalibnet() + deployer = _deployer + + const MarketContractFactory = (await ethers.getContractFactory("MarketApiUUPSUpgradeableTest", deployer.eth.signer)) as any + + const marketContract = (await upgrades.deployProxy(MarketContractFactory, [], { + kind: "uups", + unsafeAllow: ["delegatecall"], + })) as unknown as any + await utils.defaultTxDelay() + + market = { eth: { contract: marketContract, address: await marketContract.getAddress() }, fil: { address: "" } } + market.fil = { address: utils.ethAddressToFilAddress(market.eth.address) } + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: State changes on Calibnet (Before Upgrade)", async () => { + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 2: Reading from Calibnet (Before Upgrade)", async () => { + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 1: State changes on Calibnet (After Upgrade)", async () => { + await _upgradeProxy({ market, beacon, deployer }) + await test1(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + it("Test 2: Reading from Calibnet (After Upgrade)", async () => { + await _upgradeProxy({ market, beacon, deployer }) + await test2(currentTestName, { deployer, market }) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName, { deployer, market }) => { + const dbg = utils.initDbg(testName) + + let amount = BigInt(10 ** 18) + + const targetByteAddr = utils.filAddressToBytes(market.fil.address) + + //adding funds to balance + + const previousBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + await market.eth.contract.add_balance({ data: targetByteAddr }, amount, { gasLimit: 1_000_000_000, value: amount }) + await utils.defaultTxDelay() + + let previousBalanceBigInt = BigInt(previousBalance.balance.val as string) + + let expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt + amount), neg: false } + + let actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: targetByteAddr }) + + dbg("Adding Balance: " + JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + //withdrawing funds from balance + + const balanceBeforeWithdrawal = actualClientBalance.balance + + amount = BigInt(64) + const tokenAmount = { val: Uint8Array.from([64]), neg: false } + dbg(JSON.stringify({ amount, tokenAmount })) + + const withdrawalParams: MarketTypes.WithdrawBalanceParamsStruct = { + provider_or_client: { data: targetByteAddr }, + tokenAmount, + } + + const withdrawTx = await market.eth.contract.withdraw_balance(withdrawalParams, { gasLimit: 1_000_000_000 }) + dbg("withdrawTx: " + JSON.stringify({ withdrawTx })) + + await utils.defaultTxDelay() + + previousBalanceBigInt = BigInt(balanceBeforeWithdrawal.val as string) + + expectedClientBalance = { val: utils.bigIntToHexString(previousBalanceBigInt - amount), neg: false } + + actualClientBalance = await market.eth.contract.get_balance({ data: targetByteAddr }) + + dbg("Withdrawing Balance: " + JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) +} + +const test2 = async (testName, { deployer, market }) => { + for (const dealID of CHECKING_DEAL_IDS) { + const actualDealInfo = await getActualDealInfo(market, dealID) + const expectedDealInfo = EXPECTED_DEAL_INFO[dealID] + + expect(actualDealInfo.dealCommitment.data).to.eq(expectedDealInfo.dealCommitment.data) + expect(actualDealInfo.dealCommitment.size).to.eq(expectedDealInfo.dealCommitment.size) + + expect(actualDealInfo.dealClientId).to.eq(expectedDealInfo.dealClientId) + expect(actualDealInfo.dealProviderId).to.eq(expectedDealInfo.dealProviderId) + + expect(actualDealInfo.dealLabel.data).to.eq(expectedDealInfo.dealLabel.data) + expect(actualDealInfo.dealLabel.isString).to.eq(expectedDealInfo.dealLabel.isString) + + expect(actualDealInfo.dealTotalPrice.val).to.eq(expectedDealInfo.dealTotalPrice.val) + expect(actualDealInfo.dealTotalPrice.neg).to.eq(expectedDealInfo.dealTotalPrice.neg) + + expect(actualDealInfo.dealClientCollateral.val).to.eq(expectedDealInfo.dealClientCollateral.val) + expect(actualDealInfo.dealClientCollateral.neg).to.eq(expectedDealInfo.dealClientCollateral.neg) + expect(actualDealInfo.dealProviderCollateral.val).to.eq(expectedDealInfo.dealProviderCollateral.val) + expect(actualDealInfo.dealProviderCollateral.neg).to.eq(expectedDealInfo.dealProviderCollateral.neg) + + expect(actualDealInfo.dealVerified).to.eq(expectedDealInfo.dealVerified) + expect(actualDealInfo.dealActivation.activated).to.eq(expectedDealInfo.dealActivation.activated) + expect(actualDealInfo.dealActivation.terminated).to.eq(expectedDealInfo.dealActivation.terminated) + } +} + +let proxyUpgraded = false +const _upgradeProxy = async ({ market, beacon, deployer }) => { + if (proxyUpgraded) return + + const MarketContractFactoryV2 = (await ethers.getContractFactory("MarketApiUUPSUpgradeableV2Test", deployer.eth.signer)) as any + await upgrades.upgradeProxy(market.eth.contract, MarketContractFactoryV2, { + kind: "uups", + unsafeAllow: ["delegatecall"], + }) + await utils.defaultTxDelay() + + market.eth.contract = MarketContractFactoryV2.attach(market.eth.address) + + proxyUpgraded = true +} diff --git a/hh-test/localnet/e2e/account.t.ts b/hh-test/localnet/e2e/account.t.ts new file mode 100644 index 00000000..cb48ba73 --- /dev/null +++ b/hh-test/localnet/e2e/account.t.ts @@ -0,0 +1,75 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import { CommonTypes, AccountTypes } from "../../../typechain-types/contracts/v0.8/tests/account.test.sol/AccountApiTest" + +import * as utils from "../../utils" + +describe("Account Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer, client } = await utils.performGeneralSetup() + + const message = + "8eabea2a4001061ac4c9fe3c517725b8829b159149a863b2a2320cc628d026a871d3cb34947371f384a9eb49ff9bd56a019fa70e10c06ac5ca93df3c1d6f54d540c57cbe2f5209cafdc12146d5d59172dd4d8359015e10584fa6327de0ce5a6a" + + const signature = utils.lotus.signMessage(client.fil.address, message) + + dbg(`Deploying contracts... (account)`) + + const account = await utils.deployContract(deployer, "AccountApiTest") + + const slicedSignature = signature.slice(2) + + const hexSig = `0x${slicedSignature}` + const bytes = [] + for (let c = 0; c < hexSig.length; c += 2) { + bytes.push(parseInt(hexSig.substr(c, 2), 16)) + } + + const _sig = Uint8Array.from([...bytes.slice(1)]) + + const target = BigInt(utils.bytesToHex(client.fil.idAddress())) + + const params: AccountTypes.AuthenticateMessageParamsStruct = { + signature: _sig, + message: utils.hexToBytes(message), + } + + //note: no additional checks performed + // it will revert if the signature/message is incorrect + await account.eth.contract.authenticate_message(target, params) + await utils.defaultTxDelay() + + const universalReceiverParams: CommonTypes.UniversalReceiverParamsStruct = { + type_: BigInt(0), + payload: Uint8Array.from([1, 2, 3]), + } + + //note: no additional checks performed (reverts on error) + await account.eth.contract.universal_receiver_hook(target, universalReceiverParams) + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/e2e/address.t.ts b/hh-test/localnet/e2e/address.t.ts new file mode 100644 index 00000000..c19cfcaf --- /dev/null +++ b/hh-test/localnet/e2e/address.t.ts @@ -0,0 +1,41 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Address Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetup() + + console.log(`Deploying contracts... (AddressTest)`) + + const addressSC = await utils.deployContract(deployer, "AddressTest") + + //note: no additional checks performed (reverts on error) + await addressSC.eth.contract.actorid_conversion() + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/e2e/bigints.t.ts b/hh-test/localnet/e2e/bigints.t.ts new file mode 100644 index 00000000..e0c2a28d --- /dev/null +++ b/hh-test/localnet/e2e/bigints.t.ts @@ -0,0 +1,49 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("BigInt Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + const { deployer } = await utils.performGeneralSetup() + + console.log(`Deploying contracts... (BigIntsTest)`) + + const bigIntSC = await utils.deployContract(deployer, "BigIntsTest") + + //note: additional checks performed inside contracts (all revert on error) + await bigIntSC.eth.contract.to_uint256() + + await bigIntSC.eth.contract.to_int256_negative() + + await bigIntSC.eth.contract.to_int256_positive() + + await bigIntSC.eth.contract.from_uint256() + + await bigIntSC.eth.contract.from_int256_positive() + + await bigIntSC.eth.contract.from_int256_negative() +} diff --git a/hh-test/localnet/e2e/cborDecode.t.ts b/hh-test/localnet/e2e/cborDecode.t.ts new file mode 100644 index 00000000..24f2b2a7 --- /dev/null +++ b/hh-test/localnet/e2e/cborDecode.t.ts @@ -0,0 +1,56 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("CborDecode Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (CborDecodeTest)`) + + const cborDecode = await utils.deployContract(deployer, "CborDecodeTest") + + //note: additional checks performed inside contracts (all revert on error) + await cborDecode.eth.contract.decodeFixedArray() + + await cborDecode.eth.contract.decodeFalse() + + await cborDecode.eth.contract.decodeTrue() + + await cborDecode.eth.contract.decodeNull() + + await cborDecode.eth.contract.decodeInteger() + + await cborDecode.eth.contract.decodeString() + + await cborDecode.eth.contract.decodeStringWithWeirdChar() + + await cborDecode.eth.contract.decodeArrayU8() + + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/e2e/datacap.t.ts b/hh-test/localnet/e2e/datacap.t.ts new file mode 100644 index 00000000..9568e033 --- /dev/null +++ b/hh-test/localnet/e2e/datacap.t.ts @@ -0,0 +1,95 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" +import { CommonTypes, DataCapTypes } from "../../../typechain-types/contracts/v0.8/tests/datacap.test.sol/DataCapApiTest" + +describe("Datacap Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { client, deployer, anyone: user } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (DataCapApiTest)`) + + const DataCapFactory = await ethers.getContractFactory("DataCapApiTest") + const datacap = await utils.deployContract(deployer, "DataCapApiTest") + + const pDatacap = await utils.upgradeToDataCapProxy(deployer, DataCapFactory, await datacap.eth.contract.getAddress()) + await utils.defaultTxDelay() + + dbg(JSON.stringify({ pDatacapAddr: utils.ethAddressToFilAddress(await pDatacap.getAddress()) })) + + dbg("calling 'name'") + + const expectedName = "DataCap" + const actualName = await pDatacap.name() + + expect(actualName).to.eq(expectedName) + + dbg("calling 'symbol'") + + const expectedSymbol = "DCAP" + const actualSymbol = await pDatacap.symbol() + + expect(actualSymbol).to.eq(expectedSymbol) + + const expectedTotalSupply = { + val: "0x3635c9adc5dea00000", // 1000 FIL (1e21 attoFIL) minted during setup + neg: false, + } + + dbg("calling 'total_supply'") + + const actualTotalSupply = await pDatacap.total_supply() + + expect(actualTotalSupply.val).to.eq(expectedTotalSupply.val) + expect(actualTotalSupply.neg).to.eq(expectedTotalSupply.neg) + + dbg("calling 'balance'") + + const addr: CommonTypes.FilAddressStruct = { data: Uint8Array.from([0, 66]) } + const expectedBalance: CommonTypes.BigIntStruct = { val: "0x", neg: false } + const actualBalance: CommonTypes.BigIntStruct = await pDatacap.balance(addr) + + expect(actualBalance.neg).to.eq(expectedBalance.neg) + expect(actualBalance.val).to.eq(expectedBalance.val) + + dbg("calling 'allowance'") + + const allowanceParams: DataCapTypes.GetAllowanceParamsStruct = { + owner: { + data: utils.filAddressToBytes(user.fil.address), + }, + operator: { + data: utils.filAddressToBytes(user.fil.address), + }, + } + const expectedAllowance: CommonTypes.BigIntStruct = { val: "0x", neg: false } + const actualAllowance: CommonTypes.BigIntStruct = await pDatacap.allowance(allowanceParams) + + expect(actualAllowance.neg).to.eq(expectedAllowance.neg) + expect(actualAllowance.val).to.eq(expectedAllowance.val) +} diff --git a/hh-test/localnet/e2e/deserializeParams.t.ts b/hh-test/localnet/e2e/deserializeParams.t.ts new file mode 100644 index 00000000..04926b1d --- /dev/null +++ b/hh-test/localnet/e2e/deserializeParams.t.ts @@ -0,0 +1,41 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Deserialize Params Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + const { deployer } = await utils.performGeneralSetup() + + console.log(`Deploying contracts... (DeserializeParamsTest)`) + + const deserializeParamsSC = await utils.deployContract(deployer, "DeserializeParamsTest") + + //note: additional checks performed inside contracts (all revert on error) + await deserializeParamsSC.eth.contract.deserializeGetVestingFundsReturn() + + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/e2e/leb128.t.ts b/hh-test/localnet/e2e/leb128.t.ts new file mode 100644 index 00000000..633a84bc --- /dev/null +++ b/hh-test/localnet/e2e/leb128.t.ts @@ -0,0 +1,45 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Leb128 Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + // await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + const { deployer } = await utils.performGeneralSetup() + + console.log(`Deploying contracts... (Leb128Generated[1..15]Test)`) + const leb128_SC = [] + for (let i = 1; i < 15; ++i) { + const cName = `Leb128Generated${i}Test` + const sc = await utils.deployContract(deployer, cName) + leb128_SC.push(sc) + } + + for (const l128_SC of leb128_SC) { + //note: additional checks performed inside contracts (all revert on error) + await l128_SC.eth.contract.unsiged_integer_leb128_encoding_generated() + } +} diff --git a/hh-test/localnet/e2e/market.t.ts b/hh-test/localnet/e2e/market.t.ts new file mode 100644 index 00000000..451c7cc8 --- /dev/null +++ b/hh-test/localnet/e2e/market.t.ts @@ -0,0 +1,153 @@ +import { ethers } from "hardhat" +import { expect } from "chai" + +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +import * as utils from "../../utils" + +describe("Market Tests", () => { + const DBG_TESTS = {} + let currentTestName: string + + beforeEach(function () { + utils.removeProxyArtifacts() + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Basic Deal Flow", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + //test scenario adopted from rust integration tests + + const dbg = utils.initDbg(testName) + + const { deployer, anyone, client, storageProvider } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (market and helper)`) + + const market = await utils.deployContract(deployer, "MarketApiTest") + const helper = await utils.deployContract(deployer, "MarketHelper") + + dbg(`Contracts deployed:`) + + dbg(`Setting miner control address to market.eth.contract: ${market.fil.address}`) + utils.lotus.setControlAddress(market.fil.address) + + dbg(`Funding Escrows... (client and provider)`) + const amount = BigInt(10 ** 18) + + await market.eth.contract.add_balance({ data: client.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + await market.eth.contract.add_balance({ data: storageProvider.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + const expectedClientBalance = { val: utils.bigIntToHexString(amount), neg: false } + + const actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: client.fil.byteAddress }) + + dbg(JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + dbg(`Generating deal params...`) + const { deal, dealDebug } = utils.generateDealParams(client.fil.address, storageProvider.fil.address) + const serializedDealProposal = (await helper.eth.contract.serialize_deal_proposal(deal.proposal)).slice(2) + + const signedDealProposal = utils.lotus.signMessage(client.fil.address, serializedDealProposal) + + deal.client_signature = utils.hexToBytes(signedDealProposal) + + dbg(`Publishing deal...`) //Note: Anyone can issue the publishing transaction + + dbg(JSON.stringify({ popTx: await market.eth.contract.publish_storage_deals.populateTransaction({ deals: [deal] }) })) + + const tx = await market.eth.contract.publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) + + await tx.wait() + await utils.defaultTxDelay(2) + + //Asertions + + //Expected values + const expectedDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = { + data: ethers.hexlify(Uint8Array.from([0, ...Array.from(ethers.getBytes(deal.proposal.piece_cid.data))])), + size: deal.proposal.piece_size, + } + const expectedDealClientId = utils.idAddressToBigInt(client.fil.idAddress()) + const expectedDealProviderId = utils.idAddressToBigInt(storageProvider.fil.idAddress()) + + const expectedDealLabel: CommonTypes.DealLabelStruct = { data: utils.bytesToHex(deal.proposal.label.data as Uint8Array), isString: true } + + const expectedDealTerm: MarketTypes.GetDealTermReturnStruct = { + start: deal.proposal.start_epoch, + duration: dealDebug.end_epoch - dealDebug.start_epoch, + } + + const expectedDealTotalPrice = dealDebug.total_price + + const expectedDealClientCollateral = utils.bigIntStructWithStringFormat(deal.proposal.client_collateral) + const expectedDealProviderCollateral = utils.bigIntStructWithStringFormat(deal.proposal.provider_collateral) + + const expectedDealVerified = false + const expectedDealActivation: MarketTypes.GetDealActivationReturnStruct = { + activated: BigInt(0), + terminated: BigInt(0), + } + + dbg(`Getting deal info...`) + //Actual values + const dealID = await market.eth.contract.publishedDealIds(0) + + dbg(`Deal ID: ${dealID}`) + const actualDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await market.eth.contract.get_deal_data_commitment(dealID) + const actualDealClientId = await market.eth.contract.get_deal_client(dealID) + const actualDealProviderId = await market.eth.contract.get_deal_provider(dealID) + const actualDealLabel: CommonTypes.DealLabelStruct = await market.eth.contract.get_deal_label(dealID) + const actualDealTerm: MarketTypes.GetDealTermReturnStruct = await market.eth.contract.get_deal_term(dealID) + const actualDealTotalPrice: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_total_price(dealID) + const actualDealClientCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_client_collateral(dealID) + const actualDealProviderCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_provider_collateral(dealID) + + const actualDealVerified = await market.eth.contract.get_deal_verified(dealID) + const actualDealActivation: MarketTypes.GetDealActivationReturnStruct = await market.eth.contract.get_deal_activation(dealID) + + //Comparison checks + expect(actualDealCommitment.data).to.eq(expectedDealCommitment.data) + expect(actualDealCommitment.size).to.eq(expectedDealCommitment.size) + + expect(actualDealClientId).to.eq(expectedDealClientId) + expect(actualDealProviderId).to.eq(expectedDealProviderId) + + expect(actualDealLabel.data).to.eq(expectedDealLabel.data) + expect(actualDealLabel.isString).to.eq(expectedDealLabel.isString) + + expect(actualDealTerm.start).to.eq(expectedDealTerm.start) + expect(actualDealTerm.duration).to.eq(expectedDealTerm.duration) + + expect(actualDealTotalPrice.val).to.eq(expectedDealTotalPrice.val) + expect(actualDealTotalPrice.neg).to.eq(expectedDealTotalPrice.neg) + + expect(actualDealClientCollateral.val).to.eq(expectedDealClientCollateral.val) + expect(actualDealClientCollateral.neg).to.eq(expectedDealClientCollateral.neg) + expect(actualDealProviderCollateral.val).to.eq(expectedDealProviderCollateral.val) + expect(actualDealProviderCollateral.neg).to.eq(expectedDealProviderCollateral.neg) + + expect(actualDealVerified).to.eq(expectedDealVerified) + expect(actualDealActivation.activated).to.eq(expectedDealActivation.activated) + expect(actualDealActivation.terminated).to.eq(expectedDealActivation.terminated) +} diff --git a/hh-test/localnet/e2e/marketCbor.t.ts b/hh-test/localnet/e2e/marketCbor.t.ts new file mode 100644 index 00000000..2f2b71d4 --- /dev/null +++ b/hh-test/localnet/e2e/marketCbor.t.ts @@ -0,0 +1,40 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +describe("Market Cbot Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + const { deployer } = await utils.performGeneralSetup() + + console.log(`Deploying contracts... (MarketCBORTest)`) + + const marketCBOR_SC = await utils.deployContract(deployer, "MarketCBORTest") + + //note: additional checks performed inside contracts (all revert on error) + await marketCBOR_SC.eth.contract.testDealProposalSerDes() + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/e2e/power.t.ts b/hh-test/localnet/e2e/power.t.ts new file mode 100644 index 00000000..db87a6ac --- /dev/null +++ b/hh-test/localnet/e2e/power.t.ts @@ -0,0 +1,60 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" +import { CommonTypes, PowerTypes } from "../../../typechain-types/contracts/v0.8/tests/power.test.sol/PowerApiTest" + +describe("Power Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer, storageProvider } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (PowerApiTest)`) + + const powerSC = await utils.deployContract(deployer, "PowerApiTest") + + const expectedMinerCount = BigInt(1) + const actualMinerCount = await powerSC.eth.contract.miner_count() + + expect(actualMinerCount).to.eq(expectedMinerCount) + + const expectedNetworkRawPower: CommonTypes.BigIntStruct = { val: "0x1000", neg: false } + const actualNetworkRawPower = await powerSC.eth.contract.network_raw_power() + + expect(actualNetworkRawPower.neg).to.eq(expectedNetworkRawPower.neg) + expect(actualNetworkRawPower.val).to.eq(expectedNetworkRawPower.val) + + const minerId = storageProvider.fil.id + const expectedMinerRawPower: CommonTypes.BigIntStruct = { val: "0x1000", neg: false } + const actualMinerRawPower: PowerTypes.MinerRawPowerReturnStruct = await powerSC.eth.contract.miner_raw_power(minerId) + + expect(actualMinerRawPower.raw_byte_power.neg).to.eq(expectedMinerRawPower.neg) + expect(actualMinerRawPower.raw_byte_power.val).to.eq(expectedMinerRawPower.val) + + const expectedMinerConsensusCount = BigInt(1) + const actualMinerConsensusCount = await powerSC.eth.contract.miner_consensus_count() + + expect(actualMinerConsensusCount).to.eq(expectedMinerConsensusCount) +} diff --git a/hh-test/localnet/e2e/precompiles.t.ts b/hh-test/localnet/e2e/precompiles.t.ts new file mode 100644 index 00000000..f019ec48 --- /dev/null +++ b/hh-test/localnet/e2e/precompiles.t.ts @@ -0,0 +1,67 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" + +import { CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/precompiles.test.sol/PrecompilesApiTest" + +describe("Precompiles Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + const { deployer, anyone, client: f3Addr } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (PrecompilesApiTest)`) + + const precompilesSC = await utils.deployContract(deployer, "PrecompilesApiTest") + + //resolving address + const addr: CommonTypes.FilAddressStruct = { + data: utils.filAddressToBytes(anyone.fil.address), + } + + const expectedResolvedAddr = utils.lotus.findIDAddressToBigInt(anyone.fil.address) + const actualResolvedAddr = await precompilesSC.eth.contract.resolve_address(addr) + + expect(actualResolvedAddr).to.eq(expectedResolvedAddr) + + //resolving empty delegated address + const actorId = utils.lotus.findIDAddressToBigInt(f3Addr.fil.address) + const expectedDelegatedAddr = "0x" + const actualDelegatedAddr = await precompilesSC.eth.contract.lookup_delegated_address(actorId) + + expect(actualDelegatedAddr).to.eq(expectedDelegatedAddr) + + //resolving non-empty delegated address + const actorId2 = utils.lotus.findIDAddressToBigInt(anyone.fil.address) + const expectedDelegatedAddr2 = utils.bytesToHex(utils.filAddressToBytes(anyone.fil.address)) + const actualDelegatedAddr2 = await precompilesSC.eth.contract.lookup_delegated_address(actorId2) + + expect(actualDelegatedAddr2).to.eq(expectedDelegatedAddr2) + + //resolving eth address + const expectedIdForEthAddress = utils.lotus.findIDAddressToBigInt(anyone.fil.address) + const actualIdForEthAddr = await precompilesSC.eth.contract.resolve_eth_address(anyone.eth.address) + + expect(actualIdForEthAddr).to.eq(expectedIdForEthAddress) +} diff --git a/hh-test/localnet/e2e/send.t.ts b/hh-test/localnet/e2e/send.t.ts new file mode 100644 index 00000000..da7f187a --- /dev/null +++ b/hh-test/localnet/e2e/send.t.ts @@ -0,0 +1,65 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import * as utils from "../../utils" +import { CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/send.test.sol/SendApiTest" + +describe("Send Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (SendApiTest)`) + + const sendSC = await utils.deployContract(deployer, "SendApiTest") + + //send some tokens to the smart contract + await deployer.eth.signer.sendTransaction( + { + to: sendSC.eth.address, + value: 100, + }, + { gasLimit: 100000000 } + ) + await utils.defaultTxDelay() + + //note: additional checks performed inside contracts (all revert on error) + //calling `send` + dbg("calling `send`") + const target = 0x65 + const amount = 10 + await sendSC.eth.contract.send_with_actor_id(target, amount) + await utils.defaultTxDelay() + + //calling `send (address)` + dbg("calling `send (address)`") + const target2: CommonTypes.FilAddressStruct = { + data: Uint8Array.from([0x00, 0x65]), + } + const amount2 = 10 + await sendSC.eth.contract.send_with_address(target2, amount2) + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/e2e/verifreg.t.ts b/hh-test/localnet/e2e/verifreg.t.ts new file mode 100644 index 00000000..c824bfa7 --- /dev/null +++ b/hh-test/localnet/e2e/verifreg.t.ts @@ -0,0 +1,82 @@ +import { ethers } from "hardhat" +import { expect, util } from "chai" + +import { VerifRegTypes, CommonTypes, VerifRegApiTest } from "../../../typechain-types/contracts/v0.8/tests/verifreg.test.sol/VerifRegApiTest" + +import * as utils from "../../utils" + +describe("Verifreg Test", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(async () => {}) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Integration test port", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + const dbg = utils.initDbg(testName) + + const { deployer, anyone, storageProvider } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (verifreg)`) + + const VerifRegFactory = await ethers.getContractFactory("VerifRegApiTest") + const verifreg = await utils.deployContract(deployer, "VerifRegApiTest") + + const pVerifreg = await utils.upgradeToVerifRegProxy(deployer, VerifRegFactory, await verifreg.eth.contract.getAddress()) + + const addr: CommonTypes.FilAddressStruct = { + data: utils.filAddressToBytes(anyone.fil.address), + } + + const allowance: CommonTypes.BigIntStruct = { + val: utils.hexToBytes(BigInt(1_000).toString(16)), + neg: false, + } + const params: VerifRegTypes.AddVerifiedClientParamsStruct = { + addr, + allowance, + } + await pVerifreg.add_verified_client(params) + await utils.defaultTxDelay() + + dbg(`---> Added verified Client !!!`) + + const claim_ids = [0, 1] + const getClaimsParams: VerifRegTypes.GetClaimsParamsStruct = { + provider: storageProvider.fil.id, + claim_ids, + } + const res: VerifRegTypes.GetClaimsReturnStruct = await pVerifreg.get_claims(getClaimsParams) + + const removeAllocationParams: VerifRegTypes.RemoveExpiredAllocationsParamsStruct = { + client: BigInt(0x65), + allocation_ids: [], + } + + await pVerifreg.remove_expired_allocations(removeAllocationParams) + await utils.defaultTxDelay() + + const removeClaimParams: VerifRegTypes.RemoveExpiredClaimsParamsStruct = { + provider: BigInt(0x66), + claim_ids, + } + + await pVerifreg.remove_expired_claims(removeClaimParams) + await utils.defaultTxDelay() +} diff --git a/hh-test/localnet/upgradeable/market.beacon.t.ts b/hh-test/localnet/upgradeable/market.beacon.t.ts new file mode 100644 index 00000000..8296cf66 --- /dev/null +++ b/hh-test/localnet/upgradeable/market.beacon.t.ts @@ -0,0 +1,182 @@ +import { ethers, upgrades, network } from "hardhat" +import { expect } from "chai" + +import * as utils from "../../utils" + +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +describe("Market Tests (Beacon)", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(() => { + utils.removeProxyArtifacts() + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Basic Deal Flow with Beacon Proxy Upgrade", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + //test scenario adopted from rust integration tests + + const dbg = utils.initDbg(testName) + + const { deployer, client, storageProvider } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (market and helper)`) + + const MarketContractFactory = (await ethers.getContractFactory("MarketApiUpgradeableTest", deployer.eth.signer)) as any + + const beacon = await upgrades.deployBeacon(MarketContractFactory) + await utils.defaultTxDelay() + + dbg(`Market Beacon Deployed.`) + + const instance = await upgrades.deployBeaconProxy(beacon, MarketContractFactory, []) + await utils.defaultTxDelay() + + const market = { eth: { contract: instance, address: await instance.getAddress() }, fil: { address: "" } } + market.fil = { address: utils.ethAddressToFilAddress(market.eth.address) } + + const helper = await utils.deployContract(deployer, "MarketHelper") + + dbg("Contracts deployed.") + dbg(`Proxy Deployer: ${deployer.eth.address}`) + dbg(`Proxy ADMIN: ${await upgrades.erc1967.getAdminAddress(market.eth.address)}`) + + dbg(`Setting miner control address to market.eth.contract: ${market.fil.address}`) + + utils.lotus.setControlAddress(market.fil.address) + + dbg(`Funding Escrows... (client and provider)`) + const amount = BigInt(10 ** 18) + + await market.eth.contract.add_balance({ data: client.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + await market.eth.contract.add_balance({ data: storageProvider.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + const expectedClientBalance = { val: utils.bigIntToHexString(amount), neg: false } + + const actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: client.fil.byteAddress }) + + dbg(JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + dbg(`Generating deal params...`) + + const { deal, dealDebug } = utils.generateDealParams(client.fil.address, storageProvider.fil.address) + const serializedDealProposal = (await helper.eth.contract.serialize_deal_proposal(deal.proposal)).slice(2) + + const signedDealProposal = utils.lotus.signMessage(client.fil.address, serializedDealProposal) + + deal.client_signature = utils.hexToBytes(signedDealProposal) + + dbg(`Publishing deal...`) //Note: Anyone can issue the publishing transaction + + const tx = await market.eth.contract.publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) + + await utils.defaultTxDelay() + await utils.defaultTxDelay() + + dbg(`Deal published!`) //Note: Anyone can issue the publishing transaction + + //Asertions + + //Expected values + const expectedDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = { + data: ethers.hexlify(Uint8Array.from([0, ...Array.from(ethers.getBytes(deal.proposal.piece_cid.data))])), + size: deal.proposal.piece_size, + } + const expectedDealClientId = utils.idAddressToBigInt(client.fil.idAddress()) + const expectedDealProviderId = utils.idAddressToBigInt(storageProvider.fil.idAddress()) + + const expectedDealLabel: CommonTypes.DealLabelStruct = { data: utils.bytesToHex(deal.proposal.label.data as Uint8Array), isString: true } + + const expectedDealTerm: MarketTypes.GetDealTermReturnStruct = { + start: deal.proposal.start_epoch, + duration: dealDebug.end_epoch - dealDebug.start_epoch, + } + + const expectedDealTotalPrice = dealDebug.total_price + + const expectedDealClientCollateral = utils.bigIntStructWithStringFormat(deal.proposal.client_collateral) + const expectedDealProviderCollateral = utils.bigIntStructWithStringFormat(deal.proposal.provider_collateral) + + const expectedDealVerified = false + const expectedDealActivation: MarketTypes.GetDealActivationReturnStruct = { + activated: BigInt(0), + terminated: BigInt(0), + } + + for (const stage of ["beforeUpgrade", "afterUpgrade"]) { + dbg(`Assertion stage:${stage}`) + //Actual values + const dealID = await market.eth.contract.publishedDealIds(0) + const actualDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await market.eth.contract.get_deal_data_commitment(dealID) + const actualDealClientId = await market.eth.contract.get_deal_client(dealID) + const actualDealProviderId = await market.eth.contract.get_deal_provider(dealID) + const actualDealLabel: CommonTypes.DealLabelStruct = await market.eth.contract.get_deal_label(dealID) + const actualDealTerm: MarketTypes.GetDealTermReturnStruct = await market.eth.contract.get_deal_term(dealID) + const actualDealTotalPrice: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_total_price(dealID) + const actualDealClientCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_client_collateral(dealID) + const actualDealProviderCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_provider_collateral(dealID) + + const actualDealVerified = await market.eth.contract.get_deal_verified(dealID) + const actualDealActivation: MarketTypes.GetDealActivationReturnStruct = await market.eth.contract.get_deal_activation(dealID) + + //Comparison checks + expect(actualDealCommitment.data).to.eq(expectedDealCommitment.data) + expect(actualDealCommitment.size).to.eq(expectedDealCommitment.size) + + expect(actualDealClientId).to.eq(expectedDealClientId) + expect(actualDealProviderId).to.eq(expectedDealProviderId) + + expect(actualDealLabel.data).to.eq(expectedDealLabel.data) + expect(actualDealLabel.isString).to.eq(expectedDealLabel.isString) + + expect(actualDealTerm.start).to.eq(expectedDealTerm.start) + expect(actualDealTerm.duration).to.eq(expectedDealTerm.duration) + + expect(actualDealTotalPrice.val).to.eq(expectedDealTotalPrice.val) + expect(actualDealTotalPrice.neg).to.eq(expectedDealTotalPrice.neg) + + expect(actualDealClientCollateral.val).to.eq(expectedDealClientCollateral.val) + expect(actualDealClientCollateral.neg).to.eq(expectedDealClientCollateral.neg) + expect(actualDealProviderCollateral.val).to.eq(expectedDealProviderCollateral.val) + expect(actualDealProviderCollateral.neg).to.eq(expectedDealProviderCollateral.neg) + + expect(actualDealVerified).to.eq(expectedDealVerified) + expect(actualDealActivation.activated).to.eq(expectedDealActivation.activated) + expect(actualDealActivation.terminated).to.eq(expectedDealActivation.terminated) + + if (stage == "beforeUpgrade") { + dbg(`Upgrading...`) + + const MarketContractFactoryV2 = (await ethers.getContractFactory("MarketApiUpgradeableV2Test", deployer.eth.signer)) as any + await upgrades.upgradeBeacon(beacon, MarketContractFactoryV2) + market.eth.contract = MarketContractFactoryV2.attach(market.eth.address) + + dbg(`Upgraded!`) + } + } +} diff --git a/hh-test/localnet/upgradeable/market.transparent.t.ts b/hh-test/localnet/upgradeable/market.transparent.t.ts new file mode 100644 index 00000000..16b2acb6 --- /dev/null +++ b/hh-test/localnet/upgradeable/market.transparent.t.ts @@ -0,0 +1,182 @@ +import { ethers, upgrades, network } from "hardhat" +import { expect } from "chai" + +import * as utils from "../../utils" + +import { MarketApiUpgradeableTest } from "../../../typechain-types" +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +describe("Market Tests (Transparent)", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(() => { + utils.removeProxyArtifacts() + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Basic Deal Flow with Transparent Proxy Upgrade", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + //test scenario adopted from rust integration tests + + const dbg = utils.initDbg(testName) + + const { deployer, anyone: proxyAdmin, client, storageProvider } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (market and helper)`) + + const MarketContractFactory = (await ethers.getContractFactory("MarketApiUpgradeableTest", deployer.eth.signer)) as any + const marketContract: MarketApiUpgradeableTest = (await upgrades.deployProxy(MarketContractFactory, [], { + unsafeAllow: ["delegatecall"], + initialOwner: proxyAdmin.eth.address, + })) as unknown as MarketApiUpgradeableTest + + await utils.defaultTxDelay() + + const market = { eth: { contract: marketContract, address: await marketContract.getAddress() }, fil: { address: "" } } + market.fil = { address: utils.ethAddressToFilAddress(market.eth.address) } + + const helper = await utils.deployContract(deployer, "MarketHelper") + + dbg("Contracts deployed.") + dbg(`Proxy Deployer: ${deployer.eth.address}`) + dbg(`Proxy ADMIN: ${await upgrades.erc1967.getAdminAddress(market.eth.address)}`) + + dbg(`Setting miner control address to market.eth.contract: ${market.fil.address}`) + + utils.lotus.setControlAddress(market.fil.address) + + dbg(`Funding Escrows... (client and provider)`) + const amount = BigInt(10 ** 18) + + await market.eth.contract.add_balance({ data: client.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + await market.eth.contract.add_balance({ data: storageProvider.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + const expectedClientBalance = { val: utils.bigIntToHexString(amount), neg: false } + + const actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: client.fil.byteAddress }) + + dbg(JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + dbg(`Generating deal params...`) + + const { deal, dealDebug } = utils.generateDealParams(client.fil.address, storageProvider.fil.address) + const serializedDealProposal = (await helper.eth.contract.serialize_deal_proposal(deal.proposal)).slice(2) + + const signedDealProposal = utils.lotus.signMessage(client.fil.address, serializedDealProposal) + + deal.client_signature = utils.hexToBytes(signedDealProposal) + + dbg(`Publishing deal...`) //Note: Anyone can issue the publishing transaction + + const tx = await market.eth.contract.publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) + + await utils.defaultTxDelay() + await utils.defaultTxDelay() + + dbg(`Deal published!`) //Note: Anyone can issue the publishing transaction + + //Asertions + + //Expected values + const expectedDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = { + data: ethers.hexlify(Uint8Array.from([0, ...Array.from(ethers.getBytes(deal.proposal.piece_cid.data))])), + size: deal.proposal.piece_size, + } + const expectedDealClientId = utils.idAddressToBigInt(client.fil.idAddress()) + const expectedDealProviderId = utils.idAddressToBigInt(storageProvider.fil.idAddress()) + + const expectedDealLabel: CommonTypes.DealLabelStruct = { data: utils.bytesToHex(deal.proposal.label.data as Uint8Array), isString: true } + + const expectedDealTerm: MarketTypes.GetDealTermReturnStruct = { + start: deal.proposal.start_epoch, + duration: dealDebug.end_epoch - dealDebug.start_epoch, + } + + const expectedDealTotalPrice = dealDebug.total_price + + const expectedDealClientCollateral = utils.bigIntStructWithStringFormat(deal.proposal.client_collateral) + const expectedDealProviderCollateral = utils.bigIntStructWithStringFormat(deal.proposal.provider_collateral) + + const expectedDealVerified = false + const expectedDealActivation: MarketTypes.GetDealActivationReturnStruct = { + activated: BigInt(0), + terminated: BigInt(0), + } + + for (const stage of ["beforeUpgrade", "afterUpgrade"]) { + //Actual values + const dealID = await market.eth.contract.publishedDealIds(0) + const actualDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await market.eth.contract.get_deal_data_commitment(dealID) + const actualDealClientId = await market.eth.contract.get_deal_client(dealID) + const actualDealProviderId = await market.eth.contract.get_deal_provider(dealID) + const actualDealLabel: CommonTypes.DealLabelStruct = await market.eth.contract.get_deal_label(dealID) + const actualDealTerm: MarketTypes.GetDealTermReturnStruct = await market.eth.contract.get_deal_term(dealID) + const actualDealTotalPrice: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_total_price(dealID) + const actualDealClientCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_client_collateral(dealID) + const actualDealProviderCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_provider_collateral(dealID) + + const actualDealVerified = await market.eth.contract.get_deal_verified(dealID) + const actualDealActivation: MarketTypes.GetDealActivationReturnStruct = await market.eth.contract.get_deal_activation(dealID) + + //Comparison checks + expect(actualDealCommitment.data).to.eq(expectedDealCommitment.data) + expect(actualDealCommitment.size).to.eq(expectedDealCommitment.size) + + expect(actualDealClientId).to.eq(expectedDealClientId) + expect(actualDealProviderId).to.eq(expectedDealProviderId) + + expect(actualDealLabel.data).to.eq(expectedDealLabel.data) + expect(actualDealLabel.isString).to.eq(expectedDealLabel.isString) + + expect(actualDealTerm.start).to.eq(expectedDealTerm.start) + expect(actualDealTerm.duration).to.eq(expectedDealTerm.duration) + + expect(actualDealTotalPrice.val).to.eq(expectedDealTotalPrice.val) + expect(actualDealTotalPrice.neg).to.eq(expectedDealTotalPrice.neg) + + expect(actualDealClientCollateral.val).to.eq(expectedDealClientCollateral.val) + expect(actualDealClientCollateral.neg).to.eq(expectedDealClientCollateral.neg) + expect(actualDealProviderCollateral.val).to.eq(expectedDealProviderCollateral.val) + expect(actualDealProviderCollateral.neg).to.eq(expectedDealProviderCollateral.neg) + + expect(actualDealVerified).to.eq(expectedDealVerified) + expect(actualDealActivation.activated).to.eq(expectedDealActivation.activated) + expect(actualDealActivation.terminated).to.eq(expectedDealActivation.terminated) + + if (stage == "beforeUpgrade") { + dbg(`Upgrading...`) + + const MarketContractFactoryV2 = (await ethers.getContractFactory("MarketApiUpgradeableV2Test", proxyAdmin.eth.signer)) as any + await upgrades.upgradeProxy(market.eth.contract, MarketContractFactoryV2, { + unsafeAllow: ["delegatecall"], + }) + await utils.defaultTxDelay() + + dbg(`Upgraded`) + } + } +} diff --git a/hh-test/localnet/upgradeable/market.uups.t.ts b/hh-test/localnet/upgradeable/market.uups.t.ts new file mode 100644 index 00000000..394ea63c --- /dev/null +++ b/hh-test/localnet/upgradeable/market.uups.t.ts @@ -0,0 +1,182 @@ +import { ethers, upgrades, network } from "hardhat" +import { expect } from "chai" + +import * as utils from "../../utils" + +import { MarketTypes, CommonTypes } from "../../../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" + +describe("Market Tests (UUPS)", () => { + const DBG_TESTS = {} + let currentTestName: string + + before(() => { + utils.removeProxyArtifacts() + }) + + beforeEach(function () { + currentTestName = this.currentTest.title + DBG_TESTS[currentTestName] = true + }) + + it("Test 1: Basic Deal Flow with UUPS Proxy Upgrade", async () => { + await test1(currentTestName) + DBG_TESTS[currentTestName] = false + }) + + afterEach(() => { + if (DBG_TESTS[currentTestName]) { + utils.printDbgLog(currentTestName) + } + }) +}) + +const test1 = async (testName: string) => { + //test scenario adopted from rust integration tests + + const dbg = utils.initDbg(testName) + + const { deployer, client, storageProvider } = await utils.performGeneralSetup() + + dbg(`Deploying contracts... (market and helper)`) + + const MarketContractFactory = (await ethers.getContractFactory("MarketApiUUPSUpgradeableTest", deployer.eth.signer)) as any + + const marketContract = (await upgrades.deployProxy(MarketContractFactory, [], { + kind: "uups", + unsafeAllow: ["delegatecall"], + })) as unknown as any + await utils.defaultTxDelay() + + const market = { eth: { contract: marketContract, address: await marketContract.getAddress() }, fil: { address: "" } } + market.fil = { address: utils.ethAddressToFilAddress(market.eth.address) } + + const helper = await utils.deployContract(deployer, "MarketHelper") + + dbg("Contracts deployed.") + dbg(`Proxy Deployer: ${deployer.eth.address}`) + dbg(`Proxy ADMIN: ${await upgrades.erc1967.getAdminAddress(market.eth.address)}`) + + dbg(`Setting miner control address to market.eth.contract: ${market.fil.address}`) + + utils.lotus.setControlAddress(market.fil.address) + + dbg(`Funding Escrows... (client and provider)`) + const amount = BigInt(10 ** 18) + + await market.eth.contract.add_balance({ data: client.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + await market.eth.contract.add_balance({ data: storageProvider.fil.byteAddress }, amount, { gasLimit: 1_000_000_000, value: amount }) + + await utils.defaultTxDelay() + + const expectedClientBalance = { val: utils.bigIntToHexString(amount), neg: false } + + const actualClientBalance: MarketTypes.GetBalanceReturnStruct = await market.eth.contract.get_balance({ data: client.fil.byteAddress }) + + dbg(JSON.stringify({ expectedClientBalance, actualClientBalance })) + + expect(actualClientBalance.balance.val).to.eq(expectedClientBalance.val) + expect(actualClientBalance.balance.neg).to.eq(expectedClientBalance.neg) + + dbg(`Generating deal params...`) + + const { deal, dealDebug } = utils.generateDealParams(client.fil.address, storageProvider.fil.address) + const serializedDealProposal = (await helper.eth.contract.serialize_deal_proposal(deal.proposal)).slice(2) + + const signedDealProposal = utils.lotus.signMessage(client.fil.address, serializedDealProposal) + + deal.client_signature = utils.hexToBytes(signedDealProposal) + + dbg(`Publishing deal...`) //Note: Anyone can issue the publishing transaction + + const tx = await market.eth.contract.publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) + + await utils.defaultTxDelay() + await utils.defaultTxDelay() + + dbg(`Deal published!`) //Note: Anyone can issue the publishing transaction + + //Asertions + + //Expected values + const expectedDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = { + data: ethers.hexlify(Uint8Array.from([0, ...Array.from(ethers.getBytes(deal.proposal.piece_cid.data))])), + size: deal.proposal.piece_size, + } + const expectedDealClientId = utils.idAddressToBigInt(client.fil.idAddress()) + const expectedDealProviderId = utils.idAddressToBigInt(storageProvider.fil.idAddress()) + + const expectedDealLabel: CommonTypes.DealLabelStruct = { data: utils.bytesToHex(deal.proposal.label.data as Uint8Array), isString: true } + + const expectedDealTerm: MarketTypes.GetDealTermReturnStruct = { + start: deal.proposal.start_epoch, + duration: dealDebug.end_epoch - dealDebug.start_epoch, + } + + const expectedDealTotalPrice = dealDebug.total_price + + const expectedDealClientCollateral = utils.bigIntStructWithStringFormat(deal.proposal.client_collateral) + const expectedDealProviderCollateral = utils.bigIntStructWithStringFormat(deal.proposal.provider_collateral) + + const expectedDealVerified = false + const expectedDealActivation: MarketTypes.GetDealActivationReturnStruct = { + activated: BigInt(0), + terminated: BigInt(0), + } + + for (const stage of ["beforeUpgrade", "afterUpgrade"]) { + //Actual values + dbg(`Assertions stage: ${stage}`) + const dealID = await market.eth.contract.publishedDealIds(0) + const actualDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await market.eth.contract.get_deal_data_commitment(dealID) + const actualDealClientId = await market.eth.contract.get_deal_client(dealID) + const actualDealProviderId = await market.eth.contract.get_deal_provider(dealID) + const actualDealLabel: CommonTypes.DealLabelStruct = await market.eth.contract.get_deal_label(dealID) + const actualDealTerm: MarketTypes.GetDealTermReturnStruct = await market.eth.contract.get_deal_term(dealID) + const actualDealTotalPrice: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_total_price(dealID) + const actualDealClientCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_client_collateral(dealID) + const actualDealProviderCollateral: CommonTypes.BigIntStruct = await market.eth.contract.get_deal_provider_collateral(dealID) + + const actualDealVerified = await market.eth.contract.get_deal_verified(dealID) + const actualDealActivation: MarketTypes.GetDealActivationReturnStruct = await market.eth.contract.get_deal_activation(dealID) + + //Comparison checks + expect(actualDealCommitment.data).to.eq(expectedDealCommitment.data) + expect(actualDealCommitment.size).to.eq(expectedDealCommitment.size) + + expect(actualDealClientId).to.eq(expectedDealClientId) + expect(actualDealProviderId).to.eq(expectedDealProviderId) + + expect(actualDealLabel.data).to.eq(expectedDealLabel.data) + expect(actualDealLabel.isString).to.eq(expectedDealLabel.isString) + + expect(actualDealTerm.start).to.eq(expectedDealTerm.start) + expect(actualDealTerm.duration).to.eq(expectedDealTerm.duration) + + expect(actualDealTotalPrice.val).to.eq(expectedDealTotalPrice.val) + expect(actualDealTotalPrice.neg).to.eq(expectedDealTotalPrice.neg) + + expect(actualDealClientCollateral.val).to.eq(expectedDealClientCollateral.val) + expect(actualDealClientCollateral.neg).to.eq(expectedDealClientCollateral.neg) + expect(actualDealProviderCollateral.val).to.eq(expectedDealProviderCollateral.val) + expect(actualDealProviderCollateral.neg).to.eq(expectedDealProviderCollateral.neg) + + expect(actualDealVerified).to.eq(expectedDealVerified) + expect(actualDealActivation.activated).to.eq(expectedDealActivation.activated) + expect(actualDealActivation.terminated).to.eq(expectedDealActivation.terminated) + + if (stage == "beforeUpgrade") { + dbg(`Upgrading...`) + + const MarketContractFactoryV2 = (await ethers.getContractFactory("MarketApiUUPSUpgradeableV2Test", deployer.eth.signer)) as any + await upgrades.upgradeProxy(market.eth.contract, MarketContractFactoryV2, { + kind: "uups", + unsafeAllow: ["delegatecall"], + }) + + dbg(`Upgraded!`) + } + } +} diff --git a/hh-test/market.sample.t.ts b/hh-test/market.sample.t.ts deleted file mode 100644 index 0d5eb682..00000000 --- a/hh-test/market.sample.t.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ethers } from "hardhat" -import { expect, util } from "chai" - -import * as utils from "./utils" - -import { MarketApiTest, MarketHelper } from "../typechain-types" -import { MarketTypes, CommonTypes } from "../typechain-types/tests/market.test.sol/MarketApiTest" - -async function main() { - console.log(`Generating accounts...`) - const [deployer, anyone] = [ethers.Wallet.createRandom(ethers.provider), ethers.Wallet.createRandom(ethers.provider)] - const clientFilAddress = utils.lotus.createWalletBLS() - const providerFilAddress = "t01000" //default - created by lotus-miner - - console.log(`Generated:`) - console.log({ deployer: { ethAddr: deployer.address, filAddress: utils.ethAddressToFilAddress(deployer.address) } }) - console.log({ anyone: { ethAddr: anyone.address, filAddress: utils.ethAddressToFilAddress(anyone.address) } }) - console.log({ client: { filAddress: clientFilAddress } }) - console.log({ provider: { filAddress: providerFilAddress } }) - - console.log(`Funding generated wallets... (deployer, anyone and client)`) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(deployer.address), 10) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(anyone.address), 10) - utils.lotus.sendFunds(clientFilAddress, 10) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(60000) - - console.log(`DEBUG: clientIdAddress: ${utils.lotus.findIDAddressToBytes(clientFilAddress)}`) - - console.log(`Deploying contracts... (market and helper)`) - const MarketContractFactory = await ethers.getContractFactory("MarketApiTest") - const marketContract: MarketApiTest = (await MarketContractFactory.connect(deployer).deploy({ gasLimit: 1_000_000_000 })).connect(deployer) - - const HelperContractFactory = await ethers.getContractFactory("MarketHelper") - const helperContract: MarketHelper = await HelperContractFactory.connect(deployer).deploy({ gasLimit: 1_000_000_000 }) - - await marketContract.waitForDeployment() - await helperContract.waitForDeployment() - - const marketContractEthAddress = await marketContract.getAddress() - const marketContractFilAddress = utils.ethAddressToFilAddress(marketContractEthAddress) - - const helperContractEthAddress = await helperContract.getAddress() - const helperContractFilAddress = utils.ethAddressToFilAddress(helperContractEthAddress) - - console.log(`Contracts deployed:`) - console.log({ market: { ethAddress: marketContractEthAddress, filAddress: marketContractFilAddress } }) - console.log({ helper: { ethAddress: helperContractEthAddress, filAddress: helperContractFilAddress } }) - - console.log(`Setting miner control address... marketContract: ${marketContractFilAddress}`) - utils.lotus.setControlAddress(marketContractFilAddress) - - console.log(`Funding Escrows... (client and provider)`) - const amount = BigInt(10 ** 18) - const txs = await Promise.resolve([ - await marketContract.add_balance({ data: utils.filAddressToBytes(clientFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - await marketContract.add_balance({ data: utils.filAddressToBytes(providerFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - ]) - for (const tx of txs) { - tx.wait(2) - } - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - const balances = { - client: await marketContract.get_balance({ data: utils.filAddressToBytes(clientFilAddress) }), - provider: await marketContract.get_balance({ data: utils.filAddressToBytes(providerFilAddress) }), - } - console.log(`DEBUG:`) - console.log({ balances: JSON.stringify(balances) }) - - console.log(`Generating deal params...`) - const deal = utils.generateDealParams(clientFilAddress, providerFilAddress) - const serializedDealProposal = (await helperContract.serialize_deal_proposal(deal.proposal)).slice(2) - const signedDealProposal = utils.lotus.signMessage(clientFilAddress, serializedDealProposal) - - deal.client_signature = utils.hexToBytes(signedDealProposal) - - console.log(`Publishing deal...`) //Note: Anyone can issue the publishing transaction - const tx = await marketContract.connect(anyone).publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) - - await tx.wait(2) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - //Asertions - - //Expected values - const expectedDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = { - data: ethers.hexlify(Uint8Array.from([0, ...Array.from(ethers.getBytes(deal.proposal.piece_cid.data))])), - size: deal.proposal.piece_size, - } - - //Actual values - const dealID = await marketContract.publishedDealIds(0) - const actualDealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await marketContract.get_deal_data_commitment(dealID) - const actualDealClientId = await marketContract.get_deal_client(dealID) - const actualDealClient: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(actualDealClientId) - const actualDealProviderId = await marketContract.get_deal_provider(dealID) - const actualDealProvider: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(actualDealProviderId) - const actualDealLabel: CommonTypes.DealLabelStruct = await marketContract.get_deal_label(dealID) - const actualDealTerm: MarketTypes.GetDealTermReturnStruct = await marketContract.get_deal_term(dealID) - const actualDealTotalPrice: CommonTypes.BigIntStruct = await marketContract.get_deal_total_price(dealID) - const actualDealClientCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_client_collateral(dealID) - const actualDealProviderCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_provider_collateral(dealID) - - console.log(`DEBUG:`, { - dealID, - actualDealClient, - actualDealClientCollateral, - actualDealProviderCollateral, - actualDealProvider, - actualDealLabel, - actualDealTerm, - actualDealTotalPrice, - }) - - //One way to compare the values (individually) - expect(actualDealCommitment.data).to.eq(expectedDealCommitment.data) - expect(actualDealCommitment.size).to.eq(expectedDealCommitment.size) - - //Second way to compare the values (jointly) - expect(actualDealCommitment).to.eql(Object.values(expectedDealCommitment)) -} - -main().catch((error) => { - console.error(error) - process.exitCode = 1 -}) diff --git a/hh-test/upgradeable/market.beacon.t.ts b/hh-test/upgradeable/market.beacon.t.ts deleted file mode 100644 index 5dad3f69..00000000 --- a/hh-test/upgradeable/market.beacon.t.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { ethers, upgrades, network } from "hardhat" -import { expect } from "chai" - -import * as utils from "../utils" - -import { MarketApiUpgradeableTest, MarketHelper } from "../../typechain-types" -import { MarketTypes, CommonTypes } from "../../typechain-types/tests/market.test.sol/MarketApiTest" - -describe("Market contract - Beacon Proxy Upgrade", function () { - it("Should publish a deal", async function () { - const provider = new ethers.providers.JsonRpcProvider((network.config as any).url) - - console.log(`Generating accounts...`) - const [deployer, anyone] = [ethers.Wallet.createRandom().connect(provider), ethers.Wallet.createRandom(ethers.provider).connect(provider)] - - const clientFilAddress = utils.lotus.createWalletBLS() - const providerFilAddress = "t01000" //default - created by lotus-miner - - console.log(`Generated:`) - console.log({ - deployer: { - ethAddr: deployer.address, - filAddress: utils.ethAddressToFilAddress(deployer.address), - }, - }) - console.log({ - anyone: { - ethAddr: anyone.address, - filAddress: utils.ethAddressToFilAddress(anyone.address), - }, - }) - console.log({ client: { filAddress: clientFilAddress } }) - console.log({ provider: { filAddress: providerFilAddress } }) - - console.log(`Funding generated wallets... (deployer, anyone and client)`) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(deployer.address), 10) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(anyone.address), 10) - utils.lotus.sendFunds(clientFilAddress, 10) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(60000) - - console.log(`DEBUG: clientIdAddress: ${utils.lotus.findIDAddressToBytes(clientFilAddress)}`) - - console.log(`Deploying contracts... (market and helper)`) - - const HelperContractFactory = await ethers.getContractFactory("MarketHelper", deployer) - const helperContract: MarketHelper = await HelperContractFactory.connect(deployer).deploy() - - console.log({ helperContract: "deployed" }) - - const MarketContractFactory = await ethers.getContractFactory("MarketApiUpgradeableTest", deployer) - - const beacon = await upgrades.deployBeacon(MarketContractFactory) - await beacon.deployed() - const instance = await upgrades.deployBeaconProxy(beacon, MarketContractFactory, []) - await instance.deployed() - - const marketContract = instance as unknown as MarketApiUpgradeableTest - - console.log({ MarketContractProxy: "deployed" }) - - await utils.delay(30000) - - console.log(`ADMIN: ${await upgrades.erc1967.getAdminAddress(marketContract.address)}`) - - const marketContractEthAddress = marketContract.address - const marketContractFilAddress = utils.ethAddressToFilAddress(marketContractEthAddress) - - const helperContractEthAddress = helperContract.address - const helperContractFilAddress = utils.ethAddressToFilAddress(helperContractEthAddress) - - console.log(`Contracts deployed:`) - console.log({ - market: { ethAddress: marketContractEthAddress, filAddress: marketContractFilAddress }, - }) - console.log({ - helper: { ethAddress: helperContractEthAddress, filAddress: helperContractFilAddress }, - }) - - console.log(`Setting miner control address... marketContract: ${marketContractFilAddress}`) - utils.lotus.setControlAddress(marketContractFilAddress) - - console.log(`Funding Escrows... (client and provider)`) - const amount = BigInt(10 ** 18) - const txs = await Promise.resolve([ - await marketContract.add_balance({ data: utils.filAddressToBytes(clientFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - await marketContract.add_balance({ data: utils.filAddressToBytes(providerFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - ]) - for (const tx of txs) { - tx.wait(2) - } - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - const balances = { - client: await marketContract.get_balance({ - data: utils.filAddressToBytes(clientFilAddress), - }), - provider: await marketContract.get_balance({ - data: utils.filAddressToBytes(providerFilAddress), - }), - } - console.log(`DEBUG:`) - console.log({ balances: JSON.stringify(balances) }) - - console.log(`Generating deal params...`) - const deal = utils.generateDealParams(clientFilAddress, providerFilAddress) - const serializedDealProposal = (await helperContract.serialize_deal_proposal(deal.proposal)).slice(2) - const signedDealProposal = utils.lotus.signMessage(clientFilAddress, serializedDealProposal) - - deal.client_signature = utils.hexToBytes(signedDealProposal) - - console.log(`Publishing deal...`) //Note: Anyone can issue the publishing transaction - const tx = await marketContract.connect(anyone).publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) - - await tx.wait(2) - - console.log(`Deal has been published`) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - //Asertions - - //Expected values - const expected = { - dealCommitment: { - data: ethers.utils.hexlify(Uint8Array.from([0, ...ethers.utils.arrayify(deal.proposal.piece_cid.data)])), - size: deal.proposal.piece_size, - }, - dealClient: deal.proposal.client, - dealProvider: deal.proposal.provider, - dealLabel: deal.proposal.label, - dealTerm: { start: deal.proposal.start_epoch, end: deal.proposal.end_epoch }, - dealClientCollateral: deal.proposal.client_collateral, - dealProviderCollateral: deal.proposal.provider_collateral, - dealTotalPrice: deal.proposal.total_price, - } - - //Actual values - const getActualValues = async ({ dealNumber }) => { - const dealID = await marketContract.publishedDealIds(dealNumber) - const dealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await marketContract.get_deal_data_commitment(dealID) - const dealClientId = await marketContract.get_deal_client(dealID) - const dealClient: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(dealClientId) - const dealProviderId = await marketContract.get_deal_provider(dealID) - const dealProvider: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(dealProviderId) - const dealLabel: CommonTypes.DealLabelStruct = await marketContract.get_deal_label(dealID) - const dealTerm: MarketTypes.GetDealTermReturnStruct = await marketContract.get_deal_term(dealID) - const dealTotalPrice: CommonTypes.BigIntStruct = await marketContract.get_deal_total_price(dealID) - const dealClientCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_client_collateral(dealID) - const dealProviderCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_provider_collateral(dealID) - - const returnValues = { - dealID, - dealCommitment, - dealClientId, - dealClient, - dealProviderId, - dealProvider, - dealLabel, - dealTerm, - dealTotalPrice, - dealClientCollateral, - dealProviderCollateral, - } - - console.log(`DEBUG: ${JSON.stringify(returnValues)}`) - - return returnValues - } - - const compareDealInformation = ({ expected, actual }) => { - expect(actual.dealCommitment.data).to.eq(expected.dealCommitment.data) - expect(actual.dealCommitment.size.eq(ethers.BigNumber.from(expected.dealCommitment.size))).to.eq(true) - - expect(actual.dealTerm.start.eq(ethers.BigNumber.from(expected.dealTerm.start))).to.eq(true) - expect(actual.dealTerm.end.eq(ethers.BigNumber.from(expected.dealTerm.end).sub(ethers.BigNumber.from(expected.dealTerm.start)))).to.eq(true) - - expect(actual.dealClientCollateral.val).to.eq("0x" + Buffer.from(expected.dealClientCollateral.val).toString("hex")) - expect(actual.dealClientCollateral.neg).to.eq(expected.dealClientCollateral.neg) - - expect(actual.dealProvider.data).to.eq("0x" + Buffer.from(expected.dealProvider.data).toString("hex")) - - expect(actual.dealProviderCollateral.val).to.eq("0x" + Buffer.from(expected.dealProviderCollateral.val).toString("hex")) - expect(actual.dealProviderCollateral.neg).to.eq(expected.dealProviderCollateral.neg) - - expect(actual.dealTotalPrice.val).to.eq("0x" + Buffer.from(expected.dealTotalPrice.val).toString("hex")) - expect(actual.dealTotalPrice.neg).to.eq(expected.dealTotalPrice.neg) - } - - const actual = await getActualValues({ dealNumber: 0 }) - compareDealInformation({ expected, actual }) - - const MarketContractFactoryV2 = await ethers.getContractFactory("MarketApiUpgradeableV2Test", deployer) - await upgrades.upgradeBeacon(beacon, MarketContractFactoryV2) - const upgraded = MarketContractFactoryV2.attach(instance.address) - - const actualUpgraded = await getActualValues({ dealNumber: 0 }) - compareDealInformation({ expected, actual: actualUpgraded }) - }) -}) diff --git a/hh-test/upgradeable/market.transparent.t.ts b/hh-test/upgradeable/market.transparent.t.ts deleted file mode 100644 index 9abe2e69..00000000 --- a/hh-test/upgradeable/market.transparent.t.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { ethers, upgrades, network } from "hardhat" -import { expect } from "chai" - -import * as utils from "../utils" - -import { MarketApiUpgradeableTest, MarketHelper } from "../../typechain-types" -import { MarketTypes, CommonTypes } from "../../typechain-types/tests/market.test.sol/MarketApiTest" - -describe("Market contract - Transparent Proxy Upgrade", function () { - it("Should publish a deal", async function () { - const provider = new ethers.providers.JsonRpcProvider((network.config as any).url) - - console.log(`Generating accounts...`) - const [deployer, anyone] = [ethers.Wallet.createRandom().connect(provider), ethers.Wallet.createRandom(ethers.provider).connect(provider)] - - const clientFilAddress = utils.lotus.createWalletBLS() - const providerFilAddress = "t01000" //default - created by lotus-miner - - console.log(`Generated:`) - console.log({ - deployer: { - ethAddr: deployer.address, - filAddress: utils.ethAddressToFilAddress(deployer.address), - }, - }) - console.log({ - anyone: { - ethAddr: anyone.address, - filAddress: utils.ethAddressToFilAddress(anyone.address), - }, - }) - console.log({ client: { filAddress: clientFilAddress } }) - console.log({ provider: { filAddress: providerFilAddress } }) - - console.log(`Funding generated wallets... (deployer, anyone and client)`) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(deployer.address), 10) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(anyone.address), 10) - utils.lotus.sendFunds(clientFilAddress, 10) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(60000) - - console.log(`DEBUG: clientIdAddress: ${utils.lotus.findIDAddressToBytes(clientFilAddress)}`) - - console.log(`Deploying contracts... (market and helper)`) - - const HelperContractFactory = await ethers.getContractFactory("MarketHelper", deployer) - const helperContract: MarketHelper = await HelperContractFactory.connect(deployer).deploy() - - console.log({ helperContract: "deployed" }) - - const MarketContractFactory = await ethers.getContractFactory("MarketApiUpgradeableTest", deployer) - const marketContract: MarketApiUpgradeableTest = (await upgrades.deployProxy(MarketContractFactory, [], { - unsafeAllow: ["delegatecall"], - })) as unknown as MarketApiUpgradeableTest - - console.log({ MarketContractProxy: "deployed" }) - - await marketContract.deployed() - await helperContract.deployed() - - await utils.delay(30000) - - console.log(`ADMIN: ${await upgrades.erc1967.getAdminAddress(marketContract.address)}`) - - const marketContractEthAddress = marketContract.address - const marketContractFilAddress = utils.ethAddressToFilAddress(marketContractEthAddress) - - const helperContractEthAddress = helperContract.address - const helperContractFilAddress = utils.ethAddressToFilAddress(helperContractEthAddress) - - console.log(`Contracts deployed:`) - console.log({ - market: { ethAddress: marketContractEthAddress, filAddress: marketContractFilAddress }, - }) - console.log({ - helper: { ethAddress: helperContractEthAddress, filAddress: helperContractFilAddress }, - }) - - console.log(`Setting miner control address... marketContract: ${marketContractFilAddress}`) - utils.lotus.setControlAddress(marketContractFilAddress) - - console.log(`Funding Escrows... (client and provider)`) - const amount = BigInt(10 ** 18) - const txs = await Promise.resolve([ - await marketContract.add_balance({ data: utils.filAddressToBytes(clientFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - await marketContract.add_balance({ data: utils.filAddressToBytes(providerFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - ]) - for (const tx of txs) { - tx.wait(2) - } - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - const balances = { - client: await marketContract.get_balance({ - data: utils.filAddressToBytes(clientFilAddress), - }), - provider: await marketContract.get_balance({ - data: utils.filAddressToBytes(providerFilAddress), - }), - } - console.log(`DEBUG:`) - console.log({ balances: JSON.stringify(balances) }) - - console.log(`Generating deal params...`) - const deal = utils.generateDealParams(clientFilAddress, providerFilAddress) - const serializedDealProposal = (await helperContract.serialize_deal_proposal(deal.proposal)).slice(2) - const signedDealProposal = utils.lotus.signMessage(clientFilAddress, serializedDealProposal) - - deal.client_signature = utils.hexToBytes(signedDealProposal) - - console.log(`Publishing deal...`) //Note: Anyone can issue the publishing transaction - const tx = await marketContract.connect(anyone).publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) - - await tx.wait(2) - - console.log(`Deal has been published`) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - //Asertions - - //Expected values - const expected = { - dealCommitment: { - data: ethers.utils.hexlify(Uint8Array.from([0, ...ethers.utils.arrayify(deal.proposal.piece_cid.data)])), - size: deal.proposal.piece_size, - }, - dealClient: deal.proposal.client, - dealProvider: deal.proposal.provider, - dealLabel: deal.proposal.label, - dealTerm: { start: deal.proposal.start_epoch, end: deal.proposal.end_epoch }, - dealClientCollateral: deal.proposal.client_collateral, - dealProviderCollateral: deal.proposal.provider_collateral, - dealTotalPrice: deal.proposal.total_price, - } - - //Actual values - const getActualValues = async ({ dealNumber }) => { - const dealID = await marketContract.publishedDealIds(dealNumber) - const dealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await marketContract.get_deal_data_commitment(dealID) - const dealClientId = await marketContract.get_deal_client(dealID) - const dealClient: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(dealClientId) - const dealProviderId = await marketContract.get_deal_provider(dealID) - const dealProvider: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(dealProviderId) - const dealLabel: CommonTypes.DealLabelStruct = await marketContract.get_deal_label(dealID) - const dealTerm: MarketTypes.GetDealTermReturnStruct = await marketContract.get_deal_term(dealID) - const dealTotalPrice: CommonTypes.BigIntStruct = await marketContract.get_deal_total_price(dealID) - const dealClientCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_client_collateral(dealID) - const dealProviderCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_provider_collateral(dealID) - - const returnValues = { - dealID, - dealCommitment, - dealClientId, - dealClient, - dealProviderId, - dealProvider, - dealLabel, - dealTerm, - dealTotalPrice, - dealClientCollateral, - dealProviderCollateral, - } - - console.log(`DEBUG: ${JSON.stringify(returnValues)}`) - - return returnValues - } - - const compareDealInformation = ({ expected, actual }) => { - expect(actual.dealCommitment.data).to.eq(expected.dealCommitment.data) - expect(actual.dealCommitment.size.eq(ethers.BigNumber.from(expected.dealCommitment.size))).to.eq(true) - - expect(actual.dealTerm.start.eq(ethers.BigNumber.from(expected.dealTerm.start))).to.eq(true) - expect(actual.dealTerm.end.eq(ethers.BigNumber.from(expected.dealTerm.end).sub(ethers.BigNumber.from(expected.dealTerm.start)))).to.eq(true) - - expect(actual.dealClientCollateral.val).to.eq("0x" + Buffer.from(expected.dealClientCollateral.val).toString("hex")) - expect(actual.dealClientCollateral.neg).to.eq(expected.dealClientCollateral.neg) - - expect(actual.dealProvider.data).to.eq("0x" + Buffer.from(expected.dealProvider.data).toString("hex")) - - expect(actual.dealProviderCollateral.val).to.eq("0x" + Buffer.from(expected.dealProviderCollateral.val).toString("hex")) - expect(actual.dealProviderCollateral.neg).to.eq(expected.dealProviderCollateral.neg) - - expect(actual.dealTotalPrice.val).to.eq("0x" + Buffer.from(expected.dealTotalPrice.val).toString("hex")) - expect(actual.dealTotalPrice.neg).to.eq(expected.dealTotalPrice.neg) - } - - const actual = await getActualValues({ dealNumber: 0 }) - compareDealInformation({ expected, actual }) - - const MarketContractFactoryV2 = await ethers.getContractFactory("MarketApiUpgradeableV2Test", deployer) - await upgrades.upgradeProxy(marketContract.address, MarketContractFactoryV2, { - unsafeAllow: ["delegatecall"], - }) - - const actualUpgraded = await getActualValues({ dealNumber: 0 }) - compareDealInformation({ expected, actual: actualUpgraded }) - }) -}) diff --git a/hh-test/upgradeable/market.uups.t.ts b/hh-test/upgradeable/market.uups.t.ts deleted file mode 100644 index cdd87e8d..00000000 --- a/hh-test/upgradeable/market.uups.t.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { ethers, upgrades, network } from "hardhat" -import { expect } from "chai" - -import * as utils from "../utils" - -import { MarketApiUpgradeableTest, MarketHelper } from "../../typechain-types" -import { MarketTypes, CommonTypes } from "../../typechain-types/tests/market.test.sol/MarketApiTest" - -describe("Market contract - UUPS Proxy Upgrade", function () { - it("Should publish a deal", async function () { - const provider = new ethers.providers.JsonRpcProvider((network.config as any).url) - - console.log(`Generating accounts...`) - const [deployer, anyone] = [ethers.Wallet.createRandom().connect(provider), ethers.Wallet.createRandom(ethers.provider).connect(provider)] - - const clientFilAddress = utils.lotus.createWalletBLS() - const providerFilAddress = "t01000" //default - created by lotus-miner - - console.log(`Generated:`) - console.log({ - deployer: { - ethAddr: deployer.address, - filAddress: utils.ethAddressToFilAddress(deployer.address), - }, - }) - console.log({ - anyone: { - ethAddr: anyone.address, - filAddress: utils.ethAddressToFilAddress(anyone.address), - }, - }) - console.log({ client: { filAddress: clientFilAddress } }) - console.log({ provider: { filAddress: providerFilAddress } }) - - console.log(`Funding generated wallets... (deployer, anyone and client)`) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(deployer.address), 10) - utils.lotus.sendFunds(utils.ethAddressToFilAddress(anyone.address), 10) - utils.lotus.sendFunds(clientFilAddress, 10) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(60000) - - console.log(`DEBUG: clientIdAddress: ${utils.lotus.findIDAddressToBytes(clientFilAddress)}`) - - console.log(`Deploying contracts... (market and helper)`) - - const HelperContractFactory = await ethers.getContractFactory("MarketHelper", deployer) - const helperContract: MarketHelper = await HelperContractFactory.connect(deployer).deploy() - - console.log({ helperContract: "deployed" }) - - const MarketContractFactory = await ethers.getContractFactory("MarketApiUUPSUpgradeableTest", deployer) - const marketContract: MarketApiUpgradeableTest = (await upgrades.deployProxy(MarketContractFactory, [], { - kind: "uups", - unsafeAllow: ["delegatecall"], - })) as unknown as MarketApiUpgradeableTest - - console.log({ MarketContractProxy: "deployed" }) - - await marketContract.deployed() - await helperContract.deployed() - - await utils.delay(30000) - - console.log(`ADMIN: ${await upgrades.erc1967.getAdminAddress(marketContract.address)}`) - - const marketContractEthAddress = marketContract.address - const marketContractFilAddress = utils.ethAddressToFilAddress(marketContractEthAddress) - - const helperContractEthAddress = helperContract.address - const helperContractFilAddress = utils.ethAddressToFilAddress(helperContractEthAddress) - - console.log(`Contracts deployed:`) - console.log({ - market: { ethAddress: marketContractEthAddress, filAddress: marketContractFilAddress }, - }) - console.log({ - helper: { ethAddress: helperContractEthAddress, filAddress: helperContractFilAddress }, - }) - - console.log(`Setting miner control address... marketContract: ${marketContractFilAddress}`) - utils.lotus.setControlAddress(marketContractFilAddress) - - console.log(`Funding Escrows... (client and provider)`) - const amount = BigInt(10 ** 18) - const txs = await Promise.resolve([ - await marketContract.add_balance({ data: utils.filAddressToBytes(clientFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - await marketContract.add_balance({ data: utils.filAddressToBytes(providerFilAddress) }, amount, { gasLimit: 1_000_000_000, value: amount }), - ]) - for (const tx of txs) { - tx.wait(2) - } - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - const balances = { - client: await marketContract.get_balance({ - data: utils.filAddressToBytes(clientFilAddress), - }), - provider: await marketContract.get_balance({ - data: utils.filAddressToBytes(providerFilAddress), - }), - } - console.log(`DEBUG:`) - console.log({ balances: JSON.stringify(balances) }) - - console.log(`Generating deal params...`) - const deal = utils.generateDealParams(clientFilAddress, providerFilAddress) - const serializedDealProposal = (await helperContract.serialize_deal_proposal(deal.proposal)).slice(2) - const signedDealProposal = utils.lotus.signMessage(clientFilAddress, serializedDealProposal) - - deal.client_signature = utils.hexToBytes(signedDealProposal) - - console.log(`Publishing deal...`) //Note: Anyone can issue the publishing transaction - const tx = await marketContract.connect(anyone).publish_storage_deals({ deals: [deal] }, { gasLimit: 1_000_000_000 }) - - await tx.wait(2) - - console.log(`Deal has been published`) - - //Introduce artificial delay due to Filecoin's delayed execution model - await utils.delay(50000) - - //Asertions - - //Expected values - const expected = { - dealCommitment: { - data: ethers.utils.hexlify(Uint8Array.from([0, ...ethers.utils.arrayify(deal.proposal.piece_cid.data)])), - size: deal.proposal.piece_size, - }, - dealClient: deal.proposal.client, - dealProvider: deal.proposal.provider, - dealLabel: deal.proposal.label, - dealTerm: { start: deal.proposal.start_epoch, end: deal.proposal.end_epoch }, - dealClientCollateral: deal.proposal.client_collateral, - dealProviderCollateral: deal.proposal.provider_collateral, - dealTotalPrice: deal.proposal.total_price, - } - - //Actual values - const getActualValues = async ({ dealNumber }) => { - const dealID = await marketContract.publishedDealIds(dealNumber) - const dealCommitment: MarketTypes.GetDealDataCommitmentReturnStruct = await marketContract.get_deal_data_commitment(dealID) - const dealClientId = await marketContract.get_deal_client(dealID) - const dealClient: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(dealClientId) - const dealProviderId = await marketContract.get_deal_provider(dealID) - const dealProvider: CommonTypes.FilAddressStruct = await helperContract.get_address_from_id(dealProviderId) - const dealLabel: CommonTypes.DealLabelStruct = await marketContract.get_deal_label(dealID) - const dealTerm: MarketTypes.GetDealTermReturnStruct = await marketContract.get_deal_term(dealID) - const dealTotalPrice: CommonTypes.BigIntStruct = await marketContract.get_deal_total_price(dealID) - const dealClientCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_client_collateral(dealID) - const dealProviderCollateral: CommonTypes.BigIntStruct = await marketContract.get_deal_provider_collateral(dealID) - - const returnValues = { - dealID, - dealCommitment, - dealClientId, - dealClient, - dealProviderId, - dealProvider, - dealLabel, - dealTerm, - dealTotalPrice, - dealClientCollateral, - dealProviderCollateral, - } - - console.log(`DEBUG: ${JSON.stringify(returnValues)}`) - - return returnValues - } - - const compareDealInformation = ({ expected, actual }) => { - expect(actual.dealCommitment.data).to.eq(expected.dealCommitment.data) - expect(actual.dealCommitment.size.eq(ethers.BigNumber.from(expected.dealCommitment.size))).to.eq(true) - - expect(actual.dealTerm.start.eq(ethers.BigNumber.from(expected.dealTerm.start))).to.eq(true) - expect(actual.dealTerm.end.eq(ethers.BigNumber.from(expected.dealTerm.end).sub(ethers.BigNumber.from(expected.dealTerm.start)))).to.eq(true) - - expect(actual.dealClientCollateral.val).to.eq("0x" + Buffer.from(expected.dealClientCollateral.val).toString("hex")) - expect(actual.dealClientCollateral.neg).to.eq(expected.dealClientCollateral.neg) - - expect(actual.dealProvider.data).to.eq("0x" + Buffer.from(expected.dealProvider.data).toString("hex")) - - expect(actual.dealProviderCollateral.val).to.eq("0x" + Buffer.from(expected.dealProviderCollateral.val).toString("hex")) - expect(actual.dealProviderCollateral.neg).to.eq(expected.dealProviderCollateral.neg) - - expect(actual.dealTotalPrice.val).to.eq("0x" + Buffer.from(expected.dealTotalPrice.val).toString("hex")) - expect(actual.dealTotalPrice.neg).to.eq(expected.dealTotalPrice.neg) - } - - const actual = await getActualValues({ dealNumber: 0 }) - compareDealInformation({ expected, actual }) - - console.log(`Upgrading...`) - - const MarketContractFactoryV2 = await ethers.getContractFactory("MarketApiUUPSUpgradeableV2Test", deployer) - await upgrades.upgradeProxy(marketContract.address, MarketContractFactoryV2, { - kind: "uups", - unsafeAllow: ["delegatecall"], - }) - - console.log(`Upgrade perfomed`) - - const actualUpgraded = await getActualValues({ dealNumber: 0 }) - compareDealInformation({ expected, actual: actualUpgraded }) - }) -}) diff --git a/hh-test/utils.ts b/hh-test/utils.ts index a4173ebd..4396b0fe 100644 --- a/hh-test/utils.ts +++ b/hh-test/utils.ts @@ -1,12 +1,31 @@ -import { execSync } from "child_process" -import { newActorAddress, newDelegatedEthAddress, newFromString } from "@glif/filecoin-address" -import { MarketTypes } from "../typechain-types/tests/market.test.sol/MarketApiTest" -import { ethers } from "hardhat" +import { execSync, exec } from "child_process" +import { newDelegatedEthAddress, newFromString } from "@glif/filecoin-address" +import { CommonTypes, MarketTypes } from "../typechain-types/contracts/v0.8/tests/market.test.sol/MarketApiTest" +import { ethers, network } from "hardhat" +import { FilecoinClient, FilecoinSigner } from "@blitslabs/filecoin-js-signer" + +import * as rlp from "rlp" +import * as keccak from "keccak" + +import "dotenv/config" +import { readFileSync, writeFileSync } from "fs" const CID = require("cids") +const DEBUG_ON = false + +const SCRIPTS_DIR = `/var/lib/fil-sol/lib-dev/dev-env` + +const PREFIX_CMD = `/bin/bash -c "` + +export const paddForHex = (hex: string) => { + //assumes no leading `0x` + return hex.length % 2 == 1 ? `0${hex}` : hex +} + export const hexToBytes = (hex: string) => { - var bytes = [] + //assumes 0x..{even number of nibbles}... + const bytes = [] for (var c = 0; c < hex.length; c += 2) { bytes.push(parseInt(hex.substr(c, 2), 16)) @@ -44,27 +63,169 @@ export const utf8Encode = (payload: string) => { export const lotus = { setControlAddress: (filAddress: string) => { - return execSync(`docker exec lotus-miner lotus-miner actor control set --really-do-it ${filAddress}`).toString() + return execSync(`${PREFIX_CMD}lotus-miner actor control set --really-do-it ${filAddress}"`).toString() }, signMessage: (filAddress: string, message: string) => { - const signatureCmdOutput = execSync(`docker exec lotus lotus wallet sign ${filAddress} ${message}`).toString() + const signatureCmdOutput = execSync(`${PREFIX_CMD}lotus wallet sign ${filAddress} ${message}"`).toString() return signatureCmdOutput.replace("\n", "") }, sendFunds: (filAddress: string, amount: number) => { - return execSync(`docker exec lotus lotus send ${filAddress} ${amount}`).toString() + return execSync(`${PREFIX_CMD}lotus send ${filAddress} ${amount}"`).toString() }, createWalletBLS: () => { - return execSync(`docker exec lotus lotus wallet new bls`).toString().replace("\n", "") + return execSync(`${PREFIX_CMD}lotus wallet new bls"`).toString().replace("\n", "") }, findIDAddressToBytes: (filAddress: string) => { - const idAddress = execSync(`docker exec lotus lotus state lookup ${filAddress}`).toString().replace("\n", "") - return newFromString(idAddress).bytes + const idAddress = execSync(`${PREFIX_CMD}lotus state lookup ${filAddress}"`).toString().replace("\n", "") + const temp = paddForHex(BigInt(`${idAddress.slice(2, idAddress.length)}`).toString(16)) + return hexToBytes("0x" + temp) + }, + findIDAddressToBigInt: (filAddress: string) => { + const raw = execSync(`${PREFIX_CMD}lotus state lookup ${filAddress}"`).toString().replace("\n", "") + return BigInt(raw.slice(2, raw.length)) + }, + restart: async (params: { LOTUS_FEVM_ENABLEETHRPC: boolean }) => { + const exportEnvVar = `export LOTUS_FEVM_ENABLEETHRPC=${params.LOTUS_FEVM_ENABLEETHRPC}` + const scriptRun = `${SCRIPTS_DIR}/2_restart-localnet.sh &` + + const cmd = `${PREFIX_CMD}${exportEnvVar} && ${scriptRun}"` + + exec(cmd).toString().replace("\n", "") + + await delay(90_000) + }, + kill: () => { + const scriptRun = `${SCRIPTS_DIR}/4_kill-lotus-ps.sh &` + + const cmd = `${PREFIX_CMD} ${scriptRun}"` + + return execSync(cmd).toString().replace("\n", "") + }, + registerVerifier: (filAddress: string, amount: number) => { + // console.log("registerVerifier", filAddress, amount) + const rootKey1 = _getVerifier1RootKey() + const cmd = `${PREFIX_CMD}lotus-shed verifreg add-verifier ${rootKey1} ${filAddress} ${amount}"` + return execSync(cmd).toString().replace("\n", "") + }, + grantDatacap: (notaryAddress: string, filAddress: string, amount: number) => { + // console.log("grantDatacap", notaryAddress, filAddress, amount) + const cmd = `${PREFIX_CMD}lotus filplus grant-datacap --from=${notaryAddress} ${filAddress} ${amount}"` + return execSync(cmd).toString().replace("\n", "") + }, + changeMinerOwner: (newBeneficiary: string) => { + // const preCmd = `${PREFIX_CMD}lotus wallet set-default ...` + //lotus-miner actor confirm-change-beneficiary --really-do-it --new-beneficiary + const cmd = `${PREFIX_CMD}lotus-miner actor propose-change-beneficiary --really-do-it --overwrite-pending-change ${newBeneficiary} 1 1 "` + return execSync(cmd).toString().replace("\n", "") + }, + evmInvoke: (contractFilAddress: string, payload: string) => { + const payloadWithout0x = payload.replace(`0x`, ``) + const cmd = `${PREFIX_CMD}lotus evm invoke ${contractFilAddress} ${payloadWithout0x}"` + return execSync(cmd).toString().replace("\n", "") + }, + importDefaultWallets: () => { + const keyFilename = "tmp-000001.keygen" + + writeFileSync(keyFilename, process.env.F3_PK) + + let f3Addr: string + //note: wallet could have been already imported, re-import will throw an error + try { + const output = _execute(`lotus wallet import ${keyFilename}`) + f3Addr = _extractWalletAddress(output, "imported key", "successfully!") + } catch (err) { + const output = err.stderr.toString() + f3Addr = _extractWalletAddress(output, "'wallet-", "': key already exists") + } + + _execute(`rm -rf ${keyFilename}`) + + return { fil: { address: f3Addr, idAddress: BigInt(process.env.F3_ID) } } }, } +const _execute = (cmd: string, options?: any) => { + const _options = options == null ? { stdio: [] } : options + return execSync(`${PREFIX_CMD} ${cmd}"`, _options).toString() +} + +const _extractWalletAddress = (str: string, startStr: string, endStr: string) => { + const startIdx = str.indexOf(startStr) + startStr.length + let tmp = str.slice(startIdx) + return tmp.slice(0, tmp.indexOf(endStr)) +} + +const _getVerifier1RootKey = () => { + return execSync("cat /var/lib/fil-sol/lib-dev/dev-env/.internal/verifier1.txt").toString().replace("\n", "") +} + +export const getProxyFactory = async (account) => { + const addr = execSync("cat /var/lib/fil-sol/lib-dev/dev-env/.internal/proxyFactory.addr").toString().replace("\n", "") + + const pFF = await ethers.getContractFactory("_BasicProxyFactory") + + const pff = pFF.attach(addr).connect(account.eth.signer) + + return pff +} + +export const upgradeToDataCapProxy = async (account, contractFactory, contractAddress: string) => { + const pff = await getProxyFactory(account) + + const pAddr = await pff.dataCapProxy() + + const pF = await ethers.getContractFactory("_BasicProxy") + const pf = pF.attach(pAddr).connect(account.eth.signer) + + await pf.upgradeDelegate(contractAddress) + await defaultTxDelay() + + const proxiedContract = contractFactory.attach(await pf.getAddress()).connect(account.eth.signer) + + return proxiedContract +} + +export const upgradeToVerifRegProxy = async (account, contractFactory, contractAddress: string) => { + const pff = await getProxyFactory(account) + + const pAddr = await pff.verifRegProxy() + + const pF = await ethers.getContractFactory("_BasicProxy") + const pf = pF.attach(pAddr).connect(account.eth.signer) + + await pf.upgradeDelegate(contractAddress) + await defaultTxDelay() + + const proxiedContract = contractFactory.attach(await pf.getAddress()).connect(account.eth.signer) + + return proxiedContract +} + +export const upgradeToFirstAvailableProxy = async (account, contractFactory, contractAddress: string) => { + const pff = await getProxyFactory(account) + + const [pAddr, pID] = await pff.getFirstAvailableProxy() + + await pff.occupyProxy(pID) + + const pF = await ethers.getContractFactory("_BasicProxy") + + const pf = pF.attach(pAddr).connect(account.eth.signer) + await pf.upgradeDelegate(contractAddress) + await defaultTxDelay() + await defaultTxDelay() + + const proxiedContract = contractFactory.attach(await pf.getAddress()).connect(account.eth.signer) + + return proxiedContract +} + let dealID = 0 export const DEAL_INFO = [ { pieceCid: "baga6ea4seaqn7y7fwlhlshrysd2j443pyi6knof2c5qp533co2mqj5rzbq7t2pi", label: "mAXCg5AIgw4oywPmiPRxJLioYxMdIkKmaJ4FFumCvS/GC4gEzGng" }, + { pieceCid: "baga6ea4seaqlkg6mss5qs56jqtajg5ycrhpkj2b66cgdkukf2qjmmzz6ayksuci", label: "mAXCg5AIg8YBXbFjtdBy1iZjpDYAwRSt0elGLF5GvTqulEii1VcM" }, + { pieceCid: "baga6ea4seaqdl6geodjdraqwh56yqewcub4pxnlxsc7673xnfazhctawun22aha", label: "mAXCg5AIglPFhEfVlJwt+dkvz/JNQ8BakUxmAZb1dQ8F0sKnHeFE" }, + { pieceCid: "baga6ea4seaqcxsr53negpkklyb4p6pojm2726yrr34lszn5j7qiacc7htv7vueq", label: "mAXCg5AIgmtJq7yh1JTsGJkPrA1hLaSnXZIE+MfeeP1bT8OOGb4A" }, ] export const generateDealParams = (clientFilAddress: string, providerFilAddress: string) => { @@ -105,19 +266,354 @@ export const generateDealParams = (clientFilAddress: string, providerFilAddress: val: hexToBytes((1_000_000).toString(16)), neg: false, }, + }, + client_signature: new Uint8Array(), + } + + const dealInfo = { + deal, + dealDebug: { total_price: { - val: hexToBytes( - ethers.BigNumber.from((end_epoch - start_epoch) * storage_price_per_epoch) - .toHexString() - .slice(2) - ), + val: `0x${paddForHex(((end_epoch - start_epoch) * storage_price_per_epoch).toString(16))}`, neg: false, }, + start_epoch, + end_epoch, }, - client_signature: {}, } - dealID += 1 + return dealInfo +} + +export const createNetworkProvider = () => { + const provider = new ethers.JsonRpcProvider((network.config as any).url) + return provider +} + +export const defaultTxDelay = async (repeat?: number) => { + const _repeat = repeat == null ? 1 : repeat + + for (let i = 0; i < _repeat; i += 1) { + if (network.name === "localnet") { + await delay(10_000) + } else if (network.name === "calibnet") { + await delay(40_000) + } + } +} + +export const generate_f410_accounts = (n: number) => { + //generates f410 type of accounts and attaches their convient information + + const accounts = [] + const provider = createNetworkProvider() + for (let i = 0; i < n; i += 1) { + const signer = ethers.Wallet.createRandom().connect(provider) + const filAddr = ethAddressToFilAddress(signer.address) + const account = { + eth: { + signer, + address: signer.address, + }, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress: () => lotus.findIDAddressToBytes(filAddr), + }, + } + accounts.push(account) + } + return accounts +} + +export const generate_and_fund_f410_accounts = (n: number, amount: number) => { + //generates f410 type of accounts and funds them + + const accounts = generate_f410_accounts(n) + + for (const acc of accounts) { + lotus.sendFunds(acc.fil.address, amount) + } + + return accounts +} + +export const generate_and_fund_fixed_f410_accounts = (amount: number) => { + //note: hardhat localhost created + const privateKeys = [ + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + "0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0", + ] + const accounts = [] + const provider = createNetworkProvider() + for (const pk of privateKeys) { + const signer = new ethers.Wallet(pk).connect(provider) + const filAddr = ethAddressToFilAddress(signer.address) + + const account = { + eth: { + signer, + address: signer.address, + }, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress: () => lotus.findIDAddressToBytes(filAddr), + }, + } + lotus.sendFunds(account.fil.address, amount) + + accounts.push(account) + } + + return accounts +} + +export const generate_f3_accounts = async (n: number, amount?: number) => { + //generates f3 type of accounts and attaches their convient information + + const accounts = [] + for (let i = 0; i < n; i += 1) { + const filAddr = lotus.createWalletBLS() + const account = { + eth: {}, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress: () => lotus.findIDAddressToBytes(filAddr), + }, + } + if (amount > 0) lotus.sendFunds(account.fil.address, amount) + accounts.push(account) + } + return accounts +} + +export const getStorageProvider = () => { + //packs default miner info into a convient format + + const filAddr = "t01000" //default - created by lotus-miner in localnet + const idAddress = () => lotus.findIDAddressToBytes(filAddr) + return { + eth: {}, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress, + id: BigInt(`0x${Buffer.from(idAddress()).toString("hex")}`), // actor id + }, + } +} + +export const performGeneralSetup = async () => { + //general setup present in most of the tests + + if (DEBUG_ON) { + console.log(`performGeneralSetup() called`) + } + const [deployer, anyone] = generate_f410_accounts(2) + const [client] = await generate_f3_accounts(1) + const storageProvider = getStorageProvider() + + if (DEBUG_ON) { + console.log(`Generated:`, { deployer, anyone, client, storageProvider }) + console.log(`Funding generated wallets... (deployer, anyone and client)`) + } + + await defaultTxDelay() + + lotus.sendFunds(client.fil.address, 10) + lotus.sendFunds(deployer.fil.address, 10) + lotus.sendFunds(anyone.fil.address, 10) + + await defaultTxDelay() + + if (DEBUG_ON) { + console.log(`Funding done.`) + } + + return { deployer, anyone, client, storageProvider } +} + +export const performGeneralSetupOnCalibnet = async (addExtraAccounts?: boolean) => { + const master = new ethers.Wallet(process.env.ETH_PK, createNetworkProvider()) + + const deployer = { eth: { signer: master, address: await master.getAddress() }, fil: {} } + + const extraAccounts = [] + if (addExtraAccounts) { + const [deployer, anyone] = generate_f410_accounts(2) + const [client] = await generate_f3_accounts(1) + + const deployerAmount = BigInt(10) * BigInt(10 ** 18) + const anyoneAmount = BigInt(1) * BigInt(10 ** 18) + + const filecoin_signer = new FilecoinSigner() + + const clientSignMessage = async (message: string) => filecoin_signer.utils.signMessage(message, process.env.F3_PK) + + await master.sendTransaction({ + to: deployer.eth.address, + value: deployerAmount, + }) + await defaultTxDelay() + + await master.sendTransaction({ + to: anyone.eth.address, + value: anyoneAmount, + }) + await defaultTxDelay() + + extraAccounts.push(deployer) + extraAccounts.push(anyone) + } + + return { master, deployer, extraAccounts } +} + +export const deployContract = async (deployer: any, name: string, params?: { constructorParams?: []; noDelay?: boolean; nonce?: number }) => { + //deploys a contract and attaches all the needed info for tests + + const ContractFactory = await ethers.getContractFactory(name, deployer.eth.signer) + + if (DEBUG_ON) console.log(`Contract: ${name} pre-deploy ...`) + + if (DEBUG_ON) console.log("deployer balance:", await deployer.eth.signer.provider.getBalance(deployer.eth.address)) + + let contract + + const nonce = params && params.nonce + if (nonce == null) { + if (params == null || params.constructorParams == null) contract = await ContractFactory.connect(deployer.eth.signer).deploy({ gasLimit: 10000000000 }) + else contract = await ContractFactory.connect(deployer.eth.signer).deploy(...params.constructorParams) + } else { + if (params == null || params.constructorParams == null) + contract = await ContractFactory.connect(deployer.eth.signer).deploy({ gasLimit: 10000000000, nonce }) + else contract = await ContractFactory.connect(deployer.eth.signer).deploy(...params.constructorParams, { nonce }) + } + if (DEBUG_ON) console.log(`Contract: ${name} deployed.`) + + await contract.waitForDeployment() + if (params != null && params.noDelay != false) { + } else { + await defaultTxDelay() + } + + const ethAddr = await contract.getAddress() + const filAddr = ethAddressToFilAddress(ethAddr) + return { + eth: { + contract, + address: ethAddr, + }, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress: () => lotus.findIDAddressToBytes(filAddr), + }, + } +} + +export const attachToContract = async (account: any, name: string, contractAddress: string) => { + const ContractFactory = await ethers.getContractFactory(name, account.eth.signer) + const contract = ContractFactory.attach(contractAddress).connect(account.eth.signer) + + const ethAddr = await contract.getAddress() + const filAddr = ethAddressToFilAddress(ethAddr) + return { + eth: { + contract, + address: ethAddr, + }, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress: () => lotus.findIDAddressToBytes(filAddr), + }, + } +} + +export const idAddressToBigInt = (idAddress: Uint8Array) => { + const result = BigInt(bytesToHex(idAddress).replace("0x00", "0x")) + return result +} + +export const bigIntStructWithStringFormat = (bigint: CommonTypes.BigIntStruct) => { + return { val: bytesToHex(bigint.val as Uint8Array), neg: bigint.neg } +} + +export const bigIntToHexString = (bigint: BigInt) => { + return `0x${paddForHex(bigint.toString(16))}` +} + +export const computeDeploymentAddress = (sender: string, nonce = 0x00) => { + const input_arr = [sender, nonce] + const rlp_encoded = Buffer.from(rlp.encode(input_arr)) + + const contract_address_long = keccak("keccak256").update(rlp_encoded).digest("hex") + + const contract_address = `0x${contract_address_long.substring(24)}` //Trim the first 24 chars + + return ethers.getAddress(contract_address) +} + +export const getDefaultDeployer = async () => { + //note: hardhat localhost created + const PK = "0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0" + const provider = createNetworkProvider() + const signer = new ethers.Wallet(PK).connect(provider) + const filAddr = ethAddressToFilAddress(signer.address) + + const deployer = { + eth: { + signer, + address: signer.address, + }, + fil: { + address: filAddr, + byteAddress: filAddressToBytes(filAddr), + idAddress: () => lotus.findIDAddressToBytes(filAddr), + }, + } + + const currentBalance = await deployer.eth.signer.provider.getBalance(deployer.eth.address) + + if (currentBalance == BigInt(0)) { + lotus.sendFunds(deployer.fil.address, 100) + await defaultTxDelay() + } + + return deployer +} + +let _DBG_TEST_LOGS = {} +export const dbg = (testName: string, info: string) => { + if (_DBG_TEST_LOGS[testName] == null) { + _DBG_TEST_LOGS[testName] = [] + } + _DBG_TEST_LOGS[testName].push(info) +} + +export const initDbg = (testName: string) => (info: string) => dbg(testName, info) + +export const exportDbgLog = (testName: string) => { + return _DBG_TEST_LOGS[testName] +} + +export const printDbgLog = (testName: string) => { + if (_DBG_TEST_LOGS[testName] == null) { + console.log(`DBG::No logs for: '${testName}'`) + return + } + + console.log(`DBG: (${testName}) :::: `) + for (const line of _DBG_TEST_LOGS[testName]) { + console.log(`\t${line}\n`) + } +} - return deal +export const removeProxyArtifacts = () => { + execSync(`rm -rf .openzeppelin`) } diff --git a/hh-test/verifreg_deserial.t.ts b/hh-test/verifreg_deserial.t.ts index 10d0a3ab..105e38be 100644 --- a/hh-test/verifreg_deserial.t.ts +++ b/hh-test/verifreg_deserial.t.ts @@ -12,7 +12,7 @@ const DBG_LOG_ON = false const main = async () => { const deployerPk = network.config.accounts[0] - const provider = new ethers.providers.JsonRpcProvider(network.config.url) + const provider = utils.createNetworkProvider() const deployer = new ethers.Wallet(deployerPk, provider) diff --git a/lib-dev/dev-env/.env.example b/lib-dev/dev-env/.env.example new file mode 100644 index 00000000..1ebe94f6 --- /dev/null +++ b/lib-dev/dev-env/.env.example @@ -0,0 +1 @@ +FIL_SOL_DOCKER_IMG="fil-sol-dev-env:latest-amd64" \ No newline at end of file diff --git a/lib-dev/dev-env/0_sleep.sh b/lib-dev/dev-env/0_sleep.sh new file mode 100755 index 00000000..ba51f567 --- /dev/null +++ b/lib-dev/dev-env/0_sleep.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e + +sleep 100000000000 \ No newline at end of file diff --git a/lib-dev/dev-env/1_clean-start-localnet.sh b/lib-dev/dev-env/1_clean-start-localnet.sh new file mode 100755 index 00000000..39e98223 --- /dev/null +++ b/lib-dev/dev-env/1_clean-start-localnet.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +set -e + +touch localnet-setup-running.lock + +INTERNALS_DIR="/var/lib/fil-sol/lib-dev/dev-env/.internal" +LOGPATH="$INTERNALS_DIR/dbg_log.txt" +LOCALNET_JSON="$INTERNALS_DIR/localnet.json" +DEVGEN_CAR="$INTERNALS_DIR/devgen.car" + +# lotus-miner stop +# lotus daemon stop +# sleep 10 +ps -ef | grep 'lotus' | grep -v grep | awk '{print $2}' | xargs -r kill -9 +rm -rf ~/.lotus-local-net/repo.lock ~/.lotus-miner-local-net/repo.lock +sleep 5 + + +rm -rf $INTERNALS_DIR +# rm -rf bls-*.keyinfo $LOCALNET_JSON devgen.car +rm -rf ~/.genesis-sectors ~/.lotus-local-net ~/.lotus-miner-local-net + +mkdir -p $INTERNALS_DIR +echo "start" >> $LOGPATH + +cd $INTERNALS_DIR + +verifregRootKey1=$(lotus-shed keyinfo new bls) +notary1=$(lotus-shed keyinfo new bls) + +echo $verifregRootKey1 > $INTERNALS_DIR/verifier1.txt +echo $notary1 > $INTERNALS_DIR/notary1.txt + +lotus-seed pre-seal --sector-size 2KiB --num-sectors 2 + +lotus-seed genesis new $LOCALNET_JSON + +lotus-seed genesis set-signers --threshold=1 --signers $verifregRootKey1 $LOCALNET_JSON + +lotus-seed genesis add-miner $LOCALNET_JSON ~/.genesis-sectors/pre-seal-t01000.json + +echo "LOTUS_FEVM_ENABLEETHRPC=true" >> $LOGPATH + +export LOTUS_FEVM_ENABLEETHRPC=true + +lotus daemon --lotus-make-genesis=$DEVGEN_CAR --genesis-template=$LOCALNET_JSON --bootstrap=false & + +echo "Miner starting (20s delay) ..." +sleep 25 + +lotus wallet import --as-default ~/.genesis-sectors/pre-seal-t01000.key + +rm -rf $LOTUS_MINER_PATH + +lotus-miner init --genesis-miner --actor=t01000 --sector-size=2KiB --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json --nosync + +lotus-miner run --nosync & + +sleep 25 + +lotus wallet import "$INTERNALS_DIR/bls-$verifregRootKey1.keyinfo" +lotus wallet import "$INTERNALS_DIR/bls-$notary1.keyinfo" + +echo "_deployProxies=start" >> $LOGPATH +cd /var/lib/fil-sol +npx hardhat run hh-test/_deployProxies.ts +echo "_deployProxies=end" >> $LOGPATH +sleep 25 + +ps -ef | grep 'lotus' | grep -v grep | awk '{print $2}' | xargs -r kill -9 +rm -rf ~/.lotus-local-net/repo.lock ~/.lotus-miner-local-net/repo.lock +sleep 5 + +echo "LOTUS_FEVM_ENABLEETHRPC=true" >> $LOGPATH +export LOTUS_FEVM_ENABLEETHRPC=true + +lotus daemon start & +sleep 25 + +lotus-miner run --nosync & +sleep 15 + +rm -rf localnet-setup-running.lock + +echo "DONE!" >> $LOGPATH \ No newline at end of file diff --git a/lib-dev/dev-env/2_restart-localnet.sh b/lib-dev/dev-env/2_restart-localnet.sh new file mode 100755 index 00000000..63a49af4 --- /dev/null +++ b/lib-dev/dev-env/2_restart-localnet.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +INTERNALS_DIR="/var/lib/fil-sol/lib-dev/dev-env/.internal" +LOGPATH="$INTERNALS_DIR/dbg_log.txt" +LOCALNET_JSON="$INTERNALS_DIR/localnet.json" +DEVGEN_CAR="$INTERNALS_DIR/devgen.car" + +# set -e + +SLEEP_TIME=20 + +ps -ef | grep 'lotus' | grep -v grep | awk '{print $2}' | xargs -r kill -9 +rm -rf ~/.lotus-local-net/repo.lock ~/.lotus-miner-local-net/repo.lock +sleep 5 + +# lotus-miner stop +# lotus daemon stop +# sleep $SLEEP_TIME + +echo "Lotus starting ..." >> $LOGPATH +lotus daemon start & + +echo "Miner starting ($SLEEP_TIME(s) delay) ..." >> $LOGPATH +sleep $SLEEP_TIME +lotus-miner run --nosync & +sleep $SLEEP_TIME + +echo "Restart script done" >> $LOGPATH \ No newline at end of file diff --git a/lib-dev/dev-env/3_clean-simple-start.sh b/lib-dev/dev-env/3_clean-simple-start.sh new file mode 100755 index 00000000..1b6972a8 --- /dev/null +++ b/lib-dev/dev-env/3_clean-simple-start.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +# set -e + +INTERNALS_DIR="/var/lib/fil-sol/lib-dev/dev-env/.internal" +LOGPATH="$INTERNALS_DIR/dbg_log.txt" +LOCALNET_JSON="$INTERNALS_DIR/localnet.json" +DEVGEN_CAR="$INTERNALS_DIR/devgen.car" + +ps -ef | grep 'lotus' | grep -v grep | awk '{print $2}' | xargs -r kill -9 +rm -rf ~/.lotus-local-net/repo.lock ~/.lotus-miner-local-net/repo.lock +sleep 5 + +rm -rf $INTERNALS_DIR +rm -rf ~/.genesis-sectors ~/.lotus-local-net ~/.lotus-miner-local-net + +mkdir -p $INTERNALS_DIR +echo "start" >> $LOGPATH + +cd $INTERNALS_DIR + +verifregRootKey1=$(lotus-shed keyinfo new bls) +notary1=$(lotus-shed keyinfo new bls) + +echo $verifregRootKey1 > $INTERNALS_DIR/verifier1.txt +echo $notary1 > $INTERNALS_DIR/notary1.txt + +lotus-seed pre-seal --sector-size 2KiB --num-sectors 2 + +lotus-seed genesis new $LOCALNET_JSON + +lotus-seed genesis set-signers --threshold=1 --signers $verifregRootKey1 $LOCALNET_JSON + +lotus-seed genesis add-miner $LOCALNET_JSON ~/.genesis-sectors/pre-seal-t01000.json + +echo "LOTUS_FEVM_ENABLEETHRPC=true" >> $LOGPATH + +export LOTUS_FEVM_ENABLEETHRPC=true + +lotus daemon --lotus-make-genesis=$DEVGEN_CAR --genesis-template=$LOCALNET_JSON --bootstrap=false & + +echo "Miner starting (20s delay) ..." +sleep 25 + +lotus wallet import --as-default ~/.genesis-sectors/pre-seal-t01000.key + +rm -rf $LOTUS_MINER_PATH + +lotus-miner init --genesis-miner --actor=t01000 --sector-size=2KiB --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json --nosync + +lotus-miner run --nosync & + +sleep 25 + +lotus wallet import "$INTERNALS_DIR/bls-$verifregRootKey1.keyinfo" +lotus wallet import "$INTERNALS_DIR/bls-$notary1.keyinfo" + +echo "DONE!" >> $LOGPATH \ No newline at end of file diff --git a/lib-dev/dev-env/4_kill-lotus-ps.sh b/lib-dev/dev-env/4_kill-lotus-ps.sh new file mode 100755 index 00000000..af8a1ad0 --- /dev/null +++ b/lib-dev/dev-env/4_kill-lotus-ps.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# set -e + +INTERNALS_DIR="/var/lib/fil-sol/lib-dev/dev-env/.internal" +LOGPATH="$INTERNALS_DIR/dbg_log.txt" +LOCALNET_JSON="$INTERNALS_DIR/localnet.json" +DEVGEN_CAR="$INTERNALS_DIR/devgen.car" + +ps -ef | grep 'lotus' | grep -v grep | awk '{print $2}' | xargs -r kill -9 +rm -rf ~/.lotus-local-net/repo.lock ~/.lotus-miner-local-net/repo.lock diff --git a/lib-dev/dev-env/5_test-run-localnet.sh b/lib-dev/dev-env/5_test-run-localnet.sh new file mode 100755 index 00000000..cb092c39 --- /dev/null +++ b/lib-dev/dev-env/5_test-run-localnet.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +LOCK_FILE=localnet-setup-running.lock + +if [ -f $LOCK_FILE ]; then + echo "ERR: Cannot run tests - Localnet setup is still running." +else + echo "Localnet setup done - Running tests ..." + export HH_NETWORK=localnet && npx hardhat test +fi \ No newline at end of file diff --git a/lib-dev/dev-env/Dockerfile b/lib-dev/dev-env/Dockerfile new file mode 100644 index 00000000..e63e3702 --- /dev/null +++ b/lib-dev/dev-env/Dockerfile @@ -0,0 +1,60 @@ +FROM --platform=linux/amd64 golang:1.21.7-bullseye + +# Replace shell with bash +RUN rm /bin/sh && ln -s /bin/bash /bin/sh + +# Install dependencies +RUN apt-get update && apt-get -y install hwloc jq pkg-config bzr ocl-icd-opencl-dev + +ENV NODE_VERSION 20 + +# Install nvm with node, npm and yarn +RUN curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash \ + && . ~/.nvm/nvm.sh \ + && nvm install $NODE_VERSION \ + && nvm alias default $NODE_VERSION \ + && nvm use default \ + && npm i -g yarn + +ENV NODE_PATH ~/.nvm/$NODE_VERSION/lib/node_modules +ENV PATH ~/.nvm/$NODE_VERSION/bin:$PATH + +# Install rust +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc + +# Install foundry +RUN curl -L https://foundry.paradigm.xyz | bash && \ + . /root/.bashrc && \ + foundryup + +ENV LOTUS_PATH=~/.lotus-local-net \ + LOTUS_MINER_PATH=~/.lotus-miner-local-net \ + LOTUS_FEVM_ENABLEETHRPC=true \ + LOTUS_SKIP_GENESIS_CHECK=_yes_ \ + CGO_CFLAGS_ALLOW="-D__BLST_PORTABLE__" \ + CGO_CFLAGS="-D__BLST_PORTABLE__" + +# Install lotus localnet +RUN git clone https://github.com/filecoin-project/lotus lotus-local-net + +## Fix missing lib libhwloc.so.5 +RUN ls -1 /usr/lib/*/libhwloc.so.* | head -n 1 | xargs -n1 -I {} ln -s {} /usr/lib/libhwloc.so + +WORKDIR /go/lotus-local-net + +RUN git checkout releases + +RUN rm -rf ~/.genesis-sectors + +RUN make 2k + +RUN ./lotus fetch-params 2048 + +RUN make lotus-shed + +RUN echo "export PATH=\$PATH:/go/lotus-local-net/" >> $HOME/.bashrc + +COPY ./*.sh /go/_scripts/ + +RUN chmod +x /go/_scripts/*.sh \ No newline at end of file diff --git a/lib-dev/dev-env/README.md b/lib-dev/dev-env/README.md new file mode 100644 index 00000000..21ab79ab --- /dev/null +++ b/lib-dev/dev-env/README.md @@ -0,0 +1,102 @@ +# Developer Environment + +## Overview + +To make the developer environment uniform across contributors (different OS, etc.), it is best to do `filecoin-solidity` library development inside this containarized environment. + +Docker image supports Rust and all other dependencies (see [Dockerfile](./Dockerfile)) - any update in versions will require updating and rebuilding the image. + +**Notes:** + +- The docker container has access to the complete project directory (mounted at `/var/lib/fil-sol`) and all changes are reflected. + +- Also, VS Code can be attached to the container using its [Docker Plugin](https://code.visualstudio.com/docs/containers/overview). + +### Initial setup + +From project's root + +``` +cp .env.example .env +``` + +Update `.env` + +Source ENV vars: + +``` +source .env +``` + +### Building the Docker image + +``` +docker buildx build --platform=linux/amd64 -t ${FIL_SOL_DOCKER_IMG} . +``` + +### Starting Dev. Environment + +Set up the container + +``` +docker compose up +``` + +Enter into the container from VS Code (recommended), or run: + +``` +docker exec -it lotus /bin/bash +``` + +Initialize dependencies + +``` +make deps_install +``` + +For `localnet` network run: + +``` +make start_localnet +``` + +**Note: the localnet setup needs to completely finish before running hardhat tests** +If it hasn't finished yet, you will receive: + +``` +ERR: Cannot run tests - Localnet setup is still running. +``` + +For both **network** = `calibnet` || `localnet`, run: + +``` +export HH_NETWORK= && npx hardhat test +``` + +or use (basic): + +For `localnet` + +``` +make start_localnet +make test_hh_localnet +``` + +For `calibnet` + +``` +make test_hh_calibnet +``` + +### Running Rust tests (make sure you are not sometimes compiling from host, and sometimes from container): + +``` +make test_integration +``` + +## Useful notes: + +- It's advised (due to machine resources) to run either localnet or rust tests. +- [Lotus CLI Docs](https://lotus.filecoin.io/lotus/manage/lotus-cli/) + - especially `evm invoke` section +- [Lotus Miner CLI Docs](https://lotus.filecoin.io/storage-providers/operate/lotus-miner-cli/) diff --git a/lib-dev/dev-env/docker-compose.yaml b/lib-dev/dev-env/docker-compose.yaml new file mode 100644 index 00000000..f3b78e7c --- /dev/null +++ b/lib-dev/dev-env/docker-compose.yaml @@ -0,0 +1,33 @@ +version: "3.8" + +x-logging: &default-logging + options: + max-size: "20m" + max-file: "3" + driver: json-file + +networks: + default: + name: devnet + +services: + lotus: + container_name: lotus + image: ${FIL_SOL_DOCKER_IMG} + entrypoint: ["/go/_scripts/0_sleep.sh"] + healthcheck: + test: >- + curl -s -X POST -H "Content-Type: application/json" + --data '{ "jsonrpc": "2.0", "method": "Filecoin.ChainHead", "params": [], "id": 1 }' + http://lotus:1234/rpc/v0 || exit 1 + interval: 20s + retries: 5 + start_period: 6000s + timeout: 10s + ports: + - "1234:1234" + - "9090:9090" + restart: unless-stopped + logging: *default-logging + volumes: + - ../../.:/var/lib/fil-sol:rw diff --git a/package.json b/package.json index 66ccbc10..0d14ffcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "filecoin-solidity-api", - "version": "1.1.2", + "version": "1.1.3", "description": "Filecoin EVM Solidity APIs", "main": "", "directories": { @@ -33,32 +33,39 @@ "access": "public" }, "devDependencies": { + "@blitslabs/filecoin-js-signer": "^1.0.6", "@glif/filecoin-address": "^2.0.43", + "@noble/curves": "^1.4.0", "@nomicfoundation/hardhat-ethers": "^3.0.4", "@nomicfoundation/hardhat-foundry": "^1.0.3", "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@^0.3.0-beta.13", "@nomiclabs/hardhat-etherscan": "^3.1.7", - "@openzeppelin/hardhat-upgrades": "^1.28.0", + "@openzeppelin/hardhat-upgrades": "^3.1.0", "@typechain/ethers-v5": "^11.1.1", "@typechain/ethers-v6": "^0.5.0", "@typechain/hardhat": "^6.1.2", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/node": "^20.5.0", + "@zondax/filecoin-signing-tools": "^2.4.3", + "bls-signatures": "^2.0.3", "chai": "^4.3.7", "cids": "^1.1.9", "dotenv": "^16.4.5", - "ethers": "^5.5.1", - "hardhat": "^2.11.2", + "ethers": "^6.4.0", + "hardhat": "^2.19.1", "hardhat-contract-sizer": "^2.10.0", "hardhat-deploy-ethers": "^0.3.0-beta.13", + "keccak": "^3.0.4", "prettier": "^2.7.1", "prettier-plugin-solidity": "^1.0.0", + "rlp": "^3.0.0", "ts-node": "^10.9.1", "typechain": "^8.3.1", "typescript": "^5.2.2" }, "dependencies": { + "@noble/secp256k1": "^2.1.0", "solidity-cborutils": "^2.0.0" } } diff --git a/testing/rust-toolchain.toml b/testing/rust-toolchain.toml index 469626ea..ce209bf9 100644 --- a/testing/rust-toolchain.toml +++ b/testing/rust-toolchain.toml @@ -1,2 +1,4 @@ [toolchain] -channel = "1.70.0" \ No newline at end of file +channel = "1.76.0" +components = ["clippy", "llvm-tools-preview", "rustfmt"] +targets = ["wasm32-unknown-unknown"] \ No newline at end of file diff --git a/testing/src/api_contracts/datacap_test.rs b/testing/src/api_contracts/datacap_test.rs index 1c4c0f4b..ba5022a2 100644 --- a/testing/src/api_contracts/datacap_test.rs +++ b/testing/src/api_contracts/datacap_test.rs @@ -17,30 +17,30 @@ sol!{ } struct TransferParams { + bytes operator_data; FilAddress to; BigInt amount; - bytes operator_data; } struct TransferReturn { + bytes recipient_data; BigInt from_balance; BigInt to_balance; - bytes recipient_data; } struct TransferFromParams { + bytes operator_data; FilAddress from; FilAddress to; BigInt amount; - bytes operator_data; } struct TransferFromReturn { + bytes recipient_data; BigInt from_balance; BigInt to_balance; BigInt allowance; - bytes recipient_data; } struct IncreaseAllowanceParams { diff --git a/testing/tests/datacap.rs b/testing/tests/datacap.rs index 17ce692f..11342488 100644 --- a/testing/tests/datacap.rs +++ b/testing/tests/datacap.rs @@ -464,14 +464,14 @@ fn datacap_tests() { let abi_encoded_call = api_contracts::datacap_test::transferCall{ params: api_contracts::datacap_test::TransferParams{ + operator_data: fixed_bytes!("").to_vec(), to: api_contracts::datacap_test::FilAddress{ data: vec![0x00_u8, 0xc8, 0x01] }, amount: api_contracts::datacap_test::BigInt{ val: fixed_bytes!("1BC16D674EC80000").to_vec(), neg: false - }, - operator_data: fixed_bytes!("").to_vec() + } } }.abi_encode(); @@ -499,6 +499,7 @@ fn datacap_tests() { assert_eq!(res.msg_receipt.exit_code.value(), 0); let expected_transfer_return = api_contracts::datacap_test::TransferReturn{ + recipient_data: fixed_bytes!("").to_vec(), from_balance: api_contracts::datacap_test::BigInt{ val: fixed_bytes!("361A08405E8FD80000").to_vec(), neg: false @@ -506,8 +507,7 @@ fn datacap_tests() { to_balance: api_contracts::datacap_test::BigInt{ val: fixed_bytes!("1BC16D674EC80000").to_vec(), neg: false - }, - recipient_data: fixed_bytes!("").to_vec() + } }; let abi_encoded_call = api_contracts::datacap_test::TransferReturn::abi_encode(&expected_transfer_return); @@ -524,6 +524,7 @@ fn datacap_tests() { let abi_encoded_call = api_contracts::datacap_test::transfer_fromCall{ params: api_contracts::datacap_test::TransferFromParams{ + operator_data: fixed_bytes!("").to_vec(), from: api_contracts::datacap_test::FilAddress{ data: sender[0].1.to_bytes() }, @@ -533,8 +534,7 @@ fn datacap_tests() { amount: api_contracts::datacap_test::BigInt{ val: fixed_bytes!("3782DACE9D900000").to_vec(), neg: false - }, - operator_data: fixed_bytes!("").to_vec() + } } }.abi_encode(); @@ -564,6 +564,7 @@ fn datacap_tests() { assert_eq!(res.msg_receipt.exit_code.value(), 0); let expected_transfer_from = api_contracts::datacap_test::TransferFromReturn{ + recipient_data: fixed_bytes!("").to_vec(), from_balance: api_contracts::datacap_test::BigInt{ val: fixed_bytes!("35FE46D2F741100000").to_vec(), neg: false @@ -575,8 +576,7 @@ fn datacap_tests() { allowance: api_contracts::datacap_test::BigInt{ val: fixed_bytes!("02F050FE938943ACC427E27BB162700000").to_vec(), neg: false - }, - recipient_data: fixed_bytes!("").to_vec() + } }; let abi_encoded_call = api_contracts::datacap_test::TransferFromReturn::abi_encode(&expected_transfer_from);