revive-integration 1.2.0

revive compiler integration test cases
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
use alloy_primitives::{Address, Bytes, I256, U256};
use alloy_sol_types::{sol, SolCall, SolConstructor};

use resolc::test_utils::*;
use revive_llvm_context::OptimizerSettings;

#[derive(Clone)]
pub struct Contract {
    pub name: &'static str,
    pub evm_runtime: Vec<u8>,
    pub pvm_runtime: Vec<u8>,
    pub calldata: Vec<u8>,
    pub yul: String,
}

impl Contract {
    pub fn build(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
        Self {
            name,
            evm_runtime: compile_evm_bin_runtime(name, code, Default::default()),
            pvm_runtime: compile_blob(name, code),
            calldata,
            yul: compile_to_yul(name, code, true),
        }
    }

    pub fn build_size_opt(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
        Self {
            name,
            evm_runtime: compile_evm_bin_runtime(name, code, Default::default()),
            pvm_runtime: compile_blob_with_options(
                name,
                code,
                true,
                OptimizerSettings::size(),
                Default::default(),
            ),
            calldata,
            yul: compile_to_yul(name, code, true),
        }
    }
}

macro_rules! case {
    // Arguments:
    //     1. The file name, expect to live under "../contracts/"
    //     2. The Solidity contract name
    //     3. The derived Solidity function call name
    //     4. The method name on [Contract]
    //     5. Any parameters to the Solidity functions
    ($file_name:literal, $contract_name:ident, $contract_method:ident, $method_name:ident, $( $v:ident: $t:ty ),* ) => {
        impl Contract {
            pub fn $method_name($($v: $t),*) -> Self {
                let code = include_str!(concat!("../contracts/", $file_name));
                let args = $contract_name::$contract_method::new(($($v,)*)).abi_encode();
                let name = stringify!($contract_name);
                Contract::build(args, name, code)
            }
        }
    };

    // Arguments:
    //     1. The file name, expect to live under "../contracts/"
    //     2. The Solidity contract name
    //     3. Raw Calldata
    //     4. The method name on [Contract]
    ($file_name:literal, $contract_name:literal, $calldata:expr, $method_name:ident) => {
        impl Contract {
            pub fn $method_name() -> Self {
                let code = include_str!(concat!("../contracts/", $file_name));
                Contract::build($calldata, $contract_name, code)
            }
        }
    };
}

case!("Create.sol", "CreateA", vec![0; 4], create_a);
case!("Create.sol", "CreateB", vec![0; 4], create_b);

sol!(contract Baseline { function baseline() public payable; });
case!("Baseline.sol", Baseline, baselineCall, baseline,);

sol!(contract Flipper {
    constructor (bool);

    function flip() public;
});
case!("flipper.sol", Flipper, flipCall, flipper,);
case!("flipper.sol", Flipper, constructorCall, flipper_constructor, coin: bool);

sol!(contract Computation {
    function odd_product(int32 n) public pure returns (int64);
    function triangle_number(int64 n) public pure returns (int64 sum);
});
case!("Computation.sol", Computation, odd_productCall, odd_product, n: i32);
case!("Computation.sol", Computation, triangle_numberCall, triangle_number, n: i64);

sol!(
    contract FibonacciRecursive {
        function fib3(uint n) public pure returns (uint);
    }
);
case!("Fibonacci.sol", FibonacciRecursive, fib3Call, fib_recursive, n: U256);

sol!(
    contract FibonacciIterative {
        function fib3(uint n) external pure returns (uint b);
    }
);
case!("Fibonacci.sol", FibonacciIterative, fib3Call, fib_iterative, n: U256);

sol!(
    contract FibonacciBinet {
        function fib3(uint n) external pure returns (uint a);
    }
);
case!("Fibonacci.sol", FibonacciBinet, fib3Call, fib_binet, n: U256);

sol!(
    contract SHA1 {
        function sha1(bytes memory data) public pure returns (bytes20 ret);
    }
);
case!("SHA1.sol", SHA1, sha1Call, sha1, pre: Bytes);

