phink 0.1.5

🐙 Phink, a ink! smart-contract property-based and coverage-guided fuzzer
Documentation
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
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
<!DOCTYPE HTML>
<html lang="en" class="light" dir="ltr">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Phink Book</title>
        <meta name="robots" content="noindex">


        <!-- Custom HTML head -->
        
        <meta name="description" content="Documentation for Phink fuzzer">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff">

        <link rel="icon" href="favicon.svg">
        <link rel="shortcut icon" href="favicon.png">
        <link rel="stylesheet" href="css/variables.css">
        <link rel="stylesheet" href="css/general.css">
        <link rel="stylesheet" href="css/chrome.css">
        <link rel="stylesheet" href="css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="highlight.css">
        <link rel="stylesheet" href="tomorrow-night.css">
        <link rel="stylesheet" href="ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body class="sidebar-visible no-js">
    <div id="body-container">
        <!-- Provide site root to javascript -->
        <script>
            var path_to_root = "";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script>
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script>
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('light')
            html.classList.add(theme);
            var body = document.querySelector('body');
            body.classList.remove('no-js')
            body.classList.add('js');
        </script>

        <input type="checkbox" id="sidebar-toggle-anchor" class="hidden">

        <!-- Hide / unhide sidebar before it is displayed -->
        <script>
            var body = document.querySelector('body');
            var sidebar = null;
            var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            } else {
                sidebar = 'hidden';
            }
            sidebar_toggle.checked = sidebar === 'visible';
            body.classList.remove('sidebar-visible');
            body.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="INTRO.html">Introduction</a></li><li class="chapter-item expanded affix "><li class="part-title">User guide</li><li class="chapter-item expanded "><a href="START.html"><strong aria-hidden="true">1.</strong> Installation</a></li><li class="chapter-item expanded "><a href="CONFIG.html"><strong aria-hidden="true">2.</strong> Configuration</a></li><li class="chapter-item expanded "><a href="CAMPAIGN.html"><strong aria-hidden="true">3.</strong> Starting a campaign</a></li><li class="chapter-item expanded "><a href="INVARIANTS.html"><strong aria-hidden="true">4.</strong> Invariants</a></li><li class="chapter-item expanded "><a href="RUNTIME.html"><strong aria-hidden="true">5.</strong> Plug-in your runtime</a></li><li class="chapter-item expanded "><a href="SEEDS.html"><strong aria-hidden="true">6.</strong> Seeds</a></li><li class="chapter-item expanded affix "><li class="part-title">Concepts and understanding</li><li class="chapter-item expanded "><a href="CONCEPT.html"><strong aria-hidden="true">7.</strong> Concept and terminology</a></li><li class="chapter-item expanded "><a href="TECH.html"><strong aria-hidden="true">8.</strong> How does Phink work</a></li><li class="chapter-item expanded "><a href="TROUBLESHOTING.html"><strong aria-hidden="true">9.</strong> Troubleshoting</a></li><li class="chapter-item expanded "><a href="BENCHMARKS.html"><strong aria-hidden="true">10.</strong> Benchmarks</a></li><li class="chapter-item expanded "><a href="FAQ.html"><strong aria-hidden="true">11.</strong> FAQ</a></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle">
                <div class="sidebar-resize-indicator"></div>
            </div>
        </nav>

        <!-- Track and set sidebar scroll position -->
        <script>
            var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
            sidebarScrollbox.addEventListener('click', function(e) {
                if (e.target.tagName === 'A') {
                    sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
                }
            }, { passive: true });
            var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
            sessionStorage.removeItem('sidebar-scroll');
            if (sidebarScrollTop) {
                // preserve sidebar scroll position when navigating via links within sidebar
                sidebarScrollbox.scrollTop = sidebarScrollTop;
            } else {
                // scroll sidebar to current active section when navigating via "next/previous chapter" buttons
                var activeSection = document.querySelector('#sidebar .active');
                if (activeSection) {
                    activeSection.scrollIntoView({ block: 'center' });
                }
            }
        </script>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky">
                    <div class="left-buttons">
                        <label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </label>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Phink Book</h1>

                    <div class="right-buttons">
                        <a href="print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                        <a href="https://github.com/srlabs/phink/" title="Git repository" aria-label="Git repository">
                            <i id="git-repository-button" class="fa fa-github"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script>
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <center>
<img src="https://raw.githubusercontent.com/srlabs/phink/refs/heads/main/assets/phink.png" alt="phink" width="250"/>
<h1 id="introduction"><a class="header" href="#introduction">Introduction</a></h1>
</center>
<h2 id="overview-of-phink"><a class="header" href="#overview-of-phink">Overview of Phink</a></h2>
<p><strong>Phink</strong> is a blazing-fast⚡, property-based, coverage-guided fuzzer for ink! smart contracts. It lets developers
embed inviolable properties into smart contract testing workflows, equipping teams with automatic tools to detect
vulnerabilities and ensure contract reliability before deployment.</p>
<h3 id="dashboard-overview"><a class="header" href="#dashboard-overview">Dashboard overview</a></h3>
<img src="https://raw.githubusercontent.com/srlabs/phink/refs/heads/main/assets/dashboard.png" alt="phink"/>
<h3 id="key-features"><a class="header" href="#key-features">Key features</a></h3>
<h4 id="property-based-testing"><a class="header" href="#property-based-testing">Property-based testing</a></h4>
<p>Phink requires developers to define properties directly within ink! smart contracts. By prefixing functions with
<code>phink</code>, such as <code>fn phink_assert_abc_always_true()</code>, you create properties that
act as assertions. During testing, the fuzzer checks these properties against every input, which is a set of ink!
messages. If a
property’s assertion fails, this
triggers a panic. An invariant has been broken! This method ensures thorough validation of contract logic
and behavior.</p>
<h4 id="coverage-guided-fuzzing"><a class="header" href="#coverage-guided-fuzzing">Coverage-guided fuzzing</a></h4>
<p>In order to become coverage-guided, Phink needs to instrument the ink! smart contract.
Feedback is transmitted to the <code>pallet_contract</code> via the <code>debug_message</code>.
Although the fuzzer
currently adds feedback on each line executed, Phink is designed to evolve. It will eventually monitor coverage across
new
edges and code branches.</p>
<h3 id="why-use-phink"><a class="header" href="#why-use-phink">Why use Phink</a></h3>
<p>Phink addresses security concerns in these three main ways:</p>
<ol>
<li><strong>Automatically generate and test</strong> a diverse range of inputs</li>
<li><strong>Detect</strong> edge cases, logical flaws, and bugs leading to contract state reversion</li>
<li><strong>Explore</strong> different execution paths by generating input mutation</li>
</ol>
<p>This extensive testing identifies bugs and potential flaws early in the development cycle, empowering teams to
fix vulnerabilities before deployment and deliver safer applications.</p>
<div style="break-before: page; page-break-before: always;"></div><h2 id="getting-started-with-phink"><a class="header" href="#getting-started-with-phink">Getting started with Phink</a></h2>
<h3 id="installation"><a class="header" href="#installation">Installation</a></h3>
<h4 id="system-requirements"><a class="header" href="#system-requirements">System requirements</a></h4>
<p>To successfully install and run Phink, ensure your system meets the following requirements:</p>
<ul>
<li>
<p><strong>Operating System:</strong></p>
<ul>
<li><strong>Linux:</strong> <strong>Recommended</strong> for compatibility</li>
<li><strong>macOS:</strong> <em>Not recommended</em> as it doesn’t support some AFL++ plugins</li>
<li><strong>Windows:</strong> <em>Untested</em></li>
</ul>
</li>
<li>
<p><strong>Rust:</strong></p>
<ul>
<li><strong>Version:</strong> Rust <em>nightly</em></li>
<li><strong>Current Compatibility:</strong> <code>cargo 1.83.0-nightly (ad074abe3 2024-10-04)</code></li>
</ul>
</li>
</ul>
<h4 id="installation-guide"><a class="header" href="#installation-guide">Installation guide</a></h4>
<p>You can install Phink by building it from the source or by using Docker. Choose the method that best suits your setup
and IT environment. Let’s jump right into it!</p>
<h5 id="building-from-source"><a class="header" href="#building-from-source">Building from source</a></h5>
<p>Follow these 5 easy steps:</p>
<ol>
<li>
<p><strong>Clone the Repository</strong></p>
<pre><code class="language-bash">git clone https://github.com/srlabs/phink &amp;&amp; cd phink/
</code></pre>
<p>You can also run the following command:</p>
<pre><code class="language-bash">cargo +nightly install --git https://github.com/srlabs/phink
</code></pre>
</li>
<li>
<p><strong>Install Dependencies</strong></p>
<pre><code class="language-bash">cargo install --force ziggy cargo-afl honggfuzz grcov cargo-contract --locked
</code></pre>
</li>
<li>
<p><strong>Configure AFL++</strong></p>
<pre><code class="language-bash">cargo afl config --build --plugins --verbose --force
sudo cargo-afl afl system-config
</code></pre>
</li>
<li>
<p><strong>Build Phink</strong></p>
<pre><code class="language-bash">cargo build --release
</code></pre>
</li>
<li>
<p><strong>Run Phink</strong></p>
<pre><code class="language-bash">cargo run -- help  
</code></pre>
</li>
</ol>
<h5 id="using-docker"><a class="header" href="#using-docker">Using Docker</a></h5>
<ol>
<li>
<p><strong>Build the Docker Image</strong>
To build the <strong>Phink Docker image</strong>, run the following command in your terminal:</p>
<pre><code class="language-bash">docker build -t phink .
</code></pre>
</li>
</ol>
<p>For detailed Phink Docker installation instructions, refer
to <a href="https://github.com/srlabs/phink/blob/main/README.Docker.md">README.Docker.md</a>.</p>
<h3 id="basic-workflow"><a class="header" href="#basic-workflow">Basic workflow</a></h3>
<p>Follow these three high-level steps:</p>
<ol>
<li>
<p><strong>Instrument the contract</strong></p>
<ul>
<li>Use Phink to instrument your ink! smart contract for fuzzing</li>
</ul>
</li>
<li>
<p><strong>Configure fuzzing parameters</strong></p>
<ul>
<li>Edit the <code>phink.toml</code> file to set paths, deployment settings, and fuzzing options according to your project needs</li>
</ul>
</li>
<li>
<p><strong>Run your fuzzing campaign</strong></p>
<ul>
<li>Execute fuzzing with your configured settings to identify vulnerabilities early in the development cycle</li>
</ul>
</li>
</ol>
<div style="break-before: page; page-break-before: always;"></div><h1 id="phink-configuration-guide"><a class="header" href="#phink-configuration-guide">Phink configuration guide</a></h1>
<p>This guide provides an overview of the Phink configuration settings. You will learn how to configure the general
settings, specify some key paths and fuzzing options, and do some other essential tasks. Without further ado, let’s jump
right into it!</p>
<h2 id="configuration-file-overview"><a class="header" href="#configuration-file-overview">Configuration file overview</a></h2>
<p>Here’s how a configuration file looks like:</p>
<pre><code class="language-toml">### Phink Configuration

