github.com/ava-labs/avalanchego@v1.11.11/vms/avm/block/executor/manager_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package executor 5 6 import ( 7 "errors" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/require" 12 "go.uber.org/mock/gomock" 13 14 "github.com/ava-labs/avalanchego/ids" 15 "github.com/ava-labs/avalanchego/utils/set" 16 "github.com/ava-labs/avalanchego/vms/avm/block" 17 "github.com/ava-labs/avalanchego/vms/avm/state/statemock" 18 "github.com/ava-labs/avalanchego/vms/avm/txs" 19 "github.com/ava-labs/avalanchego/vms/avm/txs/txsmock" 20 ) 21 22 var ( 23 errTest = errors.New("test error") 24 errTestSyntacticVerifyFail = errors.New("test error") 25 errTestSemanticVerifyFail = errors.New("test error") 26 errTestExecutionFail = errors.New("test error") 27 ) 28 29 func TestManagerGetStatelessBlock(t *testing.T) { 30 require := require.New(t) 31 ctrl := gomock.NewController(t) 32 33 state := statemock.NewState(ctrl) 34 m := &manager{ 35 state: state, 36 blkIDToState: map[ids.ID]*blockState{}, 37 } 38 39 // Case: block is in memory 40 { 41 statelessBlk := block.NewMockBlock(ctrl) 42 blkID := ids.GenerateTestID() 43 blk := &blockState{ 44 statelessBlock: statelessBlk, 45 } 46 m.blkIDToState[blkID] = blk 47 gotBlk, err := m.GetStatelessBlock(blkID) 48 require.NoError(err) 49 require.Equal(statelessBlk, gotBlk) 50 } 51 52 // Case: block isn't in memory 53 { 54 blkID := ids.GenerateTestID() 55 blk := block.NewMockBlock(ctrl) 56 state.EXPECT().GetBlock(blkID).Return(blk, nil) 57 gotBlk, err := m.GetStatelessBlock(blkID) 58 require.NoError(err) 59 require.Equal(blk, gotBlk) 60 } 61 62 // Case: error while getting block from state 63 { 64 blkID := ids.GenerateTestID() 65 state.EXPECT().GetBlock(blkID).Return(nil, errTest) 66 _, err := m.GetStatelessBlock(blkID) 67 require.ErrorIs(err, errTest) 68 } 69 } 70 71 func TestManagerGetState(t *testing.T) { 72 require := require.New(t) 73 ctrl := gomock.NewController(t) 74 75 s := statemock.NewState(ctrl) 76 m := &manager{ 77 state: s, 78 blkIDToState: map[ids.ID]*blockState{}, 79 lastAccepted: ids.GenerateTestID(), 80 } 81 82 // Case: Block is in memory 83 { 84 diff := statemock.NewDiff(ctrl) 85 blkID := ids.GenerateTestID() 86 m.blkIDToState[blkID] = &blockState{ 87 onAcceptState: diff, 88 } 89 gotState, ok := m.GetState(blkID) 90 require.True(ok) 91 require.Equal(diff, gotState) 92 } 93 94 // Case: Block isn't in memory; block isn't last accepted 95 { 96 blkID := ids.GenerateTestID() 97 gotState, ok := m.GetState(blkID) 98 require.False(ok) 99 require.Equal(s, gotState) 100 } 101 102 // Case: Block isn't in memory; block is last accepted 103 { 104 gotState, ok := m.GetState(m.lastAccepted) 105 require.True(ok) 106 require.Equal(s, gotState) 107 } 108 } 109 110 func TestManagerVerifyTx(t *testing.T) { 111 type test struct { 112 name string 113 txF func(*gomock.Controller) *txs.Tx 114 managerF func(*gomock.Controller) *manager 115 expectedErr error 116 } 117 118 tests := []test{ 119 { 120 name: "not bootstrapped", 121 txF: func(*gomock.Controller) *txs.Tx { 122 return &txs.Tx{} 123 }, 124 managerF: func(*gomock.Controller) *manager { 125 return &manager{ 126 backend: defaultTestBackend(false, nil), 127 } 128 }, 129 expectedErr: ErrChainNotSynced, 130 }, 131 { 132 name: "fails syntactic verification", 133 txF: func(ctrl *gomock.Controller) *txs.Tx { 134 unsigned := txsmock.NewUnsignedTx(ctrl) 135 unsigned.EXPECT().Visit(gomock.Any()).Return(errTestSyntacticVerifyFail) 136 return &txs.Tx{ 137 Unsigned: unsigned, 138 } 139 }, 140 managerF: func(*gomock.Controller) *manager { 141 return &manager{ 142 backend: defaultTestBackend(true, nil), 143 } 144 }, 145 expectedErr: errTestSyntacticVerifyFail, 146 }, 147 { 148 name: "fails semantic verification", 149 txF: func(ctrl *gomock.Controller) *txs.Tx { 150 unsigned := txsmock.NewUnsignedTx(ctrl) 151 // Syntactic verification passes 152 unsigned.EXPECT().Visit(gomock.Any()).Return(nil) 153 // Semantic verification fails 154 unsigned.EXPECT().Visit(gomock.Any()).Return(errTestSemanticVerifyFail) 155 return &txs.Tx{ 156 Unsigned: unsigned, 157 } 158 }, 159 managerF: func(ctrl *gomock.Controller) *manager { 160 lastAcceptedID := ids.GenerateTestID() 161 162 // These values don't matter for this test 163 state := statemock.NewState(ctrl) 164 state.EXPECT().GetLastAccepted().Return(lastAcceptedID) 165 state.EXPECT().GetTimestamp().Return(time.Time{}) 166 167 return &manager{ 168 backend: defaultTestBackend(true, nil), 169 state: state, 170 lastAccepted: lastAcceptedID, 171 } 172 }, 173 expectedErr: errTestSemanticVerifyFail, 174 }, 175 { 176 name: "fails execution", 177 txF: func(ctrl *gomock.Controller) *txs.Tx { 178 unsigned := txsmock.NewUnsignedTx(ctrl) 179 // Syntactic verification passes 180 unsigned.EXPECT().Visit(gomock.Any()).Return(nil) 181 // Semantic verification passes 182 unsigned.EXPECT().Visit(gomock.Any()).Return(nil) 183 // Execution fails 184 unsigned.EXPECT().Visit(gomock.Any()).Return(errTestExecutionFail) 185 return &txs.Tx{ 186 Unsigned: unsigned, 187 } 188 }, 189 managerF: func(ctrl *gomock.Controller) *manager { 190 lastAcceptedID := ids.GenerateTestID() 191 192 // These values don't matter for this test 193 state := statemock.NewState(ctrl) 194 state.EXPECT().GetLastAccepted().Return(lastAcceptedID) 195 state.EXPECT().GetTimestamp().Return(time.Time{}) 196 197 return &manager{ 198 backend: defaultTestBackend(true, nil), 199 state: state, 200 lastAccepted: lastAcceptedID, 201 } 202 }, 203 expectedErr: errTestExecutionFail, 204 }, 205 { 206 name: "happy path", 207 txF: func(ctrl *gomock.Controller) *txs.Tx { 208 unsigned := txsmock.NewUnsignedTx(ctrl) 209 // Syntactic verification passes 210 unsigned.EXPECT().Visit(gomock.Any()).Return(nil) 211 // Semantic verification passes 212 unsigned.EXPECT().Visit(gomock.Any()).Return(nil) 213 // Execution passes 214 unsigned.EXPECT().Visit(gomock.Any()).Return(nil) 215 return &txs.Tx{ 216 Unsigned: unsigned, 217 } 218 }, 219 managerF: func(ctrl *gomock.Controller) *manager { 220 lastAcceptedID := ids.GenerateTestID() 221 222 // These values don't matter for this test 223 state := statemock.NewState(ctrl) 224 state.EXPECT().GetLastAccepted().Return(lastAcceptedID) 225 state.EXPECT().GetTimestamp().Return(time.Time{}) 226 227 return &manager{ 228 backend: defaultTestBackend(true, nil), 229 state: state, 230 lastAccepted: lastAcceptedID, 231 } 232 }, 233 expectedErr: nil, 234 }, 235 } 236 237 for _, test := range tests { 238 t.Run(test.name, func(t *testing.T) { 239 require := require.New(t) 240 ctrl := gomock.NewController(t) 241 242 m := test.managerF(ctrl) 243 tx := test.txF(ctrl) 244 err := m.VerifyTx(tx) 245 require.ErrorIs(err, test.expectedErr) 246 }) 247 } 248 } 249 250 func TestVerifyUniqueInputs(t *testing.T) { 251 require := require.New(t) 252 ctrl := gomock.NewController(t) 253 254 // Case: No inputs 255 { 256 m := &manager{} 257 require.NoError(m.VerifyUniqueInputs(ids.GenerateTestID(), set.Set[ids.ID]{})) 258 } 259 260 // blk0 is blk1's parent 261 blk0ID, blk1ID := ids.GenerateTestID(), ids.GenerateTestID() 262 blk0, blk1 := block.NewMockBlock(ctrl), block.NewMockBlock(ctrl) 263 blk1.EXPECT().Parent().Return(blk0ID).AnyTimes() 264 blk0.EXPECT().Parent().Return(ids.Empty).AnyTimes() // blk0's parent is accepted 265 266 inputID := ids.GenerateTestID() 267 m := &manager{ 268 blkIDToState: map[ids.ID]*blockState{ 269 blk0ID: { 270 statelessBlock: blk0, 271 importedInputs: set.Of(inputID), 272 }, 273 blk1ID: { 274 statelessBlock: blk1, 275 importedInputs: set.Of(ids.GenerateTestID()), 276 }, 277 }, 278 } 279 // [blk1]'s parent, [blk0], has [inputID] as an input 280 err := m.VerifyUniqueInputs(blk1ID, set.Of(inputID)) 281 require.ErrorIs(err, ErrConflictingParentTxs) 282 283 require.NoError(m.VerifyUniqueInputs(blk1ID, set.Of(ids.GenerateTestID()))) 284 }