Skip to content

Commit

Permalink
improve rlz coverage (#643)
Browse files Browse the repository at this point in the history
  • Loading branch information
facuMH authored Aug 14, 2024
1 parent e584e89 commit 8297255
Show file tree
Hide file tree
Showing 7 changed files with 650 additions and 210 deletions.
12 changes: 0 additions & 12 deletions .testcoverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,6 @@ threshold:
# override rules should be listed in order from specific to more general rules.
override:
# files
- threshold: 0
path: go/ct/rlz/conditions.go
- threshold: 0
path: go/ct/rlz/domains.go
- threshold: 0
path: go/ct/rlz/effect.go
- threshold: 0
path: go/ct/rlz/expressions.go
- threshold: 0
path: go/ct/rlz/parameters.go
- threshold: 0
path: go/ct/spc/specification.go
- threshold: 0
Expand Down Expand Up @@ -95,8 +85,6 @@ override:
path: go/tosca/utils.go

# packages
- threshold: 0
path: go/ct/rlz
- threshold: 0
path: go/ct/spc
- threshold: 0
Expand Down
290 changes: 153 additions & 137 deletions go/ct/rlz/conditions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ func TestCondition_String(t *testing.T) {
{IsCode(Pc()), "isCode[PC]"},
{IsData(Pc()), "isData[PC]"},
{And(Eq(Status(), st.Running), Eq(Status(), st.Failed)), "status = running ∧ status = failed"},
{IsRevision(tosca.R10_London), "revision(London)"},
{RevisionBounds(tosca.R09_Berlin, tosca.R11_Paris), "revision(Berlin-Paris)"},
{HasNotSelfDestructed(), "hasNotSelfDestructed()"},
}

for _, test := range tests {
Expand Down Expand Up @@ -270,27 +273,6 @@ func TestHasSelfDestructedCondition_CheckSelfDestructed(t *testing.T) {
}
}

func TestHasSelfDestructedCondition_HasSelfDestructRestrictsGeneratedStateToBeSelfDestructed(t *testing.T) {
condition := HasSelfDestructed()

gen := gen.NewStateGenerator()
condition.Restrict(gen)
rnd := rand.New(0)
state, err := gen.Generate(rnd)
if err != nil {
t.Errorf("%v", err)
}

got, err := condition.Check(state)
if err != nil {
t.Errorf("%v", err)
}

if !got {
t.Error("generated state does not satisfy condition")
}
}

func TestCondition_InOutRange256FromCurrentBlock_Check(t *testing.T) {
gen := gen.NewStateGenerator()
rnd := rand.New(0)
Expand Down Expand Up @@ -381,55 +363,6 @@ func TestCondition_InOutRange256FromCurrentBlock_Check(t *testing.T) {

}

func TestCondition_InOut_Restrict(t *testing.T) {
rnd := rand.New()

tests := map[string]struct {
condition Condition
}{
"inRange": {condition: InRange256FromCurrentBlock(Param(0))},
"outOfRange": {condition: OutOfRange256FromCurrentBlock(Param(0))},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
for i := 0; i < 1000; i++ {
gen := gen.NewStateGenerator()
test.condition.Restrict(gen)
state, err := gen.Generate(rnd)
if err != nil {
t.Fatalf("failed to build state: %v", err)
}
if checked, err := test.condition.Check(state); err != nil || !checked {
t.Errorf("failed to check condition: %v", err)
}
}
})
}
}

func TestCondition_InOutOfRangeGetTestValues(t *testing.T) {
want := []int64{math.MinInt64, -1, 0, 1, 255, 256, 257, math.MaxInt64}
tests := map[string]Condition{
"inRange": InRange256FromCurrentBlock(Param(0)),
"outOfRange": OutOfRange256FromCurrentBlock(Param(0)),
}

for name, condition := range tests {
t.Run(name, func(t *testing.T) {
testValues := condition.GetTestValues()
if len(testValues) != len(want) {
t.Fatalf("unexpected amount test values, got %v, want %v", len(testValues), want)
}
for i, test := range testValues {
if test.(*testValue[int64]).value != want[i] {
t.Errorf("unexpected test value, got %v, want %v", test.(*testValue[int64]).value, want[i])
}
}
})
}
}

func TestCondition_CheckUnsatisfiableTransientStorageBindings(t *testing.T) {
conditionNonZero := BindTransientStorageToNonZero(Param(0))
conditionZero := BindTransientStorageToZero(Param(0))
Expand All @@ -444,57 +377,6 @@ func TestCondition_CheckUnsatisfiableTransientStorageBindings(t *testing.T) {
}
}

