Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skale-contracts integration #978

Merged
merged 89 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
dd40631
Add ABI generation
DimaStebaev Jun 20, 2023
3cf1d61
Integrate skale-contract into the upgrade script
DimaStebaev Jul 3, 2023
950f814
Modify upgrade testing script
DimaStebaev Jul 3, 2023
ed28b96
Update ethers
DimaStebaev Jul 4, 2023
1595c65
Integrate skale-contracts
DimaStebaev Jul 10, 2023
8b099f1
Merge with develop
DimaStebaev Sep 7, 2023
8a22d5c
Update upgrade-tools
DimaStebaev Sep 19, 2023
b0a2c7c
Merge with develop
DimaStebaev Sep 19, 2023
36b6c94
Update upgrade-tools
DimaStebaev Sep 19, 2023
2993852
Update ganache
DimaStebaev Sep 20, 2023
fa1eedd
Merge branch 'develop' into skale-contracts-integration
DimaStebaev Sep 20, 2023
3a4d234
Update upgrade-tools
DimaStebaev Sep 20, 2023
f048869
Update upgrade-tools
DimaStebaev Sep 25, 2023
08252d8
Set block time during deployment testing
DimaStebaev Sep 26, 2023
9a28350
Set block time during upgrade testing
DimaStebaev Sep 26, 2023
a4962cb
Fixe FieldOperationsTester deployment
DimaStebaev Sep 27, 2023
40fe2eb
Merge branch 'develop' into skale-contracts-integration
DimaStebaev Sep 27, 2023
bda24f6
Update eslint
DimaStebaev Sep 27, 2023
79e8497
Remove shadowing
DimaStebaev Sep 27, 2023
047b220
Verify contracts after deployment
DimaStebaev Sep 29, 2023
e4e4e9f
Retry the deployment transaction if it fails
DimaStebaev Oct 2, 2023
eae18fe
Fix issue with TimeHelpersWithDebug
DimaStebaev Oct 13, 2023
4eff1f2
Merge develop
DimaStebaev Jun 17, 2024
ca6e007
Fix deployment
DimaStebaev Jun 19, 2024
2d0d4f4
Switch to ethers v6
DimaStebaev Jun 19, 2024
25158f6
Fix createSchain test
DimaStebaev Jun 19, 2024
96eeb58
Fix nodeRotation
DimaStebaev Jun 20, 2024
3f536f6
Fix hardhat.config.ts
DimaStebaev Jun 20, 2024
7406569
Fix Treetest
DimaStebaev Jun 20, 2024
929ef47
Fix createSchain
DimaStebaev Jun 20, 2024
2a7eac7
Fix deploy
DimaStebaev Jun 20, 2024
8e1df52
Upgrade upgrade-tools
DimaStebaev Jun 25, 2024
4c7e2dc
Fix submitTransactions
DimaStebaev Jun 25, 2024
c519dfd
Fix upgrade
DimaStebaev Jun 25, 2024
ddb1546
Fix dependencies
DimaStebaev Jun 26, 2024
09a9a20
Fix generateAbi
DimaStebaev Jun 26, 2024
6a4ffe0
Fix bounty
DimaStebaev Jun 26, 2024
0980c4e
Fix constants holder
DimaStebaev Jun 26, 2024
78fe7be
Fix contract manager
DimaStebaev Jun 26, 2024
58c2792
Fix ECDH
DimaStebaev Jun 26, 2024
d77f14a
Fix node rotation
DimaStebaev Jun 26, 2024
02350d6
Fix nodes data
DimaStebaev Jun 27, 2024
3db639a
Fix nodes functionality
DimaStebaev Jun 27, 2024
9a58e97
Fix pricing
DimaStebaev Jun 27, 2024
1b46433
Fix schains
DimaStebaev Jun 27, 2024
69699bd
Fix schains internal
DimaStebaev Jun 27, 2024
d784469
Fix skale dkg
DimaStebaev Jun 27, 2024
a5605e1
Fix skale dkg fake complaint
DimaStebaev Jun 27, 2024
c322670
Fix skale manager
DimaStebaev Jun 27, 2024
3c3095c
Fix skale token
DimaStebaev Jun 27, 2024
18f4371
Fix skale verifier
DimaStebaev Jun 27, 2024
c49e8f2
Fix sync manager
DimaStebaev Jun 27, 2024
e1edaf8
Fix wallets
DimaStebaev Jun 27, 2024
cb4488b
Fix delegations
DimaStebaev Jul 2, 2024
0c0df74
Fix delegation controller
DimaStebaev Jul 2, 2024
3d1792e
Fix partial differences
DimaStebaev Jul 5, 2024
b3930e5
Fix time helpers
DimaStebaev Jul 5, 2024
d1f95bf
Fix token state
DimaStebaev Jul 5, 2024
a07445f
Fix validator service
DimaStebaev Jul 5, 2024
5297812
Fix deployment
DimaStebaev Jul 5, 2024
8894876
Fix signatures
DimaStebaev Jul 5, 2024
b64cf0f
Fix math utils
DimaStebaev Jul 5, 2024
8b71911
Fix segment tree
DimaStebaev Jul 5, 2024
c4ef6d2
Remove unused imports
DimaStebaev Jul 5, 2024
82a905e
Remove support of node 16
DimaStebaev Jul 5, 2024
3943b71
Add math
DimaStebaev Jul 5, 2024
891da32
Fix deployment
DimaStebaev Jul 8, 2024
1c1d73b
Add ProxyAdmin artifact build
DimaStebaev Jul 11, 2024
414a2e9
Fix deployment during tests
DimaStebaev Jul 18, 2024
dc89854
Fix bounty tests
DimaStebaev Jul 18, 2024
bef0af8
Fix type
DimaStebaev Jul 18, 2024
8b975ef
Fix types
DimaStebaev Jul 19, 2024
fc609ec
Use uint256 instead of uint
DimaStebaev Jul 19, 2024
8bf8efc
Remove node 22
DimaStebaev Jul 19, 2024
0f85268
Add debug info
DimaStebaev Jul 22, 2024
2da2e94
Show ProxyAdmin
DimaStebaev Jul 22, 2024
56dcce9
Show manifest before copying
DimaStebaev Jul 22, 2024
3d703f1
Update upgrade-tools
DimaStebaev Jul 22, 2024
59b3ffb
Increase heap size
DimaStebaev Jul 22, 2024
18c5ca6
Fix pricing
DimaStebaev Jul 22, 2024
d750112
Increase heap size
DimaStebaev Jul 22, 2024
df45c01
Decrease heap size
DimaStebaev Jul 25, 2024
b62fa5e
Fix DKG tests
DimaStebaev Jul 25, 2024
7d0b53f
Fix SkaleManager tests
DimaStebaev Jul 29, 2024
1c00efe
Fix wallets tests
DimaStebaev Jul 29, 2024
e459d01
Fix math utils tests
DimaStebaev Jul 29, 2024
3ecde03
Merge with develop
DimaStebaev Jul 31, 2024
50ff0ed
Remove unused comments
DimaStebaev Jul 31, 2024
8a3144e
Remove commented out code
DimaStebaev Jul 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions .eslintrc