sol!(
    contract ERC20 {
        function totalSupply() external view returns (uint);

        function balanceOf(address account) external view returns (uint);

        function transfer(address recipient, uint amount) external returns (bool);

        function allowance(
            address owner,
            address spender
        ) external view returns (uint);

        function approve(address spender, uint amount) external returns (bool);

        function transferFrom(
            address sender,
            address recipient,
            uint amount
        ) external returns (bool);

        event Transfer(address indexed from, address indexed to, uint value);
        event Approval(address indexed owner, address indexed spender, uint value);
    }
);
case!("ERC20.sol", ERC20, totalSupplyCall, erc20,);

sol!(
    contract Block {
        function timestamp() public view returns (uint ret);

        function number() public view returns (uint ret);
    }
);
case!("Block.sol", Block, numberCall, block_number,);
case!("Block.sol", Block, timestampCall, block_timestamp,);

sol!(
    contract Context {
        function address_this() public view returns (address);

        function caller() public pure returns (address);
    }
);
case!("Context.sol", Context, address_thisCall, context_address,);
case!("Context.sol", Context, callerCall, context_caller,);

sol!(
    contract DivisionArithmetics {
        function div(uint n, uint d) public pure returns (uint q);

        function sdiv(int n, int d) public pure returns (int q);

        function mod(uint n, uint d) public pure returns (uint r);

        function smod(int n, int d) public pure returns (int r);
    }
);
case!("DivisionArithmetics.sol", DivisionArithmetics, divCall, division_arithmetics_div, n: U256, d: U256);
case!("DivisionArithmetics.sol", DivisionArithmetics, sdivCall, division_arithmetics_sdiv, n: I256, d: I256);
case!("DivisionArithmetics.sol", DivisionArithmetics, modCall, division_arithmetics_mod, n: U256, d: U256);
case!("DivisionArithmetics.sol", DivisionArithmetics, smodCall, division_arithmetics_smod, n: I256, d: I256);

sol!(
    contract DivConst {
        function divRhsZero(uint256 n) external pure returns (uint256);
        function divRhsOne(uint256 n) external pure returns (uint256);
        function divRhsTwo(uint256 n) external pure returns (uint256);
        function divRhsFive(uint256 n) external pure returns (uint256);
        function divRhsMax(uint256 n) external pure returns (uint256);
        function divLhsZero(uint256 d) external pure returns (uint256);
        function divLhsOne(uint256 d) external pure returns (uint256);
        function divLhsTwo(uint256 d) external pure returns (uint256);
        function divLhsFive(uint256 d) external pure returns (uint256);
        function divLhsMax(uint256 d) external pure returns (uint256);
    }

    contract SdivConst {
        function sdivRhsZero(int256 n) external pure returns (int256);
        function sdivRhsOne(int256 n) external pure returns (int256);
        function sdivRhsNegOne(int256 n) external pure returns (int256);
        function sdivRhsTwo(int256 n) external pure returns (int256);
        function sdivRhsNegTwo(int256 n) external pure returns (int256);
        function sdivRhsFive(int256 n) external pure returns (int256);
        function sdivRhsNegFive(int256 n) external pure returns (int256);
        function sdivRhsMin(int256 n) external pure returns (int256);
        function sdivRhsMinPlusOne(int256 n) external pure returns (int256);
        function sdivRhsMax(int256 n) external pure returns (int256);
        function sdivLhsZero(int256 d) external pure returns (int256);
        function sdivLhsOne(int256 d) external pure returns (int256);
        function sdivLhsNegOne(int256 d) external pure returns (int256);
        function sdivLhsTwo(int256 d) external pure returns (int256);
        function sdivLhsNegTwo(int256 d) external pure returns (int256);
        function sdivLhsFive(int256 d) external pure returns (int256);
        function sdivLhsNegFive(int256 d) external pure returns (int256);
        function sdivLhsMin(int256 d) external pure returns (int256);
        function sdivLhsMinPlusOne(int256 d) external pure returns (int256);
        function sdivLhsMax(int256 d) external pure returns (int256);
    }

    contract ModConst {
        function modRhsZero(uint256 n) external pure returns (uint256);
        function modRhsOne(uint256 n) external pure returns (uint256);
        function modRhsTwo(uint256 n) external pure returns (uint256);
        function modRhsFive(uint256 n) external pure returns (uint256);
        function modRhsMax(uint256 n) external pure returns (uint256);
        function modLhsZero(uint256 d) external pure returns (uint256);
        function modLhsOne(uint256 d) external pure returns (uint256);
        function modLhsTwo(uint256 d) external pure returns (uint256);
        function modLhsFive(uint256 d) external pure returns (uint256);
        function modLhsMax(uint256 d) external pure returns (uint256);
    }

    contract SmodConst {
        function smodRhsZero(int256 n) external pure returns (int256);
        function smodRhsOne(int256 n) external pure returns (int256);
        function smodRhsNegOne(int256 n) external pure returns (int256);
        function smodRhsTwo(int256 n) external pure returns (int256);
        function smodRhsNegTwo(int256 n) external pure returns (int256);
        function smodRhsFive(int256 n) external pure returns (int256);
        function smodRhsNegFive(int256 n) external pure returns (int256);
        function smodRhsMin(int256 n) external pure returns (int256);
        function smodRhsMax(int256 n) external pure returns (int256);
        function smodLhsZero(int256 d) external pure returns (int256);
        function smodLhsOne(int256 d) external pure returns (int256);
        function smodLhsNegOne(int256 d) external pure returns (int256);
        function smodLhsTwo(int256 d) external pure returns (int256);
        function smodLhsNegTwo(int256 d) external pure returns (int256);
        function smodLhsFive(int256 d) external pure returns (int256);
        function smodLhsNegFive(int256 d) external pure returns (int256);
        function smodLhsMin(int256 d) external pure returns (int256);
        function smodLhsMax(int256 d) external pure returns (int256);
    }
);