func TestCondition_RestrictTransientStorageAndCheck(t *testing.T) {
rnd := rand.New()

tests := map[string]Condition{
"zero": BindTransientStorageToZero(Param(0)),
"nonZero": BindTransientStorageToNonZero(Param(0)),
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
gen := gen.NewStateGenerator()
test.Restrict(gen)
state, err := gen.Generate(rnd)
if err != nil {
t.Fatalf("failed to build state: %v", err)
}
if checked, err := test.Check(state); err != nil || !checked {
t.Errorf("failed to check condition: %v", err)
}
})
}
}

func TestCondition_BlobHashes_Restrict(t *testing.T) {
rnd := rand.New()

tests := map[string]struct {
condition Condition
}{
"hasBlobHash": {condition: HasBlobHash(Param(0))},
"hasNotBlobHash": {condition: HasNoBlobHash(Param(0))},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {

for i := 0; i < 1000; i++ {
gen := gen.NewStateGenerator()
test.condition.Restrict(gen)
state, err := gen.Generate(rnd)
if err != nil {
t.Fatalf("failed to build state: %v", err)
}
if checked, err := test.condition.Check(state); err != nil || !checked {
t.Errorf("failed to check condition: %v", err)
}
}
})
}
}

func TestCondition_BlobHashes_check(t *testing.T) {
tests := map[string]struct {
condition Condition
Expand Down Expand Up @@ -598,40 +480,174 @@ func TestCondition_BlobHashesProducesGetTestValues(t *testing.T) {
}
}

func TestCondition_IsAccountWarmRestrictAndCheck(t *testing.T) {
tests := map[string]Condition{
"warm": IsAddressWarm(Param(0)),
"cold": IsAddressCold(Param(0)),
func TestCondition_ConflictingAccountWarmConditionsAreUnsatisfiable(t *testing.T) {
isWarm := IsAddressWarm(Param(0))
isCold := IsAddressCold(Param(0))

generator := gen.NewStateGenerator()
isWarm.Restrict(generator)
isCold.Restrict(generator)
_, err := generator.Generate(rand.New(0))
if !errors.Is(err, gen.ErrUnsatisfiable) {
t.Errorf("expected unsatisfiable condition")
}
}

for name, condition := range tests {
func TestCondition_RevisionBoundInvalidBounds(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Errorf("expected panic")
}
}()
RevisionBounds(tosca.R10_London, tosca.R09_Berlin)
}

func TestCondition_IsRevisionCondition(t *testing.T) {

tests := map[string]struct {
condition Condition
isRevision bool
}{
"IsRevision": {IsRevision(tosca.R10_London), true},
"RevisionBounds": {RevisionBounds(tosca.R10_London, tosca.R10_London), true},
"AnyKnownRevision": {AnyKnownRevision(), true},
"IsAddressWarm": {IsAddressWarm(Param(0)), false},
"IsCode": {IsCode(Param(0)), false},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
if got, want := IsRevisionCondition(test.condition), test.isRevision; got != want {
t.Errorf("condition %v is not a revision condition. got %v, want %v", test.condition, got, want)
}
})
}
}

func TestCondition_GetTestValues(t *testing.T) {

inOutofRangeTestValues := []any{math.MinInt64, -1, 0, 1, 255, 256, 257, math.MaxInt64}

tests := []struct {
condition Condition
want []any
}{
{IsRevision(tosca.R10_London), []any{tosca.R10_London}},
{AnyKnownRevision(), []any{tosca.R07_Istanbul, tosca.R09_Berlin, tosca.R10_London, tosca.R11_Paris, tosca.R12_Shanghai, tosca.R13_Cancun, R99_UnknownNextRevision}},
{InRange256FromCurrentBlock(Param(0)), inOutofRangeTestValues},
{OutOfRange256FromCurrentBlock(Param(0)), inOutofRangeTestValues},
{IsCode(Param(0)), []any{true, false}},
{IsData(Param(0)), []any{true, false}},
{IsStorageWarm(Param(0)), []any{true, false}},
{IsStorageCold(Param(0)), []any{true, false}},
{StorageConfiguration(tosca.StorageAssigned, Param(0), Param(1)), []any{true}},
{BindTransientStorageToNonZero(Param(0)), []any{true, false}},
{BindTransientStorageToZero(Param(0)), []any{true, false}},
{IsAddressWarm(Param(0)), []any{true}},
{IsAddressCold(Param(0)), []any{false}},
{HasSelfDestructed(), []any{true, false}},
{HasNotSelfDestructed(), []any{true, false}},
}

for _, test := range tests {
t.Run(test.condition.String(), func(t *testing.T) {
if got, want := test.condition.GetTestValues(), test.want; len(got) != len(want) {
t.Errorf("unexpected test values. wanted %v, got %v", want, got)
}
})
}
}