This file was deleted.

38 changes: 38 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-env node */
module.exports = {
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"ignorePatterns": [
"coverage/**",
"typechain-types/**"
],
"env": {
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"root": true,
"rules": {
"@typescript-eslint/no-shadow": "error",
"@typescript-eslint/no-unused-vars": "error",
"lines-around-comment": [
"error",
{"allowBlockStart": true}
],
"no-console": "off",
// Replaced with @typescript-eslint/no-shadow
"no-shadow": "off",
"no-warning-comments": "warn",
"object-curly-spacing": "error",
"one-var": [
"error",
"never"
],
"padded-blocks": [
"error",
"never"
]
}
};
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- 'custom-release-*'

env:
NODE_VERSION: 18
NODE_VERSION: 20

jobs:
build:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
node-version: [18.x, 20.x]

env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
Expand Down Expand Up @@ -57,10 +57,12 @@ jobs:
NODE_VERSION: ${{ matrix.node-version }}
run: ./scripts/test_upgrade.sh

- name: Test ABI ageneration
- name: Test ABI generation
run: npx hardhat run scripts/generateAbi.ts

- name: Run tests
env:
NODE_OPTIONS: --max-old-space-size=12288
run: npx hardhat coverage --solcoverjs .solcover.js

- name: Upload coverage to Codecov
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cache/
tmp.sol
data/*.json
scTopics
contracts/hardhat-dependency-compiler

### Node ###
# Logs
Expand Down
1 change: 0 additions & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"func-visibility": ["error", {"ignoreConstructors": true}],
"function-max-lines": "error",
"max-states-count": ["error", 20],
"named-return-values": "error",
"no-empty-blocks": "error",
"no-global-import": "error",
"no-inline-assembly": "error",
Expand Down
3 changes: 2 additions & 1 deletion .solhintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
contracts/Migrations.sol
contracts/thirdparty
contracts/hardhat-dependency-compiler
contracts/thirdparty
2 changes: 1 addition & 1 deletion contracts/ConstantsHolder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ contract ConstantsHolder is Permissions, IConstantsHolder {
allowableLatency = 150000;
deltaPeriod = 3600;
checkTime = 300;
launchTimestamp = type(uint).max;
launchTimestamp = type(uint256).max;
rotationDelay = 12 hours;
proofOfUseLockUpPeriodDays = 90;
proofOfUseDelegationPercentage = 50;
Expand Down
2 changes: 1 addition & 1 deletion contracts/NodeRotation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ contract NodeRotation is Permissions, INodeRotation {
schainsInternal.makeSchainNodesInvisible(schainHash);
require(schainsInternal.isAnyFreeNode(schainHash), "No free Nodes available for rotation");
IRandom.RandomGenerator memory randomGenerator = Random.createFromEntropy(
abi.encodePacked(uint(blockhash(block.number - 1)), schainHash)
abi.encodePacked(uint256(blockhash(block.number - 1)), schainHash)
);
nodeIndex = nodes.getRandomNodeWithFreeSpace(space, randomGenerator);
require(nodes.removeSpaceFromNode(nodeIndex, space), "Could not remove space from nodeIndex");
Expand Down
4 changes: 2 additions & 2 deletions contracts/Nodes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ contract Nodes is Permissions, INodes {
if (space > 0) {
_moveNodeToNewSpaceMap(
nodeIndex,
(uint(spaceOfNodes[nodeIndex].freeSpace) - space).toUint8()
(uint256(spaceOfNodes[nodeIndex].freeSpace) - space).toUint8()
);
}
return true;
Expand All @@ -165,7 +165,7 @@ contract Nodes is Permissions, INodes {
if (space > 0) {
_moveNodeToNewSpaceMap(
nodeIndex,
(uint(spaceOfNodes[nodeIndex].freeSpace) + space).toUint8()
(uint256(spaceOfNodes[nodeIndex].freeSpace) + space).toUint8()
);
}
}
Expand Down
6 changes: 3 additions & 3 deletions contracts/Schains.sol
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,9 @@ contract Schains is Permissions, ISchains {
return 1e18;
} else {
uint256 up = nodeDeposit * numberOfNodes * lifetime * 2;
uint256 down = uint(
uint(constantsHolder.SMALL_DIVISOR())
* uint(constantsHolder.SECONDS_TO_YEAR())
uint256 down = uint256(
uint256(constantsHolder.SMALL_DIVISOR())
* uint256(constantsHolder.SECONDS_TO_YEAR())
/ divisor
);
return up / down;
Expand Down
2 changes: 1 addition & 1 deletion contracts/SchainsInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ contract SchainsInternal is Permissions, IPruningSchainsInternal {

require(nodes.countNodesWithFreeSpace(space) >= nodesInGroup.length, "Not enough nodes to create Schain");
IRandom.RandomGenerator memory randomGenerator = Random.createFromEntropy(
abi.encodePacked(uint(blockhash(block.number - 1)), schainHash)
abi.encodePacked(uint256(blockhash(block.number - 1)), schainHash)
);
for (uint256 i = 0; i < numberOfNodes; i++) {
uint256 node = nodes.getRandomNodeWithFreeSpace(space, randomGenerator);
Expand Down
2 changes: 1 addition & 1 deletion contracts/SkaleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ contract SkaleManager is IERC777Recipient, ISkaleManager, Permissions {
0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;

bytes32 constant public ADMIN_ROLE = keccak256("ADMIN_ROLE");
uint256 constant public HEADER_COSTS = 7486;
uint256 constant public HEADER_COSTS = 5310;
uint256 constant public CALL_PRICE = 21000;

string public version;
Expand Down
2 changes: 1 addition & 1 deletion contracts/SkaleVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ contract SkaleVerifier is Permissions, ISkaleVerifier {
if (counter > 100) {
return false;
}
uint256 xCoord = uint(hash) % Fp2Operations.P;
uint256 xCoord = uint256(hash) % Fp2Operations.P;
xCoord = (xCoord + counter) % Fp2Operations.P;

uint256 ySquared = addmod(
Expand Down
2 changes: 1 addition & 1 deletion contracts/SlashingTable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract SlashingTable is Permissions, ISlashingTable {
*/
function setPenalty(string calldata offense, uint256 penalty) external override {
require(hasRole(PENALTY_SETTER_ROLE, msg.sender), "PENALTY_SETTER_ROLE is required");
uint256 offenseHash = uint(keccak256(abi.encodePacked(offense)));
uint256 offenseHash = uint256(keccak256(abi.encodePacked(offense)));
_penalties[offenseHash] = penalty;
emit PenaltyAdded(offenseHash, offense, penalty);
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/dkg/SkaleDkgResponse.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ library SkaleDkgResponse {
bytes32[2] memory publicKey = INodes(contractManager.getContract("Nodes")).getNodePublicKey(
complaints[schainHash].fromNodeToComplaint
);
uint256 pkX = uint(publicKey[0]);
uint256 pkX = uint256(publicKey[0]);

(pkX, ) = IECDH(contractManager.getContract("ECDH")).deriveKey(secretNumber, pkX, uint(publicKey[1]));
(pkX, ) = IECDH(contractManager.getContract("ECDH")).deriveKey(secretNumber, pkX, uint256(publicKey[1]));
bytes32 key = bytes32(pkX);

// Decrypt secret key contribution
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/SegmentTreeTester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ contract SegmentTreeTester is ISegmentTreeTester {

function getRandomElem(uint256 place) external view override returns (uint256 element) {
IRandom.RandomGenerator memory randomGenerator = Random.createFromEntropy(
abi.encodePacked(uint(blockhash(block.number - 1)), place)
abi.encodePacked(uint256(blockhash(block.number - 1)), place)
);
return _tree.getRandomNonZeroElementFromPlaceToLast(place, randomGenerator);
}
Expand Down
5 changes: 5 additions & 0 deletions contracts/test/interfaces/IMathUtilsTester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ pragma solidity 0.8.17;


interface IMathUtilsTester {
event UnderflowError(
uint256 a,
uint256 b
);

function boundedSub(uint256 a, uint256 b) external returns (uint256 result);
function boundedSubWithoutEvent(uint256 a, uint256 b) external pure returns (uint256 result);
function muchGreater(uint256 a, uint256 b) external pure returns (bool greater);
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/MathUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ library MathUtils {
}

function muchGreater(uint256 a, uint256 b) internal pure returns (bool result) {
assert(type(uint).max - _EPS > b);
assert(type(uint256).max - _EPS > b);
return a > b + _EPS;
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/utils/Random.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ library Random {
* @dev Generates random value
*/
function random(IRandom.RandomGenerator memory self) internal pure returns (uint256 value) {
self.seed = uint(sha256(abi.encodePacked(self.seed)));
self.seed = uint256(sha256(abi.encodePacked(self.seed)));
return self.seed;
}

Expand All @@ -54,7 +54,7 @@ library Random {
*/
function random(IRandom.RandomGenerator memory self, uint256 max) internal pure returns (uint256 value) {
assert(max > 0);
uint256 maxRand = type(uint).max - type(uint).max % max;
uint256 maxRand = type(uint256).max - type(uint256).max % max;
if (type(uint).max - maxRand == max - 1) {
return random(self) % max;
} else {
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/SegmentTree.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ library SegmentTree {
function create(Tree storage segmentTree, uint256 size) external {
require(size > 0, "Size can't be 0");
require(size & size - 1 == 0, "Size is not power of 2");
segmentTree.tree = new uint[](size * 2 - 1);
segmentTree.tree = new uint256[](size * 2 - 1);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"**/coverage.json",
"**/*.hbs",
".openzeppelin/**",
".github/**"
".github/**",
"**/*.orig"
],
"dictionaries": ["domain-terms", "libraries", "names", "skale-terms", "solidity"],
"dictionaryDefinitions": [
Expand All @@ -29,4 +30,4 @@
{ "name": "names", "path": "./dictionaries/names.txt"},
{ "name": "solidity", "path": "./dictionaries/solidity.txt"}
]
}
}
54 changes: 26 additions & 28 deletions gas/createSchain.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
import { deployContractManager } from "../test/tools/deploy/contractManager";
import { deployValidatorService } from "../test/tools/deploy/delegation/validatorService";
import { deploySkaleManager } from "../test/tools/deploy/skaleManager";
import { ContractManager, Schains, SkaleManager, ValidatorService } from "../typechain-types";
import { deploySchains } from "../test/tools/deploy/schains";
import {deployContractManager} from "../test/tools/deploy/contractManager";
import {deployValidatorService} from "../test/tools/deploy/delegation/validatorService";
import {deploySkaleManager} from "../test/tools/deploy/skaleManager";
import {ContractManager, Schains, SkaleManager, ValidatorService} from "../typechain-types";
import {deploySchains} from "../test/tools/deploy/schains";
import fs from 'fs';
import { ethers } from "hardhat";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address";
import { Event, Wallet } from "ethers";
import { getPublicKey, getValidatorIdSignature } from "../test/tools/signatures";
import { fastBeforeEach } from "../test/tools/mocha";
import { SchainType } from "../test/tools/types";
import { TypedEvent } from "../typechain-types/common";
import { SchainNodesEvent } from "../typechain-types/artifacts/@skalenetwork/skale-manager-interfaces/ISchains";
import {ethers} from "hardhat";
import {SignerWithAddress} from "@nomicfoundation/hardhat-ethers/signers";
import {ContractTransactionReceipt, EventLog, HDNodeWallet, Wallet} from "ethers";
import {getPublicKey, getValidatorIdSignature} from "../test/tools/signatures";
import {fastBeforeEach} from "../test/tools/mocha";
import {SchainType} from "../test/tools/types";

function findEvent<TargetEvent extends TypedEvent>(events: Event[] | undefined, eventName: string) {
if (events) {
const target = events.find((event) => event.event === eventName);
if (target) {
return target as TargetEvent;
} else {
throw new Error("Event was not emitted");
export function findEvent(receipt: ContractTransactionReceipt | null, eventName: string) {
if (receipt) {
const log = receipt.logs.find((event) => event instanceof EventLog && event.eventName === eventName);
if (log) {
return log as EventLog;
}
} else {
throw new Error("Event was not emitted");
}
throw new Error("Event was not emitted");
}

describe("createSchains", () => {
Expand All @@ -50,8 +45,8 @@ describe("createSchains", () => {
const validatorId = await validatorService.getValidatorId(validator.address);
await validatorService.disableWhitelist();
const maxNodesAmount = 1000;
const etherAmount = ethers.utils.parseEther("10");
const nodeAddresses: Wallet[] = [];
const etherAmount = ethers.parseEther("10");
const nodeAddresses: HDNodeWallet[] = [];
await schains.grantRole(await schains.SCHAIN_CREATOR_ROLE(), owner.address);

const gasLimit = 12e6;
Expand All @@ -72,8 +67,11 @@ describe("createSchains", () => {

const nodesAmount = nodeId + 1;
if (nodesAmount >= 16) {
const result = await (await schains.addSchainByFoundation(0, SchainType.SMALL, 0, `schain-${nodeId}`, owner.address, ethers.constants.AddressZero, [])).wait();
const nodeInGroup = findEvent<SchainNodesEvent>(result.events, "SchainNodes").args?.nodesInGroup;
const result = await (await schains.addSchainByFoundation(0, SchainType.SMALL, 0, `schain-${nodeId}`, owner.address, ethers.ZeroAddress, [])).wait();
if (!result) {
throw new Error("addSchainByFoundation was not mined");
}
const nodeInGroup = findEvent(result, "SchainNodes").args?.nodesInGroup;
console.log("Nodes in Schain:");
const setOfNodes = new Set();
for (const nodeOfSchain of nodeInGroup) {
Expand All @@ -87,8 +85,8 @@ describe("createSchains", () => {
}

measurements.push({nodesAmount, gasUsed: result.gasUsed});
console.log("create schain on", nodesAmount, "nodes:\t", result.gasUsed.toNumber(), "gu");
if (result.gasUsed.toNumber() > gasLimit) {
console.log("create schain on", nodesAmount, "nodes:\t", result.gasUsed, "gu");
if (result.gasUsed > gasLimit) {
break;
}
}
Expand Down
Loading
Loading