sol!(
    contract Send {
        function transfer_self(uint _amount) public payable;
    }
);
case!("Send.sol", Send, transfer_selfCall, send_self, amount: U256);

sol!(
    contract Transfer {
        function transfer_self(uint _amount) public payable;
    }
);
case!("Transfer.sol", Transfer, transfer_selfCall, transfer_self, amount: U256);

sol!(
    contract MStore8 {
        function mStore8(uint value) public pure returns (uint256 word);
    }
);
case!("MStore8.sol", MStore8, mStore8Call, mstore8, value: U256);

sol!(
    contract Events {
        event A(uint) anonymous;
        event E(uint indexed, uint indexed, uint indexed);

        function emitEvent(uint topics) public;
    }
);
case!("Events.sol", Events, emitEventCall, event, topics: U256);

sol!(
    contract ExtCode {
        function ExtCodeSize(address who) public view returns (uint ret);

        function CodeSize() public pure returns (uint ret);

        function ExtCodeHash(address who) public view returns (bytes32 ret);

        function CodeHash() public view returns (bytes32 ret);
    }
);
case!("ExtCode.sol", ExtCode, ExtCodeSizeCall, ext_code_size, address: Address);
case!("ExtCode.sol", ExtCode, CodeSizeCall, code_size,);
case!("ExtCode.sol", ExtCode, ExtCodeHashCall, ext_code_hash, address: Address);
case!("ExtCode.sol", ExtCode, CodeHashCall, code_hash,);

sol!(
    contract MCopy {
        function memcpy(bytes memory payload) public pure returns (bytes memory);
    }
);
case!("MCopy.sol", MCopy, memcpyCall, memcpy, payload: Bytes);

sol!(
    contract MLoad {
        constructor() payable;

        function loadAt(uint _offset) public payable returns (uint m);
    }
);
case!("MLoad.sol", MLoad, loadAtCall, load_at, _offset: U256);

sol!(
    contract Call {
        function value_transfer(address payable destination) public payable;

        function echo(bytes memory payload) public payable returns (bytes memory);

        function call(
            address callee,
            bytes memory payload
        ) public payable returns (bytes memory);
    }
);
case!("Call.sol", Call, value_transferCall, call_value_transfer, destination: Address);
case!("Call.sol", Call, callCall, call_call, destination: Address, payload: Bytes);
case!("Call.sol", "Call", vec![], call_constructor);

