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