🌳 Branching Tree Technique (BTT)

Paul Berg

Solidity Summit: Nov 16, 2023

Personal Background

  • 👨‍💻 Ethereum developer for 5+ years
  • 🐦 Exporting my brain on Twitter @PaulRBerg
  • ➗ Author of PRBMath
  • ⏳ Co-founder and Builder at Sablier
  • 🚀 Sablier V2: 100% built with Foundry and BTT

😇 How it starts

              
                function test_Foo() external {
                  uint256 x = 42;
                  assertEq(contract.foo(x), x, "value mismatch");
                }
              
            

😵‍💫 How it ends (monolith)

              
                function test_Foo_1() external {
                  // --- snip ---
                }

                function test_Foo_2() external {
                  // --- snip ---
                }

                function test_Foo_3(uint256 arg0) external {
                  // --- snip ---
                }

                function test_Foo_4(uint256 arg0, uint256 arg1) external {
                  // --- snip ---
                }
              
            

Specification Frameworks

Technique Barrier to entry Effectiveness
Braching Tree Technique Entry-level Moderately effective
Cucumber Gherkin Medium-level Moderately effective
Certora, K Framework, TLA+ Senior-level Highly effective

Example: when.tree

              
                WhenTest
                ├── when x is false
                │   └── it should revert
                └── when x is true
                    └── it should return x
					    
            

Example: when.t.sol

              
                contract WhenTest {
                    function test_RevertWhen_XIsFalse() external {
                        // it should revert
                    }

                    function test_WhenXIsTrue() external {
                        // it should return x
                    }
                }
					    
            

Example: when.t.sol

            
                contract WhenTest {
                    function test_RevertWhen_XIsFalse() external {
                        vm.expectRevert("x must not be false");
                        foo.simpleWhen({ x: false });
                    }

                    function test_WhenXIsTrue() external {
                        bool result = foo.simpleWhen({ x: true });
                        assertTrue(result);
                    }
                }
					    
            

Example: givenWhen.tree

              
                GivenWhenTest
                ├── given value is less than 100
                │  └── it should revert
                └── given value is not less than 100
                  ├── when x is false
                  │  └── it should revert
                  └── when x is true
                      └── it should return x
					    
            

Example: givenWhen.t.sol

              
                contract GivenWhenTest is BaseTest {
                    function test_RevertGiven_ValueIsLessThan100()
                        external
                    {
                        // it should revert
                    }

                    modifier givenValueIsNotLessThan100() {
                        foo.setValue({ newValue: 1337 });
                        _;
                    }

                    function test_RevertWhen_XIsFalse()
                        external
                        givenValueIsNotLessThan100
                    {
                        // it should revert
                    }

                    function test_WhenXIsTrue()
                        external
                        givenValueIsNotLessThan100
                    {
                        // it should return x
                    }
                }
					    
            

🤔 Given or When?

Scenario Answer
Is it a contract state that is prepared in advance? Given
Is it a mode of execution, e.g. call vs delegatecall? When
Is it a function parameter that the user is in control when calling the function? When
BTT Meme

The End

Thanks! Check out Bulloak and Sablier V2 on GitHub
V2 Core