# General Settings
cores = 10                # Set to 1 for single-core execution
max_messages_per_exec = 1 # Maximum number of message calls per input

# Paths
instrumented_contract_path.path = "toooooooooooz"  # Path to the instrumented contract, after `phink instrument my_contract` is invoked
report_path = "output/phink/contract_coverage" # Directory for coverage HTML files
fuzz_output = "output"                         # Directory for fuzzing output

# Deployment
deployer_address = "5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT" # Contract deployer address (Alice by default)
constructor_payload = "9BAE9D5E"                                     # Hexadecimal scale-encoded data for contract instantiation
storage_deposit_limit = "100000000000"                              # Storage deposit limit
instantiate_initial_value = "0"                                     # Value transferred during instantiation, if needed

# Fuzzing Options
fuzz_origin = false  # Attempt to call each message as a different user (affects performance)
verbose = true       # Print detailed debug messages
show_ui = true       # Display advanced UI
use_honggfuzz = false # Use Honggfuzz (set as false)
catch_trapped_contract = false # Not setting trapped contract as a bug, only detecting invariant-based bugs

# Gas Limits
[default_gas_limit]
ref_time = 100_000_000_000      # Reference time for gas
proof_size = 3_145_728          # Proof size (3 * 1024 * 1024 bytes)
</code></pre>
<h2 id="general-settings"><a class="header" href="#general-settings">General settings</a></h2>
<p>The General settings cover these 2 parameters:</p>
<ul>
<li><strong>cores</strong>: Allocate the number of CPU cores for fuzzing. Setting this to <code>1</code> enables single-core execution. We *
<em>highly</em>* not recommend using single-core, since this will dissalow <code>CMPLOG</code> feature from AFL++.</li>
<li><strong>max_messages_per_exec</strong>: Define the maximum number of message calls allowed per fuzzing input. If you want to fuzz
one function per one function, set this number to 1. Setting it to zero will fuzz zero message. Setting it, for
example,
to 4 will generate 4 different messages in one input, run all the invariants, and go to the next input.</li>
</ul>
<h2 id="paths"><a class="header" href="#paths">Paths</a></h2>
<p>The Paths settings cover these 3 parameters:</p>
<ul>
<li><strong>instrumented_contract_path.path</strong>: Specify the path to the instrumented contract, which should be set
post-invocation of <code>phink instrument my_contract</code>. This path will contain the source code of the initial contract,
with the additional instrumentation instructions. It will also contain the instrumented compiled contract.</li>
<li><strong>report_path</strong>: Designate the directory where HTML coverage reports will be generated if the user wishes to generate
a coverage report.</li>
<li><strong>fuzz_output</strong>: Indicate the directory for storing all fuzzing output. This output is important as it will contain
the log file, the corpus entries, the crashes, and way more.</li>
</ul>
<h2 id="deployment"><a class="header" href="#deployment">Deployment</a></h2>
<p>The Deployment settings include these 4 parameters:</p>
<ul>
<li><strong>deployer_address</strong>: Set the address of the smart contract deployer. The default is Alice’s address.</li>
<li><strong>constructor_payload</strong>: Hexadecimal scale-encoded data necessary for contract instantiation. This is used when
calling <code>bare_instantiate</code> extrinsic to instantiate the contract. You can use https://ui.use.ink/ to generate this
payload. By default, Phink will deploy the contract using the constructor that has no arguments <code>new()</code>.</li>
<li><strong>storage_deposit_limit</strong>: Limit for storage deposits during contract deployment. It represents
an optional cap on the amount of blockchain storage (measured in balance units) that can be used or reserved by the
contract call.</li>
<li><strong>instantiate_initial_value</strong>: Initial value to be transferred upon contract instantiation if required. So if the
contract requires a minimum amount of 3000 units during instantiation, set 3000 here.</li>
</ul>
<h2 id="fuzzing-options"><a class="header" href="#fuzzing-options">Fuzzing options</a></h2>
<p>These 4 parameters are important when you configure fuzzing options:</p>
<ul>
<li><strong>fuzz_origin</strong>: A Boolean option to try calling each message as a different user, which may impact performance. If
set to <code>false</code>, the fuzzer will fuzz any message with the one input (Alice).</li>
<li><strong>verbose</strong>: Enables detailed debugging of messages when set to <code>true</code>. This will just output more logs.</li>
<li><strong>show_ui</strong>: Toggle for displaying the advanced user interface.</li>
<li><strong>use_honggfuzz</strong>: Determines whether to use Honggfuzz; remains <code>false</code> by
default. (<strong>let it false! is not handled currently</strong>)</li>
<li><strong>catch_trapped_contract</strong>: Indicate whether the fuzzer should treat trapped contracts as bugs.
<ul>
<li>When set to <code>true</code>: The fuzzer will identify any contracts that become trapped (<code>ContractTrapped</code>) as bugs. This
is
useful for an examination of potential issues, as it covers all types of bugs, not just ones related to
logic or state invariants.</li>
<li>When set to <code>false</code>: Focuses only on catching bugs related to invariant violations, ignoring trapped contract
scenarios. This is preferable when you are only interested in logical correctness and not in trapping errors.</li>
</ul>
</li>
</ul>
<h2 id="gas-limit"><a class="header" href="#gas-limit">Gas limit</a></h2>
<p>The gas limit refers to the maximum amount of computational effort (or weight) that an execution is allowed to use when
performing a call to a contract. It controls how much balance a contract is allowed to use for expanding its state
storage during execution. The setting ensures that users won’t unintentionally spend more than they wanted on storage
allocation. Besides, it offers protection against excessive storage costs by defining an upper limit on how much can be
spent on storage
within that call.</p>
<h3 id="default-gas-limit-configuration"><a class="header" href="#default-gas-limit-configuration">Default gas limit configuration</a></h3>
<p>The key Gas limit settings include:</p>
<ul>
<li><strong>ref_time</strong>: Specify the reference time for gas allocation.</li>
<li><strong>proof_size</strong>: Define the proof size (e.g., <code>3145728</code> corresponds to 3 MB).</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h2 id="writing-properties-for-ink-contracts"><a class="header" href="#writing-properties-for-ink-contracts">Writing properties for ink! contracts</a></h2>
<h3 id="adding-properties"><a class="header" href="#adding-properties">Adding properties</a></h3>
<h4 id="inside-your-cargotoml"><a class="header" href="#inside-your-cargotoml">Inside your <code>Cargo.toml</code></a></h4>
<p>First, you need to add the <code>phink</code> feature to your <code>Cargo.toml</code>, such as:</p>
<pre><code class="language-toml">[features]
phink = []
</code></pre>
<h4 id="inside-your-filers"><a class="header" href="#inside-your-filers">Inside your <code>file.rs</code></a></h4>
<p>Then, you can use the following example to create invariants. Create another <code>impl</code> in your contract, and
put
it under the feature of <code>phink</code>. Use <code>assert!</code> or <code>panic!</code> for your properties.</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[cfg(feature = "phink")]
#[ink(impl)]
impl DomainNameService {
    // This invariant ensures that nobody registed the forbidden number
    #[ink(message)]
    #[cfg(feature = "phink")]
    pub fn phink_assert_dangerous_number(&amp;self) {
        let forbidden_number = 42;
        assert_ne!(self.dangerous_number, forbidden_number);
    }
}
<span class="boring">}</span></code></pre></pre>
<p>You can find more informations in the page dedicated to <a href="INVARIANTS.html">invariants</a>.</p>
<h2 id="running-phink"><a class="header" href="#running-phink">Running Phink</a></h2>
<h3 id="1-instrument-the-contract"><a class="header" href="#1-instrument-the-contract">1. Instrument the contract</a></h3>
<p>First things first: Let’s enable your contract for fuzzing. Run the following command to instrument your ink! smart
contract:</p>
<pre><code class="language-sh">cargo run -- instrument my_contract/
</code></pre>
<p>This step modifies the contract to include necessary hooks for Phink’s fuzzing process. It creates a fork of the
contract, so you don’t have to make a copy before.</p>
<h3 id="2-generate-seeds-optionnal-but-highly-recommended"><a class="header" href="#2-generate-seeds-optionnal-but-highly-recommended">2. Generate seeds (optionnal but highly recommended)</a></h3>
<p>The <code>cargo run -- generate-seed</code> command is an optional but powerful feature that enhances your fuzzing experience by
generating initial seeds from your existing unit and end-to-end (E2E) tests.</p>
<h4 id="what-it-does"><a class="header" href="#what-it-does">What it Does</a></h4>
<p><code>cargo run -- generate-seed</code> executes the unit tests and E2E tests of your ink! smart contract, extracting seeds based
on
executed messages. These seeds are saved in the <code>corpus/</code> directory, which highly helps to reach good coverage, as long
as you have good tests.
<strong>Therefore, we encourage to have good and various unit-tests and E2E tests in your contract.</strong></p>
<h4 id="how-it-works"><a class="header" href="#how-it-works">How It Works</a></h4>
<ul>
<li>
<p><strong>Unit Tests</strong>: The command runs through all defined unit tests and captures the invoked messages, with Alice as the
origin and a value of 0.</p>
</li>
<li>
<p><strong>End-to-End Tests</strong>: For E2E tests, Phink modifies the <code>Cargo.toml</code> to point to
a <a href="https://github.com/kevin-valerio/ink/commit/5869d341ff13a454c22a6980fd232f4520721b97">custom ink! repository</a>. This
step
ensures necessary modifications are included to print debug messages containing the message’s 4-byte hash and
scale-encoded parameters to stdout.</p>
</li>
<li>
<p>If a test invokes at least one message, Phink extracts them all as seeds for use during fuzzing.</p>
</li>
</ul>
<h4 id="usage"><a class="header" href="#usage">Usage</a></h4>
<pre><code class="language-sh">cargo run -- generate-seed &lt;CONTRACT&gt; [COMPILED_DIRECTORY]
</code></pre>
<ul>
<li><code>&lt;CONTRACT&gt;</code>: The root directory path of your ink! smart contract.</li>
<li><code>[COMPILED_DIRECTORY]</code>: Optional path for where the temporary contract will be compiled. Defaults to <code>tmp</code> if
unspecified.</li>
</ul>
<p>This will generate a set of initial inputs, derived from your current tests, to kickstart fuzzing.</p>
<h4 id="why-using-generate-seed"><a class="header" href="#why-using-generate-seed">Why using <code>generate-seed</code>?</a></h4>
<p>Generating seeds from your existing test suite can increase the efficiency of fuzz testing by:</p>
<ul>
<li>Providing a good starting point for fuzzing inputs.</li>
<li>Ensuring that the fuzzing process begins with valid and meaningful test cases.</li>
</ul>
<p>For more information on how seeds work with Phink, refer to
the <a href="SEEDS.html">seeds documentation</a>.</p>
<h3 id="3-run-the-fuzzer"><a class="header" href="#3-run-the-fuzzer">3. Run the fuzzer</a></h3>
<p>After <strong>instrumenting</strong> your contract and <strong>writing</strong> properties and <strong>configuring</strong> your <code>phink.toml</code>, let’s get our
hands on the fuzzing process:</p>
<pre><code class="language-sh">cargo run -- fuzz
</code></pre>
<p>After executing this command, your fuzzing tests will begin based on the configurations specified in your <code>phink.toml</code>
file. You should see a user interface appear.</p>
<p>If you’re utilizing the advanced UI, you’ll receive <em>real-time</em> updates on the fuzzed messages at the bottom of the
screen. For more detailed log information, you can use the following command:</p>
<pre><code class="language-sh">watch -c -t -n 0.5 "clear &amp;&amp; cat output/phink/logs/last_seed.phink" # `output` is the default, but it depends of your `phink.toml`
</code></pre>
<p>This will provide you with clearer logs by continuously updating them every <strong>0.1</strong> seconds.</p>
<h2 id="analyzing-results"><a class="header" href="#analyzing-results">Analyzing results</a></h2>
<h3 id="crashes"><a class="header" href="#crashes">Crashes</a></h3>
<p>In case of crashes, you should see something like the following.</p>
<img src="https://raw.githubusercontent.com/srlabs/phink/refs/heads/main/assets/crashed.png" alt="crash"/>
<p>To analyze the crash, you can run <code>cargo run -- execute &lt;your_crash&gt;</code>, for instance
<code>cargo run -- execute output/phink/crashes/1729082451630/id:000000,sig:06,src:000008,time:627512,execs:3066742,op:havoc,rep:2</code></p>
<div class="table-wrapper"><table><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody>
<tr><td>1729082451630</td><td>Timestamp representing when the crash was recorded</td></tr>
<tr><td>id:000000</td><td>Unique identifier for the crash</td></tr>
<tr><td>sig:06</td><td>Signal number that triggered the crash</td></tr>
<tr><td>src:000008</td><td>Source test case number</td></tr>
<tr><td>time:627512</td><td>Execution time since the start of the testing process</td></tr>
<tr><td>execs:3066742</td><td>Cumulative number of executions performed until the crash</td></tr>
<tr><td>op:havoc,rep:2</td><td>Type of fuzzing operation (havoc) and its repetition number</td></tr>
</tbody></table>
</div>
<p>By running the above command, you should get an output similar to the screenshot below:</p>
<img src="https://raw.githubusercontent.com/srlabs/phink/refs/heads/main/assets/backtrace.png" alt="crash"/>
<h3 id="coverage"><a class="header" href="#coverage">Coverage</a></h3>
<p><strong>This feature is in alpha and unstable.</strong></p>
<h4 id="generating-a-coverage-report"><a class="header" href="#generating-a-coverage-report">Generating a coverage report</a></h4>
<p>First, you need to create a <code>traces.cov</code> file. For this, execute the command below.</p>
<pre><code class="language-sh">cargo run -- run  
</code></pre>
<p>Once done, generate coverage reports to analyze which parts of the contract were tested:</p>
<pre><code class="language-sh">cargo run -- coverage my_contract/
</code></pre>
<p>Some HTML files should then be generated in the path you’ve configured inside your <code>phink.toml</code>. The coverage report
provides a visual representation of the tested code areas. As a rule of thumb, the more green lines you can see there,
the better it is for the code.</p>
<h3 id="coverage-report-example"><a class="header" href="#coverage-report-example">Coverage report example</a></h3>
<p><strong>Green Lines</strong>: Code that has been tested.</p>
<p><img src="https://raw.githubusercontent.com/srlabs/phink/refs/heads/main/assets/coverage_1.png" alt="Coverage Report Part 1" /></p>
<p><em>Figure 1: Coverage Report of one specific file.</em></p>
<img src="https://raw.githubusercontent.com/srlabs/phink/refs/heads/main/assets/coverage_2.png" alt="coverage_2" width="400"/>
<p><em>Figure 2: List of fuzzed Rust files from the ink! smart-contract.</em></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="invariants"><a class="header" href="#invariants">Invariants</a></h1>
<p>Invariants are <strong>fundamental properties that must always hold</strong> true in a smart-contract, regardless of any operations
performed. They help ensure that certain logical conditions remain constant throughout the
execution of the contract, preventing potential vulnerabilities and ensuring its reliability.</p>
<p>We suggest to use <strong>integrity</strong> and <strong>unit tests</strong> from your codebase to get inspiration to generate good invariants.</p>
<h2 id="creating-good-invariants-for-ink-smart-contracts"><a class="header" href="#creating-good-invariants-for-ink-smart-contracts">Creating good invariants for ink! smart-contracts</a></h2>
<p>Below are some guidelines to help you design robust invariants:</p>
<ol>
<li>
<p><strong>Understand the Contract’s Logic</strong>: Before crafting invariants, deeply understand the core logic and expected
behaviors of your smart contract.</p>
</li>
<li>
<p><strong>Identify Critical Properties</strong>: Determine critical properties or conditions that must hold <strong>true</strong>. This could
involve
state variables, transaction outcomes, or other interdependent conditions.</p>
</li>
<li>
<p><strong>Consider Corner Cases</strong>: Think about edge cases and potential attack vectors. Invariants should be designed to
capture unexpected or extreme scenarios.</p>
</li>
<li>
<p><strong>Focus on Consistency</strong>: Consider properties that ensure data consistency across state changes. This might involve
ensuring balances are correctly updated or ownership is properly maintained.</p>
</li>
<li>
<p><strong>Keep it Simple</strong>: While considering complex scenarios, ensure your invariants are straightforward to encourage
maintainability and clarity.</p>
</li>
</ol>
<h2 id="example-invariant-in-ink-smart-contracts"><a class="header" href="#example-invariant-in-ink-smart-contracts">Example invariant in ink! smart-contracts</a></h2>
<p>Here is a template to get you started on writing invariants for ink! smart contracts:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[cfg(feature = "phink")]
#[ink(impl)]
impl DomainNameService {
    /// Example invariant:
    #[ink(message)]
    #[cfg(feature = "phink")]
    pub fn phink_balance_invariant(&amp;self) {
        // Ensure total supply equals sum of individual balances
        assert_eq!(self.total_supply, self.calculate_total_balances(), "Balance invariant violated!");
    }
}
<span class="boring">}</span></code></pre></pre>
<h3 id="annotations-explaination"><a class="header" href="#annotations-explaination">Annotations explaination</a></h3>
<ul>
<li><strong><code>#[cfg(feature = "phink")]</code></strong>: Ensures the function is only compiled when the “phink” feature is enabled.</li>
<li><strong><code>#[ink(message)]</code></strong>: Marks the function as an executable entry defined by the ink! framework.</li>
<li><strong>Function Naming</strong>: Begin with “phink_” to indicate the purpose and correlation to fuzz testing.</li>
</ul>
<h2 id="creating-invariants-with-llm"><a class="header" href="#creating-invariants-with-llm">Creating invariants with LLM</a></h2>
<p>Large Language Models (LLMs) offer a good (<em>lazy, yes…</em>) approach to generate invariants by interpreting the logic and
identifying properties from the contract code. Here is an example prompt system you could use to generate a base of
invariants</p>
<h5 id="system-prompt"><a class="header" href="#system-prompt">System prompt</a></h5>
<pre><code class="language-markdown">You are provided with Rust files containing an ink! smart contract. Your task is to generate invariants, which are
inviolable properties that a fuzzer will check to ensure the contract's quality and correctness. Please adhere to the
following requirements while writing the invariants:

