-
Notifications
You must be signed in to change notification settings - Fork 686
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added test cases for standard EVM precompiles. (#3628)
- Loading branch information
Showing
5 changed files
with
359 additions
and
1 deletion.
There are no files selected for viewing
137 changes: 137 additions & 0 deletions
137
runtime/near-evm-runner/tests/build/StandardPrecompiles.abi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
[ | ||
{ | ||
"inputs" : [], | ||
"type" : "constructor", | ||
"stateMutability" : "payable" | ||
}, | ||
{ | ||
"type" : "function", | ||
"inputs" : [], | ||
"name" : "test_all", | ||
"stateMutability" : "view", | ||
"outputs" : [ | ||
{ | ||
"type" : "bool", | ||
"internalType" : "bool", | ||
"name" : "" | ||
} | ||
] | ||
}, | ||
{ | ||
"outputs" : [ | ||
{ | ||
"type" : "bool", | ||
"internalType" : "bool", | ||
"name" : "" | ||
} | ||
], | ||
"stateMutability" : "view", | ||
"name" : "test_blake2f", | ||
"inputs" : [], | ||
"type" : "function" | ||
}, | ||
{ | ||
"inputs" : [], | ||
"type" : "function", | ||
"stateMutability" : "view", | ||
"name" : "test_ecadd", | ||
"outputs" : [ | ||
{ | ||
"name" : "", | ||
"internalType" : "bool", | ||
"type" : "bool" | ||
} | ||
] | ||
}, | ||
{ | ||
"inputs" : [], | ||
"type" : "function", | ||
"stateMutability" : "view", | ||
"name" : "test_ecmul", | ||
"outputs" : [ | ||
{ | ||
"internalType" : "bool", | ||
"type" : "bool", | ||
"name" : "" | ||
} | ||
] | ||
}, | ||
{ | ||
"type" : "function", | ||
"inputs" : [], | ||
"name" : "test_ecpair", | ||
"stateMutability" : "view", | ||
"outputs" : [ | ||
{ | ||
"type" : "bool", | ||
"internalType" : "bool", | ||
"name" : "" | ||
} | ||
] | ||
}, | ||
{ | ||
"type" : "function", | ||
"inputs" : [], | ||
"outputs" : [ | ||
{ | ||
"internalType" : "bool", | ||
"type" : "bool", | ||
"name" : "" | ||
} | ||
], | ||
"stateMutability" : "pure", | ||
"name" : "test_ecrecover" | ||
}, | ||
{ | ||
"inputs" : [], | ||
"type" : "function", | ||
"outputs" : [ | ||
{ | ||
"name" : "", | ||
"type" : "bool", | ||
"internalType" : "bool" | ||
} | ||
], | ||
"name" : "test_identity", | ||
"stateMutability" : "view" | ||
}, | ||
{ | ||
"outputs" : [ | ||
{ | ||
"type" : "bool", | ||
"internalType" : "bool", | ||
"name" : "" | ||
} | ||
], | ||
"stateMutability" : "view", | ||
"name" : "test_modexp", | ||
"inputs" : [], | ||
"type" : "function" | ||
}, | ||
{ | ||
"stateMutability" : "pure", | ||
"name" : "test_ripemd160", | ||
"outputs" : [ | ||
{ | ||
"type" : "bool", | ||
"internalType" : "bool", | ||
"name" : "" | ||
} | ||
], | ||
"inputs" : [], | ||
"type" : "function" | ||
}, | ||
{ | ||
"stateMutability" : "pure", | ||
"name" : "test_sha256", | ||
"outputs" : [ | ||
{ | ||
"type" : "bool", | ||
"internalType" : "bool", | ||
"name" : "" | ||
} | ||
], | ||
"inputs" : [], | ||
"type" : "function" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
60806040526113b5806100136000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c8063aefff86311610066578063aefff86314610143578063d06d2a3114610163578063d7d4430414610183578063da726c1b146101a3578063ff71b530146101c35761009e565b8063166f2a2f146100a35780631d82afc7146100c35780633ae57cb6146100e357806341d5e54514610103578063a7465fca14610123575b600080fd5b6100ab6101e3565b60405180821515815260200191505060405180910390f35b6100cb610303565b60405180821515815260200191505060405180910390f35b6100eb61035e565b60405180821515815260200191505060405180910390f35b61010b6103d5565b60405180821515815260200191505060405180910390f35b61012b6104b9565b60405180821515815260200191505060405180910390f35b61014b61090c565b60405180821515815260200191505060405180910390f35b61016b61097b565b60405180821515815260200191505060405180910390f35b61018b6109e2565b60405180821515815260200191505060405180910390f35b6101ab610a4c565b60405180821515815260200191505060405180910390f35b6101cb610caf565b60405180821515815260200191505060405180910390f35b60006101ed611136565b6102857f18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc960001b7f063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f3726660001b7f07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed60001b7f06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d760001b610ce5565b90507f2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee20283970360001b816000600281106102b857fe5b60200201511480156102fd57507f301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c91560001b816001600281106102f657fe5b6020020151145b91505090565b600060606040518060400160405280602081526020017f11111111111111111111111111111111111111111111111111111111111111118152509050808051906020012061035082610d40565b805190602001201491505090565b60007fe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556002604051806000019050602060405180830381855afa1580156103a9573d6000803e3d6000fd5b5050506040513d60208110156103be57600080fd5b810190808051906020019092919050505014905090565b60006103df611136565b61043b7f2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb760001b7f21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb20460001b6711138ce750fa15c260001b610dc0565b90507f070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c60001b8160006002811061046e57fe5b60200201511480156104b357507f031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc60001b816001600281106104ac57fe5b6020020151145b91505090565b60006104c361090c565b610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6572726f6e656f75732065637265636f76657220707265636f6d70696c65000081525060200191505060405180910390fd5b61053d61035e565b6105af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6572726f6e656f75732073686132353620707265636f6d70696c65000000000081525060200191505060405180910390fd5b6105b76109e2565b610629576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6572726f6e656f757320726970656d6431363020707265636f6d70696c65000081525060200191505060405180910390fd5b610631610303565b6106a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f6572726f6e656f7573206964656e7469747920707265636f6d70696c6500000081525060200191505060405180910390fd5b6106ab61097b565b61071d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6572726f6e656f7573206d6f6465787020707265636f6d70696c65000000000081525060200191505060405180910390fd5b6107256101e3565b610797576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f6572726f6e656f757320656361646420707265636f6d70696c6500000000000081525060200191505060405180910390fd5b61079f6103d5565b610811576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f6572726f6e656f75732065636d756c20707265636f6d70696c6500000000000081525060200191505060405180910390fd5b610819610caf565b61088b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6572726f6e656f75732065637061697220707265636f6d70696c65000000000081525060200191505060405180910390fd5b610893610a4c565b610905576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f6572726f6e656f757320626c616b65326620707265636f6d70696c650000000081525060200191505060405180910390fd5b6001905090565b6000807f11111111111111111111111111111111111111111111111111111111111111119050606060405180608001604052806041815260200161133f6041913990506000731563915e194d8cfba1943570603f7606a31155089050610973838383610e14565b935050505090565b6000806003905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f905060016109d9848484610e6e565b14935050505090565b60007f9c1185a5c5e9fc54612808977ee8f548b2258d310000000000000000000000006003604051806000019050602060405180830381855afa158015610a2d573d6000803e3d6000fd5b5050506040515160601b6bffffffffffffffffffffffff191614905090565b600080600c9050610a5b611136565b7f48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa581600060028110610a8957fe5b6020020181815250507fd182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b81600160028110610ac057fe5b602002018181525050610ad1611158565b7f616263000000000000000000000000000000000000000000000000000000000081600060048110610aff57fe5b602002018181525050600081600160048110610b1757fe5b602002018181525050600081600260048110610b2f57fe5b602002018181525050600081600360048110610b4757fe5b602002018181525050610b5861117a565b7f030000000000000000000000000000000000000000000000000000000000000081600060028110610b8657fe5b602002019077ffffffffffffffffffffffffffffffffffffffffffffffff1916908177ffffffffffffffffffffffffffffffffffffffffffffffff191681525050600081600160028110610bd657fe5b602002019077ffffffffffffffffffffffffffffffffffffffffffffffff1916908177ffffffffffffffffffffffffffffffffffffffffffffffff191681525050600060019050610c25611136565b610c328686868686610ec1565b90507fba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d181600060028110610c6257fe5b6020020151148015610ca457507f7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd400992381600160028110610c9d57fe5b6020020151145b965050505050505090565b600080610cd6604051806101a0016040528061018081526020016111bf6101809139611039565b9050600160001b811491505090565b610ced611136565b610cf5611158565b604051806080016040528087815260200186815260200185815260200184815250905060408260808360065afa8060008114610d3057610d35565b600080fd5b505050949350505050565b606080825167ffffffffffffffff81118015610d5b57600080fd5b506040519080825280601f01601f191660200182016040528015610d8e5781602001600182028036833780820191505090505b50905082518060208301826020870160045afa8060008114610daf57610db4565b600080fd5b50505080915050919050565b610dc8611136565b610dd061119c565b604051806060016040528086815260200185815260200184815250905060408260608360075afa8060008114610e0557610e0a565b600080fd5b5050509392505050565b6000806000610e238686611086565b8092508193505050818015610e6357508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b925050509392505050565b600060405160208152602080820152602060408201528460608201528360808201528260a082015260208160c08360055afa8060008114610eb25782519350610eb7565b600080fd5b5050509392505050565b610ec9611136565b610ed1611136565b60608787600060028110610ee157fe5b602002015188600160028110610ef357fe5b602002015188600060048110610f0557fe5b602002015189600160048110610f1757fe5b60200201518a600260048110610f2957fe5b60200201518b600360048110610f3b57fe5b60200201518b600060028110610f4d57fe5b60200201518c600160028110610f5f57fe5b60200201518c604051602001808b63ffffffff1660e01b81526004018a81526020018981526020018881526020018781526020018681526020018581526020018477ffffffffffffffffffffffffffffffffffffffffffffffff191681526008018377ffffffffffffffffffffffffffffffffffffffffffffffff1916815260080182151560f81b81526001019a5050505050505050505050604051602081830303815290604052905060408260d56020840160095afa806000811461102457611029565b600080fd5b5050819250505095945050505050565b60008082519050600060c0828161104c57fe5b061461105757600080fd5b604051602081836020870160085afa8060008114611078578251945061107d565b600080fd5b50505050919050565b600080604183511461109e576000809150915061112f565b60008060006020860151925060408601519150606086015160001a9050600060018883868660405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611117573d6000803e3d6000fd5b50505060206040510351905060018195509550505050505b9250929050565b6040518060400160405280600290602082028036833780820191505090505090565b6040518060800160405280600490602082028036833780820191505090505090565b6040518060400160405280600290602082028036833780820191505090505090565b604051806060016040528060039060208202803683378082019150509050509056fe1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daab9f0bb08640d3c1c00761cdd0121209268f6fd3816bc98b9e6f3cc77bf82b69812ac7a61788a0fdc0e19180f14c945a8e1088a27d92a74dce81c0981fb6447441ba26469706673582212203acf67adc69694b9cae057a502d5c1741f3f0e71a7bd73034e88327c199996da64736f6c63430007030033 |
180 changes: 180 additions & 0 deletions
180
runtime/near-evm-runner/tests/contracts/StandardPrecompiles.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// SPDX-License-Identifier: Unlicense | ||
pragma solidity ^0.7.0; | ||
|
||
//import "hardhat/console.sol"; | ||
|
||
contract StandardPrecompiles { | ||
constructor() payable { | ||
//console.log("Deploying StandardPrecompiles"); | ||
} | ||
|
||
function test_all() public view returns(bool) { | ||
require(test_ecrecover(), "erroneous ecrecover precompile"); | ||
require(test_sha256(), "erroneous sha256 precompile"); | ||
require(test_ripemd160(), "erroneous ripemd160 precompile"); | ||
require(test_identity(), "erroneous identity precompile"); | ||
require(test_modexp(), "erroneous modexp precompile"); | ||
require(test_ecadd(), "erroneous ecadd precompile"); | ||
require(test_ecmul(), "erroneous ecmul precompile"); | ||
require(test_ecpair(), "erroneous ecpair precompile"); | ||
require(test_blake2f(), "erroneous blake2f precompile"); | ||
return true; | ||
} | ||
|
||
// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000001 | ||
function test_ecrecover() public pure returns(bool) { | ||
bytes32 hash = hex"1111111111111111111111111111111111111111111111111111111111111111"; | ||
bytes memory sig = hex"b9f0bb08640d3c1c00761cdd0121209268f6fd3816bc98b9e6f3cc77bf82b69812ac7a61788a0fdc0e19180f14c945a8e1088a27d92a74dce81c0981fb6447441b"; | ||
address signer = 0x1563915e194D8CfBA1943570603F7606A3115508; | ||
return ecverify(hash, sig, signer); | ||
} | ||
|
||
// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000002 | ||
function test_sha256() public pure returns(bool) { | ||
return sha256("") == hex"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; | ||
} | ||
|
||
// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000003 | ||
function test_ripemd160() public pure returns(bool) { | ||
return ripemd160("") == hex"9c1185a5c5e9fc54612808977ee8f548b2258d31"; | ||
} | ||
|
||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000004 | ||
function test_identity() public view returns(bool) { | ||
bytes memory data = hex"1111111111111111111111111111111111111111111111111111111111111111"; | ||
return keccak256(datacopy(data)) == keccak256(data); | ||
} | ||
|
||
// See: https://eips.ethereum.org/EIPS/eip-198 | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000005 | ||
function test_modexp() public view returns(bool) { | ||
uint256 base = 3; | ||
uint256 exponent = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e; | ||
uint256 modulus = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f; | ||
return modexp(base, exponent, modulus) == 1; | ||
} | ||
|
||
// See: https://eips.ethereum.org/EIPS/eip-196 | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000006 | ||
function test_ecadd() public pure returns(bool) { | ||
return true; // TODO | ||
} | ||
|
||
// See: https://eips.ethereum.org/EIPS/eip-196 | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000007 | ||
function test_ecmul() public pure returns(bool) { | ||
return true; // TODO | ||
} | ||
|
||
// See: https://eips.ethereum.org/EIPS/eip-197 | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000008 | ||
function test_ecpair() public pure returns(bool) { | ||
return true; // TODO | ||
} | ||
|
||
// See: https://eips.ethereum.org/EIPS/eip-152 | ||
// See: https://etherscan.io/address/0x0000000000000000000000000000000000000009 | ||
function test_blake2f() public view returns(bool) { | ||
uint32 rounds = 12; | ||
bytes32[2] memory h; | ||
h[0] = hex"48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"; | ||
h[1] = hex"d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"; | ||
bytes32[4] memory m; | ||
m[0] = hex"6162630000000000000000000000000000000000000000000000000000000000"; | ||
m[1] = hex"0000000000000000000000000000000000000000000000000000000000000000"; | ||
m[2] = hex"0000000000000000000000000000000000000000000000000000000000000000"; | ||
m[3] = hex"0000000000000000000000000000000000000000000000000000000000000000"; | ||
bytes8[2] memory t; | ||
t[0] = hex"03000000"; | ||
t[1] = hex"00000000"; | ||
bool f = true; | ||
bytes32[2] memory result = blake2f(rounds, h, m, t, f); | ||
return result[0] == hex"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1" && result[1] == hex"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"; | ||
} | ||
|
||
function ecverify(bytes32 hash, bytes memory sig, address signer) private pure returns (bool) { | ||
bool ret; | ||
address addr; | ||
(ret, addr) = ecrecovery(hash, sig); | ||
return ret && addr == signer; | ||
} | ||
|
||
function ecrecovery(bytes32 hash, bytes memory sig) private pure returns (bool, address) { | ||
if (sig.length != 65) | ||
return (false, address(0)); | ||
|
||
bytes32 r; | ||
bytes32 s; | ||
uint8 v; | ||
assembly { | ||
r := mload(add(sig, 32)) | ||
s := mload(add(sig, 64)) | ||
v := byte(0, mload(add(sig, 96))) | ||
} | ||
|
||
address addr = ecrecover(hash, v, r, s); | ||
return (true, addr); | ||
} | ||
|
||
function datacopy(bytes memory input) private view returns (bytes memory) { | ||
bytes memory output = new bytes(input.length); | ||
assembly { | ||
let len := mload(input) | ||
if iszero(staticcall(gas(), 0x04, add(input, 32), len, add(output, 32), len)) { | ||
revert(0, 0) | ||
} | ||
} | ||
return output; | ||
} | ||
|
||
function modexp(uint256 base, uint256 exponent, uint256 modulus) private view returns (uint256) { | ||
bytes32 ret; | ||
assembly { | ||
let ptr := mload(0x40) | ||
mstore(ptr, 32) | ||
mstore(add(ptr, 0x20), 32) | ||
mstore(add(ptr, 0x40), 32) | ||
mstore(add(ptr, 0x60), base) | ||
mstore(add(ptr, 0x80), exponent) | ||
mstore(add(ptr, 0xA0), modulus) | ||
let ok := staticcall(gas(), 0x05, ptr, 0xC0, ptr, 0x20) | ||
switch ok | ||
case 0 { | ||
revert(0, 0) | ||
} | ||
default { | ||
ret := mload(ptr) | ||
} | ||
} | ||
return uint256(ret); | ||
} | ||
|
||
function ecadd(uint256 ax, uint256 ay, uint256 bx, uint256 by) private view returns (bytes32[2] memory output) { | ||
bytes32[4] memory input; | ||
input[0] = bytes32(ax); | ||
input[1] = bytes32(ay); | ||
input[2] = bytes32(bx); | ||
input[3] = bytes32(by); | ||
assembly { | ||
let ok := staticcall(gas(), 0x06, input, 0x80, output, 0x40) | ||
switch ok | ||
case 0 { | ||
revert(0, 0) | ||
} | ||
} | ||
} | ||
|
||
function blake2f(uint32 rounds, bytes32[2] memory h, bytes32[4] memory m, bytes8[2] memory t, bool f) private view returns (bytes32[2] memory) { | ||
bytes32[2] memory output; | ||
bytes memory args = abi.encodePacked(rounds, h[0], h[1], m[0], m[1], m[2], m[3], t[0], t[1], f); | ||
assembly { | ||
if iszero(staticcall(gas(), 0x09, add(args, 32), 0xd5, output, 0x40)) { | ||
revert(0, 0) | ||
} | ||
} | ||
return output; | ||
} | ||
} |
Oops, something went wrong.