func TestCondition_RestricAndCheck(t *testing.T) {

tests := []Condition{
IsRevision(tosca.R10_London),
HasSelfDestructed(),
HasNotSelfDestructed(),
InRange256FromCurrentBlock(Param(0)),
OutOfRange256FromCurrentBlock(Param(0)),
BindTransientStorageToNonZero(Param(0)),
BindTransientStorageToZero(Param(0)),
HasBlobHash(Param(0)),
HasNoBlobHash(Param(0)),
IsAddressWarm(Param(0)),
IsAddressCold(Param(0)),
Ne(Gas(), 1),
Lt(Gas(), 2),
Le(Gas(), 2),
Gt(Gas(), 0),
Ge(Gas(), 0),
IsCode(Param(0)),
IsData(Param(0)),
IsStorageWarm(Param(0)),
IsStorageCold(Param(0)),
StorageConfiguration(tosca.StorageAssigned, Param(0), Param(1)),
}

for _, condition := range tests {
t.Run(condition.String(), func(t *testing.T) {
gen := gen.NewStateGenerator()
condition.Restrict(gen)
state, err := gen.Generate(rand.New(0))
if err != nil {
t.Fatalf("failed to build state: %v", err)
t.Errorf("failed to build state: %v", err)
}
if checked, err := condition.Check(state); err != nil || !checked {
t.Errorf("failed to restrict and check condition: %v", err)
t.Errorf("failed to check condition: %v", err)
}
})
}
}

func TestCondition_ConflictingAccountWarmConditionsAreUnsatisfiable(t *testing.T) {
isWarm := IsAddressWarm(Param(0))
isCold := IsAddressCold(Param(0))
func TestCondition_CheckFail(t *testing.T) {

generator := gen.NewStateGenerator()
isWarm.Restrict(generator)
isCold.Restrict(generator)
_, err := generator.Generate(rand.New(0))
if !errors.Is(err, gen.ErrUnsatisfiable) {
t.Errorf("expected unsatisfiable condition")
tests := []struct {
condition Condition
breakState func(st.State)
}{
// only Param and Op can return errors on eval, which is where the errors from check come from.
{Eq(Param(1), NewU256(1)), func(s st.State) { s.Stack.Resize(0) }},
{Ne(Param(1), NewU256(1)), func(s st.State) { s.Stack.Resize(0) }},
// Op and Param expressions only support equality constraints,
// hence the inequality constraints cannot produce error on check with the current expressions.
// {Lt(Param(1), NewU256(1)), func(s st.State) { s.Stack.Resize(0) }},
// {Le(Param(1), NewU256(1)), func(s st.State) { s.Stack.Resize(0) }},
// {Gt(Param(1), NewU256(1)), func(s st.State) { s.Stack.Resize(0) }},
// {Ge(Param(1), NewU256(1)), func(s st.State) { s.Stack.Resize(0) }},
{IsCode(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{IsData(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{IsStorageWarm(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{IsStorageCold(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{StorageConfiguration(tosca.StorageAssigned, Param(1), Param(2)), func(s st.State) { s.Stack.Resize(1) }},
{StorageConfiguration(tosca.StorageAssigned, Param(1), Param(2)), func(s st.State) { s.Stack.Resize(0) }},
{BindTransientStorageToNonZero(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{BindTransientStorageToZero(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{IsAddressWarm(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{InRange256FromCurrentBlock(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{OutOfRange256FromCurrentBlock(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{IsAddressCold(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
{HasNoBlobHash(Param(1)), func(s st.State) { s.Stack.Resize(0) }},
}

for _, test := range tests {
t.Run(test.condition.String(), func(t *testing.T) {
gen := gen.NewStateGenerator()
test.condition.Restrict(gen)
state, err := gen.Generate(rand.New(0))
if err != nil {
t.Errorf("unexpected error generating state. %v", err)
}
test.breakState(*state)
if checked, err := test.condition.Check(state); err == nil {
t.Errorf("expected error checking condition. err %v. checked: %v", err, checked)
}
})
}
}

////////////////////////////////////////////////////////////////////////////
// Benchmarks

func BenchmarkCondition_IsAddressWarmCheckWarm(b *testing.B) {
state := st.NewState(st.NewCode([]byte{}))
state.Accounts.MarkWarm(NewAddress(NewU256(42)))
Expand Down
Loading

0 comments on commit 8297255

Please sign in to comment.