sol!(
    contract Value {
        function balance_of(address _address) public view returns (uint ret);
        function balance_self() public view returns (uint ret);
    }
);
case!("Value.sol", Value, balance_ofCall, value_balance_of, address: Address);
case!("Value.sol", Value, balance_selfCall, value_balance_self,);

sol!(
    contract Bitwise {
        function opByte(uint i, uint x) public payable returns (uint ret);
    }
);
case!("Bitwise.sol", Bitwise, opByteCall, bitwise_byte, index: U256, value: U256);

sol!(
    contract UlongRem {
        function bigMulMod(uint a, uint b, uint m) external pure returns (uint);
    }
);
case!("UlongRem.sol", UlongRem, bigMulModCall, ulongrem_big_mulmod, a: U256, b: U256, m: U256);

sol!(
    contract Storage {
        function transient(uint value) public returns (uint ret);
    }
);
case!("Storage.sol", Storage, transientCall, storage_transient, value: U256);

sol!(
    contract Predicted {
        constructor(uint _foo);
    }
    contract AddressPredictor {
        constructor(uint _foo, bytes memory _bytecode) payable;
    }
);
case!("AddressPredictor.sol", Predicted, constructorCall, predicted_constructor, salt: U256);
case!("AddressPredictor.sol", AddressPredictor, constructorCall, address_predictor_constructor, salt: U256, bytecode: Bytes);

#[cfg(test)]
mod tests {
    use rayon::iter::{IntoParallelIterator, ParallelIterator};
    use serde::{de::Deserialize, Serialize};
    use std::{collections::BTreeMap, fs::File};

    use super::Contract;

    #[test]
    fn codesize() {
        let path = "codesize.json";

        let existing = File::open(path)
            .map(|file| {
                BTreeMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
                    file,
                ))
                .expect("should be able to deserialze codesize data")
            })
            .ok();

        let extract_code_size = |compile: fn() -> Contract| {
            let contract = compile();
            let contract_length = contract.pvm_runtime.len();
            let size_change = existing
                .as_ref()
                .and_then(|map| map.get(contract.name))
                .filter(|old| **old != 0)
                .map(|old| {
                    let old = *old as f32;
                    let p = (contract_length as f32 - old) / old * 100.0;
                    format!("({p}% change from {old} bytes)",)
                })
                .unwrap_or_default();

            println!("{}: {contract_length} bytes {size_change}", contract.name);

            (contract.name, contract_length)
        };

        [
            (|| {
                Contract::build_size_opt(
                    vec![],
                    "Baseline",
                    include_str!("../contracts/Baseline.sol"),
                )
            }) as _,
            (|| {
                Contract::build_size_opt(
                    vec![],
                    "Flipper",
                    include_str!("../contracts/flipper.sol"),
                )
            }) as _,
            (|| {
                Contract::build_size_opt(
                    vec![],
                    "Computation",
                    include_str!("../contracts/Computation.sol"),
                )
            }) as _,
            (|| {
                Contract::build_size_opt(
                    vec![],
                    "FibonacciIterative",
                    include_str!("../contracts/Fibonacci.sol"),
                )
            }) as _,
            (|| Contract::build_size_opt(vec![], "ERC20", include_str!("../contracts/ERC20.sol")))
                as _,
            (|| Contract::build_size_opt(vec![], "SHA1", include_str!("../contracts/SHA1.sol")))
                as _,
            (|| {
                Contract::build_size_opt(
                    vec![],
                    "DivisionArithmetics",
                    include_str!("../contracts/DivisionArithmetics.sol"),
                )
            }) as _,
            (|| Contract::build_size_opt(vec![], "Events", include_str!("../contracts/Events.sol")))
                as _,
        ]
        .into_par_iter()
        .map(extract_code_size)
        .collect::<BTreeMap<_, _>>()
        .serialize(&mut serde_json::Serializer::pretty(
            File::create(path).unwrap(),
        ))
        .unwrap_or_else(|err| panic!("can not write codesize data to '{path}': {err}"));
    }
}