github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/transactionStorageLimiter_test.go (about) 1 package fvm_test 2 3 import ( 4 "testing" 5 6 "github.com/onflow/cadence" 7 "github.com/stretchr/testify/mock" 8 "github.com/stretchr/testify/require" 9 10 "github.com/onflow/flow-go/fvm" 11 "github.com/onflow/flow-go/fvm/environment" 12 fvmmock "github.com/onflow/flow-go/fvm/environment/mock" 13 "github.com/onflow/flow-go/fvm/errors" 14 "github.com/onflow/flow-go/fvm/storage/snapshot" 15 "github.com/onflow/flow-go/fvm/systemcontracts" 16 "github.com/onflow/flow-go/fvm/tracing" 17 "github.com/onflow/flow-go/model/flow" 18 ) 19 20 func TestTransactionStorageLimiter(t *testing.T) { 21 owner := flow.HexToAddress("1") 22 executionSnapshot := &snapshot.ExecutionSnapshot{ 23 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 24 flow.NewRegisterID(owner, "a"): flow.RegisterValue("foo"), 25 flow.NewRegisterID(owner, "b"): flow.RegisterValue("bar"), 26 }, 27 } 28 29 ctx := fvm.Context{ 30 EnvironmentParams: environment.EnvironmentParams{ 31 Chain: flow.Emulator.Chain(), 32 }, 33 } 34 35 t.Run("capacity > storage -> OK", func(t *testing.T) { 36 env := &fvmmock.Environment{} 37 env.On("LimitAccountStorage").Return(true) 38 env.On("StartChildSpan", mock.Anything).Return( 39 tracing.NewMockTracerSpan()) 40 env.On("GetStorageUsed", mock.Anything).Return(uint64(99), nil) 41 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 42 cadence.NewArray([]cadence.Value{ 43 bytesToUFix64(100), 44 }), 45 nil, 46 ) 47 48 d := &fvm.TransactionStorageLimiter{} 49 err := d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 50 require.NoError(t, err, "Transaction with higher capacity than storage used should work") 51 }) 52 t.Run("capacity = storage -> OK", func(t *testing.T) { 53 env := &fvmmock.Environment{} 54 env.On("LimitAccountStorage").Return(true) 55 env.On("StartChildSpan", mock.Anything).Return( 56 tracing.NewMockTracerSpan()) 57 env.On("GetStorageUsed", mock.Anything).Return(uint64(100), nil) 58 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 59 cadence.NewArray([]cadence.Value{ 60 bytesToUFix64(100), 61 }), 62 nil, 63 ) 64 65 d := &fvm.TransactionStorageLimiter{} 66 err := d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 67 require.NoError(t, err, "Transaction with equal capacity than storage used should work") 68 }) 69 t.Run("capacity = storage -> OK (dedup payer)", func(t *testing.T) { 70 env := &fvmmock.Environment{} 71 env.On("LimitAccountStorage").Return(true) 72 env.On("StartChildSpan", mock.Anything).Return( 73 tracing.NewMockTracerSpan()) 74 env.On("GetStorageUsed", mock.Anything).Return(uint64(100), nil) 75 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 76 cadence.NewArray([]cadence.Value{ 77 bytesToUFix64(100), 78 }), 79 nil, 80 ) 81 82 d := &fvm.TransactionStorageLimiter{} 83 err := d.CheckStorageLimits(ctx, env, executionSnapshot, owner, 0) 84 require.NoError(t, err, "Transaction with equal capacity than storage used should work") 85 }) 86 t.Run("capacity < storage -> Not OK", func(t *testing.T) { 87 env := &fvmmock.Environment{} 88 env.On("LimitAccountStorage").Return(true) 89 env.On("StartChildSpan", mock.Anything).Return( 90 tracing.NewMockTracerSpan()) 91 env.On("GetStorageUsed", mock.Anything).Return(uint64(101), nil) 92 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 93 cadence.NewArray([]cadence.Value{ 94 bytesToUFix64(100), 95 }), 96 nil, 97 ) 98 99 d := &fvm.TransactionStorageLimiter{} 100 err := d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 101 require.Error(t, err, "Transaction with lower capacity than storage used should fail") 102 }) 103 t.Run("capacity > storage -> OK (payer not updated)", func(t *testing.T) { 104 env := &fvmmock.Environment{} 105 env.On("LimitAccountStorage").Return(true) 106 env.On("StartChildSpan", mock.Anything).Return( 107 tracing.NewMockTracerSpan()) 108 env.On("GetStorageUsed", mock.Anything).Return(uint64(99), nil) 109 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 110 cadence.NewArray([]cadence.Value{ 111 bytesToUFix64(100), 112 }), 113 nil, 114 ) 115 116 executionSnapshot = &snapshot.ExecutionSnapshot{} 117 118 d := &fvm.TransactionStorageLimiter{} 119 err := d.CheckStorageLimits(ctx, env, executionSnapshot, owner, 1) 120 require.NoError(t, err, "Transaction with higher capacity than storage used should work") 121 }) 122 t.Run("capacity < storage -> Not OK (payer not updated)", func(t *testing.T) { 123 env := &fvmmock.Environment{} 124 env.On("LimitAccountStorage").Return(true) 125 env.On("StartChildSpan", mock.Anything).Return( 126 tracing.NewMockTracerSpan()) 127 env.On("GetStorageUsed", mock.Anything).Return(uint64(101), nil) 128 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 129 cadence.NewArray([]cadence.Value{ 130 bytesToUFix64(100), 131 }), 132 nil, 133 ) 134 135 executionSnapshot = &snapshot.ExecutionSnapshot{} 136 137 d := &fvm.TransactionStorageLimiter{} 138 err := d.CheckStorageLimits(ctx, env, executionSnapshot, owner, 1000) 139 require.Error(t, err, "Transaction with lower capacity than storage used should fail") 140 }) 141 t.Run("if ctx LimitAccountStorage false-> OK", func(t *testing.T) { 142 env := &fvmmock.Environment{} 143 env.On("LimitAccountStorage").Return(false) 144 env.On("StartChildSpan", mock.Anything).Return( 145 tracing.NewMockTracerSpan()) 146 env.On("GetStorageCapacity", mock.Anything).Return(uint64(100), nil) 147 env.On("GetStorageUsed", mock.Anything).Return(uint64(101), nil) 148 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 149 cadence.NewArray([]cadence.Value{ 150 bytesToUFix64(100), 151 }), 152 nil, 153 ) 154 155 d := &fvm.TransactionStorageLimiter{} 156 err := d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 157 require.NoError(t, err, "Transaction with higher capacity than storage used should work") 158 }) 159 t.Run( 160 "non existing accounts or any other errors on fetching storage used -> Not OK", 161 func(t *testing.T) { 162 env := &fvmmock.Environment{} 163 env.On("LimitAccountStorage").Return(true) 164 env.On("StartChildSpan", mock.Anything).Return( 165 tracing.NewMockTracerSpan()) 166 env.On("GetStorageUsed", mock.Anything). 167 Return(uint64(0), errors.NewAccountNotFoundError(owner)) 168 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything).Return( 169 cadence.NewArray([]cadence.Value{ 170 bytesToUFix64(100), 171 }), 172 nil, 173 ) 174 175 d := &fvm.TransactionStorageLimiter{} 176 err := d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 177 require.Error( 178 t, 179 err, 180 "check storage used on non existing account (not general registers) should fail", 181 ) 182 }, 183 ) 184 t.Run("special account is skipped", func(t *testing.T) { 185 sc := systemcontracts.SystemContractsForChain(ctx.Chain.ChainID()) 186 evm := sc.EVMStorage.Address 187 188 executionSnapshot := &snapshot.ExecutionSnapshot{ 189 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 190 flow.NewRegisterID(evm, "a"): flow.RegisterValue("foo"), 191 }, 192 } 193 194 env := &fvmmock.Environment{} 195 env.On("LimitAccountStorage").Return(true) 196 env.On("StartChildSpan", mock.Anything).Return( 197 tracing.NewMockTracerSpan()) 198 env.On("GetStorageUsed", mock.Anything). 199 Return(uint64(0), errors.NewAccountNotFoundError(owner)) 200 env.On("AccountsStorageCapacity", mock.Anything, mock.Anything, mock.Anything). 201 Run(func(args mock.Arguments) { 202 require.Len(t, args.Get(0).([]flow.Address), 0) 203 }). 204 Return( 205 // since the special account is skipped, the resulting array from AccountsStorageCapacity should be empty 206 cadence.NewArray([]cadence.Value{}), 207 nil, 208 ) 209 210 d := &fvm.TransactionStorageLimiter{} 211 212 // if EVM is disabled don't skip the storage check 213 err := d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 214 require.Error(t, err) 215 216 // if EVM is enabled skip the storage check 217 ctx := fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true)) 218 err = d.CheckStorageLimits(ctx, env, executionSnapshot, flow.EmptyAddress, 0) 219 require.NoError(t, err) 220 }) 221 } 222 223 func bytesToUFix64(b uint64) cadence.Value { 224 return cadence.UFix64(b * 100) 225 }