1. Ensure that the `impl` block is annotated with `#[cfg(feature = "phink")] #[ink(impl)]`.
2. Confirm that the `impl DomainNameService` is the main implementation block of the contract.
3. Each invariant must be annotated with:
    - `#[ink(message)]`
    - `#[cfg(feature = "phink")]`
    - Function names must start with "phink_".
4. Each invariant function must contain at least one assertion statement, such as `assert`, `assert_ne`, `panic`, etc.
5. Be creative and consider corner cases to ensure the thoroughness of the invariants.

Output example:

```rust
#[cfg(feature = "phink")]
#[ink(impl)]
impl DomainNameService {
    // This invariant ensures that `domains` doesn't contain the forbidden domain that nobody should register 
    #[ink(message)]
    #[cfg(feature = "phink")]
    pub fn phink_assert_hash42_cant_be_registered(&amp;self) {
        for i in 0..self.domains.len() {
            if let Some(domain) = self.domains.get(i) {
                // Invariant triggered! We caught an invalid domain in the storage...
                assert_ne!(domain.clone().as_mut(), FORBIDDEN_DOMAIN);
            }
        }
    }
}
`` `
</code></pre>
<h5 id="sources-in-the-prompt"><a class="header" href="#sources-in-the-prompt">Sources in the prompt</a></h5>
<p>If your contract is small enough and contains multiple Rust files, you could use the following snippet, to put
everything inside <code>everything.rs</code>.</p>
<pre><code class="language-sh">find . -name "*.rs" -not -path "./target/*" -exec cat {} + &gt; everything.rs
</code></pre>
<p>Copy paste the content after your <em>system prompt</em>, and examine the LLM invariants. Otherwise, simply copy paste the code
from your <code>lib.rs</code></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="runtime-integration"><a class="header" href="#runtime-integration">Runtime integration</a></h1>
<p>Phink provides developers with the flexibility to customize their fuzzing environment through a simple interface. By
editing <code>contract/custom/custom.rs</code> and <code>contract/custom/preferences.rs</code>, developers can tailor the runtime storage and
contract initialization
processes to suit their testing needs. Have a clone of Phink if you want to modify the source code.</p>
<h2 id="custom-runtime-storage"><a class="header" href="#custom-runtime-storage">Custom runtime storage</a></h2>
<p>Phink allows developers to tailor the runtime environment by customizing the storage configuration. Let’s
create some realistic testing scenarios!</p>
<h3 id="example"><a class="header" href="#example">Example</a></h3>
<pre><code class="language-rust ignore">impl DevelopperPreferences for Preferences {
    fn runtime_storage() -&gt; Storage {
        let storage = RuntimeGenesisConfig {
            balances: BalancesConfig {
                balances: (0..u8::MAX) // Allocates substantial balance to accounts
                    .map(|i| [i; 32].into())
                    .collect::&lt;Vec&lt;_&gt;&gt;()
                    .iter()
                    .cloned()
                    .map(|k| (k, 10000000000000000000 * 2))
                    .collect(),
            },
            ..Default::default()
        }
            .build_storage()
            .unwrap();
        storage
    }
}</code></pre>
<h3 id="customization-points"><a class="header" href="#customization-points">Customization points</a></h3>
<ul>
<li><strong><code>runtime_storage</code>:</strong> This function is your gateway to defining any mocks or <code>RuntimeGenesisConfig</code> settings needed
for your testing environment. Whether it’s allocating funds, initializing storage items, or setting up custom
storage, you can adjust these configurations to mirror your deployment scenarios closely. This flexibility allows
you to test how your ink! smart contract behave in various simulated network states.</li>
</ul>
<h2 id="contract-initialization"><a class="header" href="#contract-initialization">Contract initialization</a></h2>
<p>The <code>on_contract_initialize</code> function can be adapted to execute additional initialization logic, such as uploading
supplementary contracts or handling dependencies.</p>
<h3 id="usage-example"><a class="header" href="#usage-example">Usage example</a></h3>
<pre><code class="language-rust ignore">fn on_contract_initialize() -&gt; anyhow::Result&lt;()&gt; {
    Contracts::bare_upload_code(
        AccountId32::new([1; 32]),
        fs::read("adder.wasm")?,
        None,
        Determinism::Enforced,
    );
    Ok(())
}</code></pre>
<h3 id="customization-points-1"><a class="header" href="#customization-points-1">Customization points</a></h3>
<ul>
<li><strong><code>runtime_storage</code>:</strong> Use this function as your gateway to defining any mocks or <code>RuntimeGenesisConfig</code> settings
needed
for your testing environment. Whether it’s allocating funds, initializing storage items, or setting up custom
storage, adjust these configurations to mirror your deployment scenarios closely. This flexibility lets
you test how your ink! smart contract behaves in various simulated network states.</li>
</ul>
<h2 id="contract-initialization-1"><a class="header" href="#contract-initialization-1">Contract initialization</a></h2>
<p>The <code>on_contract_initialize</code> function can be adapted to execute additional initialization logic, such as uploading
supplementary contracts or handling dependencies.</p>
<h3 id="example-1"><a class="header" href="#example-1">Example</a></h3>
<pre><code class="language-rust ignore">fn on_contract_initialize() -&gt; anyhow::Result&lt;()&gt; {
    Contracts::bare_upload_code(
        AccountId32::new([1; 32]),
        fs::read("adder.wasm")?,
        None,
        Determinism::Enforced,
    );
    Ok(())
}</code></pre>
<h3 id="customization-points-2"><a class="header" href="#customization-points-2">Customization points</a></h3>
<ul>
<li><strong><code>on_contract_initialize</code>:</strong> Use this function to automate contract uploads, configure dependencies, or perform any
setup necessary before testing.</li>
</ul>
<h2 id="custom-runtime-parameters"><a class="header" href="#custom-runtime-parameters">Custom runtime parameters</a></h2>
<p>Phink provides default runtime configurations, but developers can provide their own runtime parameters in
<code>contract/runtime.rs</code>. This can be particularly useful if you wish to connect your fuzzing environment to your own
Substrate runtime, so Phink can be adapted to work with your specific runtime.
<strong>You can edit the runtime configure <a href="https://github.com/srlabs/phink/blob/main/src/contract/runtime.rs">here</a>.</strong></p>
<h3 id="example-custom-runtime-configuration"><a class="header" href="#example-custom-runtime-configuration">Example: custom runtime configuration</a></h3>
<p>For instance, customize the <code>pallet_timestamp</code> runtime parameters like this:</p>
<pre><code class="language-rust ignore">impl pallet_timestamp::Config for Runtime {
    type MinimumPeriod = CustomMinimumPeriod;
    ...
}</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="seed-format"><a class="header" href="#seed-format">Seed format</a></h1>
<p>In Phink, a seed is structured to guide the fuzzing process effectively. The seed is composed of these 4 parts:</p>
<ul>
<li><strong>4 bytes</strong>: Represents the balance value to be transferred to the message if it’s payable</li>
<li><strong>1 byte</strong>: Specifies the origin; applicable if fuzzing origin is enabled in the configuration</li>
<li><strong>4 bytes</strong>: Identifies the message selector</li>
<li><strong>Remaining bytes</strong>: Contains the SCALE-encoded parameters for the message</li>
</ul>
<p>If your configuration allows more than one message per input, Phink uses the delimiter <code>"********"</code> to separate multiple
messages within a single input. This enables comprehensive testing across multiple scenarios from a single seed.</p>
<h2 id="example-2"><a class="header" href="#example-2">Example</a></h2>
<p>Here’s a breakdown for the seed
<code>0000000001fa80c2f6002a2a2a2a2a2a2a2a0000000103ba70c3aa18040008000f00100017002a00</code>:</p>
<div class="table-wrapper"><table><thead><tr><th>Segment</th><th>Bytes</th><th>Description</th></tr></thead><tbody>
<tr><td>Balance transfer</td><td><code>00000000</code></td><td>4 bytes for balance (no transfer in this case)</td></tr>
<tr><td>Origin</td><td><code>01</code></td><td>1 byte indicating the origin (Alice) (enabled in config)</td></tr>
<tr><td>Message selector 1</td><td><code>fa80c2f6</code></td><td>4 bytes for the first message selector</td></tr>
<tr><td>Parameters 1</td><td><code>00</code></td><td>SCALE-encoded parameters for the first message</td></tr>
<tr><td>Message delimiter</td><td><code>2a2a2a2a2a2a2a2a</code></td><td>Delimits the first and second messages (<code>********</code>)</td></tr>
<tr><td>Balance transfer</td><td><code>00000001</code></td><td>4 bytes for balance (1 unit transfered)</td></tr>
<tr><td>Origin</td><td><code>03</code></td><td>1 byte indicating the origin (Charlie) for the second message</td></tr>
<tr><td>Message selector 2</td><td><code>ba70c3aa</code></td><td>4 bytes for the second message selector</td></tr>
<tr><td>Parameters 2</td><td><code>18040008000f00100017002a00</code></td><td>SCALE-encoded vector: [4, 8, 15, 16, 23, 42]</td></tr>
</tbody></table>
</div>
<h3 id="explanation"><a class="header" href="#explanation">Explanation</a></h3>
<ul>
<li><strong>Balance transfer</strong>: The 4 bytes representing the balance transfer amount (set to <code>00000000</code> for the first message),
indicating no value is being transferred for either message.</li>
<li><strong>Origin</strong>: A single byte is used (<code>01</code> for the first message and <code>03</code> for the second) to specify the origin of the
call. This is useful for testing scenarios with different origins.</li>
<li><strong>Message selector</strong>: The first message, for example, begins with a 4-byte identifier (<code>fa80c2f6</code>), indicating which
message within the contract is being invoked.</li>
<li><strong>Parameters</strong>: Following the message selector, SCALE-encoded parameters are specified (example: <code>00</code>), representing
the input data for each message.</li>
<li><strong>Message delimiter</strong>: This seed uses the delimiter <code>********</code> (represented as <code>2a2a2a2a2a2a2a2a</code>) to separate
multiple messages within a single input, allowing more complex interactions to be tested.</li>
</ul>
<h1 id="running-one-seed"><a class="header" href="#running-one-seed">Running one seed</a></h1>
<p>To execute a single seed, use the following command:</p>
<pre><code class="language-bash">cargo run -- execute my_seed.bin
</code></pre>
<p>This command runs the specific seed <code>my_seed.bin</code>, providing targeted fuzzing for individual transaction testing.</p>
<h1 id="running-all-the-seeds"><a class="header" href="#running-all-the-seeds">Running all the seeds</a></h1>
<p>To run all seeds sequentially, use the following command:</p>
<pre><code class="language-bash">cargo run -- run
</code></pre>
<p>This command iterates over the <code>corpus</code> folder, executing each seed. This ensures a comprehensive fuzzing process that
covers
all previously discovered cases.</p>
<h1 id="minimizing-the-corpus"><a class="header" href="#minimizing-the-corpus">Minimizing the corpus</a></h1>
<p>To minimize the corpus folder containing seeds, use the following command:</p>
<pre><code class="language-bash">cargo run -- minimize
</code></pre>
<p>The goal of the corpus minimization process is to streamline the set of seeds in the corpus folder, reducing it to the
most essential and impactful test cases. Minimization makes fuzzing more efficient by eliminating
redundant seeds, speeding up the speed and focusing only on seeds that reveal new or unique coverage.</p>
<h3 id="what-it-does-1"><a class="header" href="#what-it-does-1">What it does</a></h3>
<p><code>cargo run -- minimize</code> analyzes the seeds within the corpus and identifies those that are redundant
or do not contribute additional value to the fuzzing campaign. It executes each seed to determine their individual
impact
and removes any seeds that do not enhance coverage or expose new bugs. This results in a minimized set of seeds, savind
time time and
also optimizing resource usage.</p>
<h1 id="generating-a-seed"><a class="header" href="#generating-a-seed">Generating a seed</a></h1>
<p>To generate a new seed, all you need to do is construct it using the prescribed format. Start with the required byte
sequences for
balance, origin, message selector, and parameters, and then save it in your designated seed directory.</p>
<h2 id="importance-of-seed-generation"><a class="header" href="#importance-of-seed-generation">Importance of seed generation</a></h2>
<p>How can we detect and fix more potential
vulnerabilities and edge cases faster? The ability to manually create seeds is crucial for enhancing the effectiveness
of the fuzz testing process. By creating
custom seeds, developers can guide the fuzzer to explore paths and scenarios that might not be easily discovered through
automated means. This, in turn, increases the overall coverage of the fuzzing campaign. If you need to generate the
SCALE-encoded parameters, it’s best to
utilize tools like <code>cargo contract</code>
or <a href="https://polkadot.js.org/apps/">Polkadot.js</a>.</p>
<h1 id="adding-a-seed-to-the-corpus"><a class="header" href="#adding-a-seed-to-the-corpus">Adding a seed to the corpus</a></h1>
<p>To add a custom seed to the corpus, use the following command:</p>
<pre><code class="language-bash">cargo ziggy add-seeds -i my_custom_seeds/ -z output/
</code></pre>
<ul>
<li><code>my_custom_seeds/</code>: Directory containing your custom seeds</li>
<li><code>output/</code>: Directory where the fuzzing output is stored</li>
</ul>
<p>Once added, the corpus will use these seeds in subsequent fuzzing processes.</p>
<h1 id="viewing-and-editing-seeds"><a class="header" href="#viewing-and-editing-seeds">Viewing and editing seeds</a></h1>
<p>To view the hexadecimal content of a seed, issue the following command:</p>
<pre><code class="language-bash">xxd -c 3000 -p output/phink/corpus/one_seed.bin &gt; abc.out
</code></pre>
<p>This useful command converts the binary seed file into hex for easier reading and editing.</p>
<p>To edit a seed, complete these 3 easy tasks:</p>
<ol>
<li>
<p>Open the hex file in your preferred editor, and edit it</p>
<pre><code class="language-bash">vim abc.out
</code></pre>
</li>
<li>
<p>Save the changes and revert the hex file to binary</p>
<pre><code class="language-bash">rm seed.bin # Used to bypass cached seed
xxd -r -p abc.out seed.bin
</code></pre>
</li>
<li>
<p>Execute the updated seed</p>
<pre><code class="language-bash">cargo run -- execute seed.bin
</code></pre>
</li>
</ol>
<p>Congratulations! We’re off to the races again.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="concepts-and-terminology"><a class="header" href="#concepts-and-terminology">Concepts and terminology</a></h1>
<h2 id="concepts"><a class="header" href="#concepts">Concepts</a></h2>
<h3 id="fuzzing"><a class="header" href="#fuzzing">Fuzzing</a></h3>
<p><strong>Fuzzing</strong> is an automated software testing technique that involves providing random data inputs to
a program. The primary goal is to uncover anomalies, such as crashes and assertion failures. These are intriguing
because they pinpoint
potential vulnerabilities.</p>
<h3 id="property-based-fuzzing"><a class="header" href="#property-based-fuzzing">Property-based fuzzing</a></h3>
<p><strong>Property-based testing</strong> involves specifying properties or invariants that your ink! contract should always satisfy.
In
Phink, these properties act as assertions. Phink makes it possible for developers to
define properties directly within ink! smart contracts. Such properties are then tested against varied
inputs. In this way, the contract maintains its invariants across all possible data conditions. But there is a final
twist in the fuzzing tale.</p>
<h3 id="coverage-guided-fuzzing-1"><a class="header" href="#coverage-guided-fuzzing-1">Coverage-guided fuzzing</a></h3>
<p><strong>Coverage-guided fuzzing</strong> is a fuzzing strategy that focuses on maximizing code coverage during testing. It uses
feedback from code execution paths to guide input generation, focusing on unexplored parts of the code.
Phink instruments ink! smart contracts to track code coverage. Optimizing fuzzing efforts by targeting less examined
paths is what makes the game worth playing.</p>
<h2 id="terminology"><a class="header" href="#terminology">Terminology</a></h2>
<h3 id="corpus"><a class="header" href="#corpus">Corpus</a></h3>
<p>A <strong>corpus</strong> refers to the collection of all input samples used during the testing process. It is
continuously updated with new inputs that lead to unique execution paths.</p>
<hr />
<h3 id="seed"><a class="header" href="#seed">Seed</a></h3>
<p>A <strong>seed</strong> is an initial input provided to the fuzzer to start the testing process. Seeds serve as the starting point
for generating new test cases and are crucial for initializing a diverse and effective fuzzing campaign. A strong set of
seed inputs can significantly enhance the fuzzing campaign.</p>
<hr />
<h3 id="invariants-1"><a class="header" href="#invariants-1">Invariants</a></h3>
<p><strong>Invariants</strong> are conditions or properties that must remain true at all times during the execution of a program or
contract. In property-based testing, invariants are used as assertions to verify the consistent behavior of smart
contracts under various input conditions. Breaking an invariant indicates a potential bug or vulnerability.</p>
<hr />
<h3 id="instrumentation"><a class="header" href="#instrumentation">Instrumentation</a></h3>
<p><strong>Instrumentation</strong> involves modifying a program to collect runtime information such as code coverage data. In fuzzing,
instrumentation traces execution paths, enabling coverage-guided techniques to generate more informed and
effective test cases.</p>
<hr />
<h3 id="coverage-1"><a class="header" href="#coverage-1">Coverage</a></h3>
<p><strong>Coverage</strong> measures how much of a program’s code is tested during fuzzing. High coverage corresponds to a
good assessment of the contract’s logic.</p>
<hr />
<h3 id="contract-selectors"><a class="header" href="#contract-selectors">Contract selectors</a></h3>
<p><strong>ink! contract selectors</strong> are unique identifiers for functions within ink! smart contracts. Selectors are derived from
function signatures and are used to call specific functions within a contract deployed on the blockchain.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="how-phink-works"><a class="header" href="#how-phink-works">How Phink works</a></h1>
<p>Phink is built on top of AFL++, leveraging its capabilities to provide effective fuzz testing for ink! smart contracts.
Here’s an overview of how the fuzzer operates.</p>
<h2 id="afl-integration"><a class="header" href="#afl-integration">AFL++ integration</a></h2>
<p>Phink utilizes AFL++ through two key components:</p>
<ul>
<li><strong>ziggy</strong>: A multifuzzing crate that enables integration with multiple fuzzers.</li>
<li><strong>afl.rs</strong>: A crate that spawns AFL++ fuzzers, facilitating seamless mutation and coverage tracking.</li>
</ul>
<h3 id="afl-mechanics"><a class="header" href="#afl-mechanics">AFL++ mechanics</a></h3>
<p>AFL++ mutates the input bytes and evaluates whether these mutations increase code coverage. If a mutation results in new
execution paths, the modified seed is retained in the corpus. This iterative process enhances the likelihood of
discovering hidden vulnerabilities.</p>
<h3 id="monitoring-execution"><a class="header" href="#monitoring-execution">Monitoring execution</a></h3>
<p>Users can monitor the execution logs using familiar AFL++ tools. For instance, by using <code>tail</code>, you can view real-time
fuzzer logs and activity:</p>
<pre><code class="language-bash">tail -f output/phink/logs/afl.log
tail -f output/phink/logs/afl_1.log #if multi-threaded
</code></pre>
<p>Additionally, tools like <code>afl_showmap</code> allow developers to debug and visualize the coverage maps.</p>
<h2 id="coverage-guided-strategy"><a class="header" href="#coverage-guided-strategy">Coverage-guided strategy</a></h2>
<p>Currently, Phink employs a partially coverage-guided approach. While full coverage feedback from low-level
instrumentation is not available yet, plans are underway to integrate this capability
via <a href="https://github.com/wasmi-labs/wasmi">WASMI</a> or <a href="https://github.com/koute/polkavm">PolkaVM</a> in future
releases.</p>
<h2 id="execution-and-validation"><a class="header" href="#execution-and-validation">Execution and validation</a></h2>
<p>For each generated seed, Phink executes the associated input on a mock-emulated ‘node’.
This setup ensures that invariants are verified: known selectors are checked to ensure that
invariants hold across different message calls.</p>
<h2 id="contract-instrumentation"><a class="header" href="#contract-instrumentation">Contract instrumentation</a></h2>
<p>Phink instruments contracts using the <code>syn</code> crate, allowing for precise modification and analysis of the smart contract
code. For each high-level Rust instructions, a feedback is returned via the <code>debug_message</code> map to the fuzzing engine, mapping each instruction to a unique <code>u64</code> identifier. This map is then “expanded”, instrumented by AFL++ compiler, and ultimately updated the AFL++ shared map everytime a new edge is hit.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h1>
<h2 id="debugging-phink"><a class="header" href="#debugging-phink">Debugging Phink</a></h2>
<h3 id="afl-logs"><a class="header" href="#afl-logs">AFL++ logs</a></h3>
<p>If you encounter unexpected behavior, examining the AFL++ logs can provide good insights. In most cases, developers
will find more information by executing:</p>
<pre><code class="language-sh">tail -f your_output/phink/logs/afl.log
</code></pre>
<p>Replace <code>your_output</code> with the directory defined in your <code>phink.toml</code> under <code>fuzz_output</code>. This will give you a
real-time view of the log output, helping you identify any issues during the fuzzing process.</p>
<h3 id="executing-a-single-seed"><a class="header" href="#executing-a-single-seed">Executing a Single Seed</a></h3>
<p>To debug specific cases where a contract crashes, you can execute a single seed. This method allows you to instantiate a
contract and identify crash points more easily:</p>
<pre><code class="language-sh">cargo run -- execute output/phink/corpus/selector_1.bin
</code></pre>
<p>This command runs a single fuzzing input, making it easier to pinpoint problems.</p>
<h3 id="harness-coverage"><a class="header" href="#harness-coverage">Harness coverage</a></h3>
<p>Use the harness coverage feature for debugging. You should only use it if you want to have a coverage of Phink itself.
For instance, if you’re planning to contribute to Phink, or to debug it.</p>
<pre><code class="language-sh">cargo run -- harness-cover
</code></pre>
<p>Be aware that this is primarily for those who want to dive deeper into the coverage of Phink and is not generally
necessary for regular debugging.</p>
<h3 id="support-channels"><a class="header" href="#support-channels">Support channels</a></h3>
<p>You can find us on <a href="https://discord.gg/4MakDGwFEK">Discord</a>. Alternatively, you can message me
on <a href="kevin%5B%F0%9F%8E%A9%5Dsrlabs.de">kevin[🎩]srlabs.de</a>.</p>
<p>Happy fuzzing!</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="benchmarking"><a class="header" href="#benchmarking">Benchmarking</a></h1>
<p>Benchmarking provides insights into Phink’s performance in real-world scenarios, in order to vizualise its efficiency
and
fuzzing ink! smart contracts. Below are the benchmark results for various smart contracts, detailing
coverage, speed, corpus size, and the potential usage of <code>generate-seed</code>.
Each contract were fuzzed for maximum a day.
Statistics (especially <em>average speed</em>) are given for <strong>one</strong> core only. The coverage percent is calculated using the
number of
lines covered divided the number of reachable lines, as a percentage.</p>
<blockquote>
<p>⚠️ The point of the benchmark is to demonstrate how much coverage is reachable within a day of fuzzing without doing
proper seed creation. In a real fuzzing campaign, the developers would aim for 100% coverage, by creating seeds,
adding <code>GenesisConfig</code> values, more (E2E) tests extracted with <code>seed-generator</code>, etc.</p>
</blockquote>
<h3 id="benchmarks"><a class="header" href="#benchmarks">Benchmarks</a></h3>
<div class="table-wrapper"><table><thead><tr><th>Contract name</th><th>Coverage percent</th><th>Average speed (<em>execs/sec</em>)</th><th>AFL++ corpus size</th><th>Using Phink seed generation</th></tr></thead><tbody>
<tr><td>abax_governor</td><td><strong>48%</strong></td><td>1500 (early phase) <strong>and</strong> 100 (late phase)</td><td>1639</td><td><strong>NO</strong> (no tests available)</td></tr>
<tr><td>erc1155</td><td><strong>89%</strong></td><td>1300 (early phase phase) <strong>and</strong> 140 (late phase)</td><td>949</td><td><strong>YES</strong> (without E2E)</td></tr>
<tr><td>multisig</td><td><strong>91%</strong></td><td>1400 (early phase phase) <strong>and</strong> 113 (late phase)</td><td>1524</td><td><strong>YES</strong> (without E2E)</td></tr>
</tbody></table>
</div>
<ul>
<li>Github for
<code>abax_governor</code> : <a href="https://github.com/AbaxFinance/dao-contracts/tree/main/src/contracts/abax_governor/">AbaxFinance/dao-contracts/tree/main/src/contracts/abax_governor</a></li>
<li>Github for
<code>multisig</code> : <a href="https://github.com/use-ink/ink-examples/blob/main/multisig/lib.rs">use-ink/ink-examples/blob/main/multisig/lib.rs</a></li>
<li>Github for
<code>erc1155</code> : <a href="https://github.com/use-ink/ink-examples/blob/main/erc1155/lib.rs">use-ink/ink-examples/blob/main/erc1155/lib.rs</a></li>
</ul>
<h5 id="dummy-benchmark"><a class="header" href="#dummy-benchmark">Dummy benchmark</a></h5>
<p>The <a href="https://github.com/srlabs/phink/blob/main/sample/dummy/lib.rs">dummy</a> benchmark involves a simple nested
if-condition. It acts as a reference to ensure that the fuzzer is
effectively coverage guided. The results for this benchmark are as follows:</p>
<ul>
<li><strong>Average speed</strong>: 7,500 executions per second in average</li>
<li><strong>Number of cores used</strong>: 10</li>
<li><strong>Time until invariant triggered</strong>: 48 seconds</li>
<li><strong>Stability</strong>: 99.43%</li>
<li><strong>Fuzzing origin</strong>: false</li>
<li><strong>Final corpus size</strong>: 12 seeds</li>
</ul>
<h6 id="dummy-logic"><a class="header" href="#dummy-logic">Dummy logic</a></h6>
<p>The logic tested in the dummy benchmark can simply be represented that way:</p>
<pre><code class="language-rust  ignore">if data.len() &gt; 3 &amp;&amp; data.len() &lt; 7 {
    if data.chars().nth(0).unwrap() == 'f' {
        if data.chars().nth(1).unwrap() == 'u' {
            if data.chars().nth(2).unwrap() == 'z' {
                if data.chars().nth(3).unwrap() == 'z' {
                    self.forbidden_number = 42;
                }
            }
        }
    }
}</code></pre>
<h4 id="contracts"><a class="header" href="#contracts">Contracts</a></h4>
<h6 id="erc-1155"><a class="header" href="#erc-1155">ERC-1155</a></h6>
<blockquote>
<p>The ERC-1155 contract is a standard for creating multiple token types within a single contract. It allows for the
creation of both fungible and non-fungible tokens and enables batch transfers, making it easy to transfer multiple
tokens at once.</p>
</blockquote>
<h6 id="multisig-wallet"><a class="header" href="#multisig-wallet">Multisig Wallet</a></h6>
<blockquote>
<p>The Multisig Wallet contract is a multi-owner wallet that requires a certain number of owners to agree on a
transaction before it can be executed. Each owner can submit a transaction, and when enough owners confirm, it can be
executed.</p>
</blockquote>
<h6 id="abaxgovernor"><a class="header" href="#abaxgovernor">AbaxGovernor</a></h6>
<blockquote>
<p>The Abax Governor contract is a governance contract that allows for staking of PSP22 tokens in exchange for
non-transferrable PSP22Vault shares (votes). It enables users to propose and vote on proposals, with the number of
shares held by a user determining their voting power.</p>
</blockquote>
<h3 id="explanation-of-terms"><a class="header" href="#explanation-of-terms">Explanation of terms</a></h3>
<ul>
<li>
<p><strong>Coverage</strong>: Represents the percentage of the code that have been executed during the fuzzing campaign. Higher
coverage
indicates more thorough testing (<em>the higher the better</em>).</p>
</li>
<li>
<p><strong>Average speed (for 1 core)</strong>: The number of executions per second that the fuzzer can handle on a single CPU core.
As a reminder, one execution contains multiple calls up to
<code>max_messages_per_exec</code>.</p>
</li>
<li>
<p><strong>AFL++ corpus size</strong>: The size of the corpus generated by AFL++ during fuzzing. A larger
corpus implies a diverse set of inputs to test the contract.</p>
</li>
<li>
<p><strong>generate-seed usage</strong>: Indicates whether <code>generate-seed</code> was used to seed the initial tests. This depends if the
contract include tests or not.</p>
</li>
</ul>
<h3 id="environment-details"><a class="header" href="#environment-details">Environment details</a></h3>
<ul>
<li><strong>CPU</strong>: AMD EPYC 7282 16-Cores</li>
<li><strong>Operating System</strong>: Linux 5.4.0-189-generic #209-Ubuntu x86_64</li>
<li><strong>Phink Version</strong>: 0.1.4</li>
</ul>
<h3 id="contributing-to-the-benchmarks"><a class="header" href="#contributing-to-the-benchmarks">Contributing to the benchmarks</a></h3>
<p>We encourage contributions to our benchmarks! If you have a contract you would like to see benchmarked, please submit a
pull request to our repository.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="faq"><a class="header" href="#faq">FAQ</a></h1>
<h4 id="why-the-name-phink-"><a class="header" href="#why-the-name-phink-">Why the name ‘<em>Phink</em>’ ?</a></h4>
<p>Mystère et boule de gomme.</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->


                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">

            </nav>

        </div>




        <script>
            window.playground_copyable = true;
        </script>


        <script src="elasticlunr.min.js"></script>
        <script src="mark.min.js"></script>
        <script src="searcher.js"></script>

        <script src="clipboard.min.js"></script>
        <script src="highlight.js"></script>
        <script src="book.js"></script>

        <!-- Custom JS scripts -->

        <script>
        window.addEventListener('load', function() {
            window.setTimeout(window.print, 100);
        });
        </script>

    </div>
    </body>
</html>