diff --git a/API.md b/API.md index 1521e4c6..5b12f334 100644 --- a/API.md +++ b/API.md @@ -18,7 +18,7 @@ When deploying contracts, you should use the latest released version of Solidity * [\.exoticToSlice()](#tvmcellexotictoslice) * [\.loadExoticCell() and \.loadExoticCellQ()](#tvmcellloadexoticcell-and-tvmcellloadexoticcellq) * [TvmSlice](#tvmslice) - * [\.empty()](#tvmsliceempty) + * [\.empty(), \.bitEmpty() and \.refEmpty()](#tvmsliceempty-tvmslicebitempty-and-tvmslicerefempty) * [\.size()](#tvmslicesize) * [\.bits()](#tvmslicebits) * [\.refs()](#tvmslicerefs) @@ -27,6 +27,8 @@ When deploying contracts, you should use the latest released version of Solidity * [\.depth()](#tvmslicedepth) * [\.hasNBits(), \.hasNRefs() and \.hasNBitsAndRefs()](#tvmslicehasnbits-tvmslicehasnrefs-and-tvmslicehasnbitsandrefs) * [\.compare()](#tvmslicecompare) + * [\.startsWith()](#tvmslicestartswith) + * [\.startsWithOne()](#tvmslicestartswithone) * [TvmSlice load primitives](#tvmslice-load-primitives) * [\.load()](#tvmsliceload) * [\.loadQ()](#tvmsliceloadq) @@ -257,6 +259,7 @@ When deploying contracts, you should use the latest released version of Solidity * [`code` option usage](#code-option-usage) * [Other deploy options](#other-deploy-options) * [Deploy via \.transfer()](#deploy-via-addresstransfer) + * [Deploy the contract with no constructor](#deploy-the-contract-with-no-constructor) * [New contract address problem](#new-contract-address-problem) * [Misc functions from `tvm`](#misc-functions-from-tvm) * [tvm.code()](#tvmcode) @@ -266,6 +269,20 @@ When deploying contracts, you should use the latest released version of Solidity * [tvm.resetStorage()](#tvmresetstorage) * [tvm.exit() and tvm.exit1()](#tvmexit-and-tvmexit1) * [tvm.sendrawmsg()](#tvmsendrawmsg) + * [**bls** namespace](#bls-namespace) + * [bls.verify()](#blsverify) + * [bls.aggregate()](#blsaggregate) + * [bls.fastAggregateVerify()](#blsfastaggregateverify) + * [bls.aggregateVerify()](#blsaggregateverify) + * [bls.g1Zero() and bls.g2Zero()](#blsg1zero-and-blsg2zero) + * [bls.g1IsZero() and bls.g2IsZero()](#blsg1iszero-and-blsg2iszero) + * [bls.g1Add() and bls.g2Add()](#blsg1add-and-blsg2add) + * [bls.g1Sub() and bls.g2Sub()](#blsg1sub-and-blsg2sub) + * [bls.g1Neg() and bls.g2Neg()](#blsg1neg-and-blsg2neg) + * [bls.g1Mul() and bls.g2Mul()](#blsg1mul-and-blsg2mul) + * [bls.g1InGroup() and bls.g2InGroup()](#blsg1ingroup-and-blsg2ingroup) + * [bls.r()](#blsr) + * [bls.g1MultiExp() and bls.g2MultiExp()](#blsg1multiexp-and-blsg2multiexp) * [**math** namespace](#math-namespace) * [math.min() math.max()](#mathmin-mathmax) * [math.minmax()](#mathminmax) @@ -275,6 +292,7 @@ When deploying contracts, you should use the latest released version of Solidity * [math.divmod()](#mathdivmod) * [math.muldiv() math.muldivr() math.muldivc()](#mathmuldiv-mathmuldivr-mathmuldivc) * [math.muldivmod()](#mathmuldivmod) + * [math.mulmod()](#mathmulmod) * [math.sign()](#mathsign) * [**tx** namespace](#tx-namespace) * [tx.logicaltime](#txlogicaltime) @@ -323,16 +341,16 @@ When deploying contracts, you should use the latest released version of Solidity TVM Solidity compiler adds its current version to the generated code. This version can be obtained: -1) using [tvm_linker](https://github.com/tonlabs/TVM-linker#2-decoding-of-boc-messages-prepared-externally) from a `*.tvc` file: +1) using [tvm_linker](https://github.com/everx-labs/TVM-linker#2-decoding-of-boc-messages-prepared-externally) from a `*.tvc` file: ```bash tvm_linker decode --tvm ``` -2) using [tonos-cli](https://github.com/tonlabs/tonos-cli#48-decode-commands) from a `*.boc` file, a `*.tvc` file, or a network account: +2) using [ever-cli](https://github.com/everx-labs/ever-cli#48-decode-commands) from a `*.boc` file, a `*.tvc` file, or a network account: ```bash -tonos-cli decode tvc [--tvc] [--boc] +ever-cli decode tvc [--tvc] [--boc] ``` ### TVM specific types @@ -562,15 +580,33 @@ Comparison operators: **Note:** only data bits from the root cells are compared. References are ignored. +String literals can be converted to `TvmSlice`: + +```TVMSolidity +TvmSlice s = "0189abef_"; +``` + `TvmSlice` can be converted to `bytes`. It costs at least 500 gas units. -##### \.empty() +##### \.empty(), \.bitEmpty() and \.refEmpty() ```TVMSolidity +(1) .empty() returns (bool); +(2) +.bitEmpty() returns (bool); +3() +.refEmpty() returns (bool); ``` -Checks whether the `TvmSlice` is empty (i.e., contains no data bits and no cell references). +(1) +Checks whether the `TvmSlice` contains no data bits and no cell references. + +(2) +Checks whether the `TvmSlice` contains no data bits. + +(3) +Checks whether the `TvmSlice` contains no cell references. ##### \.size() @@ -653,6 +689,22 @@ Lexicographically compares the `slice` and `other` data bits of the root slices * 0 - `slice` == `other` * -1 - `slice` < `other` +##### \.startsWith() + +```TVMSolidity +.startsWith(TvmSlice prefix) returns (bool); +``` + +Checks whether `prefix` is a prefix of `TvmSlice`. + +##### \.startsWithOne() + +```TVMSolidity +.startsWithOne() returns (bool); +``` + +Checks whether the first bit of `TvmSlice` is a one. + ##### TvmSlice load primitives All `load*` functions below modify the `TvmSlice` object. If you wants to load second reference from the `TvmSlice`, you should load the first one with [\.loadRef()](#tvmsliceloadref) and then load the reference you need. The same rule is applied to data bits. To load bits from 2 to 10 positions, you should load or skip first two bits. @@ -2413,7 +2465,7 @@ no effect. * `flag + 32` - means that the current account must be destroyed if its resulting balance is zero. For example, `flag: 128 + 32` is used to send all balance and destroy the contract. -In order to clarify flags usage see [this sample](https://github.com/tonlabs/samples/blob/master/solidity/20_bomber.sol). +In order to clarify flags usage see [this sample](https://github.com/everx-labs/samples/blob/master/solidity/20_bomber.sol). ```TVMSolidity address dest = ...; @@ -2437,7 +2489,7 @@ destination.transfer({value: 1 ever, bounce: false, stateInit: stateInit}); See example of `address.transfer()` usage: -* [giver](https://github.com/tonlabs/samples/blob/master/solidity/7_Giver.sol) +* [giver](https://github.com/everx-labs/samples/blob/master/solidity/7_Giver.sol) #### mapping @@ -2480,8 +2532,8 @@ If you use mapping as an input or output param for public/external functions, See example of how to work with mappings: -* [database](https://github.com/tonlabs/samples/blob/master/solidity/13_BankCollector.sol) -* [client](https://github.com/tonlabs/samples/blob/master/solidity/13_BankCollectorClient.sol) +* [database](https://github.com/everx-labs/samples/blob/master/solidity/13_BankCollector.sol) +* [client](https://github.com/everx-labs/samples/blob/master/solidity/13_BankCollectorClient.sol) ##### Keyword `emptyMap` @@ -3046,7 +3098,7 @@ Defines that code is compiled with special selector that is needed to upgrade Fu #### Decoding state variables -You can decode state variables using tonos-cli. See `tonos-cli decode account --help`. +You can decode state variables using ever-cli. See `ever-cli decode account --help`. See also: [abi.decodeData()](#abidecodedata). @@ -3253,7 +3305,7 @@ If the `onBounce` function throws an exception, then another bounced messages ar Example of how to use `onBounce` function for option 2: -* [onBounceHandler](https://github.com/tonlabs/samples/blob/master/solidity/16_onBounceHandler.sol) +* [onBounceHandler](https://github.com/everx-labs/samples/blob/master/solidity/16_onBounceHandler.sol) Example of getting function ID if `CapBounceMsgBody` and `CapFullBodyInBounced` [capabilities](#tvm-capabilities) are set: @@ -3301,8 +3353,8 @@ Function `onCodeUpgrade` had function id = 2 (for compiler <= 0.65.0). Now, it h See example of how to upgrade code of the contract: -* [old contract](https://github.com/tonlabs/samples/blob/master/solidity/12_BadContract.sol) -* [new contract](https://github.com/tonlabs/samples/blob/master/solidity/12_NewVersion.sol) +* [old contract](https://github.com/everx-labs/samples/blob/master/solidity/12_BadContract.sol) +* [new contract](https://github.com/everx-labs/samples/blob/master/solidity/12_NewVersion.sol) It's good to pass `TvmCell cell` to the public function that calls `onCodeUpgrade(TvmCell cell, ...)` function. `TvmCell cell` may contain some data that may be useful for the new contract. @@ -3343,7 +3395,7 @@ NB: Do not use [tvm.commit()](#tvmcommit) or [tvm.accept()](#tvmaccept) in this See also: [Contract execution](#contract-execution). See an example of how to define this function: -* [Custom replay protection](https://github.com/tonlabs/samples/blob/master/solidity/14_CustomReplayProtection.sol) +* [Custom replay protection](https://github.com/everx-labs/samples/blob/master/solidity/14_CustomReplayProtection.sol) ### Function specifiers @@ -3542,7 +3594,7 @@ function f(uint n) public responsible pure { TVM Solidity compiler allows specifying different parameters of the outbound internal message that is sent via external function call. Note, all external function calls are asynchronous, so callee function will be called after termination of the current transaction. -`value`, `currencies`, `bounce` or `flag` options can be set. See [\.transfer()](#addresstransfer) +`value`, `currencies`, `bounce`, `flag` and `stateInit` options can be set. See [\.transfer()](#addresstransfer) where these options are described. **Note:** if `value` isn't set, then the default value is equal to 0.01 ever, or 10^7 nanoever. It's equal to 10_000 units of gas in workchain. @@ -3616,9 +3668,9 @@ contract Caller { See also: -* Example of callback usage: [24_SquareProvider](https://github.com/tonlabs/samples/blob/master/solidity/24_SquareProvider.sol) -* Example of callback usage: [4.1_CentralBank](https://github.com/tonlabs/samples/blob/master/solidity/4.1_CentralBank.sol) -and [4.1_CurrencyExchange.sol](https://github.com/tonlabs/samples/blob/master/solidity/4.1_CurrencyExchange.sol) +* Example of callback usage: [24_SquareProvider](https://github.com/everx-labs/samples/blob/master/solidity/24_SquareProvider.sol) +* Example of callback usage: [4.1_CentralBank](https://github.com/everx-labs/samples/blob/master/solidity/4.1_CentralBank.sol) +and [4.1_CurrencyExchange.sol](https://github.com/everx-labs/samples/blob/master/solidity/4.1_CurrencyExchange.sol) * [return](#return) ### Delete variables @@ -3811,7 +3863,7 @@ This action is required to process external messages that bring no value. See example of how to use this function: -* [accumulator](https://github.com/tonlabs/samples/blob/master/solidity/1_Accumulator.sol) +* [accumulator](https://github.com/everx-labs/samples/blob/master/solidity/1_Accumulator.sol) ##### tvm.setGasLimit() @@ -3983,8 +4035,8 @@ after the successful termination of the current run of the smart contract). See example of how to use this function: -* [old contract](https://github.com/tonlabs/samples/blob/master/solidity/12_BadContract.sol) -* [new contract](https://github.com/tonlabs/samples/blob/master/solidity/12_NewVersion.sol) +* [old contract](https://github.com/everx-labs/samples/blob/master/solidity/12_BadContract.sol) +* [new contract](https://github.com/everx-labs/samples/blob/master/solidity/12_NewVersion.sol) ##### tvm.configParam() @@ -4062,7 +4114,7 @@ Example: tvm.rawReserve(1 ever, 4 + 8); ``` -See also: [23_rawReserve.sol](https://github.com/tonlabs/samples/blob/master/solidity/23_rawReserve.sol) +See also: [23_rawReserve.sol](https://github.com/everx-labs/samples/blob/master/solidity/23_rawReserve.sol) ##### tvm.initCodeHash() @@ -4159,12 +4211,12 @@ onchain) and use `code` if you want to create account state in the `new` express Constructor function parameters don't influence the address. See [New contract address problem](#new-contract-address-problem). -[Step-by-step description how to deploy contracts from the contract here](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.md). +[Step-by-step description how to deploy contracts from the contract here](https://github.com/everx-labs/samples/blob/master/solidity/17_ContractProducer.md). Examples: -* [WalletProducer](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.sol). -* [SelfDeployer](https://github.com/tonlabs/samples/blob/master/solidity/21_self_deploy.sol). +* [WalletProducer](https://github.com/everx-labs/samples/blob/master/solidity/17_ContractProducer.sol). +* [SelfDeployer](https://github.com/everx-labs/samples/blob/master/solidity/21_self_deploy.sol). ##### `stateInit` option usage @@ -4242,8 +4294,20 @@ address newWallet = new SimpleWallet{ You can also deploy the contract via [\.transfer()](#addresstransfer). Just set the option `stateInit`. -* [Example of usage](https://github.com/tonlabs/samples/blob/master/solidity/11_ContractDeployer.sol) -* [Step-by-step description how to deploy contracts from the contract here](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.md). +* [Example of usage](https://github.com/everx-labs/samples/blob/master/solidity/11_ContractDeployer.sol) +* [Step-by-step description how to deploy contracts from the contract here](https://github.com/everx-labs/samples/blob/master/solidity/17_ContractProducer.md). + +##### Deploy the contract with no constructor + +If the contract does not have constructor explicitly and does not have state variables with initialisation, then in `*.abi.json` file there is no `constructor` function and no `_constructorFlag` field. + +For example: [1_Accumulator_no_ctor.sol](https://github.com/everx-labs/samples/blob/master/solidity/1_Accumulator_no_ctor.sol) and [1_Accumulator_no_ctor.abi.json](https://github.com/everx-labs/samples/blob/master/solidity/1_Accumulator_no_ctor.abi.json). To deploy this contractor by external message with help `ever-cli`, use parameter `method` for `deploy` and `deployx` commands: + +```bash +ever-cli deploy --method add '{"delta": 123}' ... +``` + +To deploy a contractor by internal message, use option `stateInit` for [External function calls](#external-function-calls). See `deployNoConstructor` and `deployNoConstructor2` functions [11_ContractDeployer.sol](https://github.com/everx-labs/samples/blob/master/solidity/11_ContractDeployer.sol) as samples of deploying [11_Waller_no_constructor.sol](https://github.com/everx-labs/samples/blob/master/solidity/11_Waller_no_constructor.sol). ##### New contract address problem @@ -4258,18 +4322,18 @@ Let's consider how to protect against this problem: We must Check if we didn't forget to set the public key in the contract and the inbound message is signed by that key. If hacker doesn't have your private key, then he can't sign message to call the constructor. -See [constructor of WalletProducer](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.sol). +See [constructor of WalletProducer](https://github.com/everx-labs/samples/blob/master/solidity/17_ContractProducer.sol). 2. Constructor is called by internal message. We should define static variable in the new contract that will contain address of the creator. Address of the creator will be a part of the `stateInit`. And in the constructor we must check address of the message sender. -See [function `deployWallet` how to deploy contract](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.sol). -See [constructor of SimpleWallet](https://github.com/tonlabs/samples/blob/master/solidity/17_SimpleWallet.sol). +See [function `deployWallet` how to deploy contract](https://github.com/everx-labs/samples/blob/master/solidity/17_ContractProducer.sol). +See [constructor of SimpleWallet](https://github.com/everx-labs/samples/blob/master/solidity/17_SimpleWallet.sol). If some contract should deploy plenty of contracts (with some contract's public key), then it's a good idea to declare static variable in the deployed contract. This variable can contain some sequence number. It will allow each new contact to have unique `stateInit`. -See [SimpleWallet](https://github.com/tonlabs/samples/blob/master/solidity/17_SimpleWallet.sol). +See [SimpleWallet](https://github.com/everx-labs/samples/blob/master/solidity/17_SimpleWallet.sol). **Note**: contract's public key (`tvm.pubkey()`) is a part of `stateInit`. ##### Misc functions from `tvm` @@ -4282,7 +4346,7 @@ tvm.code() returns (TvmCell); Returns contract's code. [Capabilities](#tvm-capabilities) required: `CapMycode`. -See [SelfDeployer](https://github.com/tonlabs/samples/blob/master/solidity/21_self_deploy.sol). +See [SelfDeployer](https://github.com/everx-labs/samples/blob/master/solidity/21_self_deploy.sol). ##### tvm.pubkey() @@ -4312,8 +4376,8 @@ after termination of the current run of the smart contract. See example of how to use this function: -* [old contract](https://github.com/tonlabs/samples/blob/master/solidity/12_BadContract.sol) -* [new contract](https://github.com/tonlabs/samples/blob/master/solidity/12_NewVersion.sol) +* [old contract](https://github.com/everx-labs/samples/blob/master/solidity/12_BadContract.sol) +* [new contract](https://github.com/everx-labs/samples/blob/master/solidity/12_NewVersion.sol) ##### tvm.resetStorage() @@ -4365,7 +4429,7 @@ Possible values of `flag` are described here: [\.transfer()](#addresst **Note:** make sure that `msg` has a correct format and follows the [TL-B scheme][3] of `Message X`. For example: -``` TVMSolidity +```TVMSolidity TvmCell msg = ... tvm.sendrawmsg(msg, 2); ``` @@ -4374,6 +4438,257 @@ If the function is called by external message and `msg` has a wrong format (for `init` of `Message X` is not valid), then the transaction will be replayed despite the usage of flag 2. It will happen because the transaction will fail at the action phase. +#### **bls** namespace + +Operations on a pairing friendly BLS12-381 curve. BLS values are represented in TVM in the following way: + * G1-points and public keys: 48-byte slice. + * G2-points and signatures: 96-byte slice. + * Elements of field FP: 48-byte slice. + * Elements of field FP2: 96-byte slice. + * Messages: slice. Number of bits should be divisible by 8. + +When input value is a point or a field element, the slice may have more than 48/96 bytes. In this case only the first 48/96 bytes are taken. If the slice has less bytes (or if message size is not divisible by 8), cell underflow exception is thrown. + +[Capabilities](#tvm-capabilities) required: `CapTvmV20`. + +#### bls.verify + +```TVMSolidity +bls.verify(TvmSlice pubkey, TvmSlice message, TvmSlice sign) returns (bool) +``` + +Checks BLS signature. Returns `true` on success, `false` otherwise. Example: + +```TVMSolidity +TvmSlice pubkey = "b65cfaf56cebd6083320bf9a1c2010d4775310c5e7b348546dce0f62aa1ad0c29e15a58e251582faa7879d74e9d4034b"; +TvmSlice message = TvmSlice(bytes("Hello, BLS verify!")); +TvmSlice sign = "aa652737cad33a9b332300ecd53f1995e5d6c6ff5eb233c04b2e32ca1169524fee64d58575cb42a1a34e1bf3a61c550814d0147b2b82a668ef7c917c756e489e8ff57d64efbbf533d7995db28377d6442ec952268a2bf30d5770d4e8a9d56f9c"; +bool ok = bls.verify(pubkey, message, sign); +``` + +#### bls.aggregate + +```TVMSolidity +(1) +bls.aggregate(vector(TvmSlice) signs) returns (TvmSlice sign) +(2) +bls.aggregate(TvmSlice sign0, TvmSlice sign1, ...) returns (TvmSlice sign) +``` + +(1) Aggregates signatures if `signs.length() > 0`. Throw exception if `signs.empty()` or if some `signs[i]` is not a valid signature. + +(2) Same as (1) but takes `TvmSlice`'s. + +Example: + +```TVMSolidity +vector(TvmSlice) signs; +signs.push("8b1eac18b6e7a38f2b2763c9a03c3b6cff4110f18c4d363eec455463bd5c8671fb81204c4732406d72468a1474df6133147a2240f4073a472ef419f23011ee4d6cf02fceb844398e33e2e331635dace3b26464a6851e10f6895923c568582fbd"); +signs.push("94ec60eb8d2b657dead5e1232b8f9cc0162467b08f02e252e97622297787a74b6496607036089837fe5b52244bbbb6d00d3d7cc43812688451229d9e96f704401db053956c588203ba7638e8882746c16e701557f34b0c08bbe097483aec161e"); +signs.push("8cdbeadb3ee574a4f796f10d656885f143f454cc6a2d42cf8cabcd592d577c5108e4258a7b14f0aafe6c86927b3e70030432a2e5aafa97ee1587bbdd8b69af044734defcf3c391515ab26616e15f5825b4b022a7df7b44f65a8792c54762e579"); +TvmSlice sign = bls.aggregate(signs); +``` + +```TVMSolidity +TvmSlice sign0 = "8b1eac18b6e7a38f2b2763c9a03c3b6cff4110f18c4d363eec455463bd5c8671fb81204c4732406d72468a1474df6133147a2240f4073a472ef419f23011ee4d6cf02fceb844398e33e2e331635dace3b26464a6851e10f6895923c568582fbd"; +TvmSlice sign1 = "94ec60eb8d2b657dead5e1232b8f9cc0162467b08f02e252e97622297787a74b6496607036089837fe5b52244bbbb6d00d3d7cc43812688451229d9e96f704401db053956c588203ba7638e8882746c16e701557f34b0c08bbe097483aec161e"; +TvmSlice sign2 = "8cdbeadb3ee574a4f796f10d656885f143f454cc6a2d42cf8cabcd592d577c5108e4258a7b14f0aafe6c86927b3e70030432a2e5aafa97ee1587bbdd8b69af044734defcf3c391515ab26616e15f5825b4b022a7df7b44f65a8792c54762e579"; +TvmSlice sign = bls.aggregate(sign0, sign1, sign2); +``` + +#### bls.fastAggregateVerify + +```TVMSolidity +(1) +bls.fastAggregateVerify(vector(TvmSlice) pubkeys, TvmSlice message, TvmSlice singature) returns (bool ok) +(2) +bls.fastAggregateVerify(TvmSlice pubkey0, TvmSlice pubkey1, ..., TvmSlice message, TvmSlice singature) returns (bool ok) +``` + +(1) Checks aggregated BLS signature for `pubkeys` and `message`. Returns `true` on success, `false` otherwise. Return `false` if `pubkeys.empty()`. + +(2) Same as (1) but takes `TvmSlice`'s. + +Example: + +```TVMSolidity +vector(TvmSlice) pubkeys; +pubkeys.push("a44184a47ad3fc0069cf7a95650a28af2ed715beab28651a7ff433e26c0fff714d21cc5657367bc563c6df28fb446d8f"); +pubkeys.push("832c0eca9f8cae87a1c6362838b34723cf63a1f69e366d64f3c61fc237217c4bea601cfbf4d6c18849ed4f9487b4a20c"); +pubkeys.push("9595aa3c5cb3d7c763fa6b52294ebde264bdf49748efbbe7737c35532db8fabc666bb0d186f329c8bdafddfbdcbc3ca6"); +TvmSlice message = TvmSlice(bytes("Hello, BLS fast aggregate and verify!")); +TvmSlice singature = "8420b1944c64f74dd67dc9f5ab210bab928e2edd4ce7e40c6ec3f5422c99322a5a8f3a8527eb31366c9a74752d1dce340d5a98fbc7a04738c956e74e7ba77b278cbc52afc63460c127998aae5aa1c3c49e8c48c30cc92451a0a275a47f219602"; +bool ok = bls.fastAggregateVerify(pubkeys, message, singature); +``` + +```TVMSolidity +TvmSlice pk0 = "a44184a47ad3fc0069cf7a95650a28af2ed715beab28651a7ff433e26c0fff714d21cc5657367bc563c6df28fb446d8f"; +TvmSlice pk1 = "832c0eca9f8cae87a1c6362838b34723cf63a1f69e366d64f3c61fc237217c4bea601cfbf4d6c18849ed4f9487b4a20c"; +TvmSlice pk2 = "9595aa3c5cb3d7c763fa6b52294ebde264bdf49748efbbe7737c35532db8fabc666bb0d186f329c8bdafddfbdcbc3ca6"; +TvmSlice message = TvmSlice(bytes("Hello, BLS fast aggregate and verify!")); +TvmSlice singature = "8420b1944c64f74dd67dc9f5ab210bab928e2edd4ce7e40c6ec3f5422c99322a5a8f3a8527eb31366c9a74752d1dce340d5a98fbc7a04738c956e74e7ba77b278cbc52afc63460c127998aae5aa1c3c49e8c48c30cc92451a0a275a47f219602"; +bool ok = bls.fastAggregateVerify(pk0, pk1, pk2, message, singature); +``` + +#### bls.aggregateVerify() + +```TVMSolidity +(1) +bls.aggregateVerify(vector(TvmSlice, TvmSlice) pubkeysMessages, TvmSlice singature) returns (bool ok) +(2) +bls.aggregateVerify(TvmSlice pubkey0, TvmSlice pubkey1, ..., TvmSlice message0, TvmSlice message1, ..., TvmSlice singature) returns (bool ok) +``` + +(1) Checks aggregated BLS signature for key-message pairs `pubkeysMessages`. Returns `true` on success, `false` otherwise. Returns `false` if `pubkeysMessages.empty()`. + +(2) Same as (1) but takes `TvmSlice`'s. + +```TVMSolidity +vector(TvmSlice, TvmSlice) pubkeysMessages; +TvmSlice pubkey0 = "b75f0360095de73c4790f803153ded0f3e6aefa6f0aac8bfd344a44a3de361e3f6f111c0cf0ad0c4a0861492f9f1aeb1"; +TvmSlice message0 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 0!")); +pubkeysMessages.push(pubkey0, message0); +TvmSlice pubkey1 = "a31e12bb4ffa75aabbae8ec2367015ba3fc749ac3826539e7d0665c285397d02b48414a23f8b33ecccc750b3afffacf6"; +TvmSlice message1 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 1!")); +pubkeysMessages.push(pubkey1, message1); +TvmSlice pubkey2 = "8de5f18ca5938efa896fbc4894c6044cdf89e778bf88584be48d6a6235c504cd45a44a68620f763aea043b6381add1f7"; +TvmSlice message2 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 2!")); +pubkeysMessages.push(pubkey2, message2); +TvmSlice singature = "8b8238896dfe3b02dc463c6e645e36fb78add51dc8ce32f40ecf60a418e92762856c3427b672be67278b5c4946b8c5a30fee60e5c38fdb644036a4f29ac9a039ed4e3b64cb7fef303052f33ac4391f95d482a27c8341246516a13cb72e58097b"; +bool ok = bls.aggregateVerify(pubkeysMessages, singature); +``` + +```TVMSolidity +TvmSlice pubkey0 = "b75f0360095de73c4790f803153ded0f3e6aefa6f0aac8bfd344a44a3de361e3f6f111c0cf0ad0c4a0861492f9f1aeb1"; +TvmSlice message0 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 0!")); +TvmSlice pubkey1 = "a31e12bb4ffa75aabbae8ec2367015ba3fc749ac3826539e7d0665c285397d02b48414a23f8b33ecccc750b3afffacf6"; +TvmSlice message1 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 1!")); +TvmSlice pubkey2 = "8de5f18ca5938efa896fbc4894c6044cdf89e778bf88584be48d6a6235c504cd45a44a68620f763aea043b6381add1f7"; +TvmSlice message2 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 2!")); +TvmSlice singature = "8b8238896dfe3b02dc463c6e645e36fb78add51dc8ce32f40ecf60a418e92762856c3427b672be67278b5c4946b8c5a30fee60e5c38fdb644036a4f29ac9a039ed4e3b64cb7fef303052f33ac4391f95d482a27c8341246516a13cb72e58097b"; +bool ok = bls.aggregateVerify(pubkey0, message0, pubkey1, message1, pubkey2, message2, singature); +``` + +#### bls.g1Zero() and bls.g2Zero() + +```TVMSolidity +bls.g1Zero() returns (TvmSlice) +bls.g2Zero() returns (TvmSlice) +``` + +Returns zero point in G1/G2. + +#### bls.g1IsZero() and bls.g2IsZero() + +```TVMSolidity +bls.g1Zero(TvmSlice x) returns (bool isZero) +bls.g2Zero(TvmSlice x) returns (bool isZero) +``` + +Checks that G1/G2 point `x` is equal to zero. + +#### bls.g1Add() and bls.g2Add() + +```TVMSolidity +bls.g1Add(TvmSlice a, TvmSlice b) returns (TvmSlice res) +bls.g2Add(TvmSlice a, TvmSlice b) returns (TvmSlice res) +``` + +Addition on G1/G2. + +#### bls.g1Sub() and bls.g2Sub() + +```TVMSolidity +bls.g1Sub(TvmSlice a, TvmSlice b) returns (TvmSlice res) +bls.g2Sub(TvmSlice a, TvmSlice b) returns (TvmSlice res) +``` + +Subtraction on G1/G2. + +#### bls.g1Neg() and bls.g2Neg() + +```TVMSolidity +bls.g1Neg(TvmSlice x) returns (TvmSlice res) +bls.g2Neg(TvmSlice x) returns (TvmSlice res) +``` + +Negation on G1/G2. + +#### bls.g1Mul() and bls.g2Mul() + +```TVMSolidity +bls.g1Mul(TvmSlice x, int s) returns (TvmSlice res) +bls.g2Mul(TvmSlice x, int s) returns (TvmSlice res) +``` + +Multiplies G1/G2 point `x` by scalar `s`. Any `s` is valid, including negative. + +#### bls.g1InGroup() and bls.g2InGroup() + +```TVMSolidity +bls.g1Mul(TvmSlice x) returns (bool ok) +bls.g2Mul(TvmSlice x) returns (bool ok) +``` + +Checks that slice `x` represents a valid element of G1/G2. + +#### bls.r() + +```TVMSolidity +bls.r() returns (uint255) +``` + +Pushes the order of G1 and G2 (approx. 2^255). It's 52435875175126190479447740508185965837690552500527637822603658699938581184513. + +#### bls.g1MultiExp() and bls.g2MultiExp() + +```TVMSolidity +(1) +bls.g1MultiExp(vector(TvmSlice, int) x_s) returns (TvmSlice) +bls.g2MultiExp(vector(TvmSlice, int) x_s) returns (TvmSlice) +(2) +bls.g1MultiExp(TvmSlice x0, int s0, TvmSlice x1, int s1, ...) returns (TvmSlice) +bls.g2MultiExp(TvmSlice x0, int s0, TvmSlice x1, int s1, ...) returns (TvmSlice) +``` + +(1) Calculates `x_1*s_1+...+x_n*s_n` for G1/G2 points `x_i` and scalars `s_i`. Returns zero point if `n==0`. Any `s_i` is valid, including negative. + +(2) Same as (1) but takes `TvmSlice`'s and `int`'s. + +```TVMSolidity +TvmSlice a = bls.mapToG1("7abd13983c76661a98659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92"); +TvmSlice b = bls.mapToG1("7abd13983c76661118659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92"); +TvmSlice c = bls.mapToG1("7abd13983c76661118659da83066c71bd658100020c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92"); +vector(TvmSlice, int) values; +values.push(a, 2); +values.push(b, 5); +values.push(c, 13537812947843); + +TvmSlice res = bls.g1MultiExp(values); + +TvmSlice aa = bls.g1Mul(a, 2); +TvmSlice bb = bls.g1Mul(b, 5); +TvmSlice cc = bls.g1Mul(c, 13537812947843); +TvmSlice res2 = bls.g1Add(bls.g1Add(aa, bb), cc); + +require(res == res2); +``` + +```TVMSolidity +TvmSlice a = bls.mapToG1("7abd13983c76661a98659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92"); +TvmSlice b = bls.mapToG1("7abd13983c76661118659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92"); +TvmSlice c = bls.mapToG1("7abd13983c76661118659da83066c71bd658100020c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92"); + +TvmSlice res = bls.g1MultiExp(a, 2, b, 5, c, 13537812947843); + +TvmSlice aa = bls.g1Mul(a, 2); +TvmSlice bb = bls.g1Mul(b, 5); +TvmSlice cc = bls.g1Mul(c, 13537812947843); +TvmSlice res2 = bls.g1Add(bls.g1Add(aa, bb), cc); + +require(res == res2); +``` + #### **math** namespace `T` is an integer, [variable integer](#varint-and-varuint), [qintN and quintN](#qintn-and-quintn) or fixed point type in the `math.*` functions where applicable. @@ -4501,7 +4816,7 @@ uint res = math.muldivc(3, 7, 2); // res == 11 ##### math.muldivmod() ```TVMSolidity -math.muldivmod(T a, T b, T c) returns (T /*result*/, T /*remainder*/); +math.muldivmod(T a, T b, T c) returns (T /*quotient*/, T /*remainder*/); ``` This instruction multiplies first two arguments, divides the result by third argument and returns @@ -4521,6 +4836,25 @@ int g = 2; (int h, int p) = math.muldivmod(e, f, g); // (h, p) == (-2, 1) ``` +##### math.mulmod() + +```TVMSolidity +math.mulmod(T a, T b, T c) returns (T /*remainder*/); +``` + +Same as [math.muldivmod()](#mathmuldivmod) but returns only remainder. Example: + +```TVMSolidity +uint constant P = 2**255 - 19; + +function f() public pure { + uint a = rnd.next(P); + uint b = rnd.next(P); + uint c = math.mulmod(a, b, P); + //... +} +``` + ##### math.sign() ```TVMSolidity @@ -4901,7 +5235,7 @@ uint16 dataDepth = data.depth(); uint256 hash = abi.stateInitHash(codeHash, dataHash, codeDepth, dataDepth); ``` -See also [internal doc](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/docs/internal/stateInit_hash.md) to read more about this +See also [internal doc](https://github.com/everx-labs/TVM-Solidity-Compiler/blob/master/docs/internal/stateInit_hash.md) to read more about this function mechanics. ##### abi.encodeBody() @@ -4961,7 +5295,7 @@ Loads parameters of the function or constructor (if contract type is provided). See example of how to use **onBounce** function: -* [onBounceHandler](https://github.com/tonlabs/samples/blob/master/solidity/16_onBounceHandler.sol) +* [onBounceHandler](https://github.com/everx-labs/samples/blob/master/solidity/16_onBounceHandler.sol) ##### abi.codeSalt() @@ -5018,7 +5352,7 @@ contract MyContract { See example of how to use this function: -* [onBounceHandler](https://github.com/tonlabs/samples/blob/master/solidity/16_onBounceHandler.sol) +* [onBounceHandler](https://github.com/everx-labs/samples/blob/master/solidity/16_onBounceHandler.sol) ##### abi.encodeIntMsg() @@ -5044,7 +5378,7 @@ described. See also: -* sample [22_sender.sol](https://github.com/tonlabs/samples/blob/master/solidity/22_sender.sol) +* sample [22_sender.sol](https://github.com/everx-labs/samples/blob/master/solidity/22_sender.sol) * [abi.encodeBody()](#abiencodebody) ### **gosh** namespace @@ -5140,7 +5474,7 @@ of the current smart contract and destroys the current account. See example of how to use the `selfdestruct` function: -* [Kamikaze](https://github.com/tonlabs/samples/blob/master/solidity/8_Kamikaze.sol) +* [Kamikaze](https://github.com/everx-labs/samples/blob/master/solidity/8_Kamikaze.sol) #### sha256 @@ -5196,10 +5530,10 @@ Returns the remaining gas. ### TVM capabilities Rust implementation of TVM has capabilities. Capabilities are flags that can be set to turn on -some features or behavior of TVM. Full list of capabilities can be found in `enum GlobalCapabilities` in [ever-block](https://github.com/tonlabs/ever-block/blob/master/src/config_params.rs) repo. +some features or behavior of TVM. Full list of capabilities can be found in `enum GlobalCapabilities` in [ever-block](https://github.com/everx-labs/ever-block/blob/master/src/config_params.rs) repo. Set capabilities store in 8th parameter of the global config of the blockchain. To get it you can use command: ```bash -tonos-cli --json getconfig 8 +ever-cli --json getconfig 8 ``` ### TVM exception codes diff --git a/Cargo.lock b/Cargo.lock index f64033c4..42f54740 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aes-ctr" version = "0.6.0" @@ -75,52 +60,59 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "assert_cmd" version = "2.0.14" @@ -149,33 +141,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" - -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.10.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base64" @@ -189,15 +157,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -209,9 +168,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" dependencies = [ "cc", "glob", @@ -244,9 +203,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.94" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" [[package]] name = "cfg-if" @@ -279,9 +238,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -289,9 +248,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -301,21 +260,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.60", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "cmake" @@ -328,9 +287,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "const-oid" @@ -389,29 +348,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest 0.10.7", + "digest", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -425,7 +370,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn", ] [[package]] @@ -444,22 +389,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "crypto-common", ] @@ -475,15 +411,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature 1.6.4", -] - [[package]] name = "ed25519" version = "2.2.3" @@ -491,65 +418,110 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", - "signature 2.2.0", + "signature", ] [[package]] name = "ed25519-dalek" -version = "1.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 3.2.0", - "ed25519 1.5.3", - "rand 0.7.3", + "curve25519-dalek", + "ed25519", + "rand_core", "serde", - "sha2 0.9.9", + "sha2", + "subtle", "zeroize", ] [[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +name = "ever_abi" +version = "2.6.1" +source = "git+https://github.com/everx-labs/ever-abi.git?tag=2.6.1#9b789948eb3d9be16b080c7c29453cce0be25f15" dependencies = [ - "curve25519-dalek 4.1.2", - "ed25519 2.2.3", - "rand_core 0.6.4", + "anyhow", + "byteorder", + "chrono", + "ever_block", + "hex 0.3.2", + "num-bigint", + "num-traits", "serde", - "sha2 0.10.8", - "subtle", - "zeroize", + "serde_derive", + "serde_json", + "thiserror", ] [[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +name = "ever_assembler" +version = "1.6.2" +source = "git+https://github.com/everx-labs/ever-assembler.git?tag=1.6.2#82ef6cc5749908e66886a250ecb4b55f4785fb69" dependencies = [ - "backtrace", - "failure_derive", + "anyhow", + "clap", + "ever_block", + "ever_vm", + "hex 0.4.3", + "log", + "num", + "num-traits", + "serde", + "serde_json", + "thiserror", ] [[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +name = "ever_block" +version = "1.11.0" +source = "git+https://github.com/everx-labs/ever-block.git?tag=1.11.0#c4d06d5e033897134aac6b54ee077b87775a35b4" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", + "aes-ctr", + "anyhow", + "base64", + "blst", + "crc", + "curve25519-dalek", + "ed25519", + "ed25519-dalek", + "getrandom", + "hex 0.4.3", + "lazy_static", + "lockfree", + "log", + "num", + "num-derive", + "num-traits", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror", + "x25519-dalek", +] + +[[package]] +name = "ever_vm" +version = "2.2.1" +source = "git+https://github.com/everx-labs/ever-vm.git?tag=2.2.1#280daf4d22a04e108881951e32926ed925be86e5" +dependencies = [ + "anyhow", + "ever_block", + "hex 0.4.3", + "lazy_static", + "log", + "num", + "num-traits", + "thiserror", ] [[package]] name = "fiat-crypto" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "float-cmp" @@ -572,32 +544,17 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - [[package]] name = "glob" version = "0.3.1" @@ -660,6 +617,12 @@ dependencies = [ "cc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itoa" version = "1.0.11" @@ -677,44 +640,35 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lockfree" version = "0.5.1" -source = "git+https://github.com/tonlabs/lockfree.git#bfcb66587dc4ffed9e8e9248995ad2fe8dc3669e" +source = "git+https://github.com/everx-labs/lockfree.git#bfcb66587dc4ffed9e8e9248995ad2fe8dc3669e" dependencies = [ "owned-alloc", ] [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" - -[[package]] -name = "miniz_oxide" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "normalize-line-endings" @@ -724,9 +678,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -738,33 +692,32 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] [[package]] name = "num-derive" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -778,9 +731,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -789,11 +742,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -801,9 +753,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -818,15 +770,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -855,12 +798,6 @@ dependencies = [ "spki", ] -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -899,9 +836,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -915,19 +852,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -935,18 +859,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -956,16 +870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -974,23 +879,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1000,9 +896,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1011,15 +907,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc_version" @@ -1032,60 +922,47 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", "serde", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.8" @@ -1094,22 +971,16 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - [[package]] name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1120,23 +991,22 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "sold" -version = "0.74.0" +version = "0.75.0" dependencies = [ + "anyhow", "assert_cmd", "atty", "clap", "cmake", "dunce", - "failure", + "ever_abi", + "ever_assembler", + "ever_block", "once_cell", "predicates", "serde", "serde_json", "strip-ansi-escapes", - "ton_abi", - "ton_block", - "ton_labs_assembler", - "ton_types", ] [[package]] @@ -1166,15 +1036,15 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -1182,34 +1052,31 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.60" +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "thiserror" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "thiserror-impl", ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "thiserror-impl" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "unicode-xid", + "syn", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "threadpool" version = "1.8.1" @@ -1219,103 +1086,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "ton_abi" -version = "2.4.25" -source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.4.25#860adc6d485c18782fb7e1c12aa4e4a660e9b095" -dependencies = [ - "base64 0.10.1", - "byteorder", - "chrono", - "failure", - "hex 0.3.2", - "num-bigint", - "num-traits", - "serde", - "serde_derive", - "serde_json", - "sha2 0.10.8", - "ton_block", - "ton_types", -] - -[[package]] -name = "ton_block" -version = "1.9.141" -source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.141#c4b2b6a1b8469a52da1276231d93f9f6c11f77bc" -dependencies = [ - "failure", - "hex 0.4.3", - "log", - "num", - "num-traits", - "sha2 0.10.8", - "ton_types", -] - -[[package]] -name = "ton_labs_assembler" -version = "1.4.52" -source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.4.52#5c9e46a58d2d0589ca95ab54ab85380ded112173" -dependencies = [ - "clap", - "failure", - "hex 0.4.3", - "log", - "num", - "num-traits", - "serde", - "serde_json", - "ton_types", - "ton_vm", -] - -[[package]] -name = "ton_types" -version = "2.0.39" -source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.39#8032c3f19c0e9eaea150340af6ebaf1a33d822ae" -dependencies = [ - "aes-ctr", - "base64 0.13.1", - "blst", - "crc", - "curve25519-dalek 4.1.2", - "ed25519 2.2.3", - "ed25519-dalek 2.1.1", - "failure", - "hex 0.4.3", - "lazy_static", - "lockfree", - "log", - "num", - "num-derive", - "num-traits", - "rand 0.8.5", - "serde", - "serde_json", - "sha2 0.10.8", - "smallvec", - "x25519-dalek", -] - -[[package]] -name = "ton_vm" -version = "1.9.22" -source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.9.22#ca670283e11119f9da253660e056e41b825eedc3" -dependencies = [ - "ed25519 1.5.3", - "ed25519-dalek 1.0.1", - "failure", - "hex 0.4.3", - "lazy_static", - "log", - "num", - "num-traits", - "rand 0.7.3", - "ton_block", - "ton_types", -] - [[package]] name = "typenum" version = "1.17.0" @@ -1328,17 +1098,11 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" @@ -1358,9 +1122,9 @@ dependencies = [ [[package]] name = "vte_generate_state_changes" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ "proc-macro2", "quote", @@ -1375,12 +1139,6 @@ dependencies = [ "libc", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1408,7 +1166,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn", "wasm-bindgen-shared", ] @@ -1430,7 +1188,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1551,17 +1309,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 4.1.2", - "rand_core 0.6.4", + "curve25519-dalek", + "rand_core", "serde", "zeroize", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -1574,5 +1332,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn", ] diff --git a/Changelog.md b/Changelog.md index edcb113f..881ce9c0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,31 @@ +### 0.75.0 (2024-06-04) + +Breaking changes: + * If the contract does not have constructor explicitly and does not have state variables with initialisation, then in `*.abi.json` file there is no `constructor` function and no `_constructorFlag` field. See [Deploy the contract with no constructor](API.md#deploy-the-contract-with-no-constructor) for more details. + +Bugfixes: + * Peephole optimizer generated invalid code. See [issue #153](https://github.com/everx-labs/TVM-Solidity-Compiler/issues/153). + * Fixed segmentation fault that occurred in some cases of using incorrect types for `math.*` and some another functions. + * Fixed minor bugs in TypeChecker (using bad type for mapping value). + +Compiler features: + * Supported `stateInit`option for [External function calls](./API.md#external-function-calls). + * Supported [math.mulmod()](./API.md#mathmulmod). + * Supported functions to work with `TvmSlice`'s: + * [\.bitEmpty() and \.refEmpty()](./API.md#tvmsliceempty-tvmslicebitempty-and-tvmslicerefempty) + * [\.startsWith()](./API.md#tvmslicestartswith) + * [\.startsWithOne()](./API.md#tvmslicestartswithone) + * Supported [operations on a pairing friendly BLS12-381 curve](./API.md#bls-namespace). + * Supported conversion string literals to `TvmSlice`, e.g. `TvmSlice s = "0189abef_";`. + +Usability: + * Print function-candidates on compilation failure (for function overloading). + +Optimizations: + * 14-bit numbers are used for function ids for private/internal functions for code optimizations instead of 32-bit. + 32-bit numbers are still used for `onCodeUpgrade` functions for back compatibility. + * peephole optimizations. + ### 0.74.0 (2024-04-11) Breaking changes: @@ -31,11 +59,11 @@ Other changes: Update compiler frontend (from original version 0.8.17 to 0.8.24). Full changelog can be found [here](./compiler/Changelog.md). Breaking changes: - * Supported [ABI 2.4](https://github.com/tonlabs/ever-abi/blob/master/CHANGELOG.md#version-240). + * Supported [ABI 2.4](https://github.com/everx-labs/ever-abi/blob/master/CHANGELOG.md#version-240). * Deleted `tvm.insertPubkey()` function. * Deleted `address.makeAddrNone()` function. Use [address.addrNone](./API.md#addressaddrnone). * Default value for `address` type was `address(0)` but now it is [address.addrNone](./API.md#addressaddrnone). - * `byteN` in `*abi.json` file marked as `fixedbytesN`. See [ABI.md](https://github.com/tonlabs/ever-abi/blob/master/docs/ABI.md#fixedbytesn). + * `byteN` in `*abi.json` file marked as `fixedbytesN`. See [ABI.md](https://github.com/everx-labs/ever-abi/blob/master/docs/ABI.md#fixedbytesn). * [abi.encodeStateInit()](./API.md#abiencodestateinit), [abi.encodeData()](./API.md#abiencodedata) functions and [Deploy via new](./API.md#deploy-via-new) generate contract's data in the new format. To deploy old contracts (with version < 0.72.0) from new ones (with version >= 0.72.0) use [abi.encodeOldDataInit()](./API.md#abiencodeolddatainit). * [format()](API.md#format) no longer throws an exception if formatted value exceeds the specified width. E.g. `format("{:1d}", 10) == "10"` now is correct, no exception. * Functions and constructions previously working with `uint128` value, now using `varUint16`: @@ -81,7 +109,7 @@ Other changes: ### 0.72.0 (2023-10-31) -Use [sold](https://github.com/tonlabs/TVM-Solidity-Compiler/tree/master/sold) to compile contracts. If you used `solc`+`tvm_linker`, then use `solc`+[asm](https://github.com/tonlabs/ever-assembler). Generated `*.code` files have some another format. +Use [sold](https://github.com/everx-labs/TVM-Solidity-Compiler/tree/master/sold) to compile contracts. If you used `solc`+`tvm_linker`, then use `solc`+[asm](https://github.com/everx-labs/ever-assembler). Generated `*.code` files have some another format. Breaking changes: * The conversion for integer type is only allowed when there is at most one change in sign, width or type-category (`int`, `address`, `bytesNN`, etc.). To perform multiple changes, use multiple conversions. See [Solidity v0.8.0 Breaking Changes](https://docs.soliditylang.org/en/v0.8.17/080-breaking-changes.html#new-restrictions). For example, to convert `int8 x;` to `uint` you can use at least two ways: 1) `uint(uint8(x))`, 2) `uint(int(x))`. @@ -268,10 +296,10 @@ Compiler features: ### 0.64.0 (2022-08-18) -Fixed build [sold](https://github.com/tonlabs/TVM-Solidity-Compiler/tree/master/sold) for Windows and macOS. +Fixed build [sold](https://github.com/everx-labs/TVM-Solidity-Compiler/tree/master/sold) for Windows and macOS. Compiler features: - * Supported [ABI v2.3](https://github.com/tonlabs/ton-labs-abi/blob/master/docs/ABI_2.3_spec.md). + * Supported [ABI v2.3](https://github.com/everx-labs/ever-abi/blob/master/docs/ABI_2.3_spec.md). * Supported try-catch (experimental feature). * Supported type `variant`. @@ -513,7 +541,7 @@ Compiler features: * Added support of `bytes` to `bytesN` conversion. Support ABI 2.1: - * Field `fields` appeared in abi file. You can parse state variable. See `tonos-cli decode account --help`. + * Field `fields` appeared in abi file. You can parse state variable. See `ever-cli decode account --help`. * Supported `string` type in abi. Bugfixes: diff --git a/README.md b/README.md index 644b0c6a..a4098ce8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # The TVM Solidity compiler -[![GitHub](https://img.shields.io/github/license/tonlabs/TVM-Solidity-Compiler?style=for-the-badge)](./LICENSE) +[![GitHub](https://img.shields.io/github/license/everx-labs/TVM-Solidity-Compiler?style=for-the-badge)](./LICENSE) [![Everscale](https://custom-icon-badges.demolab.com/badge/-everscale-13173e?style=for-the-badge&logoColor=yellow&logo=everscale)](https://everscale.network/) @@ -12,13 +12,13 @@ Port of the Solidity smart-contract [compiler](https://github.com/ethereum/solid ## TVM Solidity API reference -[API documentation is here](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/API.md) +[API documentation is here](https://github.com/everx-labs/TVM-Solidity-Compiler/blob/master/API.md) ## Build and Install ### Sold driver -We recommend using `sold` to compile smart-contracts. Documentation is available at [README.md](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/sold/README.md). +We recommend using `sold` to compile smart-contracts. Documentation is available at [README.md](https://github.com/everx-labs/TVM-Solidity-Compiler/blob/master/sold/README.md). ### Building compiler @@ -27,7 +27,7 @@ Original Instructions about how to build and install the Solidity compiler can b #### Ubuntu Linux ```shell -git clone https://github.com/tonlabs/TVM-Solidity-Compiler +git clone https://github.com/everx-labs/TVM-Solidity-Compiler cd TVM-Solidity-Compiler sh ./compiler/scripts/install_deps.sh mkdir build @@ -42,7 +42,7 @@ Install Visual Studio Build Tools 2019, Git bash, cmake. Run Developer PowerShell for VS 2019 ```shell -git clone https://github.com/tonlabs/TVM-Solidity-Compiler +git clone https://github.com/everx-labs/TVM-Solidity-Compiler cd TVM-Solidity-Compiler compiler\scripts\install_deps.ps1 mkdir build @@ -53,11 +53,11 @@ cmake --build . --config Release -- /m ## Links - * [Ever assembler and disassembler](https://github.com/tonlabs/ever-assembler) - * [Code samples](https://github.com/tonlabs/samples/tree/master/solidity) in TVM Solidity - * [tonos-cli](https://github.com/tonlabs/tonos-cli) command line interface for TVM compatible blockchains - * Example of usage `tonos-cli` for working (deploying, calling etc.) with TVM compatible blockchains can be found there: [Write smart contract in Solidity](https://docs.ton.dev/86757ecb2/p/950f8a-write-smart-contract-in-solidity) - * [Changelog](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/Changelog_TON.md) + * [Ever assembler and disassembler](https://github.com/everx-labs/ever-assembler) + * [Code samples](https://github.com/everx-labs/samples/tree/master/solidity) in TVM Solidity + * [ever-cli](https://github.com/everx-labs/ever-cli) command line interface for TVM compatible blockchains + * Example of usage `ever-cli` for working (deploying, calling etc.) with TVM compatible blockchains can be found there: [Write smart contract in Solidity](https://docs.ton.dev/86757ecb2/p/950f8a-write-smart-contract-in-solidity) + * [Changelog](https://github.com/everx-labs/TVM-Solidity-Compiler/blob/master/Changelog_TON.md) ## License [GNU GENERAL PUBLIC LICENSE Version 3](./LICENSE) diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index 3bd64b9e..f39776eb 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.74.0") +set(PROJECT_VERSION "0.75.0") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp b/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp index d6703235..0cd3ef2a 100644 --- a/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -281,11 +281,10 @@ void DeclarationTypeChecker::endVisit(Optional const& _optional) types.emplace_back(c->annotation().type); } - if (comp.size() == 1) { + if (comp.size() == 1) _optional.annotation().type = TypeProvider::optional(types.at(0)); - } else { + else _optional.annotation().type = TypeProvider::optional(TypeProvider::tuple(types)); - } } void DeclarationTypeChecker::endVisit(TvmVector const& _tvmVector) @@ -293,8 +292,16 @@ void DeclarationTypeChecker::endVisit(TvmVector const& _tvmVector) if (_tvmVector.annotation().type) return; - TypeName const& type = _tvmVector.type(); - _tvmVector.annotation().type = TypeProvider::tvmVector(type.annotation().type); + std::vector> const& comp = _tvmVector.types(); + std::vector types; + for (const ASTPointer& c : comp) { + types.emplace_back(c->annotation().type); + } + + if (comp.size() == 1) + _tvmVector.annotation().type = TypeProvider::tvmVector(types.at(0)); + else + _tvmVector.annotation().type = TypeProvider::tvmVector(TypeProvider::tuple(types)); } void DeclarationTypeChecker::endVisit(TvmStack const& _tvmStack) diff --git a/compiler/libsolidity/analysis/GlobalContext.cpp b/compiler/libsolidity/analysis/GlobalContext.cpp index 1976525e..9f4e1606 100644 --- a/compiler/libsolidity/analysis/GlobalContext.cpp +++ b/compiler/libsolidity/analysis/GlobalContext.cpp @@ -44,12 +44,7 @@ int magicVariableToID(std::string const& _name) else if (_name == "blockhash") return -5; else if (_name == "ecrecover") return -6; else if (_name == "gasleft") return -7; - else if (_name == "format") return -105; else if (_name == "keccak256") return -8; - else if (_name == "logtvm") return -102; - else if (_name == "math") return -103; - else if (_name == "rnd") return -105; - else if (_name == "gosh") return -107; else if (_name == "msg") return -15; else if (_name == "mulmod") return -16; else if (_name == "now") return -17; @@ -59,9 +54,7 @@ int magicVariableToID(std::string const& _name) else if (_name == "selfdestruct") return -21; else if (_name == "sha256") return -22; else if (_name == "sha3") return -23; - else if (_name == "stoi") return -106; else if (_name == "super") return -25; - else if (_name == "tvm") return -101; else if (_name == "tx") return -26; else if (_name == "type") return -27; else if (_name == "this") return -28; @@ -70,6 +63,14 @@ int magicVariableToID(std::string const& _name) else if (_name == "valueToGas") return -61; else if (_name == "bitSize") return -62; else if (_name == "uBitSize") return -63; + else if (_name == "tvm") return -101; + else if (_name == "logtvm") return -102; + else if (_name == "math") return -103; + else if (_name == "format") return -104; + else if (_name == "rnd") return -105; + else if (_name == "stoi") return -106; + else if (_name == "gosh") return -107; + else if (_name == "bls") return -108; else solAssert(false, "Unknown magic variable: \"" + _name + "\"."); } @@ -107,6 +108,7 @@ inline std::vector> constructMag FunctionType::Kind::Stoi, StateMutability::Pure) ), + magicVarDecl("bls", TypeProvider::magic(MagicType::Kind::BLS)), magicVarDecl("tvm", TypeProvider::magic(MagicType::Kind::TVM)), magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)), // Accepts a MagicType that can be any contract type or an Integer type and returns a diff --git a/compiler/libsolidity/analysis/TypeChecker.cpp b/compiler/libsolidity/analysis/TypeChecker.cpp index 55adc553..5ba005a6 100644 --- a/compiler/libsolidity/analysis/TypeChecker.cpp +++ b/compiler/libsolidity/analysis/TypeChecker.cpp @@ -1324,7 +1324,7 @@ void TypeChecker::endVisit(Return const& _return) _return.options().at(i)->location(), "Unknown call option \"" + name + - R"(". Valid options are "value", "currencies", "bounce", and "flag".)" + R"(". Possible options: "value", "currencies", "bounce", and "flag".)" ); } else { Type const* expType = nameToType.at(name); @@ -2493,19 +2493,15 @@ void TypeChecker::typeCheckABIEncodeFunctions( FunctionDefinition const* TypeChecker::getFunctionDefinition(Expression const* expr) { - if (expr->annotation().type->category() != Type::Category::Function) { + if (expr->annotation().type->category() != Type::Category::Function) return nullptr; - } + auto identifier = dynamic_cast(expr); Declaration const* declaration{}; if (identifier) declaration = identifier->annotation().referencedDeclaration; - else if (auto member = dynamic_cast(expr)) { + else if (auto member = dynamic_cast(expr)) declaration = member->annotation().referencedDeclaration; - } - if (declaration == nullptr) { - return nullptr; - } return dynamic_cast(declaration); } @@ -3086,11 +3082,12 @@ void TypeChecker::typeCheckFunctionGeneralChecks( }; const bool isNewExpression = dynamic_cast(&functionCallOpt->expression()) != nullptr; - if (isNewExpression) { - arr = {"stateInit", "code", "pubkey", "varInit", "splitDepth", "wid", "value", "currencies", "bounce", "flag"}; - } else { - arr = {"value", "currencies", "bounce", "flag", "callback"}; - } + if (isNewExpression) + arr = {"code", "pubkey", "varInit", "splitDepth", "wid"}; + else + arr = {"callback"}; + for (auto const x : {"stateInit", "value", "currencies", "bounce", "flag"}) + arr.emplace_back(x); auto names = functionCallOpt->names(); const std::vector> &options = functionCallOpt->options(); @@ -3101,7 +3098,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( 4187_error, functionCallOpt->location(), "Unknown option \"" + name + "\". " + - "Valid options are " + fold() + "." + "Possible options: " + fold() + "." ); } if (name == "pubkey") @@ -3497,14 +3494,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (const auto & arg : arguments) { auto t = arg->annotation().type; - Type::Category cat = t->mobileType()->category(); - if (cat != Type::Category::Integer && cat != Type::Category::VarInteger) { + auto printError = [&](){ m_errorReporter.fatalTypeError( 5329_error, arg->location(), "Expected an integer or variable integer type." ); - } + }; + if (auto mobileType = t->mobileType()) { + Type::Category cat = mobileType->category(); + if (cat != Type::Category::Integer && cat != Type::Category::VarInteger) + printError(); + } else + printError(); } }; @@ -3524,14 +3526,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (const auto & arg : arguments) { auto t = arg->annotation().type; - Type::Category cat = t->mobileType()->category(); - if (cat != Type::Category::Integer && cat != Type::Category::VarInteger && cat != Type::Category::QInteger) { + auto printError = [&](){ m_errorReporter.fatalTypeError( 5640_error, arg->location(), "Expected an integer, qinteger or variable integer type." ); - } + }; + if (auto mobileType = t->mobileType()) { + Type::Category cat = mobileType->category(); + if (cat != Type::Category::Integer && cat != Type::Category::VarInteger && cat != Type::Category::QInteger) + printError(); + } else + printError(); } }; @@ -3549,17 +3556,23 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ); for (const auto & arg : arguments) { - Type::Category cat = arg->annotation().type->mobileType()->category(); - if (cat != Type::Category::Integer && - cat != Type::Category::QInteger && - cat != Type::Category::FixedPoint && - cat != Type::Category::VarInteger - ) + auto printError = [&](){ m_errorReporter.fatalTypeError( 5943_error, arg->location(), "Expected integer, qinteger, variable integer or fixed point type." ); + }; + if (auto mobileType = arg->annotation().type->mobileType()) { + Type::Category cat = mobileType->category(); + if (cat != Type::Category::Integer && + cat != Type::Category::QInteger && + cat != Type::Category::FixedPoint && + cat != Type::Category::VarInteger + ) + printError(); + } else + printError(); } }; @@ -3640,10 +3653,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (size_t i = 0; i < paramTypes.size(); ++i) { const Type *givenType = arguments.at(i)->annotation().type; const Type *expType = paramTypes.at(i); - if (!givenType->isImplicitlyConvertibleTo(*expType)) { + if (!givenType->isImplicitlyConvertibleTo(*expType)) m_errorReporter.typeError(1580_error, arguments.at(i)->location(), "Expected " + expType->canonicalName() + " type, but given " + givenType->canonicalName()); - } } }; @@ -3764,58 +3776,48 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } case FunctionType::Kind::ABIDecodeData: - { - if (arguments.size() != 2) { - m_errorReporter.fatalTypeError( - 3017_error, - _functionCall.location(), - std::string("Expected two arguments.") - ); - } - ContractType const* ct = getContractType(arguments.front().get()); - if (ct == nullptr) { - m_errorReporter.fatalTypeError( - 8880_error, - arguments.front()->location(), - "Expected contract type." - ); - } - - std::vector stateVars = ::stateVariables(&ct->contractDefinition(), false); - returnTypes.push_back(TypeProvider::uint256()); // pubkey - returnTypes.push_back(TypeProvider::uint(64)); // timestamp - returnTypes.push_back(TypeProvider::boolean()); // constructor flag - for (VariableDeclaration const * v : stateVars) { - returnTypes.push_back(v->type()); - } - paramTypes.emplace_back(arguments.at(0)->annotation().type); - paramTypes.emplace_back(arguments.at(1)->annotation().type); - break; - } case FunctionType::Kind::TVMSliceLoadStateVars: { - if (arguments.size() != 1) { - m_errorReporter.fatalTypeError( + if (functionType->kind() == FunctionType::Kind::ABIDecodeData) + { + if (arguments.size() != 2) + m_errorReporter.fatalTypeError( + 3017_error, + _functionCall.location(), + std::string("Expected two arguments.") + ); + } + else if (functionType->kind() == FunctionType::Kind::TVMSliceLoadStateVars) + { + if (arguments.size() != 1) + m_errorReporter.fatalTypeError( 5457_error, _functionCall.location(), std::string("Expected one argument.") - ); + ); } + else + solUnimplemented(""); + ContractType const* ct = getContractType(arguments.front().get()); - if (ct == nullptr) { + if (ct == nullptr) m_errorReporter.fatalTypeError( - 7161_error, - arguments.front()->location(), // TODO move - "Expected contract type." + 8880_error, + arguments.front()->location(), + "Expected contract type." ); - } std::vector stateVars = ::stateVariables(&ct->contractDefinition(), false); returnTypes.push_back(TypeProvider::uint256()); // pubkey returnTypes.push_back(TypeProvider::uint(64)); // timestamp - returnTypes.push_back(TypeProvider::boolean()); // constructor flag - for (VariableDeclaration const * v : stateVars) { + if (::hasConstructor(ct->contractDefinition())) + returnTypes.push_back(TypeProvider::boolean()); // constructor flag + for (VariableDeclaration const * v : stateVars) returnTypes.push_back(v->type()); + if (functionType->kind() == FunctionType::Kind::ABIDecodeData) + { + paramTypes.emplace_back(arguments.at(0)->annotation().type); + paramTypes.emplace_back(arguments.at(1)->annotation().type); } break; } @@ -3825,7 +3827,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (arguments.empty()) { returnTypes.push_back(TypeProvider::uint256()); } else { - Type const* result = arguments.at(0)->annotation().type->mobileType(); + Type const* result = arguments.at(0)->annotation().type->mobileType(); // != null checked paramTypes.push_back(result); returnTypes.push_back(result); } @@ -3870,6 +3872,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } case FunctionType::Kind::MathMulDiv: + case FunctionType::Kind::MathMulMod: case FunctionType::Kind::MathMulDivMod: { checkArgNumAndIsIntOrQintOrVarInt(arguments, 3, std::equal_to<>(), "Expected three arguments."); @@ -3886,7 +3889,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::MathAbs: { checkArgQtyAndIsIntOrQIntOrVarIntOrFixedPoint(arguments, 1, std::equal_to<>(), "Expected one argument."); - Type const* argType = arguments.at(0)->annotation().type->mobileType(); + Type const* argType = arguments.at(0)->annotation().type->mobileType(); // != null checked Type::Category cat = argType->category(); paramTypes.push_back(argType); @@ -3946,14 +3949,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ); } - returnTypes.push_back(arguments.at(0)->annotation().type->mobileType()); + returnTypes.push_back(arguments.at(0)->annotation().type->mobileType()); // != null checked break; } case FunctionType::Kind::MathSign: { checkArgNumAndIsIntOrQintOrVarInt(arguments, 1, std::equal_to<>(), "Expected one argument."); Type const* argType = arguments.at(0)->annotation().type; - paramTypes.emplace_back(argType->mobileType()); + paramTypes.emplace_back(argType->mobileType()); // != null checked if (argType->category() == Type::Category::QInteger) returnTypes.emplace_back(TypeProvider::qInteger(2, IntegerType::Modifier::Signed)); else @@ -4177,15 +4180,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } case FunctionType::Kind::SHA256: { - if (arguments.size() != 1) { + if (arguments.size() != 1) m_errorReporter.fatalTypeError(3449_error, _functionCall.location(), "Expected one argument."); - } Type const* argType = arguments.at(0)->annotation().type->mobileType(); auto arrayType = dynamic_cast(argType); - if (!((arrayType && arrayType->isByteArrayOrString()) || dynamic_cast(argType))) { + if (!((arrayType && arrayType->isByteArrayOrString()) || dynamic_cast(argType))) m_errorReporter.fatalTypeError(7972_error, arguments.at(0)->location(), "Expected bytes, string or TvmSlice type."); - } paramTypes.push_back(argType); returnTypes.emplace_back(TypeProvider::uint256()); break; @@ -4202,56 +4203,58 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) paramTypes.push_back(TypeProvider::uint(16)); } if (arguments.size() >= 3) { - paramTypes.push_back(arguments.at(2)->annotation().type->mobileType()); - } - if (arguments.size() >= 4) { + auto mobile = arguments.at(2)->annotation().type->mobileType(); + if (mobile == nullptr) + m_errorReporter.fatalTypeError(6263_error, arguments.at(2)->location(), "Unexpected type."); + paramTypes.push_back(mobile); + } + if (arguments.size() >= 4) m_errorReporter.typeError(7843_error, _functionCall.location(), "Expected at most 3 arguments."); - } checkArgConversion(); break; } case FunctionType::Kind::Revert: { - if (!arguments.empty()) { + if (!arguments.empty()) paramTypes.push_back(TypeProvider::uint(16)); - } if (arguments.size() >= 2) { - paramTypes.push_back(arguments.at(1)->annotation().type->mobileType()); - } - if (arguments.size() >= 3) { + auto mobile = arguments.at(1)->annotation().type->mobileType(); + if (mobile == nullptr) + m_errorReporter.fatalTypeError(2671_error, arguments.at(1)->location(), "Unexpected type."); + paramTypes.push_back(mobile); + } + if (arguments.size() >= 3) m_errorReporter.typeError(1683_error, _functionCall.location(), "Expected at most 2 arguments."); - } checkArgConversion(); break; } case FunctionType::Kind::TVMDump: { - if (arguments.size() != 1) { + if (arguments.size() != 1) m_errorReporter.typeError(3797_error, _functionCall.location(), "Expected one argument."); - } auto type = arguments[0]->annotation().type->mobileType(); - auto cat = type->category(); - if (cat != Type::Category::Integer && cat !=Type::Category::TvmCell) { + if (!type || (type->category() != Type::Category::Integer && type->category() !=Type::Category::TvmCell)) m_errorReporter.fatalTypeError( 8093_error, arguments[0]->location(), "Argument must have a TvmCell or integer type." ); - } paramTypes.push_back(type); break; } case FunctionType::Kind::TVMHash: { if (arguments.size() != 1) m_errorReporter.typeError(1218_error, _functionCall.location(), "Expected one argument."); - auto type = arguments[0]->annotation().type->mobileType(); - auto cat = type->category(); - if (!isByteArrayOrString(type) && cat != Type::Category::TvmCell && cat != Type::Category::TvmSlice) { + auto mobileType = arguments[0]->annotation().type->mobileType(); + if (!mobileType || (!isByteArrayOrString(mobileType) && + mobileType->category() != Type::Category::TvmCell && + mobileType->category() != Type::Category::TvmSlice) + ) m_errorReporter.fatalTypeError( 5802_error, arguments[0]->location(), - "Expected string, bytes, TvmCell or TvmSlice types, but got " + type->toString() + " type." + "Expected string, bytes, TvmCell or TvmSlice types, but got " + + arguments[0]->annotation().type->toString() + " type." ); - } - paramTypes.push_back(type); + paramTypes.push_back(mobileType); returnTypes.push_back(TypeProvider::uint256()); break; } @@ -4436,7 +4439,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) 7867_error, _functionCallOptions.location(), "Unknown option \"" + name + "\". " + - "Valid options are " + fold() + "." + "Possible options: " + fold() + "." ); } else if (name == "pubkey") { setCheckOption(setPubkey, "pubkey", i); @@ -4654,13 +4657,19 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ); } else if (possibleMembers.size() > 1) + { + std::string candidates; + for (auto const& candidate : possibleMembers) + candidates += " * " + candidate.type->humanReadableName() + "\n"; m_errorReporter.fatalTypeError( 6675_error, _memberAccess.location(), "Member \"" + memberName + "\" not unique " "after argument-dependent lookup in " + exprType->humanReadableName() + - (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".") + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".") + + "\nCandidates:\n" + candidates ); + } annotation.referencedDeclaration = possibleMembers.front().declaration; annotation.type = possibleMembers.front().type; diff --git a/compiler/libsolidity/analysis/ViewPureChecker.cpp b/compiler/libsolidity/analysis/ViewPureChecker.cpp index 914d5730..1366a734 100644 --- a/compiler/libsolidity/analysis/ViewPureChecker.cpp +++ b/compiler/libsolidity/analysis/ViewPureChecker.cpp @@ -360,6 +360,29 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::ABI, "functionId"}, {MagicType::Kind::ABI, "setCodeSalt"}, {MagicType::Kind::ABI, "stateInitHash"}, + {MagicType::Kind::BLS, "aggregate"}, + {MagicType::Kind::BLS, "verify"}, + {MagicType::Kind::BLS, "fastAggregateVerify"}, + {MagicType::Kind::BLS, "aggregateVerify"}, + {MagicType::Kind::BLS, "mapToG1"}, + {MagicType::Kind::BLS, "mapToG2"}, + {MagicType::Kind::BLS, "g1Add"}, + {MagicType::Kind::BLS, "g1Sub"}, + {MagicType::Kind::BLS, "g1Mul"}, + {MagicType::Kind::BLS, "g1Neg"}, + {MagicType::Kind::BLS, "g1IsZero"}, + {MagicType::Kind::BLS, "g1InGroup"}, + {MagicType::Kind::BLS, "g2Add"}, + {MagicType::Kind::BLS, "g2Sub"}, + {MagicType::Kind::BLS, "g2Mul"}, + {MagicType::Kind::BLS, "g2Neg"}, + {MagicType::Kind::BLS, "g2IsZero"}, + {MagicType::Kind::BLS, "g2InGroup"}, + {MagicType::Kind::BLS, "g1Zero"}, + {MagicType::Kind::BLS, "g2Zero"}, + {MagicType::Kind::BLS, "r"}, + {MagicType::Kind::BLS, "g1MultiExp"}, + {MagicType::Kind::BLS, "g2MultiExp"}, {MagicType::Kind::Block, "blockhash"}, {MagicType::Kind::Block, "logicaltime"}, {MagicType::Kind::Block, "timestamp"}, @@ -383,6 +406,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::Math, "min"}, {MagicType::Kind::Math, "minmax"}, {MagicType::Kind::Math, "modpow2"}, + {MagicType::Kind::Math, "mulmod"}, {MagicType::Kind::Math, "muldiv"}, {MagicType::Kind::Math, "muldivc"}, {MagicType::Kind::Math, "muldivmod"}, diff --git a/compiler/libsolidity/ast/AST.h b/compiler/libsolidity/ast/AST.h index 95ec9b86..93ddaaa1 100644 --- a/compiler/libsolidity/ast/AST.h +++ b/compiler/libsolidity/ast/AST.h @@ -1579,16 +1579,16 @@ class TvmVector: public TypeName TvmVector( int64_t _id, SourceLocation const& _location, - ASTPointer const& _type + std::vector> const& _types ): - TypeName(_id, _location), m_type(_type) {} + TypeName(_id, _location), m_types(_types) {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - TypeName const& type() const { return *m_type.get(); } + std::vector> const& types() const { return m_types; } private: - ASTPointer m_type; + std::vector> m_types; }; /** diff --git a/compiler/libsolidity/ast/ASTJsonExporter.cpp b/compiler/libsolidity/ast/ASTJsonExporter.cpp index 73e0d4c0..f067cdaf 100644 --- a/compiler/libsolidity/ast/ASTJsonExporter.cpp +++ b/compiler/libsolidity/ast/ASTJsonExporter.cpp @@ -619,7 +619,7 @@ bool ASTJsonExporter::visit(Optional const& _node) { bool ASTJsonExporter::visit(TvmVector const& _node) { setJsonNode(_node, "vector", { - std::make_pair("type", toJson(_node.type())), + std::make_pair("types", toJson(_node.types())), }); return false; } diff --git a/compiler/libsolidity/ast/AST_accept.h b/compiler/libsolidity/ast/AST_accept.h index c9aac9ec..9a6cf83d 100644 --- a/compiler/libsolidity/ast/AST_accept.h +++ b/compiler/libsolidity/ast/AST_accept.h @@ -514,7 +514,8 @@ void TvmVector::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { - m_type->accept(_visitor); + for (ASTPointer& t : m_types) + t->accept(_visitor); } _visitor.endVisit(*this); } @@ -523,7 +524,8 @@ void TvmVector::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { - m_type->accept(_visitor); + for (ASTPointer const& t : m_types) + t->accept(_visitor); } _visitor.endVisit(*this); } diff --git a/compiler/libsolidity/ast/TypeProvider.cpp b/compiler/libsolidity/ast/TypeProvider.cpp index a9c192d2..701d4f7d 100644 --- a/compiler/libsolidity/ast/TypeProvider.cpp +++ b/compiler/libsolidity/ast/TypeProvider.cpp @@ -1130,7 +1130,7 @@ std::array, 32> const TypeProvider::m_bytesM{{ {std::make_unique(32)} }}; -std::array, 8> const TypeProvider::m_magics{{ +std::array, 9> const TypeProvider::m_magics{{ {std::make_unique(MagicType::Kind::Block)}, {std::make_unique(MagicType::Kind::Message)}, {std::make_unique(MagicType::Kind::Transaction)}, @@ -1138,7 +1138,8 @@ std::array, 8> const TypeProvider::m_magics{{ {std::make_unique(MagicType::Kind::TVM)}, {std::make_unique(MagicType::Kind::Math)}, {std::make_unique(MagicType::Kind::Rnd)}, - {std::make_unique(MagicType::Kind::Gosh)} + {std::make_unique(MagicType::Kind::Gosh)}, + {std::make_unique(MagicType::Kind::BLS)} // MetaType is stored separately }}; diff --git a/compiler/libsolidity/ast/TypeProvider.h b/compiler/libsolidity/ast/TypeProvider.h index d6f7b57a..c19bc075 100644 --- a/compiler/libsolidity/ast/TypeProvider.h +++ b/compiler/libsolidity/ast/TypeProvider.h @@ -253,7 +253,7 @@ class TypeProvider static std::array, 256> const m_quintM; static std::unique_ptr const m_qbool; static std::array, 32> const m_bytesM; - static std::array, 8> const m_magics; ///< MagicType's except MetaType + static std::array, 9> const m_magics; ///< MagicType's except MetaType std::map, std::unique_ptr> m_varinterger{}; std::map, std::unique_ptr> m_ufixedMxN{}; diff --git a/compiler/libsolidity/ast/Types.cpp b/compiler/libsolidity/ast/Types.cpp index af13e9a5..b0db235d 100644 --- a/compiler/libsolidity/ast/Types.cpp +++ b/compiler/libsolidity/ast/Types.cpp @@ -28,6 +28,8 @@ #include +#include + #include #include #include @@ -1465,6 +1467,28 @@ BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) return arrayType->isByteArrayOrString(); } + else if (_convertTo.category() == Type::Category::TvmSlice) + { + size_t invalidSequence{}; + if (!util::validateSlice(value(), invalidSequence)) + return BoolResult::err( + "Contains invalid slice character at position " + + util::toString(invalidSequence) + + "." + ); + else { + int bitLength = StrUtils::toBitString("x" + value()).size(); + bool fitToCell = bitLength <= 1023; + if (!fitToCell) { + return BoolResult::err( + "String literal is too long: " + std::to_string(bitLength) + " bits." + " It can not be fitted to the cell. Maximum allowed bit length is 1023 bits." + ); + } + } + + return true; + } else return false; } @@ -3205,6 +3229,30 @@ std::string FunctionType::richIdentifier() const std::string id = "t_function_"; switch (m_kind) { + case Kind::BlsVerify: id += "blsverify"; break; + case Kind::BlsAggregate: id += "blsaggregate"; break; + case Kind::BlsFastAggregateVerify: id += "blsfastaggregateverify"; break; + case Kind::BlsAggregateVerify: id += "blsaggregateverify"; break; + case Kind::BlsG1Add: id += "blsg1add"; break; + case Kind::BlsG1Sub: id += "blsg1sub"; break; + case Kind::BlsG1Neg: id += "blsg1neg"; break; + case Kind::BlsG1Mul: id += "blsg1mul"; break; + case Kind::BlsMapToG1: id += "blsmaptog1"; break; + case Kind::BlsG1IsZero: id += "blsg1iszero"; break; + case Kind::BlsG1InGroup: id += "blsg1ingroup"; break; + case Kind::BlsG2Add: id += "blsg2add"; break; + case Kind::BlsG2Sub: id += "blsg2sub"; break; + case Kind::BlsG2Neg: id += "blsg2neg"; break; + case Kind::BlsG2Mul: id += "blsg2mul"; break; + case Kind::BlsMapToG2: id += "blsmaptog2"; break; + case Kind::BlsG2IsZero: id += "blsg2iszero"; break; + case Kind::BlsG2InGroup: id += "blsg2ingroup"; break; + case Kind::BlsG1Zero: id += "blsg1zero"; break; + case Kind::BlsG2Zero: id += "blsg2zero"; break; + case Kind::BlsPushR: id += "blspushr"; break; + case Kind::BlsG1MultiExp: id += "blsg1multiexp"; break; + case Kind::BlsG2MultiExp: id += "blsg2multiexp"; break; + case Kind::IntCast: id += "integercast"; break; case Kind::StructUnpack: id += "structunpack"; break; @@ -3349,6 +3397,7 @@ std::string FunctionType::richIdentifier() const case Kind::MathMulDivMod: id += "mathmuldivmod"; break; case Kind::MathDivMod: id += "mathdivmod"; break; case Kind::MathSign: id += "mathsign"; break; + case Kind::MathMulMod: id += "mathmulmod"; break; case Kind::MappingAt: id += "mappingat"; break; case Kind::MappingDelMinOrMax: id += "mapdelmin"; break; @@ -4533,6 +4582,8 @@ std::string MagicType::richIdentifier() const return "t_magic_rnd"; case Kind::Gosh: return "t_magic_gosh"; + case Kind::BLS: + return "t_magic_bls"; } return ""; } @@ -4841,6 +4892,12 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const TypeProvider::function( {}, {}, {}, {}, FunctionType::Kind::MathDivR, StateMutability::Pure ) + }, + { + "mulmod", + TypeProvider::function( + {}, {}, {}, {}, FunctionType::Kind::MathMulMod, StateMutability::Pure + ) } }; members.emplace_back("max", TypeProvider::function( @@ -5132,6 +5189,354 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const nullptr, FunctionType::Options::withArbitraryParameters() )} }); + case Kind::BLS: { + auto members = MemberList::MemberMap({ + { + "verify", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::tvmslice(), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{"", "", ""}, + strings{""}, + FunctionType::Kind::BlsVerify, + StateMutability::Pure + ) + }, + { + "aggregate", + TypeProvider::function( + TypePointers{TypeProvider::tvmVector(TypeProvider::tvmslice())}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsAggregate, + StateMutability::Pure + ) + }, + { + "fastAggregateVerify", + TypeProvider::function( + TypePointers{TypeProvider::tvmVector(TypeProvider::tvmslice()), TypeProvider::tvmslice(), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{"", "", ""}, + strings{""}, + FunctionType::Kind::BlsFastAggregateVerify, + StateMutability::Pure + ) + }, + { + "aggregateVerify", + TypeProvider::function( + TypePointers{TypeProvider::tvmVector(TypeProvider::tuple({TypeProvider::tvmslice(), TypeProvider::tvmslice()})), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsAggregateVerify, + StateMutability::Pure + ) + }, + { + "g1Add", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsG1Add, + StateMutability::Pure + ) + }, + { + "g1Sub", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsG1Sub, + StateMutability::Pure + ) + }, + { + "g1Neg", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG1Neg, + StateMutability::Pure + ) + }, + { + "g1Mul", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::int257()}, + TypePointers{TypeProvider::tvmslice()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsG1Mul, + StateMutability::Pure + ) + }, + { + "mapToG1", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsMapToG1, + StateMutability::Pure + ) + }, + { + "g1IsZero", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG1IsZero, + StateMutability::Pure + ) + }, + { + "g1InGroup", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG1InGroup, + StateMutability::Pure + ) + }, + { + "g2Add", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsG2Add, + StateMutability::Pure + ) + }, + { + "g2Sub", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsG2Sub, + StateMutability::Pure + ) + }, + { + "g2Neg", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG2Neg, + StateMutability::Pure + ) + }, + { + "g2Mul", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice(), TypeProvider::int257()}, + TypePointers{TypeProvider::tvmslice()}, + strings{"", ""}, + strings{""}, + FunctionType::Kind::BlsG2Mul, + StateMutability::Pure + ) + }, + { + "mapToG2", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsMapToG2, + StateMutability::Pure + ) + }, + { + "g2IsZero", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG2IsZero, + StateMutability::Pure + ) + }, + { + "g2InGroup", + TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG2InGroup, + StateMutability::Pure + ) + }, + { + "g1Zero", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{""}, + FunctionType::Kind::BlsG1Zero, + StateMutability::Pure + ) + }, + { + "g2Zero", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{""}, + FunctionType::Kind::BlsG2Zero, + StateMutability::Pure + ) + }, + { + "r", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(255)}, + strings{}, + strings{""}, + FunctionType::Kind::BlsPushR, + StateMutability::Pure + ) + }, + { + "g1MultiExp", + TypeProvider::function( + TypePointers{TypeProvider::tvmVector(TypeProvider::tuple({TypeProvider::tvmslice(),TypeProvider::int257()}))}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG1MultiExp, + StateMutability::Pure + ) + }, + { + "g2MultiExp", + TypeProvider::function( + TypePointers{TypeProvider::tvmVector(TypeProvider::tuple({TypeProvider::tvmslice(),TypeProvider::int257()}))}, + TypePointers{TypeProvider::tvmslice()}, + strings{""}, + strings{""}, + FunctionType::Kind::BlsG2MultiExp, + StateMutability::Pure + ) + } + }); + auto args = TypePointers{TypeProvider::tvmslice()}; + auto argNames = strings{""}; + for (int n = 1; n <= 255; ++n) + { + members.emplace_back( + "aggregate", + TypeProvider::function( + args, + TypePointers{TypeProvider::tvmslice()}, + argNames, + strings{""}, + FunctionType::Kind::BlsAggregate, + StateMutability::Pure + ) + ); + { + auto favArgs = args; + favArgs.emplace_back(TypeProvider::tvmslice()); + favArgs.emplace_back(TypeProvider::tvmslice()); + auto favNames = argNames; + favNames.emplace_back(""); + favNames.emplace_back(""); + members.emplace_back( + "fastAggregateVerify", + TypeProvider::function( + favArgs, + TypePointers{TypeProvider::boolean()}, + favNames, + strings{""}, + FunctionType::Kind::BlsFastAggregateVerify, + StateMutability::Pure + ) + ); + } + { + auto avArgs = args; + avArgs.insert(avArgs.end(), args.begin(), args.end()); + avArgs.emplace_back(TypeProvider::tvmslice()); + auto avNames = argNames; + avNames.insert(avNames.end(), argNames.begin(), argNames.end()); + avNames.emplace_back(""); + members.emplace_back( + "aggregateVerify", + TypeProvider::function( + avArgs, + TypePointers{TypeProvider::boolean()}, + avNames, + strings{""}, + FunctionType::Kind::BlsAggregateVerify, + StateMutability::Pure + ) + ); + } + { + TypePointers mulArgs; + strings mulNames; + for (int i = 0; i < n; ++i) { + mulArgs.emplace_back(TypeProvider::tvmslice()); + mulArgs.emplace_back(TypeProvider::int257()); + mulNames.emplace_back(""); + mulNames.emplace_back(""); + } + members.emplace_back( + "g1MultiExp", + TypeProvider::function( + mulArgs, + TypePointers{TypeProvider::tvmslice()}, + mulNames, + strings{""}, + FunctionType::Kind::BlsG1MultiExp, + StateMutability::Pure + ) + ); + members.emplace_back( + "g2MultiExp", + TypeProvider::function( + mulArgs, + TypePointers{TypeProvider::tvmslice()}, + mulNames, + strings{""}, + FunctionType::Kind::BlsG1MultiExp, + StateMutability::Pure + ) + ); + } + + // + args.emplace_back(TypeProvider::tvmslice()); + argNames.emplace_back(""); + } + return members; + } case Kind::Gosh: { MemberList::MemberMap members; for (auto const&[name, type] : std::vector>{ @@ -5300,6 +5705,8 @@ std::string MagicType::toString(bool _withoutDataLocation) const return "rnd"; case Kind::Gosh: return "gosh"; + case Kind::BLS: + return "bls"; } solAssert(false, "Unknown kind of magic."); return {}; @@ -6022,6 +6429,26 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { StateMutability::Pure ) }, + { + "bitEmpty", TypeProvider::function( + {}, + {TypeProvider::boolean()}, + {}, + {{}}, + FunctionType::Kind::TVMSliceEmpty, + StateMutability::Pure + ) + }, + { + "refEmpty", TypeProvider::function( + {}, + {TypeProvider::boolean()}, + {}, + {{}}, + FunctionType::Kind::TVMSliceEmpty, + StateMutability::Pure + ) + }, { "bits", TypeProvider::function( TypePointers{}, @@ -6101,7 +6528,27 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { FunctionType::Kind::TVMSliceCompare, StateMutability::Pure ) - } + }, + { + "startsWithOne", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::boolean()}, + strings{}, + strings{""}, + FunctionType::Kind::TVMSliceSize, + StateMutability::Pure + ) + }, + { + "startsWith", TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::boolean()}, + strings{""}, + strings{""}, + FunctionType::Kind::TVMSliceSize, + StateMutability::Pure + ) + }, }; return members; } @@ -6225,14 +6672,26 @@ TypeResult TvmVectorType::unaryOperatorResult(Token _operator) const { MemberList::MemberMap TvmVectorType::nativeMembers(const ASTNode *) const { + TypePointers comps; + strings names; + if (auto tuple = dynamic_cast(valueType())) { + for (Type const* comp : tuple->components()) { + comps.emplace_back(comp); + names.emplace_back(""); + } + } else { + comps.emplace_back(valueType()); + names.emplace_back(""); + } + MemberList::MemberMap members = { { "push", TypeProvider::function( - TypePointers{valueType()}, + comps, TypePointers{}, - strings{std::string()}, + names, strings{}, FunctionType::Kind::TVMVectorPush, StateMutability::Pure @@ -6253,9 +6712,9 @@ MemberList::MemberMap TvmVectorType::nativeMembers(const ASTNode *) const "pop", TypeProvider::function( TypePointers{}, - TypePointers{valueType()}, + comps, strings{}, - strings{std::string("last")}, + names, FunctionType::Kind::TVMVectorPop, StateMutability::Pure ) @@ -6275,9 +6734,9 @@ MemberList::MemberMap TvmVectorType::nativeMembers(const ASTNode *) const "last", TypeProvider::function( TypePointers{}, - TypePointers{valueType()}, + comps, strings{}, - strings{std::string("last")}, + names, FunctionType::Kind::TVMVectorLast, StateMutability::Pure ) diff --git a/compiler/libsolidity/ast/Types.h b/compiler/libsolidity/ast/Types.h index d7f51192..dca7c82b 100644 --- a/compiler/libsolidity/ast/Types.h +++ b/compiler/libsolidity/ast/Types.h @@ -1535,6 +1535,31 @@ class FunctionType: public Type MathMulDivMod, ///< math.muldivmod() MathDivMod, ///< math.divmod() MathSign, ///< math.sign() + MathMulMod, ///< math.mulmod() + + BlsVerify, ///< bls.verify() + BlsAggregate, ///< bls.aggregate() + BlsFastAggregateVerify, ///< bls.fastAggregateVerify() + BlsAggregateVerify, ///< bls.aggregateVerify() + BlsG1Add, + BlsG1Sub, + BlsG1Neg, + BlsG1Mul, + BlsMapToG1, + BlsG1IsZero, + BlsG1InGroup, + BlsG2Add, + BlsG2Sub, + BlsG2Neg, + BlsG2Mul, + BlsMapToG2, + BlsG2IsZero, + BlsG2InGroup, + BlsG1Zero, + BlsG2Zero, + BlsPushR, + BlsG1MultiExp, + BlsG2MultiExp, ABIBuildIntMsg, ///< abi.encodeIntMsg() ABICodeSalt, ///< abi.codeSalt() @@ -1554,7 +1579,7 @@ class FunctionType: public Type TVMCommit, ///< tvm.commit() TVMConfigParam, ///< tvm.configParam() TVMDeploy, ///< functions to deploy contract from contract - TVMDump, ///< tvm.xxxdump() + TVMDump, ///< tvm.bindump() or tvm.hexdump() TVMExit, ///< tvm.exit() TVMExit1, ///< tvm.exit1() TVMHash, ///< tvm.hash() @@ -2096,6 +2121,7 @@ class MagicType: public Type Math, ///< "math" Rnd, ///< "rnd" Gosh, ///< "gosh" + BLS, ///< "bls" MetaType ///< "type(...)" }; diff --git a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp index 13f32083..2a34fc60 100644 --- a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp +++ b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp @@ -30,8 +30,6 @@ using namespace solidity::util; namespace solidity::frontend { -bool PeepholeOptimizer::withBlockPush = false; - struct Result { int removeQty{}; @@ -52,11 +50,9 @@ struct Result { class PrivatePeepholeOptimizer { public: - explicit PrivatePeepholeOptimizer(std::vector> instructions, bool _withUnpackOpaque, bool _optimizeSlice, bool _withTuck) : + explicit PrivatePeepholeOptimizer(std::vector> instructions, std::bitset<3> const _flags) : m_instructions{std::move(instructions)}, - m_withUnpackOpaque{_withUnpackOpaque}, - m_optimizeSlice{_optimizeSlice}, - m_withTuck{_withTuck} + m_flags{_flags} { } vector> const &instructions() const { return m_instructions; } @@ -69,21 +65,21 @@ class PrivatePeepholeOptimizer { void insert(int idx, const Pointer& node); std::optional optimizeAt(int idx1) const; std::optional optimizeSlice(int idx1) const; - static std::optional optimizeAt1(Pointer const& cmd1, bool m_withUnpackOpaque); - static std::optional optimizeAt2(Pointer const& cmd1, Pointer const& cmd2, bool _withTuck); - static std::optional optimizeAt3(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, bool m_withUnpackOpaque); - static std::optional optimizeAt4(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, - Pointer const& cmd4); - static std::optional optimizeAt5(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, - Pointer const& cmd4, Pointer const& cmd5); - static std::optional optimizeAt6(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, - Pointer const& cmd4, Pointer const& cmd5, Pointer const& cmd6); + std::optional optimizeAt1(Pointer const& cmd1) const; + std::optional optimizeAt2(Pointer const& cmd1, Pointer const& cmd2) const; + std::optional optimizeAt3(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3) const; + std::optional optimizeAt4(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, + Pointer const& cmd4) const; + std::optional optimizeAt5(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, + Pointer const& cmd4, Pointer const& cmd5) const; + std::optional optimizeAt6(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, + Pointer const& cmd4, Pointer const& cmd5, Pointer const& cmd6) const; std::optional optimizeAtInf(int idx1) const; static bool hasRetOrJmp(TvmAstNode const* _node); void updateLinesAndIndex(int idx1, const std::optional& res); std::optional unsquash(bool _withUnpackOpaque, int idx1) const; - std::optional squashPush(int idx1) const; + std::optional squash(int idx1) const; bool optimize(const std::function(int)> &f); static std::optional> isBLKDROP2(Pointerconst& node); @@ -105,9 +101,7 @@ class PrivatePeepholeOptimizer { static bool isStack(Pointer const& node, Stack::Opcode op); private: std::vector> m_instructions{}; - bool m_withUnpackOpaque{}; - bool m_optimizeSlice{}; - bool m_withTuck{}; + std::bitset<3> const m_flags; }; int PrivatePeepholeOptimizer::nextCommandLine(int idx) const { @@ -148,26 +142,66 @@ void PrivatePeepholeOptimizer::insert(int idx, const Pointer& node) std::optional PrivatePeepholeOptimizer::optimizeSlice(int idx1) const { int idx2 = nextCommandLine(idx1); + int idx3 = nextCommandLine(idx2); Pointer const &cmd1 = get(idx1); Pointer const &cmd2 = get(idx2); + Pointer const &cmd3 = get(idx3); - // PUSHSLICE xXXX + // PUSHSLICE xXXX // firstCase + // STSLICER + // or + // PUSHSLICE xXXX // secondCase + // NEWC // STSLICE - if (isPlainPushSlice(cmd1) && is(cmd2, "STSLICER")) { + bool const firstCase = cmd2 && is(cmd2, "STSLICER"); + bool const secondCase = cmd2 && is(cmd2, "NEWC") && cmd3 && is(cmd3, "STSLICE"); + if (isPlainPushSlice(cmd1) && (firstCase || secondCase)) { std::string const& slice = isPlainPushSlice(cmd1)->blob(); std::string const& binStr = StrUtils::toBitString(slice); - int len = binStr.length(); + int sliceBits = binStr.length(); // PUSHINT len - // STZERO + // STZEROES + // or + // NEWC + // PUSHINT len + // STZEROES if (all_of(binStr.begin(), binStr.end(), [](char ch) { return ch == '0'; })) { - return Result{2, gen("PUSHINT " + toString(len)), gen("STZEROES")}; + if (firstCase) + return Result{2, gen("PUSHINT " + toString(sliceBits)), gen("STZEROES")}; + else + return Result{3, gen("NEWC"), gen("PUSHINT " + toString(sliceBits)), gen("STZEROES")}; } - // PUSHINT N - // STUR len + + std::optional negNum = StrUtils::toNegBigint(binStr); + int negNumLength = std::numeric_limits::max() / 2; + if (negNum) + negNumLength = StrUtils::toBinString(-negNum.value()).length() + 5; // approximate length, opcode "PUSHINT" may contain more bits bigint num = StrUtils::toBigint(binStr); - int numLength = StrUtils::toBinString(num).length(); - if (numLength <= 255 && len > numLength) { - return Result{2, gen("PUSHINT " + toString(num)), gen("STUR " + toString(len))}; + int numLength = StrUtils::toBinString(num).length() + 5; + if (sliceBits <= 256 && sliceBits > std::min(numLength, negNumLength)) { + if (firstCase) { + if (numLength < negNumLength) + // PUSHINT N + // STUR sliceBits + return Result{2, gen("PUSHINT " + toString(num)), gen("STUR " + toString(sliceBits))}; + else + // PUSHINT N + // STIR sliceBits + return Result{2, gen("PUSHINT " + toString(*negNum)), gen("STIR " + toString(sliceBits))}; + } + else { + if (numLength < negNumLength) + // PUSHINT N + // NEWC + // STU sliceBits + return Result{3, gen("PUSHINT " + toString(num)), gen("NEWC"), gen("STU " + toString(sliceBits))}; + else { + // PUSHINT N + // NEWC + // STI sliceBits + return Result{3, gen("PUSHINT " + toString(*negNum)), gen("NEWC"), gen("STI " + toString(sliceBits))}; + } + } } } @@ -176,10 +210,6 @@ std::optional PrivatePeepholeOptimizer::optimizeSlice(int idx1) const { std::optional PrivatePeepholeOptimizer::optimizeAt(const int idx1) const { std::optional res; - if (m_optimizeSlice) { - res = optimizeSlice(idx1); - if (res) return res; - } int idx2 = nextCommandLine(idx1); int idx3 = nextCommandLine(idx2); @@ -194,18 +224,18 @@ std::optional PrivatePeepholeOptimizer::optimizeAt(const int idx1) const Pointer const &cmd5 = get(idx5); Pointer const &cmd6 = get(idx6); - res = optimizeAt1(cmd1, m_withUnpackOpaque); + res = optimizeAt1(cmd1); if (res) return res; res = optimizeAtInf(idx1); if (res) return res; if (!cmd2) return {}; - res = optimizeAt2(cmd1, cmd2, m_withTuck); + res = optimizeAt2(cmd1, cmd2); if (res) return res; if (!cmd3) return {}; - res = optimizeAt3(cmd1, cmd2, cmd3, m_withUnpackOpaque); + res = optimizeAt3(cmd1, cmd2, cmd3); if (res) return res; if (!cmd4) return {}; @@ -223,7 +253,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt(const int idx1) const return {}; } -std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer const& cmd1, bool m_withUnpackOpaque) { +std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer const& cmd1) const { auto cmd1CodeBlock = to(cmd1.get()); auto cmd1GenOpcode = to(cmd1.get()); auto cmd1IfElse = to(cmd1.get()); @@ -349,7 +379,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer // } // IFELSE // Z - if (m_withUnpackOpaque && cmd1IfElse && cmd1IfElse->falseBody() != nullptr && cmd1IfElse->ret() == 0) { + if (m_flags.test(OptFlags::UnpackOpaque) && cmd1IfElse && cmd1IfElse->falseBody() != nullptr && cmd1IfElse->ret() == 0) { std::vector> const& t = cmd1IfElse->trueBody()->instructions(); std::vector> const& f = cmd1IfElse->falseBody()->instructions(); if (!t.empty() && @@ -511,7 +541,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer // ZERO/NULL SWAP/ROTR IF [NOT] // PUSHCONT { ... } // IF[ELSE][NOT][JMP] - if (m_withUnpackOpaque && cmd1IfElse) { + if (m_flags.test(OptFlags::UnpackOpaque) && cmd1IfElse) { for (bool isZero : {true, false}) { if (isZero && *GlobalParams::g_tvmVersion == langutil::TVMVersion::ton()){ // ignore ZERO SWAP/ROTR IF [NOT] @@ -544,7 +574,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer return {}; } -std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer const& cmd1, Pointer const& cmd2, bool _withTuck) { +std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer const& cmd1, Pointer const& cmd2) const { using namespace MathConsts; auto cmd1GenOp = to(cmd1.get()); auto cmd1Glob = to(cmd1.get()); @@ -631,7 +661,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer // BLKPUSH N, index / DROP // BLKDROP N - if (PeepholeOptimizer::withBlockPush && isBLKPUSH1 && isDrop(cmd2)) { + if (m_flags.test(OptFlags::UseCompoundOpcodes) && isBLKPUSH1 && isDrop(cmd2)) { auto [qty, index] = isBLKPUSH1.value(); int diff = qty - isDrop(cmd2).value(); if (diff == 0) @@ -672,14 +702,13 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer // => // BLKDROP2 down, top-1 // PUSH S? - if (_withTuck && isPUSH(cmd1) && _isBLKDROP2) { + if (m_flags.test(OptFlags::UseCompoundOpcodes) && isPUSH(cmd1) && _isBLKDROP2) { int n = *isPUSH(cmd1); auto [down, top] = _isBLKDROP2.value(); - if (n < top) { + if (n + 2 <= top) return Result{2, makeBLKDROP2(down, top - 1), cmd1}; - } else if (n >= down + top - 1) { + else if (n >= down + top - 1) return Result{2, makeBLKDROP2(down, top - 1), makePUSH(n - down)}; - } } // BLKPUSH // BLKDROP2 @@ -1038,8 +1067,8 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer is(cmd2, "STIR") && fetchInt(cmd2) == 1 ) { if (is(cmd1, "FALSE")) - return Result{2, gen("STZERO")}; - return Result{2, gen("STONE")}; + return Result{2, gen("STSLICECONST 0")}; + return Result{2, gen("STSLICECONST 1")}; } if ( is(cmd1, "PUSHINT") && pushintValue(cmd1) == 0 && @@ -1060,7 +1089,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer is(cmd1, "PUSHINT") && pushintValue(cmd1) == 1 && is(cmd2, "STZEROES") ) { - return Result{2, gen("STZERO")}; + return Result{2, gen("STSLICECONST 0")}; } // REVERSE N, 1 @@ -1206,7 +1235,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer // BLKPUSH Q, 0 / DUP // => // BLKPUSH N+Q, 0 - if (PeepholeOptimizer::withBlockPush && isBLKPUSH(cmd1) && isBLKPUSH(cmd2)) { + if (m_flags.test(OptFlags::UseCompoundOpcodes) && isBLKPUSH(cmd1) && isBLKPUSH(cmd2)) { auto [qty0, index0] = isBLKPUSH(cmd1).value(); auto [qty1, index1] = isBLKPUSH(cmd2).value(); if (index0 == 0 && index1 == 0 && qty0 + qty1 <= 15) @@ -1244,7 +1273,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer } std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer const& cmd1, Pointer const& cmd2, - Pointer const& cmd3, bool m_withUnpackOpaque) { + Pointer const& cmd3) const { auto isPUSH1 = isPUSH(cmd1); auto cmd1PushCellOrSlice = to(cmd1.get()); auto isPUSH2 = isPUSH(cmd2); @@ -1393,8 +1422,8 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer is(cmd3, "STI") && arg(cmd3) == "1" ) { if (is(cmd1, "TRUE")) - return Result{3, gen("NEWC"), gen("STONE")}; - return Result{3, gen("NEWC"), gen("STZERO")}; + return Result{3, gen("NEWC"), gen("STSLICECONST 1")}; + return Result{3, gen("NEWC"), gen("STSLICECONST 0")}; } if ( @@ -1419,7 +1448,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer // => // gen(0, 1) // BLKPUSH N+1, 0 - if (PeepholeOptimizer::withBlockPush && // ? + if (m_flags.test(OptFlags::UseCompoundOpcodes) && // ? isPureGen01(*cmd1) && isBLKPUSH(cmd2) && isPureGen01(*cmd3) && @@ -1467,7 +1496,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer // IFREF { CALL $c7_to_c4$ / $upd_only_time_in_c4$ } // => // IFREF { CALL $c7_to_c4$ / $upd_only_time_in_c4$ } - if (m_withUnpackOpaque && isPUSH(cmd1)) { + if (m_flags.test(OptFlags::UnpackOpaque) && isPUSH(cmd1)) { if (auto ifRef = to(cmd2.get()); ifRef && !ifRef->withJmp() && !ifRef->withNot() && ifRef->falseBody() == nullptr ) { @@ -1486,7 +1515,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer } std::optional PrivatePeepholeOptimizer::optimizeAt4(Pointer const& cmd1, Pointer const& cmd2, - Pointer const& cmd3, Pointer const& cmd4) { + Pointer const& cmd3, Pointer const& cmd4) const { auto cmd3Exc = to(cmd3.get()); if (is(cmd1, "PUSHINT") && is(cmd3, "PUSHINT")) { @@ -1533,15 +1562,13 @@ std::optional PrivatePeepholeOptimizer::optimizeAt4(Pointer if (is(cmd1, "PUSHINT") && is(cmd2, "NEWC") && is(cmd3, "STSLICECONST") && - is(cmd4, "STU")) { - std::string bitStr = StrUtils::toBitString(arg(cmd3)) + - StrUtils::toBitString(pushintValue(cmd1), fetchInt(cmd4)); - std::optional slice = StrUtils::unitBitStringToHex(bitStr, ""); - if (slice.has_value()) { - return Result{4, - genPushSlice(*slice), - gen("NEWC"), - gen("STSLICE")}; + is(cmd4, "STU", "STI")) { + std::string bitStr = StrUtils::toBitString(arg(cmd3)); + if (auto x = StrUtils::toBitString(pushintValue(cmd1), fetchInt(cmd4), is(cmd4, "STI"))) { + bitStr += x.value(); + std::optional slice = StrUtils::unitBitStringToHex(bitStr, ""); + if (slice.has_value()) + return Result{4, genPushSlice(*slice), gen("NEWC"), gen("STSLICE")}; } } if ( @@ -1599,7 +1626,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt4(Pointer std::optional PrivatePeepholeOptimizer::optimizeAt5(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, Pointer const& cmd4, - Pointer const& cmd5) { + Pointer const& cmd5) const { // PUSHSLICE A // PUSHSLICE B // NEWC @@ -1635,10 +1662,34 @@ std::optional PrivatePeepholeOptimizer::optimizeAt5(Pointer isPlainPushSlice(cmd2) && is(cmd3, "NEWC") && is(cmd4, "STSLICE") && - is(cmd5, "STU") + (is(cmd5, "STU") || is(cmd5, "STI")) + ) { + std::string bitStr = StrUtils::toBitString(isPlainPushSlice(cmd2)->blob()); + if (auto v = StrUtils::toBitString(pushintValue(cmd1), fetchInt(cmd5), is(cmd5, "STI"))) { + bitStr += v.value(); + std::optional slice = StrUtils::unitBitStringToHex(bitStr, ""); + if (slice.has_value()) { + return Result{5, + genPushSlice(*slice), + gen("NEWC"), + gen("STSLICE")}; + } + } + } + + // NULL + // PUSHSLICE ? + // NEWC + // STSLICE ? + // STDICT + if (is(cmd1, "NULL") && + isPlainPushSlice(cmd2) && + is(cmd3, "NEWC") && + is(cmd4, "STSLICE") && + is(cmd5, "STDICT") ) { std::string bitStr = StrUtils::toBitString(isPlainPushSlice(cmd2)->blob()) + - StrUtils::toBitString(pushintValue(cmd1), fetchInt(cmd5)); + "0"; std::optional slice = StrUtils::unitBitStringToHex(bitStr, ""); if (slice.has_value()) { return Result{5, @@ -1652,7 +1703,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt5(Pointer std::optional PrivatePeepholeOptimizer::optimizeAt6(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, Pointer const& cmd4, - Pointer const& cmd5, Pointer const& cmd6) { + Pointer const& cmd5, Pointer const& cmd6) const { if ( isPlainPushSlice(cmd1) && @@ -1710,7 +1761,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { int i = idx1; std::string bitString; int opcodeQty = 0; - int iterQty = 0; + vector> takenOpcodes; bool withBuilder = false; { int j = nextCommandLine(i); @@ -1726,23 +1777,29 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { withBuilder = true; std::string hexSlice = isPlainPushSlice(cmd1)->blob(); bitString += StrUtils::toBitString(hexSlice); - ++iterQty; opcodeQty += 3; + takenOpcodes.emplace_back(cmd1); + takenOpcodes.emplace_back(cmd2); + takenOpcodes.emplace_back(cmd3); i = nextCommandLine(k); } // PUSHINT ? // NEWC - // STU y + // STU y | STI y else if ( is(cmd1, "PUSHINT") && is(cmd2, "NEWC") && - is(cmd3, "STU") + (is(cmd3, "STU") || is(cmd3, "STI")) ) { - withBuilder = true; - bitString += StrUtils::toBitString(pushintValue(cmd1), fetchInt(cmd3)); - ++iterQty; - opcodeQty += 3; - i = nextCommandLine(k); + if (auto bitStr = StrUtils::toBitString(pushintValue(cmd1), fetchInt(cmd3), is(cmd3, "STI"))) { + withBuilder = true; + bitString += bitStr.value(); + opcodeQty += 3; + takenOpcodes.emplace_back(cmd1); + takenOpcodes.emplace_back(cmd2); + takenOpcodes.emplace_back(cmd3); + i = nextCommandLine(k); + } } } @@ -1755,69 +1812,78 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { } // TODO ADD LD[I|U]LE[4|8] - if (is(c1, "STZERO")) { - bitString += "0"; - ++opcodeQty; - i = j; - } else if (is(c1, "STONE")) { - bitString += "1"; - ++opcodeQty; - i = j; - } else if (is(c1, "STSLICECONST")) { + int curOpcodeQty = 0; + if (is(c1, "STSLICECONST")) { bitString += StrUtils::toBitString(arg(c1)); - ++opcodeQty; + curOpcodeQty = 1; i = j; - } else if (c2 && is(c1, "PUSHINT") && is(c2, "STUR")) { + } else if (c2 && is(c1, "PUSHINT") && (is(c2, "STUR") || is(c2, "STIR"))) { bigint num = pushintValue(c1); int len = fetchInt(c2); - bitString += StrUtils::toBitString(num, len); - opcodeQty += 2; - i = nextCommandLine(j); - } else if (c2 && is(c1, "PUSHINT") && is(c2, "STIR")) { - bigint num = pushintValue(c1); - int len = fetchInt(c2); - bitString += StrUtils::toBitString(num, len); - opcodeQty += 2; - i = nextCommandLine(j); + if (auto bitStr = StrUtils::toBitString(num, len, is(c2, "STIR"))) { + bitString += bitStr.value(); + curOpcodeQty = 2; + i = nextCommandLine(j); + } else + break; } else if (c2 && is(c1, "PUSHINT") && is(c2, "STVARUINT16", "STGRAMS")) { bigint num = pushintValue(c1); bitString += StrUtils::tonsToBinaryString(num); - opcodeQty += 2; + curOpcodeQty = 2; i = nextCommandLine(j); } else if (c2 && is(c1, "PUSHINT") && is(c2, "STZEROES")) { int len = fetchInt(c1); bitString += std::string(len, '0'); - opcodeQty += 2; + curOpcodeQty = 2; i = nextCommandLine(j); } else if (c2 && isPlainPushSlice(c1) && is(c2, "STSLICER")) { std::string hexSlice = isPlainPushSlice(c1)->blob(); bitString += StrUtils::toBitString(hexSlice); - opcodeQty += 2; + curOpcodeQty = 2; i = nextCommandLine(j); } else { break; } - ++iterQty; + opcodeQty += curOpcodeQty; + takenOpcodes.emplace_back(c1); + if (curOpcodeQty >= 2) + takenOpcodes.emplace_back(c2); } std::optional slice = StrUtils::unitBitStringToHex(bitString, ""); if (slice) { vector> opcodes; - if (withBuilder) { - opcodes.push_back(gen("NEWC")); - } std::optional res; if (StrUtils::toBitString(*slice).length() <= TvmConst::MaxSTSLICECONST) { + if (withBuilder) + opcodes.push_back(gen("NEWC")); opcodes.push_back(gen("STSLICECONST " + *slice)); res = Result{opcodeQty, opcodes}; } else { - opcodes.push_back(genPushSlice(*slice)); - opcodes.push_back(gen("STSLICER")); + if (withBuilder) { + opcodes.push_back(genPushSlice(*slice)); + opcodes.push_back(gen("NEWC")); + opcodes.push_back(gen("STSLICE")); + } else { + opcodes.push_back(genPushSlice(*slice)); + opcodes.push_back(gen("STSLICER")); + } res = Result{opcodeQty, opcodes}; } - if (opcodeQty > int(res->commands.size()) || (opcodeQty == int(res->commands.size()) && iterQty >= 2)) { + if (opcodeQty > int(res->commands.size())) + { return res; } + if (opcodeQty == int(res->commands.size())) + { + bool eq = true; + for (int i = 0; i < int(takenOpcodes.size()); ++i) { + bool curEq = *takenOpcodes.at(i) == *res->commands.at(i); + eq &= curEq; + } + if (!eq) + return res; + } } } @@ -2004,7 +2070,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { ++opcodeQty; gasCost += OpcodeUtils::gasCost(*stack); - auto newGasCost = StackOpcodeSquasher::gasCost(startStackSize, state, m_withTuck); + auto newGasCost = StackOpcodeSquasher::gasCost(startStackSize, state, m_flags.test(OptFlags::UseCompoundOpcodes)); if (newGasCost.has_value() && newGasCost < gasCost) { bestResult = BestResult{state, startStackSize, opcodeQty}; } @@ -2016,7 +2082,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { if (bestResult.has_value()) { auto newOpcodes = StackOpcodeSquasher::recover(bestResult.value().bestStartStackSize, bestResult.value().bestState, - m_withTuck); + m_flags.test(OptFlags::UseCompoundOpcodes)); return Result{bestResult.value().bestOpcodeQty, newOpcodes}; } } @@ -2166,7 +2232,7 @@ std::optional PrivatePeepholeOptimizer::unsquash(bool _withUnpackOpaque, return {}; } -std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const { +std::optional PrivatePeepholeOptimizer::squash(const int idx1) const { int idx2 = nextCommandLine(idx1); int idx3 = nextCommandLine(idx2); Pointer const& cmd1 = get(idx1); @@ -2175,7 +2241,7 @@ std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const auto cmd1Gen = to(cmd1.get()); auto cmd1PushCellOrSlice = to(cmd1.get()); - if (PeepholeOptimizer::withBlockPush && isPUSH(cmd1) && isPUSH(cmd2)) { + if (isPUSH(cmd1) && isPUSH(cmd2)) { int i = idx1, n = 0; while (isPUSH(get(i)) && isPUSH(get(i)).value() == isPUSH(cmd1).value()) { n++; @@ -2186,7 +2252,7 @@ std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const } } - if (PeepholeOptimizer::withBlockPush && isPUSH(cmd1) && isPUSH(cmd2) && isPUSH(cmd3)) { + if (isPUSH(cmd1) && isPUSH(cmd2) && isPUSH(cmd3)) { const int si = *isPUSH(cmd1); const int sj = *isPUSH(cmd2) - 1 == -1? si : *isPUSH(cmd2) - 1; const int sk = *isPUSH(cmd3) - 2 == -1? si : ( @@ -2197,7 +2263,7 @@ std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const } } - if (PeepholeOptimizer::withBlockPush && isPUSH(cmd1) && isPUSH(cmd2)) { + if (isPUSH(cmd1) && isPUSH(cmd2)) { const int si = *isPUSH(cmd1); const int sj = *isPUSH(cmd2) - 1 == -1? si : *isPUSH(cmd2) - 1; if (si <= 15 && sj <= 15) { @@ -2206,8 +2272,8 @@ std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const } // TODO delete - // squash PUSHINT, NEWDICT, NILL, FALSE, TRUE, (PUSHINT 0 NEWDICT PAIR) etc - if (PeepholeOptimizer::withBlockPush && isPureGen01(*cmd1)) { + // squash PUSHINT, NULL, NILL, FALSE, TRUE, (PUSHINT 0 NILL PAIR) etc + if (isPureGen01(*cmd1)) { int i = idx1; int n = 0; while (true) { @@ -2225,7 +2291,7 @@ std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const } // squash PUSHREF/PUSHREFSLICE/CELL - if (PeepholeOptimizer::withBlockPush && cmd1PushCellOrSlice) { + if (cmd1PushCellOrSlice) { int i = idx1; int n = 0; while (true) { @@ -2243,34 +2309,32 @@ std::optional PrivatePeepholeOptimizer::squashPush(const int idx1) const } } - if (m_withTuck) { - // PUSH Si - // SWAP - // => - // PUXC Si, S-1 - if (isPUSH(cmd1) && - isXCHG_S0(cmd2) && isXCHG_S0(cmd2).value() == 1 - ) { - int i = *isPUSH(cmd1); - if (0 <= i && i <= 15) - return Result{2, makePUXC(i, -1)}; - } + // PUSH Si + // SWAP + // => + // PUXC Si, S-1 + if (isPUSH(cmd1) && + isXCHG_S0(cmd2) && isXCHG_S0(cmd2).value() == 1 + ) { + int i = *isPUSH(cmd1); + if (0 <= i && i <= 15) + return Result{2, makePUXC(i, -1)}; + } - // XCHG Si - // PUSH Sj - // => - // XCPU Si, Sj - if (m_optimizeSlice && - isXCHG_S0(cmd1) && cmd2 && isPUSH(cmd2) + // XCHG Si + // PUSH Sj + // => + // XCPU Si, Sj + if (m_flags.test(OptFlags::UseCompoundOpcodes) && + isXCHG_S0(cmd1) && cmd2 && isPUSH(cmd2) + ) { + int i = isXCHG_S0(cmd1).value(); + int j = isPUSH(cmd2).value(); + if ( + 0 <= i && i <= 15 && + 0 <= j && j <= 15 ) { - int i = isXCHG_S0(cmd1).value(); - int j = isPUSH(cmd2).value(); - if ( - 0 <= i && i <= 15 && - 0 <= j && j <= 15 - ) { - return Result{2, makeXCPU(i, j)}; - } + return Result{2, makeXCPU(i, j)}; } } @@ -2471,20 +2535,18 @@ bool PeepholeOptimizer::visit(CodeBlock &_node) { return true; } -void PeepholeOptimizer::endVisit(CodeBlock &_node) { - optimizeBlock(_node); -} - bool PeepholeOptimizer::visit(Function &/*_node*/) { - //if (_node.name() == "") - // _node.block()->accept(*this); - //return false; +// return _node.name() == "default_data_cell"; // for debug return true; } +void PeepholeOptimizer::endVisit(CodeBlock &_node) { + optimizeBlock(_node); +} + void PeepholeOptimizer::optimizeBlock(CodeBlock &_node) const { { - std::optional r = PrivatePeepholeOptimizer::optimizeAt1(_node.shared_from_this(), m_withUnpackOpaque); + std::optional r = PrivatePeepholeOptimizer{{}, m_flags}.optimizeAt1(_node.shared_from_this()); if (r && r.value().commands.size() == 1) { auto newBlock = to(r.value().commands.at(0).get()); _node.upd(newBlock->instructions()); @@ -2494,14 +2556,20 @@ void PeepholeOptimizer::optimizeBlock(CodeBlock &_node) const { std::vector> instructions = _node.instructions(); - PrivatePeepholeOptimizer optimizer{instructions, m_withUnpackOpaque, m_optimizeSlice, m_withTuck}; + PrivatePeepholeOptimizer optimizer{instructions, m_flags}; optimizer.optimize([&](int index){ - return optimizer.unsquash(m_withUnpackOpaque, index); + return optimizer.unsquash(m_flags.test(OptFlags::UnpackOpaque), index); }); - while (optimizer.optimize([&optimizer](int index){ return optimizer.optimizeAt(index); })) {} + if (m_flags.test(OptFlags::OptimizeSlice)) + while (optimizer.optimize([&optimizer](int index){ return optimizer.optimizeSlice(index); })){ + } + else + while (optimizer.optimize([&optimizer](int index){ return optimizer.optimizeAt(index); })) { + } - optimizer.optimize([&optimizer](int index){ return optimizer.squashPush(index);}); + if (m_flags.test(OptFlags::UseCompoundOpcodes)) + optimizer.optimize([&optimizer](int index){ return optimizer.squash(index);}); _node.upd(optimizer.instructions()); } diff --git a/compiler/libsolidity/codegen/PeepholeOptimizer.hpp b/compiler/libsolidity/codegen/PeepholeOptimizer.hpp index 80cdc382..7332d36f 100644 --- a/compiler/libsolidity/codegen/PeepholeOptimizer.hpp +++ b/compiler/libsolidity/codegen/PeepholeOptimizer.hpp @@ -16,24 +16,26 @@ #pragma once +#include #include namespace solidity::frontend { - class PeepholeOptimizer : public TvmAstVisitor { - public: - explicit PeepholeOptimizer(bool _withUnpackOpaque, bool _optimizeSlice, bool _withTuck) - : m_withUnpackOpaque{_withUnpackOpaque}, m_optimizeSlice{_optimizeSlice}, m_withTuck{_withTuck} {} - bool visit(Function &_node) override; - bool visit(CodeBlock &_node) override; - void endVisit(CodeBlock &_node) override; - private: - void optimizeBlock(CodeBlock &_node) const; - private: - bool m_withUnpackOpaque{}; - bool m_optimizeSlice{}; - bool m_withTuck{}; - public: - static bool withBlockPush; - }; +enum OptFlags : unsigned { + UnpackOpaque = 0, + OptimizeSlice = 1, + UseCompoundOpcodes = 2 +}; +class PeepholeOptimizer : public TvmAstVisitor { +public: + explicit PeepholeOptimizer(std::bitset<3> const _flags) + : m_flags{_flags } { } + bool visit(CodeBlock &_node) override; + bool visit(Function &_node) override; + void endVisit(CodeBlock &_node) override; +private: + void optimizeBlock(CodeBlock &_node) const; +private: + std::bitset<3> m_flags; +}; } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMABI.cpp b/compiler/libsolidity/codegen/TVMABI.cpp index c3aa67ad..adeb29ed 100644 --- a/compiler/libsolidity/codegen/TVMABI.cpp +++ b/compiler/libsolidity/codegen/TVMABI.cpp @@ -36,18 +36,18 @@ Json::Value TVMABI::generateFunctionIdsJson( StackPusher pusher{&ctx}; ChainDataEncoder encoder{&pusher}; std::vector publicFunctions = TVMABI::publicFunctions(contract); - std::map map; + std::map func2id; for (FunctionDefinition const* func : publicFunctions) { uint32_t functionID = encoder.calculateFunctionIDWithReason( func, ReasonOfOutboundMessage::RemoteCallInternal ); const std::string name = TVMCompilerContext::getFunctionExternalName(func); - map[name] = functionID; - } - if (map.count("constructor") == 0) { - map["constructor"] = encoder.calculateConstructorFunctionID(); + func2id[name] = functionID; } + if (func2id.count("constructor") == 0 && ctx.hasConstructor()) + func2id["constructor"] = encoder.calculateConstructorFunctionID(); + for (VariableDeclaration const* vd : ctx.c4StateVariables()) { if (vd->isPublic()) { std::vector outputs = {vd}; @@ -59,12 +59,12 @@ Json::Value TVMABI::generateFunctionIdsJson( nullopt, false ); - map[vd->name()] = functionId; + func2id[vd->name()] = functionId; } } Json::Value root(Json::objectValue); - for (const auto&[func, functionID] : map) { + for (const auto&[func, functionID] : func2id) { std::stringstream ss; ss << "0x" << std::hex << std::setfill('0') << std::setw(8) << functionID; root[func] = ss.str(); @@ -135,15 +135,14 @@ Json::Value TVMABI::generateABIJson( { Json::Value functions(Json::arrayValue); for (FunctionDefinition const* f : publicFunctions) { - auto fname = TVMCompilerContext::getFunctionExternalName(f); - if (used.count(fname)) { + auto funcName = TVMCompilerContext::getFunctionExternalName(f); + if (used.count(funcName)) continue; - } - used.insert(fname); - functions.append(toJson(fname, convertArray(f->parameters()), convertArray(f->returnParameters()), f)); + used.insert(funcName); + functions.append(toJson(funcName, convertArray(f->parameters()), convertArray(f->returnParameters()), f)); } - if (used.count("constructor") == 0) { + if (used.count("constructor") == 0 && ctx.hasConstructor()) { functions.append(toJson("constructor", {}, {}, nullptr)); } @@ -181,10 +180,11 @@ Json::Value TVMABI::generateABIJson( { Json::Value fields(Json::arrayValue); std::vector> offset{{"_pubkey", "uint256"}}; - if (ctx.storeTimestampInC4()) { + if (ctx.storeTimestampInC4()) offset.emplace_back("_timestamp", "uint64"); - } - offset.emplace_back("_constructorFlag", "bool"); + + if (ctx.hasConstructor()) + offset.emplace_back("_constructorFlag", "bool"); for (const auto& [name, type] : offset) { Json::Value field(Json::objectValue); diff --git a/compiler/libsolidity/codegen/TVMCommons.cpp b/compiler/libsolidity/codegen/TVMCommons.cpp index d5f882b7..47e8c666 100644 --- a/compiler/libsolidity/codegen/TVMCommons.cpp +++ b/compiler/libsolidity/codegen/TVMCommons.cpp @@ -80,6 +80,17 @@ FunctionDefinition const* getSuperFunction( return prev; } +FunctionDefinition const* hasConstructor(ContractDefinition const& contract) { + for (ContractDefinition const* c : getContractsChain(&contract)) { + for (const auto f : c->definedFunctions()) { + if (f->isConstructor()) + return f; + } + } + return nullptr; +} + + const Type *getType(const VariableDeclaration *var) { return var->annotation().type; } @@ -235,16 +246,6 @@ std::vector stateVariables(ContractDefinition const return variableDeclarations; } -vector> -getContractFunctionPairs(ContractDefinition const *contract) { - vector> result; - for (ContractDefinition const* c : getContractsChain(contract)) { - for (const auto f : c->definedFunctions()) - result.emplace_back(f, c); - } - return result; -} - bool isSuper(Expression const *expr) { if (auto identifier = to(expr)) { return identifier->name() == "super"; @@ -296,6 +297,7 @@ bool isEmptyFunction(FunctionDefinition const* f) { std::vector convertArray(std::vector> const& arr) { std::vector ret; + ret.reserve(arr.size()); for (const auto& v : arr) ret.emplace_back(v.get()); return ret; @@ -304,6 +306,7 @@ convertArray(std::vector> const& arr) { std::vector getTypesFromVarDecls(std::vector> const& arr) { std::vector ret; + ret.reserve(arr.size()); for (const auto& v : arr) ret.emplace_back(v->type()); return ret; @@ -479,15 +482,32 @@ int qtyWithoutLoc(std::vector> const& arr) { return qtyWithoutLoc(arr.begin(), arr.end()); } -std::string StrUtils::toBitString(bigint value, int bitlen) { +std::optional StrUtils::toBitString(bigint value, int bitlen, bool isSign) { + if (bitlen == 0) { + if (value == 0) + return ""; + return {}; + } + bigint const one = 1; + if (isSign) { + bigint p2 = one << (bitlen - 1); + if (!(-p2 <= value && value <= p2 - 1)) + return {}; + } else { + bigint p2 = one << bitlen; + if (!(0 <= value && value < p2)) + return {}; + } if (value < 0) { value = pow(bigint(2), bitlen) + value; + solAssert(value > 0, ""); } std::string s; for (int i = 0; i < bitlen; ++i) { s += value % 2 == 0 ? "0" : "1"; value /= 2; } + solAssert(value == 0, ""); std::reverse(s.rbegin(), s.rbegin() + bitlen); return s; } @@ -517,32 +537,23 @@ std::string StrUtils::toBitString(const std::string& slice) { std::string bitString; if (slice.at(0) == 'x') { for (std::size_t i = 1; i < slice.size(); ++i) { - if (i + 2 == slice.size() && slice[i + 1] == '_') { + if (slice.at(i) == '_') { + while (!bitString.empty() && *bitString.rbegin() == '0') // trim last zeroes + bitString.pop_back(); + if (!bitString.empty()) // trim last one + bitString.pop_back(); + solAssert(i + 1 == slice.size(), ""); + } else { size_t pos{}; - int value = std::stoi(slice.substr(i, 1), &pos, 16); + auto sss = slice.substr(i, 1); + int value = std::stoi(sss, &pos, 16); solAssert(pos == 1, ""); - int bitLen = 4; - while (true) { - bool isOne = value % 2 == 1; - --bitLen; - value /= 2; - if (isOne) { - break; - } - } - bitString += StrUtils::toBitString(value, bitLen); - break; + bitString += StrUtils::toBitString(value, 4, false).value(); } - size_t pos{}; - auto sss = slice.substr(i, 1); - int value = std::stoi(sss, &pos, 16); - solAssert(pos == 1, ""); - bitString += StrUtils::toBitString(value, 4); } + } else if (isIn(slice, "0", "1")) { + bitString = slice; } else { - if (isIn(slice, "0", "1")) { - return slice; - } solUnimplemented(""); } return bitString; @@ -602,7 +613,7 @@ std::string StrUtils::literalToSliceAddress(bigint const& value) { s += "10"; s += "0"; s += std::string(8, '0'); - s += StrUtils::toBitString(value); + s += StrUtils::toBitString(value, 256, false).value(); return s; } @@ -616,6 +627,13 @@ bigint StrUtils::toBigint(const std::string& binStr) { return res; } +std::optional StrUtils::toNegBigint(const std::string& binStr) { + if (binStr.at(0) != '1') + return {}; + bigint res = StrUtils::toBigint(binStr) - (bigint(1) << binStr.length()); + return res; +} + std::string StrUtils::toBinString(bigint num) { solAssert(num >= 0, ""); std::string res; diff --git a/compiler/libsolidity/codegen/TVMCommons.hpp b/compiler/libsolidity/codegen/TVMCommons.hpp index 41362baa..121fd213 100644 --- a/compiler/libsolidity/codegen/TVMCommons.hpp +++ b/compiler/libsolidity/codegen/TVMCommons.hpp @@ -161,9 +161,6 @@ realDictKeyValue(Type const* type); std::vector getContractsChain(ContractDefinition const* contract); std::vector stateVariables(ContractDefinition const* contract, bool _withNoStorage); -std::vector> -getContractFunctionPairs(ContractDefinition const* contract); - bool isSuper(Expression const* expr); bool isAddressThis(const FunctionCall* funCall); @@ -173,6 +170,8 @@ FunctionDefinition const* getSuperFunction( const std::string& hexName ); +FunctionDefinition const* hasConstructor(ContractDefinition const& contract); + [[noreturn]] void cast_error(const ASTNode& node, const std::string& error_message); @@ -377,7 +376,7 @@ int qtyWithoutLoc(std::vector>::const_iterator beg, int qtyWithoutLoc(std::vector> const& arr); namespace StrUtils { - std::string toBitString(bigint value, int bitlen = 256); + std::optional toBitString(bigint value, int bitlen, bool isSign); std::string binaryStringToSlice(const std::string & s); std::string toBitString(const std::string& slice); std::optional unitSlices(const std::string& sliceA, const std::string& sliceB); @@ -387,6 +386,7 @@ namespace StrUtils { std::string boolToBinaryString(bool value); std::string literalToSliceAddress(bigint const& value); bigint toBigint(const std::string& binStr); + std::optional toNegBigint(const std::string& binStr); std::string toBinString(bigint num); std::string stringToHex(const std::string& str); } @@ -405,4 +405,5 @@ namespace MathConsts { bool isFitUselessUnary(Type const* common, Token op); bool isFitUseless(Type const* left, Type const* right, Type const* common, Token op); + } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.cpp b/compiler/libsolidity/codegen/TVMContractCompiler.cpp index 967e1be9..59ae3895 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.cpp @@ -105,7 +105,7 @@ TVMContractCompiler::generateContractCode( fillInlineFunctions(ctx, contract); // generate global constructor which inlines all contract's constructors - if (!ctx.isStdlib()) { + if (!ctx.isStdlib() && ctx.hasConstructor()) { StackPusher pusher{&ctx}; TVMConstructorCompiler compiler(pusher); Pointer f = compiler.generateConstructors(); @@ -150,8 +150,8 @@ TVMContractCompiler::generateContractCode( ReasonOfOutboundMessage::RemoteCallInternal); ctx.addPublicFunction(functionId, _function->name()); } - std::string const functionName = ctx.getFunctionInternalName(_function); - functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, _function, functionName)); + auto const[functionName, id] = ctx.functionInternalName(_function); + functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, _function, functionName, id)); } } } @@ -200,11 +200,11 @@ TVMContractCompiler::generateContractCode( "Modifiers for library functions are not supported yet."); } if (!function->parameters().empty()) { - const std::string name = TVMCompilerContext::getLibFunctionName(function, true); + const std::string name = ctx.functionInternalName(function, true).first; functions.emplace_back(TVMFunctionCompiler::generateLibFunctionWithObject(ctx, function, name)); } - const std::string name = TVMCompilerContext::getLibFunctionName(function, false); - functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name)); + auto const [name, id] = ctx.functionInternalName(function, false); + functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name, id)); } } } @@ -220,11 +220,11 @@ TVMContractCompiler::generateContractCode( cast_error(*function->modifiers().at(0).get(), "Modifiers for free functions are not supported yet."); if (!function->parameters().empty()) { - const std::string name = ctx.getFunctionInternalName(function, true); + const std::string name = ctx.functionInternalName(function, true).first; functions.emplace_back(TVMFunctionCompiler::generateLibFunctionWithObject(ctx, function, name)); } - const std::string name = ctx.getFunctionInternalName(function, false); - functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name)); + auto const [name, id] = ctx.functionInternalName(function, false); + functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name, id)); } } } @@ -306,22 +306,23 @@ void TVMContractCompiler::optimizeCode(Pointer& c) { c->accept(lce); for (int i = 0; i < 10; ++i) { // TODO - PeepholeOptimizer::withBlockPush = false; // TODO - PeepholeOptimizer peepHole{false, false, false}; + PeepholeOptimizer peepHole{{}}; c->accept(peepHole); - PeepholeOptimizer::withBlockPush = true; StackOptimizer opt; c->accept(opt); } - PeepholeOptimizer peepHole = PeepholeOptimizer{false, false, false}; + PeepholeOptimizer peepHole = PeepholeOptimizer{{}}; + c->accept(peepHole); + + peepHole = PeepholeOptimizer{1 << OptFlags::UnpackOpaque}; c->accept(peepHole); - peepHole = PeepholeOptimizer{true, false, false}; // TODO provide mask with bits + peepHole = PeepholeOptimizer{(1 << OptFlags::UnpackOpaque) | (1 << OptFlags::UseCompoundOpcodes)}; c->accept(peepHole); - peepHole = PeepholeOptimizer{true, true, true}; // TODO provide mask with bits + peepHole = PeepholeOptimizer{(1 << OptFlags::OptimizeSlice) | (1 << OptFlags::UseCompoundOpcodes)}; c->accept(peepHole); LocSquasher sq = LocSquasher{}; diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp index 77eabf55..853d58a4 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp @@ -125,7 +125,7 @@ void TVMExpressionCompiler::visit2(Literal const &_node) { m_pusher.pushNaN(); break; case Type::Category::EmptyMap: - m_pusher << "NEWDICT"; + m_pusher << "NULL"; break; case Type::Category::StringLiteral: visitStringLiteralAbiV2(_node); @@ -178,7 +178,7 @@ void TVMExpressionCompiler::visitHonest(TupleExpression const& _tupleExpression, // values... m_pusher.pushInt(0); // values... index - m_pusher << "NEWDICT"; + m_pusher << "NULL"; // values... index dict m_pusher.pushInt(n); // values... index dict totalSize @@ -251,7 +251,7 @@ void TVMExpressionCompiler::visit2(Identifier const &_identifier) { } else if (auto funDecl = to(_identifier.annotation().referencedDeclaration)) { m_pusher.pushPrivateFunctionId(*funDecl, false); } else { - cast_error(_identifier, "Unsupported identifier: " + name); + cast_error(_identifier, "Unsupported identifier: " + name + "."); } } @@ -878,7 +878,7 @@ void TVMExpressionCompiler::visitMsgMagic(MemberAccess const &_node) { "ADDCONST -5", "PICK", "PUSHCONT {", - " NEWDICT", + " NULL", "}", "PUSHCONT {", " DEPTH", @@ -1020,7 +1020,7 @@ void TVMExpressionCompiler::visit2(MemberAccess const &_node) { } } - cast_error(_node, "Not supported"); + cast_error(_node, "Not supported."); } bool TVMExpressionCompiler::checkForAddressMemberAccess(MemberAccess const &_node, Type::Category category) { @@ -1146,12 +1146,16 @@ void TVMExpressionCompiler::visit2(IndexAccess const &indexAccess) { } else if (baseType->category() == Type::Category::TvmVector) { acceptExpr(&indexAccess.baseExpression()); // tuple const auto& val = ExprUtils::constValue(*indexAccess.indexExpression()); - if (val.has_value() && val <= 15) { + if (val.has_value() && val <= 15) m_pusher.indexWithExcep(boost::lexical_cast(val.value().str())); - } else { + else { acceptExpr(indexAccess.indexExpression()); // vector index m_pusher << "INDEXVAR"; } + auto vectorType = to(indexAccess.baseExpression().annotation().type); + auto valueTupleType = to(vectorType->valueType()); + if (valueTupleType) + m_pusher << "UNTUPLE " + toString(valueTupleType->components().size()); return; } else if (baseType->category() == Type::Category::FixedBytes) { acceptExpr(&indexAccess.baseExpression()); // integer diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.cpp b/compiler/libsolidity/codegen/TVMFunctionCall.cpp index a75344e3..3b6c1841 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.cpp @@ -132,6 +132,8 @@ void FunctionCallCompiler::compile() { } else { reportError(); } + } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "bls") { + blsFunction(); } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "rnd") { rndFunction(*m_memberAccess); } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "gosh") { @@ -402,7 +404,7 @@ void FunctionCallCompiler::superFunctionCall(MemberAccess const &_node) { someFunDecl->externalIdentifierHex() ); solAssert(superFunc, ""); - std::string functionName = m_pusher.ctx().getFunctionInternalName(superFunc, true); + std::string functionName = m_pusher.ctx().functionInternalName(superFunc, true).first; m_pusher.pushCallOrCallRef(superFunc, std::nullopt, true); } @@ -421,8 +423,8 @@ void FunctionCallCompiler::addressMethods(MemberAccess const &_node) { const auto& len = ExprUtils::constValue(*m_arguments.at(1)); if (num.has_value() && len.has_value()) { std::string addr = "01"; - addr += StrUtils::toBitString(u256(len.value()), 9); - addr += StrUtils::toBitString(u256(num.value()), int(len.value())); + addr += StrUtils::toBitString(len.value(), 9, false).value(); + addr += StrUtils::toBitString(num.value(), int(len.value()), false).value(); m_pusher.pushSlice("x" + StrUtils::binaryStringToSlice(addr)); } else { pushArgs(); @@ -439,9 +441,10 @@ void FunctionCallCompiler::addressMethods(MemberAccess const &_node) { const auto& wid = ExprUtils::constValue(*m_arguments.at(0)); const auto& val = ExprUtils::constValue(*m_arguments.at(1)); if (wid.has_value() && val.has_value()) { + // TODO delete this std::string addr = "100"; - addr += StrUtils::toBitString(u256(wid.value()), 8); - addr += StrUtils::toBitString(u256(val.value()), 256); + addr += StrUtils::toBitString(wid.value(), 8, true).value(); + addr += StrUtils::toBitString(val.value(), 256, false).value(); m_pusher.pushSlice("x" + StrUtils::binaryStringToSlice(addr)); } else { pushArgs(true); @@ -518,7 +521,7 @@ std::function FunctionCallCompiler::generateDataSection( if (data_map_supported) { return [pushKey, this, vars, ct, getDeclAndIndex]() { // creat dict with variable values - m_pusher << "NEWDICT"; + m_pusher << "NULL"; // stack: builder dict IntegerType keyType = getKeyTypeOfC4(); Type const* valueType = TypeProvider::uint256(); @@ -588,7 +591,8 @@ std::function FunctionCallCompiler::generateDataSection( m_pusher << "STU 256"; if (ctx.storeTimestampInC4()) m_pusher << "STU 64"; - m_pusher << "STZERO"; // constructor flag + if (hasConstructor(ct->contractDefinition())) + m_pusher << "STSLICECONST 0"; // constructor flag const std::vector& memberTypes = ctx.c4StateVariableTypes(); if (!memberTypes.empty()) { ChainDataEncoder encoder{&m_pusher}; @@ -607,44 +611,50 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa std::function pushSendrawmsgFlag; FunctionDefinition const* functionDefinition{}; std::optional callbackFunctionId; + std::function appendStateInit; if (auto functionOptions = to(&_functionCall.expression())) { auto memberAccess = to(&functionOptions->expression()); - if (!memberAccess) { + if (!memberAccess) return false; - } // parse options they are stored in two vectors: names and options for (const auto &option: functionOptions->names()) - if (!isIn(*option, "flag", "value", "currencies", "bounce", "callback")) + if (!isIn(*option, "stateInit", "flag", "value", "currencies", "bounce", "callback")) cast_error(_functionCall, "Unsupported function call option: " + *option); + // Search for stateInit option + if (Expression const* stateInit = findOption("stateInit")) + appendStateInit = [this, stateInit](){ + // Either StateInit ^StateInit + m_pusher << "STSLICECONST 1"; // ^StateInit + acceptExpr(stateInit); + m_pusher.pushS(0); + checkStateInit(); + m_pusher << "STREFR"; + }; // Search for bounce option - if (Expression const* bounce = findOption("bounce")) { + if (Expression const* bounce = findOption("bounce")) exprs[TvmConst::int_msg_info::bounce] = bounce; - } else { + else constParams[TvmConst::int_msg_info::bounce] = "1"; - } // Search for currencies option - if (Expression const* currencies = findOption("currencies")) { + if (Expression const* currencies = findOption("currencies")) exprs[TvmConst::int_msg_info::currency] = currencies; - } else { + else constParams[TvmConst::int_msg_info::currency] = "0"; - } // Search for value (ton) option if (Expression const* valueExpr = findOption("value")) { const auto& value = ExprUtils::constValue(*valueExpr); - if (value.has_value()) { + if (value.has_value()) constParams[TvmConst::int_msg_info::tons] = StrUtils::tonsToBinaryString(u256(value.value())); - } else { + else exprs[TvmConst::int_msg_info::tons] = valueExpr; - } - } else { + } else constParams[TvmConst::int_msg_info::tons] = getDefaultMsgValue(); - } // remote_addr exprs[TvmConst::int_msg_info::dest] = &memberAccess->expression(); @@ -707,7 +717,7 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa ); }; - m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag, nullptr); + m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag, appendStateInit); return true; } @@ -827,7 +837,7 @@ void FunctionCallCompiler::abiBuildIntMsg() { if (stateInit != -1) { appendStateInit = [&](){ // Either StateInit ^StateInit - m_pusher << "STONE"; // ^StateInit + m_pusher << "STSLICECONST 1"; // ^StateInit pushArgAndConvert(stateInit, "stateInit"); m_pusher.pushS(0); checkStateInit(); @@ -1003,9 +1013,8 @@ void FunctionCallCompiler::abiDecodeData() { int FunctionCallCompiler::decodeData() { std::vector stateVarTypes; auto retTuple = to(m_retType); - for (Type const* type : retTuple->components()) { + for (Type const* type : retTuple->components()) stateVarTypes.push_back(type); - } // lvalue.. slice ChainDataDecoder decoder{&m_pusher}; @@ -1059,6 +1068,12 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { if (memberName == "empty") { acceptExpr(&_node.expression()); m_pusher << "SEMPTY"; + } else if (memberName == "bitEmpty") { + acceptExpr(&_node.expression()); + m_pusher << "SDEMPTY"; + } else if (memberName == "refEmpty") { + acceptExpr(&_node.expression()); + m_pusher << "SREMPTY"; } else if (memberName == "dataSize") { acceptExpr(&_node.expression()); pushArgAndConvert(0); @@ -1077,6 +1092,13 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { acceptExpr(&_node.expression()); pushArgAndConvert(0); m_pusher << "SDLEXCMP"; + } else if (memberName == "startsWithOne") { + acceptExpr(&_node.expression()); + m_pusher << "SDFIRST"; + } else if (memberName == "startsWith") { + acceptExpr(&_node.expression()); + pushArgs(); + m_pusher << "SDPFXREV"; } else if (memberName == "hasNBits") { acceptExpr(&_node.expression()); pushArgAndConvert(0); @@ -1410,10 +1432,17 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { } void FunctionCallCompiler::tvmVectorMethods() { + auto vectorType = to(m_memberAccess->expression().annotation().type); + auto valueTupleType = to(vectorType->valueType()); + ASTString const& memberName = m_memberAccess->memberName(); if (memberName == "push") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); - acceptExpr(m_arguments[0].get()); + pushArgs(); + if (valueTupleType) { + // lValue... vector element... + m_pusher << "TUPLE " + toString(valueTupleType->components().size()); + } // lValue... vector element m_pusher << "TPUSH"; m_exprCompiler.collectLValue(lValueInfo, true); @@ -1424,6 +1453,8 @@ void FunctionCallCompiler::tvmVectorMethods() { m_pusher.blockSwap(lValueInfo.stackSizeDiff, 1); m_exprCompiler.collectLValue(lValueInfo, true); // lastElement + if (valueTupleType) + m_pusher << "UNTUPLE " + toString(valueTupleType->components().size()); } else if (memberName == "length") { acceptExpr(&m_memberAccess->expression()); m_pusher << "TLEN"; @@ -1434,6 +1465,8 @@ void FunctionCallCompiler::tvmVectorMethods() { } else if (memberName == "last") { acceptExpr(&m_memberAccess->expression()); m_pusher << "LAST"; + if (valueTupleType) + m_pusher << "UNTUPLE " + toString(valueTupleType->components().size()); } else { solUnimplemented(""); } @@ -1816,6 +1849,7 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { m_pusher.startContinuation(); { // opt default + // TODO correct m_pusher.dropUnder(retQty, retQty); !!!!! m_pusher.dropUnder(1, retQty); // default... m_pusher.fixStack(+1); } @@ -1963,7 +1997,7 @@ void FunctionCallCompiler::addressMethod() { auto setAppendStateInit = [&](Expression const* expr) { appendStateInit = [expr, this](){ // Either StateInit ^StateInit - m_pusher << "STONE"; // ^StateInit + m_pusher << "STSLICECONST 1"; // ^StateInit acceptExpr(expr); m_pusher.pushS(0); checkStateInit(); @@ -2228,7 +2262,7 @@ bool FunctionCallCompiler::checkForTvmConfigParamFunction(MemberAccess const &_n m_pusher << "PUSHINT 0"; // total m_pusher << "PUSHINT 0"; // main m_pusher << "PUSHINT 0"; // total_weight - m_pusher << "NEWDICT"; // ValidatorDescr + m_pusher << "NULL"; // ValidatorDescr m_pusher << "FALSE"; // m_pusher.endContinuation(); @@ -2304,35 +2338,240 @@ void FunctionCallCompiler::rndFunction(MemberAccess const &_node) { } } +void FunctionCallCompiler::blsFunction() { + bool useTuple = + !m_arguments.empty() && + m_arguments.at(0)->annotation().type->category() == Type::Category::TvmVector; + + switch (m_funcType->kind()) { + case FunctionType::Kind::BlsVerify: + pushArgs(); + m_pusher << "BLS_VERIFY"; + break; + case FunctionType::Kind::BlsAggregate: + pushArgs(); + if (useTuple) + m_pusher.push(createNode(std::vector{ + "DUP", + "TLEN", + "EXPLODEVAR", + "BLS_AGGREGATE", + }, 1, 1, false)); + else { + m_pusher.pushInt(m_arguments.size()); + m_pusher.push(createNode(std::vector{ + "BLS_AGGREGATE" + }, m_arguments.size() + 1, 1, false)); + } + break; + case FunctionType::Kind::BlsFastAggregateVerify: + if (useTuple) { + pushArgs(); + m_pusher.push(createNode(std::vector{ + // pks msg sig + "ROT", // msg sig pks + "DUP", // msg sig pks pks + "TLEN", // msg sig pks n + "EXPLODEVAR", // msg sig pk1 .. pksN n + "PUSHINT 2", // msg sig pk1 .. pksN n 2 + "PUSH S1", // msg sig pk1 .. pksN n 2 n + "INC", // msg sig pk1 .. pksN n 2 n+1 + "BLKSWX", + "BLS_FASTAGGREGATEVERIFY", + }, 3, 1, false)); + } else { + int n = m_arguments.size() - 2; + for (int i = 0; i < n; ++i) + pushArgAndConvert(i); + m_pusher.pushInt(n); + pushArgAndConvert(n); + pushArgAndConvert(n + 1); + m_pusher.push(createNode(std::vector{ + "BLS_FASTAGGREGATEVERIFY", + }, n + 3, 1, false)); + } + break; + case FunctionType::Kind::BlsAggregateVerify: + if (useTuple) { + pushArgs(); + m_pusher.push(createNode(std::vector{ + // pksMsgs sig + "SWAP", // sig pksMsgs + "DUP", // sig pksMsgs pksMsgs + "TLEN", // sig pksMsgs n + "EXPLODEVAR", // sig (pk, msg)... n + "DUP", // sig (pk, msg)... n i + "ADDCONST 1", // sig (pk, msg)... n i // i = n+1..1 + "PUSH S1", // sig (pk, msg)... n i n + "PUSHCONT {", + // sig (pk, msg)... n i + "DUP", // sig (pk, msg)... n i i + "\tROLLX", // sig (pk, msg)... n i (pk[i], msg[i]) + "\tUNPAIR", // sig (pk, msg)... n i pk[i] msg[i] + "\tPUSH S2", // sig (pk, msg)... n i pk[i] msg[i] i + "\tPUSHINT 2",// sig (pk, msg)... n i pk[i] msg[i] i 2 + "\tBLKSWX", // sig (pk, msg)... n i + "\tDEC", + "}", + "REPEAT", + // sig pk0, msg0 ... pkN, msgN n 1 + "PUSH S1", // sig pk0, msg0 ... pkN, msgN n 1 n + "MULCONST 2", // sig pk0, msg0 ... pkN, msgN n 1 2*n + "ADD", // sig pk0, msg0 ... pkN, msgN n 2*n+1 + "ROLLX", // pk0, msg0 ... pkN, msgN n sig + "BLS_AGGREGATEVERIFY", + }, 2, 1, false)); + } else { + int n = (m_arguments.size() - 1) / 2; + for (int i = 0; i < 2 * n; ++i) + pushArgAndConvert(i); + m_pusher.pushInt(n); + pushArgAndConvert(2 * n); + solAssert(2 * n == int(m_arguments.size()) - 1, "ddddd"); + m_pusher.push(createNode(std::vector{ + "BLS_AGGREGATEVERIFY", + }, 2 * n + 2, 1, false)); + } + break; + case FunctionType::Kind::BlsG1Add: + pushArgs(); + m_pusher << "BLS_G1_ADD"; + break; + case FunctionType::Kind::BlsG1Sub: + pushArgs(); + m_pusher << "BLS_G1_SUB"; + break; + case FunctionType::Kind::BlsG1Neg: + pushArgs(); + m_pusher << "BLS_G1_NEG"; + break; + case FunctionType::Kind::BlsG1Mul: + pushArgs(); + m_pusher << "BLS_G1_MUL"; + break; + case FunctionType::Kind::BlsMapToG1: + pushArgs(); + m_pusher << "BLS_MAP_TO_G1"; + break; + case FunctionType::Kind::BlsG1IsZero: + pushArgs(); + m_pusher << "BLS_G1_ISZERO"; + break; + case FunctionType::Kind::BlsG1InGroup: + pushArgs(); + m_pusher << "BLS_G1_INGROUP"; + break; + + case FunctionType::Kind::BlsG2Add: + pushArgs(); + m_pusher << "BLS_G2_ADD"; + break; + case FunctionType::Kind::BlsG2Sub: + pushArgs(); + m_pusher << "BLS_G2_SUB"; + break; + case FunctionType::Kind::BlsG2Neg: + pushArgs(); + m_pusher << "BLS_G2_NEG"; + break; + case FunctionType::Kind::BlsG2Mul: + pushArgs(); + m_pusher << "BLS_G2_MUL"; + break; + case FunctionType::Kind::BlsMapToG2: + pushArgs(); + m_pusher << "BLS_MAP_TO_G2"; + break; + case FunctionType::Kind::BlsG2IsZero: + pushArgs(); + m_pusher << "BLS_G2_ISZERO"; + break; + case FunctionType::Kind::BlsG2InGroup: + pushArgs(); + m_pusher << "BLS_G2_INGROUP"; + break; + case FunctionType::Kind::BlsG1Zero: + pushArgs(); + m_pusher << "BLS_G1_ZERO"; + break; + case FunctionType::Kind::BlsG2Zero: + pushArgs(); + m_pusher << "BLS_G2_ZERO"; + break; + case FunctionType::Kind::BlsPushR: + pushArgs(); + m_pusher << "BLS_PUSHR"; + break; + case FunctionType::Kind::BlsG1MultiExp: + case FunctionType::Kind::BlsG2MultiExp: { + pushArgs(); + std::string opcode = m_funcType->kind() == FunctionType::Kind::BlsG1MultiExp? "BLS_G1_MULTIEXP" : "BLS_G2_MULTIEXP"; + if (useTuple) { + m_pusher.push(createNode(std::vector{ + // xs + "DUP", // xs xs + "TLEN", // xs n + "EXPLODEVAR", // (x, s)... n + "DUP", // (x, s)... n i + "ADDCONST 1", // (x, s)... n i // i = n+1..1 + "PUSH S1", // (x, s)... n i n + "PUSHCONT {", + // (x, s)... n i + "DUP", // (x, s)... n i i + "\tROLLX", // (x, s)... n i (x[i], s[i]) + "\tUNPAIR", // (x, s)... n i x[i] s[i] + "\tPUSH S2", // (x, s)... n i x[i] s[i] i + "\tPUSHINT 2",// (x, s)... n i x[i] s[i] i 2 + "\tBLKSWX", // (x, s)... n i + "\tDEC", + "}", + "REPEAT", + // x0, s0 ... xN, sN n 1 + "DROP", // x0, s0 ... xN, sN n + opcode + }, 1, 1, false)); + } else { + m_pusher.pushInt(m_arguments.size() / 2); + m_pusher.push(createNode(std::vector{ + opcode + }, m_arguments.size() + 1, 1, false)); + } + break; + } + default: + solUnimplemented("Unsupported bls function call"); + } +} + void FunctionCallCompiler::goshFunction() { auto typeToOpcode = [&]() -> std::string { switch (m_funcType->kind()) { - case FunctionType::Kind::GoshDiff: - return "DIFF"; - case FunctionType::Kind::GoshApplyPatch: - return "DIFF_PATCH"; - case FunctionType::Kind::GoshZip: - return "ZIP"; - case FunctionType::Kind::GoshUnzip: - return "UNZIP"; - case FunctionType::Kind::GoshZipDiff: - return "DIFF_ZIP"; - case FunctionType::Kind::GoshApplyZipPatch: - return "DIFF_PATCH_ZIP"; - case FunctionType::Kind::GoshApplyPatchQ: - return "DIFF_PATCHQ"; - case FunctionType::Kind::GoshApplyZipPatchQ: - return "DIFF_PATCH_ZIPQ"; - case FunctionType::Kind::GoshApplyBinPatch: - return "DIFF_PATCH_BINARY"; - case FunctionType::Kind::GoshApplyBinPatchQ: - return "DIFF_PATCH_BINARYQ"; - case FunctionType::Kind::GoshApplyZipBinPatch: - return "DIFF_PATCH_BINARY_ZIP"; - case FunctionType::Kind::GoshApplyZipBinPatchQ: - return "DIFF_PATCH_BINARY_ZIPQ"; - default: - solUnimplemented("Unsupported function call"); + case FunctionType::Kind::GoshDiff: + return "DIFF"; + case FunctionType::Kind::GoshApplyPatch: + return "DIFF_PATCH"; + case FunctionType::Kind::GoshZip: + return "ZIP"; + case FunctionType::Kind::GoshUnzip: + return "UNZIP"; + case FunctionType::Kind::GoshZipDiff: + return "DIFF_ZIP"; + case FunctionType::Kind::GoshApplyZipPatch: + return "DIFF_PATCH_ZIP"; + case FunctionType::Kind::GoshApplyPatchQ: + return "DIFF_PATCHQ"; + case FunctionType::Kind::GoshApplyZipPatchQ: + return "DIFF_PATCH_ZIPQ"; + case FunctionType::Kind::GoshApplyBinPatch: + return "DIFF_PATCH_BINARY"; + case FunctionType::Kind::GoshApplyBinPatchQ: + return "DIFF_PATCH_BINARYQ"; + case FunctionType::Kind::GoshApplyZipBinPatch: + return "DIFF_PATCH_BINARY_ZIP"; + case FunctionType::Kind::GoshApplyZipBinPatchQ: + return "DIFF_PATCH_BINARY_ZIPQ"; + default: + solUnimplemented("Unsupported function call"); } }; @@ -2728,7 +2967,7 @@ void FunctionCallCompiler::abiFunction() { break; } default: - cast_error(m_functionCall, "Not supported"); + cast_error(m_functionCall, "Not supported."); } } @@ -2767,6 +3006,9 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { if (!isFitUseless(leftType, rightType, m_retType, Token::Div) && !m_pusher.ctx().ignoreIntegerOverflow()) m_pusher.checkFit(m_retType); + } else if (isIn(_node.memberName(), "mulmod")) { + pushArgs(); + m_pusher << prefix + "MULMOD"; } else if (isIn(_node.memberName(), "muldiv", "muldivr", "muldivc")) { pushArgs(); m_pusher << prefix + boost::to_upper_copy(_node.memberName()); @@ -3219,7 +3461,7 @@ bool FunctionCallCompiler::checkLocalFunctionOrLibCallOrFuncVarCall() { int returnCnt = m_funcType->returnParameterTypes().size(); int paramCnt = m_funcType->parameterTypes().size(); m_pusher.pushC3(); - m_pusher.execute(paramCnt + 2, returnCnt); + m_pusher.callx(paramCnt + 2, returnCnt); } else { return false; } @@ -3375,7 +3617,7 @@ void FunctionCallCompiler::deployNewContract( if (wid.index() == 0) { int8_t w = std::get<0>(wid); std::string binWID = "100"; - binWID += StrUtils::toBitString(u256(w), 8); + binWID += StrUtils::toBitString(w, 8, true).value(); m_pusher << "NEWC"; m_pusher << "STSLICECONST x" + StrUtils::binaryStringToSlice(binWID); } else { @@ -3523,7 +3765,7 @@ void FunctionCallCompiler::honestArrayCreation(bool onlyDict) { pushArgAndConvert(0); // N DataType const& dataType = m_pusher.pushDefaultValueForDict(&key, arrayBaseType); // N value m_pusher.pushInt(0); // N value iter - m_pusher << "NEWDICT"; // N value iter dict + m_pusher << "NULL"; // N value iter dict m_pusher.pushS(3); // N value iter dict N solAssert(stackSize + 5 == m_pusher.stackSize(), ""); @@ -3566,8 +3808,8 @@ bool FunctionCallCompiler::structMethodCall() { void FunctionCallCompiler::encodeStateInit(std::map> exprs) { solAssert(exprs.count(StateInitMembers::Special) == 0, ""); solAssert(exprs.count(StateInitMembers::Library) == 0, ""); - solAssert(exprs.count(StateInitMembers::Code) == 1, "Code must present"); - solAssert(exprs.count(StateInitMembers::Data) == 1, "Data must present"); + solAssert(exprs.count(StateInitMembers::Code) == 1, "Code must be present"); + solAssert(exprs.count(StateInitMembers::Data) == 1, "Data must be present"); const int ss = m_pusher.stackSize(); @@ -3597,7 +3839,7 @@ void FunctionCallCompiler::encodeStateInit(std::map TVMFunctionCompiler::generateDefaultC4(TVMCompilerContext& ctx pusher << "STU 256"; if (ctx.storeTimestampInC4()) pusher << "STU 64"; - pusher << "STZERO"; // constructor flag + if (ctx.hasConstructor()) + pusher << "STSLICECONST 0"; // constructor flag const std::vector& memberTypes = ctx.c4StateVariableTypes(); if (!memberTypes.empty()) { ChainDataEncoder encoder{&pusher}; @@ -269,7 +270,8 @@ Pointer TVMFunctionCompiler::generateFunction( TVMCompilerContext& ctx, FunctionDefinition const* function, - std::string const& name + std::string const& name, + uint32_t id ) { ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; @@ -280,13 +282,12 @@ TVMFunctionCompiler::generateFunction( int take = function->parameters().size(); int ret = function->returnParameters().size(); ctx.resetCurrentFunction(); - uint32_t id = ChainDataEncoder::toHash256(name); return createNode(take, ret, name, id, Function::FunctionType::Fragment, pusher.getBlock(), function); } Pointer TVMFunctionCompiler::generateOnCodeUpgrade(TVMCompilerContext& ctx, FunctionDefinition const* function) { - const std::string name = ctx.getFunctionInternalName(function, false); + const auto [name, id] = ctx.functionInternalName(function, false); ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, 0, function, false, true, 0}; @@ -297,7 +298,6 @@ TVMFunctionCompiler::generateOnCodeUpgrade(TVMCompilerContext& ctx, FunctionDefi pusher._throw("THROW 0"); int take = function->parameters().size(); ctx.resetCurrentFunction(); - uint32_t id = function->functionID() ? function->functionID().value() : ChainDataEncoder::toHash256(name); return createNode(take, 0, name, id, Function::FunctionType::OnCodeUpgrade, pusher.getBlock(), function); } @@ -321,7 +321,7 @@ TVMFunctionCompiler::generateOnTickTock(TVMCompilerContext& ctx, FunctionDefinit } TVMFunctionCompiler funCompiler{pusher, 0, function, false, false, 0}; - funCompiler.setCopyleftAndTryCatch(); + funCompiler.setCopyleft(); funCompiler.setGlobSenderAddressIfNeed(); funCompiler.visitFunctionWithModifiers(); @@ -364,7 +364,8 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef pusher.fixStack(+1); // slice with args pusher.fixStack(+1); // functionId pusher.drop(); // drop function id - pusher.checkCtorCalled(); + if (ctx.hasConstructor()) + pusher.checkCtorCalled(); funCompiler.pushC4ToC7IfNeed(); funCompiler.pushLocation(*function); @@ -385,7 +386,7 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef int retQty = function->returnParameters().size(); // stack: selector, arg0, arg1, arg2 ... // +1 because function may use selector - pusher.pushFragmentInCallRef(paramQty + 1, retQty + 1, pusher.ctx().getFunctionInternalName(function)); + pusher.pushFragmentInCallRef(paramQty + 1, retQty + 1, pusher.ctx().functionInternalName(function).first); solAssert(pusher.stackSize() == retQty, ""); // emit @@ -514,7 +515,8 @@ Pointer TVMFunctionCompiler::generateReceiveOrFallbackOrOnBounce( ) { StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, 0, function, false, true, 0}; - pusher.checkCtorCalled(); + if (ctx.hasConstructor()) + pusher.checkCtorCalled(); funCompiler.pushC4ToC7IfNeed(); funCompiler.visitFunctionWithModifiers(); funCompiler.updC4IfItNeeds(); @@ -1577,12 +1579,12 @@ void TVMFunctionCompiler::setCtorFlag() { m_pusher.setGlob(TvmConst::C7::ConstructorFlag); } -void TVMFunctionCompiler::setCopyleftAndTryCatch() { +void TVMFunctionCompiler::setCopyleft() { const std::optional>> copyleft = m_pusher.ctx().pragmaHelper().hasCopyleft(); if (copyleft.has_value()) { const std::optional& addr = ExprUtils::constValue(*copyleft.value().at(1)); const std::optional& type = ExprUtils::constValue(*copyleft.value().at(0)); - const std::string addrSlice = "x" + StrUtils::binaryStringToSlice(StrUtils::toBitString(addr.value(), 256)); + const std::string addrSlice = "x" + StrUtils::binaryStringToSlice(StrUtils::toBitString(addr.value(), 256, false).value()); m_pusher.pushSlice(addrSlice); m_pusher.pushInt(type.value()); m_pusher << "COPYLEFT"; @@ -1605,7 +1607,7 @@ Pointer TVMFunctionCompiler::generateMainExternal( StackPusher pusher{&ctx}; TVMFunctionCompiler f{pusher, contract}; - f.setCopyleftAndTryCatch(); + f.setCopyleft(); f.setGlobSenderAddressIfNeed(); pusher.pushS(1); @@ -1758,8 +1760,9 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, contract}; - funCompiler.setCopyleftAndTryCatch(); - funCompiler.setCtorFlag(); + funCompiler.setCopyleft(); + if (ctx.hasConstructor()) + funCompiler.setCtorFlag(); pusher.pushS(2); pusher << "CTOS"; @@ -1906,7 +1909,10 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { } else { m_pusher.pushS(1); m_pusher << "SEMPTY ; isEmpty"; - m_pusher.checkIfCtorCalled(true); + if (m_pusher.ctx().hasConstructor()) + m_pusher.checkIfCtorCalled(true); + else + m_pusher.ifret(); m_pusher.pushS(1); // body -> funcId body' @@ -1915,9 +1921,12 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { callFallback(); m_pusher.endOpaque(1, 2); - // stack: funcId body' - m_pusher.pushS(1); - m_pusher.checkIfCtorCalled(false); + // funcId body' + m_pusher.pushS(1); // funcId body' funcId + if (m_pusher.ctx().hasConstructor()) + m_pusher.checkIfCtorCalled(false); + else + m_pusher.ifNotRet(); } } diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp index 6b55a0c5..bfc74787 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp @@ -43,7 +43,7 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable static Pointer generateBuildTuple(TVMCompilerContext& ctx, std::string const& name, const std::vector& types); static Pointer generateNewArrays(TVMCompilerContext& ctx, std::string const& name, FunctionCall const* arr); static Pointer generateConstArrays(TVMCompilerContext& ctx, std::string const& name, TupleExpression const* arr); - static Pointer generateFunction(TVMCompilerContext& ctx, FunctionDefinition const* function, std::string const& name); + static Pointer generateFunction(TVMCompilerContext& ctx, FunctionDefinition const* function, std::string const& name, uint32_t id); static Pointer generateMainExternal(TVMCompilerContext& ctx, ContractDefinition const *contract); static Pointer generateMainInternal(TVMCompilerContext& ctx, ContractDefinition const *contract); static Pointer generateOnCodeUpgrade(TVMCompilerContext& ctx, FunctionDefinition const* function); @@ -115,7 +115,7 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable void setGlobSenderAddressIfNeed(); void setCtorFlag(); - void setCopyleftAndTryCatch(); + void setCopyleft(); Pointer generateMainExternalForAbiV2(); void pushMsgPubkey(); diff --git a/compiler/libsolidity/codegen/TVMPusher.cpp b/compiler/libsolidity/codegen/TVMPusher.cpp index e6011cf6..95f8fe46 100644 --- a/compiler/libsolidity/codegen/TVMPusher.cpp +++ b/compiler/libsolidity/codegen/TVMPusher.cpp @@ -86,7 +86,8 @@ Pointer StackPusher::generateC7ToC4() { if (ctx().storeTimestampInC4()) { *this << "STU 64"; } - *this << "STONE"; // constructor flag + if (ctx().hasConstructor()) + *this << "STSLICECONST 1"; // constructor flag if (!memberTypes.empty()) { ChainDataEncoder encoder{this}; DecodePositionAbiV2 position{m_ctx->getOffsetC4(), 0, memberTypes}; @@ -659,8 +660,7 @@ void StackPusher::pushSlice(std::string const& data) { } void StackPusher::pushPrivateFunctionId(FunctionDefinition const& funDef, bool isCalledByPoint) { - std::string funName = ctx().getFunctionInternalName(&funDef, isCalledByPoint); - uint32_t id = funDef.functionID().has_value() ? funDef.functionID().value() : ChainDataEncoder::toHash256(funName); + auto const [funName, id] = ctx().functionInternalName(&funDef, isCalledByPoint); pushInt(id); ctx().callGraph().addPrivateFunction(id, funName); } @@ -990,8 +990,14 @@ void StackPusher::popC7() { m_instructions.back().push_back(opcode); } -void StackPusher::execute(int take, int ret) { - auto opcode = createNode("EXECUTE", take, ret); +void StackPusher::callx(int take, int ret) { + auto opcode = createNode("CALLX", take, ret); + change(take, ret); + m_instructions.back().push_back(opcode); +} + +void StackPusher::call(uint32_t id, int take, int ret) { + auto opcode = createNode("CALL " + toString(id), take, ret); change(take, ret); m_instructions.back().push_back(opcode); } @@ -1621,13 +1627,18 @@ void StackPusher::pushCallOrCallRef( std::make_pair(_functionDef->parameters().size(), _functionDef->returnParameters().size()); std::string curFunctionName = ctx().currentFunctionName(); - std::string functionName = ctx().getFunctionInternalName(_functionDef, isCalledByPoint); + auto const [functionName, id] = ctx().functionInternalName(_functionDef, isCalledByPoint); if (_functionDef->name() == "onCodeUpgrade" || m_ctx->callGraph().tryToAddEdge(curFunctionName, functionName) // Does it have a loop? ) { - pushPrivateFunctionId(*_functionDef, isCalledByPoint); - pushC3(); - execute(take + 2, ret); + if (id < (1 << 14)) { + ctx().callGraph().addPrivateFunction(id, functionName); + call(id, take, ret); + } else { + pushPrivateFunctionId(*_functionDef, isCalledByPoint); + pushC3(); + callx(take + 2, ret); + } } else { pushFragmentInCallRef(take, ret, functionName); } @@ -2159,29 +2170,34 @@ bool TVMCompilerContext::isStdlib() const { return m_contract->name() == "stdlib"; } -string TVMCompilerContext::getFunctionInternalName(FunctionDefinition const* _function, bool calledByPoint) const { - if (isStdlib()) - return _function->name(); - +std::pair +TVMCompilerContext::functionInternalName(FunctionDefinition const* _function, bool calledByPoint) const { std::string functionName; - const std::string hexName = _function->externalIdentifierHex(); - ContractDefinition const* contract = _function->annotation().contract; - if (contract && contract->isLibrary()) - functionName = getLibFunctionName(_function, calledByPoint); - else if (calledByPoint && isBaseFunction(_function)) - functionName = _function->annotation().contract->name() + "_" + _function->name() + "_" + hexName; - else if (_function->isFree()) - functionName = (calledByPoint ? "with_obj_" : "") + _function->name() + "_" + hexName + "_free_internal"; - else - functionName = _function->name() + "_" + hexName + "_internal"; - return functionName; -} - -string TVMCompilerContext::getLibFunctionName(FunctionDefinition const* _function, bool withObject) { - std::string name = _function->annotation().contract->name() + - (withObject ? "_with_obj_" : "_no_obj_") + - _function->name() + "_" + _function->externalIdentifierHex(); - return name; + if (isStdlib()) { + functionName = _function->name(); + } else { + const std::string hexName = _function->externalIdentifierHex(); + ContractDefinition const* contract = _function->annotation().contract; + if (contract && contract->isLibrary()) + functionName = _function->annotation().contract->name() + "_" + + (calledByPoint ? "with_obj_" : "") + _function->name() + "_" + hexName; + else if (calledByPoint && isBaseFunction(_function)) + functionName = _function->annotation().contract->name() + "_" + _function->name() + "_" + hexName; + else if (_function->isFree()) + functionName = (calledByPoint ? "with_obj_" : "") + _function->name() + "_" + hexName + "_free_internal"; + else + functionName = _function->name() + "_" + hexName + "_internal"; + } + + uint32_t id; + if (_function->functionID().has_value()) + id = _function->functionID().value(); + else { + id = ChainDataEncoder::toHash256(functionName); + if (_function->name() != "onCodeUpgrade") // to support upgrading old contracts + id &= (1 << 14) - 1; + } + return {functionName, id}; } string TVMCompilerContext::getFunctionExternalName(FunctionDefinition const *_function) { @@ -2200,6 +2216,10 @@ const ContractDefinition *TVMCompilerContext::getContract() const { return m_contract; } +bool TVMCompilerContext::hasConstructor() const { + return ::hasConstructor(*getContract()); +} + bool TVMCompilerContext::ignoreIntegerOverflow() const { solAssert(!m_isUncheckedBlock.empty(), ""); return ignoreIntOverflow || m_isUncheckedBlock.top(); @@ -2226,7 +2246,7 @@ int TVMCompilerContext::getOffsetC4() const { return 256 + // pubkey (storeTimestampInC4() ? 64 : 0) + - 1; // constructor flag + (hasConstructor() ? 1 : 0); // constructor flag } std::vector> TVMCompilerContext::getStaticVariables() const { @@ -2264,7 +2284,7 @@ bool TVMCompilerContext::isBaseFunction(CallableDeclaration const* d) const { void StackPusher::pushEmptyArray() { pushInt(0); - *this << "NEWDICT"; + *this << "NULL"; *this << "TUPLE 2"; } @@ -2308,7 +2328,7 @@ void StackPusher::pushDefaultValue(Type const* _type) { pushEmptyArray(); break; case Type::Category::Mapping: - *this << "NEWDICT"; + *this << "NULL"; break; case Type::Category::Struct: { auto structType = to(_type); @@ -2867,6 +2887,10 @@ void TypeConversion::fromStringLiteral(Type const* leftType, StringLiteralType c break; case Type::Category::Array: break; + case Type::Category::TvmSlice: + m_pusher.drop(); + m_pusher.pushSlice("x" + rightType->value()); + break; default: solUnimplemented(leftType->toString()); break; diff --git a/compiler/libsolidity/codegen/TVMPusher.hpp b/compiler/libsolidity/codegen/TVMPusher.hpp index 4e32806c..1bae4f68 100644 --- a/compiler/libsolidity/codegen/TVMPusher.hpp +++ b/compiler/libsolidity/codegen/TVMPusher.hpp @@ -89,10 +89,11 @@ class TVMCompilerContext { std::vector c4StateVariableTypes() const; PragmaDirectiveHelper const& pragmaHelper() const; bool isStdlib() const; - std::string getFunctionInternalName(FunctionDefinition const* _function, bool calledByPoint = true) const; - static std::string getLibFunctionName(FunctionDefinition const* _function, bool withObject) ; + std::pair + functionInternalName(FunctionDefinition const* _function, bool calledByPoint = true) const; static std::string getFunctionExternalName(FunctionDefinition const* _function); const ContractDefinition* getContract() const; + bool hasConstructor() const; bool ignoreIntegerOverflow() const; FunctionDefinition const* afterSignatureCheck() const; bool storeTimestampInC4() const; @@ -265,7 +266,8 @@ class StackPusher { void pushC7(); void popC3(); void popC7(); - void execute(int take, int ret); + void callx(int take, int ret); + void call(uint32_t id, int take, int ret); void setGlob(int index); void setGlob(VariableDeclaration const * vd); void pushS(int i); diff --git a/compiler/libsolidity/codegen/TVMSimulator.cpp b/compiler/libsolidity/codegen/TVMSimulator.cpp index f1322cb2..a979a914 100644 --- a/compiler/libsolidity/codegen/TVMSimulator.cpp +++ b/compiler/libsolidity/codegen/TVMSimulator.cpp @@ -132,7 +132,7 @@ bool Simulator::visit(StackOpcode &_node) { m_stackSize += _node.ret() - _node.take(); m_commands.emplace_back(_node.shared_from_this()); } - m_wasCall |= isIn(_node.opcode(), "CALL", "EXECUTE", ".inline"); + m_wasCall |= isIn(_node.opcode(), "CALL", "CALLX", ".inline"); return false; } diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.cpp b/compiler/libsolidity/codegen/TVMTypeChecker.cpp index 7c77eb59..866db565 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.cpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.cpp @@ -207,6 +207,14 @@ bool TVMTypeChecker::visit(const Mapping &_mapping) { } } } + + TypeChecker{langutil::EVMVersion{}, m_errorReporter}.typeCheckTvmEncodeArg( + _mapping.valueType().annotation().type, + _mapping.valueType().location(), + "This type can not be used for mapping value type.", + false + ); + return true; } @@ -548,6 +556,29 @@ bool TVMTypeChecker::visit(MemberAccess const& _memberAccess) { return true; } +bool TVMTypeChecker::visit(FunctionCallOptions const& _node) { + if (auto memberAccess = to(&_node.expression())) { + if (FunctionDefinition const* funDef = to(memberAccess->annotation().referencedDeclaration)) { + ContractDefinition const* contract = funDef->annotation().contract; + if (contract && ::hasConstructor(*contract)) { + std::vector> options = _node.options(); + std::vector> const& names = _node.names(); + for (std::size_t i = 0; i < options.size(); ++i) { + if (*names.at(i) == "stateInit") { + m_errorReporter.typeError( + 1074_error, + options.at(i)->location(), + SecondarySourceLocation().append("Constructor is here: ", ::hasConstructor(*contract)->location()), + "\"stateInit\" option can be used only for contract that does not have a constructor.\n" + "Hint: if you want to deploy contact, then deploy via `new ContractName{...}(...);`."); + } + } + } + } + } + return true; +} + bool TVMTypeChecker::visit(ContractDefinition const& cd) { contractDefinition = &cd; m_inherHelper = std::make_unique(&cd); @@ -559,3 +590,23 @@ void TVMTypeChecker::endVisit(ContractDefinition const& ) { contractDefinition = nullptr; m_inherHelper = nullptr; } + +void TVMTypeChecker::checkMainContract(ContractDefinition const *_mainContract, PragmaDirectiveHelper const& pragmaHelper) { + if (_mainContract->canBeDeployed()) { + TVMCompilerContext ctx{_mainContract, pragmaHelper}; + if (!ctx.hasConstructor()) { + for (auto const *contr : _mainContract->annotation().linearizedBaseContracts) { + for (VariableDeclaration const* stateVar : contr->stateVariables()) { + if (!stateVar->isConstant() && stateVar->value()) { + m_errorReporter.typeError( + 1074_error, + _mainContract->location(), + SecondarySourceLocation().append("Initialization of state variable is here: ", stateVar->value()->location()), + "The contract must have a constructor because it has initialization of state variable.\n" + "Hint: define a constructor in the contract `constructor() { /*...*/ }`."); + } + } + } + } + } +} diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.hpp b/compiler/libsolidity/codegen/TVMTypeChecker.hpp index 07a23eb4..bb28556f 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.hpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.hpp @@ -16,6 +16,7 @@ namespace solidity::frontend { class InherHelper; +class PragmaDirectiveHelper; class TVMTypeChecker : public ASTConstVisitor { public: @@ -37,7 +38,10 @@ class TVMTypeChecker : public ASTConstVisitor { bool visit(FunctionCall const& ) override; bool visit(PragmaDirective const& ) override; bool visit(MemberAccess const& ) override; - void endVisit(ContractDefinition const& ) override; + bool visit(FunctionCallOptions const& _node) override; + void endVisit(ContractDefinition const&) override; + + void checkMainContract(ContractDefinition const *_mainContract, PragmaDirectiveHelper const& pragmaHelper); private: std::unique_ptr m_inherHelper; diff --git a/compiler/libsolidity/codegen/TvmAst.cpp b/compiler/libsolidity/codegen/TvmAst.cpp index 2b027fbd..35123fa0 100644 --- a/compiler/libsolidity/codegen/TvmAst.cpp +++ b/compiler/libsolidity/codegen/TvmAst.cpp @@ -530,7 +530,6 @@ Pointer gen(const std::string& cmd) { {"MYADDR", {0, 1, true}}, {"MYCODE", {0, 1, true}}, {"NEWC", {0, 1, true}}, - {"NEWDICT", {0, 1, true}}, {"NOW", {0, 1, true}}, {"NULL", {0, 1, true}}, {"PUSHINT", {0, 1, true}}, @@ -539,6 +538,9 @@ Pointer gen(const std::string& cmd) { {"RANDU256", {0, 1}}, {"STORAGEFEE", {0, 1, true}}, {"TRUE", {0, 1, true}}, + {"BLS_G1_ZERO", {0, 1, true}}, + {"BLS_G2_ZERO", {0, 1, true}}, + {"BLS_PUSHR", {0, 1, true}}, {"ADDRAND", {1, 0}}, {"BUYGAS", {1, 0}}, @@ -553,7 +555,6 @@ Pointer gen(const std::string& cmd) { {"BDEPTH", {1, 1}}, {"BINDUMP", {1, 1}}, {"BITNOT", {1, 1}}, // pseudo opcode. Alias for NOT - {"QBITNOT", {1, 1, true}}, // pseudo opcode. Alias for QNOT {"BITSIZE", {1, 1, true}}, {"BLESS", {1, 1}}, {"BREFS", {1, 1, true}}, @@ -563,7 +564,6 @@ Pointer gen(const std::string& cmd) { {"CONFIGOPTPARAM", {1, 1, true}}, {"CTOS", {1, 1}}, {"DEC", {1, 1}}, - {"QDEC", {1, 1, true}}, {"DICTEMPTY", {1, 1, true}}, {"ENDC", {1, 1}}, {"EQINT", {1, 1, true}}, @@ -575,7 +575,6 @@ Pointer gen(const std::string& cmd) { {"HASHSU", {1, 1, true}}, {"HEXDUMP", {1, 1}}, {"INC", {1, 1}}, - {"QINC", {1, 1, true}}, {"INDEX2", {1, 1}}, {"INDEX3", {1, 1}}, {"INDEX_EXCEP", {1, 1}}, @@ -589,13 +588,10 @@ Pointer gen(const std::string& cmd) { {"LAST", {1, 1}}, {"LESSINT", {1, 1, true}}, {"MODPOW2", {1, 1}}, - {"QMODPOW2", {1, 1, true}}, {"MULCONST", {1, 1}}, {"NEGATE", {1, 1}}, - {"QNEGATE", {1, 1, true}}, {"NEQINT", {1, 1, true}}, {"NOT", {1, 1, true}}, // logical not - {"QNOT", {1, 1, true}}, // logical not {"PARSEMSGADDR", {1, 1}}, {"PLDDICT", {1, 1}}, {"PLDI", {1, 1}}, @@ -609,27 +605,41 @@ Pointer gen(const std::string& cmd) { {"PLDULE4", {1, 1}}, {"PLDULE8", {1, 1}}, {"POW2", {1, 1}}, + {"QBITNOT", {1, 1, true}}, // pseudo opcode. Alias for QNOT + {"QDEC", {1, 1, true}}, {"QFITS", {1, 1, true}}, + {"QINC", {1, 1, true}}, + {"QMODPOW2", {1, 1, true}}, + {"QNEGATE", {1, 1, true}}, + {"QNOT", {1, 1, true}}, // logical not + {"QSGN", {1, 1, true}}, {"QUFITS", {1, 1, true}}, {"RAND", {1, 1}}, {"SBITS", {1, 1, true}}, {"SDEMPTY", {1, 1, true}}, {"SDEPTH", {1, 1}}, + {"SDFIRST", {1, 1, true}}, {"SEMPTY", {1, 1, true}}, {"SGN", {1, 1, true}}, - {"QSGN", {1, 1, true}}, {"SHA256U", {1, 1, true}}, {"SREFS", {1, 1, true}}, - {"STONE", {1, 1}}, + {"SREMPTY", {1, 1, true}}, {"STRDUMP", {1, 1}}, {"STSLICECONST", {1, 1}}, - {"STZERO", {1, 1}}, {"TLEN", {1, 1}}, {"UBITSIZE", {1, 1}}, {"UFITS", {1, 1}}, {"UNZIP", {1, 1}}, {"XLOAD", {1, 1}}, {"ZIP", {1, 1}}, + {"BLS_G1_NEG", {1, 1}}, + {"BLS_MAP_TO_G1", {1, 1}}, + {"BLS_G1_ISZERO", {1, 1}}, + {"BLS_G1_INGROUP", {1, 1}}, + {"BLS_G2_NEG", {1, 1}}, + {"BLS_MAP_TO_G2", {1, 1}}, + {"BLS_G2_ISZERO", {1, 1}}, + {"BLS_G2_INGROUP", {1, 1}}, {"BBITREFS", {1, 2, true}}, {"BREMBITREFS", {1, 2, true}}, @@ -662,9 +672,13 @@ Pointer gen(const std::string& cmd) { {"SENDRAWMSG", {2, 0}}, {"ADD", {2, 1}}, - {"QADD", {2, 1, true}}, {"AND", {2, 1, true}}, - {"QAND", {2, 1, true}}, + {"BLS_G1_ADD", {2, 1}}, + {"BLS_G1_MUL", {2, 1}}, + {"BLS_G1_SUB", {2, 1}}, + {"BLS_G2_ADD", {2, 1}}, + {"BLS_G2_MUL", {2, 1}}, + {"BLS_G2_SUB", {2, 1}}, {"CMP", {2, 1, true}}, {"DIFF", {2, 1}}, {"DIFF_PATCH", {2, 1}}, @@ -677,43 +691,49 @@ Pointer gen(const std::string& cmd) { {"DIFF_PATCH_ZIPQ", {2, 1, true}}, {"DIFF_ZIP", {2, 1}}, {"DIV", {2, 1}}, - {"QDIV", {2, 1, true}}, {"DIVC", {2, 1}}, - {"QDIVC", {2, 1, true}}, {"DIVR", {2, 1}}, - {"QDIVR", {2, 1, true}}, {"ENDXC", {2, 1}}, {"EQUAL", {2, 1, true}}, - {"QEQUAL", {2, 1, true}}, {"GEQ", {2, 1, true}}, - {"QGEQ", {2, 1, true}}, {"GREATER", {2, 1, true}}, - {"QGREATER", {2, 1, true}}, {"INDEXVAR", {2, 1}}, // only for vector {"LEQ", {2, 1, true}}, - {"QLEQ", {2, 1, true}}, {"LESS", {2, 1, true}}, - {"QLESS", {2, 1, true}}, {"MAX", {2, 1, true}}, - {"QMAX", {2, 1, true}}, {"MIN", {2, 1, true}}, - {"QMIN", {2, 1, true}}, {"MOD", {2, 1}}, - {"QMOD", {2, 1, true}}, {"MUL", {2, 1}}, - {"QMUL", {2, 1, true}}, {"NEQ", {2, 1, true}}, - {"QNEQ", {2, 1, true}}, {"OR", {2, 1, true}}, - {"QOR", {2, 1, true}}, {"PLDIX", {2, 1}}, {"PLDREFVAR", {2, 1}}, {"PLDSLICEX", {2, 1}}, {"PLDUX", {2, 1}}, + {"QADD", {2, 1, true}}, + {"QAND", {2, 1, true}}, + {"QDIV", {2, 1, true}}, + {"QDIVC", {2, 1, true}}, + {"QDIVR", {2, 1, true}}, + {"QEQUAL", {2, 1, true}}, + {"QGEQ", {2, 1, true}}, + {"QGREATER", {2, 1, true}}, + {"QLEQ", {2, 1, true}}, + {"QLESS", {2, 1, true}}, + {"QMAX", {2, 1, true}}, + {"QMIN", {2, 1, true}}, + {"QMOD", {2, 1, true}}, + {"QMUL", {2, 1, true}}, + {"QNEQ", {2, 1, true}}, + {"QOR", {2, 1, true}}, + {"QSUB", {2, 1, true}}, + {"QXOR", {2, 1, true}}, + {"QXOR", {2, 1, true}}, {"SCHKBITSQ", {2, 1, true}}, {"SCHKREFSQ", {2, 1, true}}, {"SDEQ", {2, 1, true}}, {"SDLEXCMP", {2, 1}}, + {"SDPFXREV", {2, 1, true}}, {"SDSKIPFIRST", {2, 1}}, {"SETINDEX", {2, 1}}, {"SETINDEXQ", {2, 1, true}}, @@ -742,11 +762,9 @@ Pointer gen(const std::string& cmd) { {"STVARUINT32", {2, 1}}, {"STZEROES", {2, 1}}, {"SUB", {2, 1}}, - {"QSUB", {2, 1, true}}, {"SUBR", {2, 1}}, // TODO add QSUBR ? {"TPUSH", {2, 1}}, {"XOR", {2, 1, true}}, - {"QXOR", {2, 1, true}}, {"DIVMOD", {2, 2}}, {"QDIVMOD", {2, 2, true}}, @@ -762,14 +780,16 @@ Pointer gen(const std::string& cmd) { {"RAWRESERVEX", {3, 0}}, + {"BLS_VERIFY", {3, 1}}, {"CHKSIGNS", {3, 1}}, {"CHKSIGNU", {3, 1}}, {"CONDSEL", {3, 1}}, {"MULDIV", {3, 1}}, - {"QMULDIV", {3, 1, true}}, {"MULDIVC", {3, 1}}, - {"QMULDIVC", {3, 1, true}}, {"MULDIVR", {3, 1}}, + {"MULMOD", {3, 1}}, + {"QMULDIV", {3, 1, true}}, + {"QMULDIVC", {3, 1, true}}, {"QMULDIVR", {3, 1, true}}, {"SCHKBITREFSQ", {3, 1, true}}, {"SCUTFIRST", {3, 1}}, diff --git a/compiler/libsolidity/interface/CompilerStack.cpp b/compiler/libsolidity/interface/CompilerStack.cpp index 3f589172..4ee136c1 100644 --- a/compiler/libsolidity/interface/CompilerStack.cpp +++ b/compiler/libsolidity/interface/CompilerStack.cpp @@ -624,6 +624,17 @@ bool CompilerStack::analyzeLegacy(bool _noErrorsSoFar) if (noErrors) { + if (std::optional>> res = + findMainContract() + ) { + ContractDefinition const *targetContract{}; + std::vector targetPragmaDirectives; + std::tie(targetContract, targetPragmaDirectives) = res.value(); + PragmaDirectiveHelper pragmaDirectiveHelper{targetPragmaDirectives}; + TVMTypeChecker checker(m_errorReporter); + checker.checkMainContract(targetContract, pragmaDirectiveHelper); + } + // Check for TVM specific issues. // TODO merge TVMTypeChecker and TVMAnalyzer ? for (Source const* source: m_sourceOrder) { @@ -687,6 +698,89 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con return false; } +std::optional>> +CompilerStack::findMainContract() { + ContractDefinition const *targetContract{}; + std::vector targetPragmaDirectives; + + for (Source const *source: m_sourceOrder) { + std::string curSrcPath = *source->ast->annotation().path; + if (curSrcPath != m_inputFile) + continue; + + std::vector pragmaDirectives = getPragmaDirectives(source); + + std::vector contracts; + for (ASTPointer const &node: source->ast->nodes()) + if (auto contract = dynamic_cast(node.get())) + contracts.push_back(contract); + + for (ContractDefinition const *contract: contracts) { + if (contract->isLibrary()) + continue; + + if (m_mainContract.empty()) { + if (m_generateAbi && !m_generateCode) { + if (targetContract != nullptr) { + m_errorReporter.typeError( + 4605_error, + targetContract->location(), + SecondarySourceLocation().append("Previous contract:", + contract->location()), + "Source file contains at least two contracts/interfaces." + " Consider adding the option --contract in compiler command line to select the desired contract/interface." + ); + return {}; + } + targetContract = contract; + targetPragmaDirectives = pragmaDirectives; + } else if (contract->canBeDeployed()) { + if (targetContract != nullptr) { + m_errorReporter.typeError( + 5205_error, + targetContract->location(), + SecondarySourceLocation().append("Previous deployable contract:", + contract->location()), + "Source file contains at least two deployable contracts." + " Consider adding the option --contract in compiler command line to select the desired contract." + ); + return {}; + } + targetContract = contract; + targetPragmaDirectives = pragmaDirectives; + } + } else { + if (contract->name() == m_mainContract) { + if (m_generateCode && !contract->canBeDeployed()) { + m_errorReporter.typeError( + 3715_error, + contract->location(), + "The desired contract isn't deployable (it does not have public constructor or it is abstract or it is interface or it is library)." + ); + return {}; + } + targetContract = contract; + targetPragmaDirectives = pragmaDirectives; + } + } + } + } + + if (!m_mainContract.empty() && targetContract == nullptr) { + m_errorReporter.typeError( + 1468_error, + SourceLocation(), + "Source file doesn't contain the desired contract \"" + m_mainContract + "\"." + ); + return {}; + } + + if (targetContract == nullptr) + return {}; + + return {{targetContract, targetPragmaDirectives}}; +} + std::pair CompilerStack::compile(bool json) { bool didCompileSomething{}; @@ -698,137 +792,64 @@ std::pair CompilerStack::compile(bool json) solThrow(CompilerError, "Called compile with errors."); if (m_generateAbi || m_generateCode || m_doPrintFunctionIds || m_doPrivateFunctionIds) { - ContractDefinition const *targetContract{}; - std::vector targetPragmaDirectives; - - bool findSrc = false; - for (Source const *source: m_sourceOrder) { - std::string curSrcPath = *source->ast->annotation().path; - if (curSrcPath != m_inputFile) { - continue; - } - - findSrc = true; - std::vector pragmaDirectives = getPragmaDirectives(source); - - std::vector contracts; - for (ASTPointer const &node: source->ast->nodes()) { - if (auto contract = dynamic_cast(node.get())) { - contracts.push_back(contract); - } - } - - - for (ContractDefinition const *contract: contracts) { - if (contract->isLibrary()) { - continue; - } - - if (!m_mainContract.empty()) { - if (contract->name() == m_mainContract) { - if (m_generateCode && !contract->canBeDeployed()) { - m_errorReporter.typeError( - 3715_error, - contract->location(), - "The desired contract isn't deployable (it has not public constructor or it's abstract or it's interface or it's library)." - ); - return {false, didCompileSomething}; + auto res = findMainContract(); + if (res) { + ContractDefinition const *targetContract{}; + std::vector targetPragmaDirectives; + std::tie(targetContract, targetPragmaDirectives) = res.value(); + + if (targetContract != nullptr) { + try { + if (json) { + std::vector pragmaDirectives = getPragmaDirectives(&source(m_inputFile)); + PragmaDirectiveHelper pragmaHelper{pragmaDirectives}; + Contract const& c = contract(targetContract->name()); + if (m_generateAbi) { + Json::Value abi = TVMABI::generateABIJson(targetContract, getSourceUnits(), pragmaDirectives); + c.abi = std::make_unique(abi); } - targetContract = contract; - targetPragmaDirectives = pragmaDirectives; - } - } else { - if (m_generateAbi && !m_generateCode) { - if (targetContract != nullptr) { - m_errorReporter.typeError( - 4605_error, - targetContract->location(), - SecondarySourceLocation().append("Previous contract:", - contract->location()), - "Source file contains at least two contracts/interfaces." - " Consider adding the option --contract in compiler command line to select the desired contract/interface." - ); - return {false, didCompileSomething}; + if (m_generateCode) { + Pointer codeContract = + TVMContractCompiler::generateContractCode(targetContract, getSourceUnits(), pragmaHelper); + std::ostringstream out; + Printer p{out}; + codeContract->accept(p); + Json::Value code = Json::Value(out.str()); + c.code = std::make_unique(code); } - targetContract = contract; - targetPragmaDirectives = pragmaDirectives; - } else if (contract->canBeDeployed()) { - if (targetContract != nullptr) { - m_errorReporter.typeError( - 5205_error, - targetContract->location(), - SecondarySourceLocation().append("Previous deployable contract:", - contract->location()), - "Source file contains at least two deployable contracts." - " Consider adding the option --contract in compiler command line to select the desired contract." - ); - return {false, didCompileSomething}; + if (m_doPrintFunctionIds) + { + auto functionIds = TVMABI::generateFunctionIdsJson(*c.contract, pragmaHelper); + c.functionIds = std::make_unique(functionIds); } - targetContract = contract; - targetPragmaDirectives = pragmaDirectives; - } - } - } - } - if (!findSrc) { - solAssert(findSrc, "Can't find src file"); - } - - if (!m_mainContract.empty() && targetContract == nullptr) { - m_errorReporter.typeError( - 1468_error, - SourceLocation(), - "Source file doesn't contain the desired contract \"" + m_mainContract + "\"." - ); - return {false, didCompileSomething}; - } - - if (targetContract != nullptr) { - try { - if (json) { - std::vector pragmaDirectives = getPragmaDirectives(&source(m_inputFile)); - PragmaDirectiveHelper pragmaHelper{pragmaDirectives}; - Contract const& c = contract(targetContract->name()); - if (m_generateAbi) { - Json::Value abi = TVMABI::generateABIJson(targetContract, getSourceUnits(), pragmaDirectives); - c.abi = std::make_unique(abi); - } - if (m_generateCode) { - Pointer codeContract = - TVMContractCompiler::generateContractCode(targetContract, getSourceUnits(), pragmaHelper); - std::ostringstream out; - Printer p{out}; - codeContract->accept(p); - Json::Value code = Json::Value(out.str()); - c.code = std::make_unique(code); - } - if (m_doPrintFunctionIds) - { - auto functionIds = TVMABI::generateFunctionIdsJson(*c.contract, pragmaHelper); - c.functionIds = std::make_unique(functionIds); - } - if (m_doPrivateFunctionIds) - { - auto functionIds = TVMABI::generatePrivateFunctionIdsJson(*c.contract, getSourceUnits(), pragmaHelper); - c.privateFunctionIds = std::make_unique(functionIds); + if (m_doPrivateFunctionIds) + { + auto functionIds = TVMABI::generatePrivateFunctionIdsJson(*c.contract, getSourceUnits(), pragmaHelper); + c.privateFunctionIds = std::make_unique(functionIds); + } + if (m_doPrivateFunctionIds) + { + auto functionIds = TVMABI::generatePrivateFunctionIdsJson(*c.contract, getSourceUnits(), pragmaHelper); + c.privateFunctionIds = std::make_unique(functionIds); + } + } else { + TVMCompilerProceedContract( + *targetContract, + getSourceUnits(), + &targetPragmaDirectives, + m_generateAbi, + m_generateCode, + m_inputFile, + m_folder, + m_file_prefix, + m_doPrintFunctionIds, + m_doPrivateFunctionIds + ); } - } else { - TVMCompilerProceedContract( - *targetContract, - getSourceUnits(), - &targetPragmaDirectives, - m_generateAbi, - m_generateCode, - m_inputFile, - m_folder, - m_file_prefix, - m_doPrintFunctionIds, - m_doPrivateFunctionIds - ); + didCompileSomething = true; + } catch (FatalError const &) { + return {false, didCompileSomething}; } - didCompileSomething = true; - } catch (FatalError const &) { - return {false, didCompileSomething}; } } } diff --git a/compiler/libsolidity/interface/CompilerStack.h b/compiler/libsolidity/interface/CompilerStack.h index dbd290bf..058d96e4 100644 --- a/compiler/libsolidity/interface/CompilerStack.h +++ b/compiler/libsolidity/interface/CompilerStack.h @@ -246,6 +246,9 @@ class CompilerStack: public langutil::CharStreamProvider /// @returns false on error. bool parseAndAnalyze(State _stopAfter = State::CompilationSuccessful); + std::optional>> + findMainContract(); + /// Compiles the source units that were previously added and parsed. /// @returns false on error. std::pair compile(bool json = false); diff --git a/compiler/libsolidity/interface/StandardCompiler.cpp b/compiler/libsolidity/interface/StandardCompiler.cpp index 351dde91..56fea937 100644 --- a/compiler/libsolidity/interface/StandardCompiler.cpp +++ b/compiler/libsolidity/interface/StandardCompiler.cpp @@ -1185,23 +1185,14 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting std::string file = contractName.substr(0, colon); std::string name = contractName.substr(colon + 1); - // ABI, storage layout, documentation and metadata Json::Value contractData(Json::objectValue); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental)) - contractData["abi"] = compilerStack.contractABI(contractName); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "assembly", wildcardMatchesExperimental)) - contractData["assembly"] = compilerStack.contractCode(contractName); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "showFunctionIds", wildcardMatchesExperimental)) - contractData["functionIds"] = compilerStack.functionIds(contractName); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "showPrivateFunctionIds", wildcardMatchesExperimental)) - contractData["privateFunctionIds"] = compilerStack.privateFunctionIds(contractName); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental)) - contractData["metadata"] = compilerStack.metadata(contractName); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental)) - contractData["userdoc"] = compilerStack.natspecUser(contractName); - if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesExperimental)) - contractData["devdoc"] = compilerStack.natspecDev(contractName); - + contractData["abi"] = compilerStack.contractABI(contractName); + contractData["assembly"] = compilerStack.contractCode(contractName); + contractData["functionIds"] = compilerStack.functionIds(contractName); + contractData["privateFunctionIds"] = compilerStack.privateFunctionIds(contractName); + contractData["metadata"] = compilerStack.metadata(contractName); + contractData["userdoc"] = compilerStack.natspecUser(contractName); + contractData["devdoc"] = compilerStack.natspecDev(contractName); if (!contractData.empty()) { if (!contractsOutput.isMember(file)) diff --git a/compiler/libsolidity/parsing/Parser.cpp b/compiler/libsolidity/parsing/Parser.cpp index 6f41bb42..068b49ae 100644 --- a/compiler/libsolidity/parsing/Parser.cpp +++ b/compiler/libsolidity/parsing/Parser.cpp @@ -1391,9 +1391,8 @@ ASTPointer Parser::parseOptional() ASTPointer cur = parseTypeName(); components.emplace_back(std::move(cur)); Token token = m_scanner->currentToken(); - if (token != Token::Comma) { + if (token != Token::Comma) break; - } expectToken(Token::Comma); } expectToken(Token::RParen); @@ -1407,10 +1406,18 @@ ASTPointer Parser::parseTvmVector() ASTNodeFactory nodeFactory(*this); expectToken(Token::TvmVector); expectToken(Token::LParen); - ASTPointer type = parseTypeName(); + std::vector> components; + while (true) { + ASTPointer cur = parseTypeName(); + components.emplace_back(std::move(cur)); + Token token = m_scanner->currentToken(); + if (token != Token::Comma) + break; + expectToken(Token::Comma); + } expectToken(Token::RParen); nodeFactory.markEndPosition(); - return nodeFactory.createNode(type); + return nodeFactory.createNode(components); } ASTPointer Parser::parseTvmStack() diff --git a/compiler/libsolutil/UTF8.cpp b/compiler/libsolutil/UTF8.cpp index baeda14b..67668cf3 100644 --- a/compiler/libsolutil/UTF8.cpp +++ b/compiler/libsolutil/UTF8.cpp @@ -139,4 +139,21 @@ bool validateUTF8(std::string const& _input, size_t& _invalidPosition) return validateUTF8(reinterpret_cast(_input.c_str()), _input.length(), _invalidPosition); } +bool validateSlice(std::string const& _input, size_t& _invalidPosition) +{ + for (size_t i = 0; i < _input.size(); ++i) { + char ch = _input[i]; + bool isValidChar = + ('0' <= ch && ch <= '9') || + ('a' <= ch && ch <= 'f') || + ('A' <= ch && ch <= 'F') || + (ch == '_' && i + 1 == _input.size()); + if (!isValidChar) { + _invalidPosition = i; + return false; + } + } + return true; +} + } diff --git a/compiler/libsolutil/UTF8.h b/compiler/libsolutil/UTF8.h index 59e2d414..25c00db1 100644 --- a/compiler/libsolutil/UTF8.h +++ b/compiler/libsolutil/UTF8.h @@ -19,7 +19,7 @@ * @author Alex Beregszaszi * @date 2016 * - * UTF-8 related helpers + * UTF-8 and slice related helpers */ #pragma once @@ -32,6 +32,7 @@ namespace solidity::util /// Validate an input for UTF8 encoding /// @returns false if it is invalid and the first invalid position in invalidPosition bool validateUTF8(std::string const& _input, size_t& _invalidPosition); +bool validateSlice(std::string const& _input, size_t& _invalidPosition); inline bool validateUTF8(std::string const& _input) { diff --git a/lib/stdlib_sol.tvm b/lib/stdlib_sol.tvm index 78da41bb..8ee5f1fb 100644 --- a/lib/stdlib_sol.tvm +++ b/lib/stdlib_sol.tvm @@ -488,13 +488,13 @@ .loc stdlib.sol, 269 PUSH S4 ADDCONST 127 - OVER } PUSHCONT { .loc stdlib.sol, 271 - PUSH2 S4, S0 + PUSH S4 } IFELSE + OVER SUB POP S5 .loc stdlib.sol, 0 @@ -725,13 +725,13 @@ .loc stdlib.sol, 190 PUSH S4 ADDCONST 127 - OVER } PUSHCONT { .loc stdlib.sol, 192 - PUSH2 S4, S0 + PUSH S4 } IFELSE + OVER SUB POP S5 .loc stdlib.sol, 0 @@ -1028,7 +1028,7 @@ FIRST PUSH S8 PUSH C3 - EXECUTE + CALLX PUSHCONT { .loc stdlib.sol, 577 BLKPUSH 2, 2 diff --git a/sold/Cargo.toml b/sold/Cargo.toml index 450179b2..98ce641f 100644 --- a/sold/Cargo.toml +++ b/sold/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = '2021' name = 'sold' -version = '0.74.0' +version = '0.75.0' [[bin]] name = 'sold' @@ -10,16 +10,15 @@ path = 'src/main.rs' [dependencies] atty = '0.2' dunce = '1.0' -failure = '0.1' +anyhow = '1.0' once_cell = '1.19' serde_json = { features = [ 'unbounded_depth' ], version = '1.0' } strip-ansi-escapes = '0.2' clap = { features = [ 'derive' ], version = '4.5' } serde = { features = [ 'derive' ], version = '1.0' } -ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.4.25' } -ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.141' } -ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.39' } -ton_labs_assembler = { features = [ 'gosh' ], git = 'https://github.com/tonlabs/ever-assembler.git', tag = '1.4.52' } +ever_abi = { git = 'https://github.com/everx-labs/ever-abi.git', tag = '2.6.1' } +ever_block = { git = 'https://github.com/everx-labs/ever-block.git', tag = '1.11.0' } +ever_assembler = { features = [ 'gosh' ], git = 'https://github.com/everx-labs/ever-assembler.git', tag = '1.6.2' } [build-dependencies] cmake = '0.1' diff --git a/sold/README.md b/sold/README.md index 7a08ebb2..11446ec6 100644 --- a/sold/README.md +++ b/sold/README.md @@ -1,4 +1,4 @@ -# Solidity Compiler Driver +# The TVM Solidity Compiler Driver ## Build and Install diff --git a/sold/src/lib.rs b/sold/src/lib.rs index 7d853b21..85b5d60a 100644 --- a/sold/src/lib.rs +++ b/sold/src/lib.rs @@ -5,11 +5,11 @@ use std::os::raw::{c_char, c_void}; use std::path::Path; use clap::{ValueEnum, Parser}; -use failure::{bail, format_err}; +use anyhow::{bail, format_err}; use serde::Deserialize; -use ton_types::{Result, Status}; -use ton_labs_assembler::{DbgInfo, Engine, Units}; +use ever_block::{Result, Status}; +use ever_assembler::{DbgInfo, Engine, Units}; mod libsolc; mod printer; @@ -402,7 +402,7 @@ pub fn build(args: Args) -> Status { format!("{}/{}", output_dir, output_tvc) }; - let bytes = ton_types::write_boc(&output)?; + let bytes = ever_block::write_boc(&output)?; let mut file = File::create(output_filename)?; file.write_all(&bytes)?; diff --git a/sold/src/printer.rs b/sold/src/printer.rs index 5e60534c..30a5f544 100644 --- a/sold/src/printer.rs +++ b/sold/src/printer.rs @@ -13,10 +13,10 @@ use std::fs::File; use std::io::Write; -use failure::format_err; +use anyhow::format_err; use serde::Serialize; -use ton_types::{Result, Status}; +use ever_block::{Result, Status}; pub fn print_abi_json_canonically(out: &mut File, value: &serde_json::Value) -> Status { let root = value.as_object().ok_or_else(|| format_err!("ABI parsing failed"))?; diff --git a/sold/tests/ImportRemote.sol b/sold/tests/ImportRemote.sol index 48f576a0..7ec5089d 100644 --- a/sold/tests/ImportRemote.sol +++ b/sold/tests/ImportRemote.sol @@ -1,4 +1,4 @@ pragma tvm-solidity >=0.66.0; -import "github.com/tonlabs/debots/Remote.sol"; +import "github.com/everx-labs/debots/Remote.sol"; contract ImportRemote { } diff --git a/sold/tests/tests.rs b/sold/tests/tests.rs index 36452097..29956eab 100644 --- a/sold/tests/tests.rs +++ b/sold/tests/tests.rs @@ -12,6 +12,7 @@ use predicates::prelude::*; use assert_cmd::Command; +use sold_lib::ERROR_MSG_NO_OUTPUT; type Status = Result<(), Box>; const BIN_NAME: &str = "sold"; @@ -90,7 +91,7 @@ fn test_library() -> Status { .arg("tests") .assert() .success() - .stderr(predicate::str::contains("Compiler run successful, no output requested.")) + .stderr(predicate::str::contains(ERROR_MSG_NO_OUTPUT)) ; Ok(()) @@ -148,22 +149,22 @@ fn test_private_function_ids() -> Status { .success() .stdout(predicate::str::contains(r#"[ { - "id": 4199241165, + "id": 5581, "scope": "C", "sign": "f(uint32,uint256,uint256)" }, { - "id": 2254871888, + "id": 7504, "scope": "C", "sign": "add(uint256,uint256)" }, { - "id": 4034881437, + "id": 10141, "scope": "C", "sign": "sub(uint256,uint256)" }, { - "id": 4048818487, + "id": 10143, "scope": "Math", "sign": "mul(uint256,uint256)" } @@ -177,7 +178,7 @@ fn test_remapping() -> Status { .arg("tests/ImportRemote.sol") .arg("--output-dir") .arg("tests") - .arg("github.com/tonlabs/debots/=tests/remote/") + .arg("github.com/everx-labs/debots/=tests/remote/") .assert() .success();