github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/powchain/block_reader_test.go (about) 1 package powchain 2 3 import ( 4 "context" 5 "math/big" 6 "testing" 7 "time" 8 9 "github.com/ethereum/go-ethereum/common" 10 "github.com/ethereum/go-ethereum/common/hexutil" 11 gethTypes "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/trie" 13 dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" 14 mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" 15 contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" 16 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 17 "github.com/prysmaticlabs/prysm/shared/testutil/require" 18 ) 19 20 var endpoint = "http://127.0.0.1" 21 22 func setDefaultMocks(service *Service) *Service { 23 service.eth1DataFetcher = &goodFetcher{} 24 service.httpLogger = &goodLogger{} 25 service.cfg.StateNotifier = &goodNotifier{} 26 return service 27 } 28 29 func TestLatestMainchainInfo_OK(t *testing.T) { 30 testAcc, err := contracts.Setup() 31 require.NoError(t, err, "Unable to set up simulated backend") 32 33 beaconDB := dbutil.SetupDB(t) 34 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 35 HttpEndpoints: []string{endpoint}, 36 DepositContract: testAcc.ContractAddr, 37 BeaconDB: beaconDB, 38 }) 39 require.NoError(t, err, "Unable to setup web3 ETH1.0 chain service") 40 41 web3Service = setDefaultMocks(web3Service) 42 web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend} 43 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 44 45 web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) 46 require.NoError(t, err) 47 testAcc.Backend.Commit() 48 49 exitRoutine := make(chan bool) 50 51 go func() { 52 web3Service.run(web3Service.ctx.Done()) 53 <-exitRoutine 54 }() 55 56 header, err := web3Service.eth1DataFetcher.HeaderByNumber(web3Service.ctx, nil) 57 require.NoError(t, err) 58 59 tickerChan := make(chan time.Time) 60 web3Service.headTicker = &time.Ticker{C: tickerChan} 61 tickerChan <- time.Now() 62 web3Service.cancel() 63 exitRoutine <- true 64 65 assert.Equal(t, web3Service.latestEth1Data.BlockHeight, header.Number.Uint64()) 66 assert.Equal(t, hexutil.Encode(web3Service.latestEth1Data.BlockHash), header.Hash().Hex()) 67 assert.Equal(t, web3Service.latestEth1Data.BlockTime, header.Time) 68 } 69 70 func TestBlockHashByHeight_ReturnsHash(t *testing.T) { 71 beaconDB := dbutil.SetupDB(t) 72 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 73 HttpEndpoints: []string{endpoint}, 74 BeaconDB: beaconDB, 75 }) 76 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 77 78 web3Service = setDefaultMocks(web3Service) 79 ctx := context.Background() 80 81 header := &gethTypes.Header{ 82 Number: big.NewInt(15), 83 Time: 150, 84 } 85 86 wanted := header.Hash() 87 88 hash, err := web3Service.BlockHashByHeight(ctx, big.NewInt(0)) 89 require.NoError(t, err, "Could not get block hash with given height") 90 require.DeepEqual(t, wanted.Bytes(), hash.Bytes(), "Block hash did not equal expected hash") 91 92 exists, _, err := web3Service.headerCache.HeaderInfoByHash(wanted) 93 require.NoError(t, err) 94 require.Equal(t, true, exists, "Expected block info to be cached") 95 } 96 97 func TestBlockHashByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) { 98 beaconDB := dbutil.SetupDB(t) 99 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 100 HttpEndpoints: []string{endpoint}, 101 BeaconDB: beaconDB, 102 }) 103 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 104 105 web3Service = setDefaultMocks(web3Service) 106 web3Service.eth1DataFetcher = nil 107 ctx := context.Background() 108 109 _, err = web3Service.BlockHashByHeight(ctx, big.NewInt(0)) 110 require.ErrorContains(t, "nil eth1DataFetcher", err) 111 } 112 113 func TestBlockExists_ValidHash(t *testing.T) { 114 beaconDB := dbutil.SetupDB(t) 115 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 116 HttpEndpoints: []string{endpoint}, 117 BeaconDB: beaconDB, 118 }) 119 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 120 121 web3Service = setDefaultMocks(web3Service) 122 123 block := gethTypes.NewBlock( 124 &gethTypes.Header{ 125 Number: big.NewInt(0), 126 }, 127 []*gethTypes.Transaction{}, 128 []*gethTypes.Header{}, 129 []*gethTypes.Receipt{}, 130 new(trie.Trie), 131 ) 132 133 exists, height, err := web3Service.BlockExists(context.Background(), block.Hash()) 134 require.NoError(t, err, "Could not get block hash with given height") 135 require.Equal(t, true, exists) 136 require.Equal(t, 0, height.Cmp(block.Number())) 137 138 exists, _, err = web3Service.headerCache.HeaderInfoByHeight(height) 139 require.NoError(t, err) 140 require.Equal(t, true, exists, "Expected block to be cached") 141 142 } 143 144 func TestBlockExists_InvalidHash(t *testing.T) { 145 beaconDB := dbutil.SetupDB(t) 146 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 147 HttpEndpoints: []string{endpoint}, 148 BeaconDB: beaconDB, 149 }) 150 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 151 152 web3Service = setDefaultMocks(web3Service) 153 154 _, _, err = web3Service.BlockExists(context.Background(), common.BytesToHash([]byte{0})) 155 require.NotNil(t, err, "Expected BlockExists to error with invalid hash") 156 } 157 158 func TestBlockExists_UsesCachedBlockInfo(t *testing.T) { 159 beaconDB := dbutil.SetupDB(t) 160 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 161 HttpEndpoints: []string{endpoint}, 162 BeaconDB: beaconDB, 163 }) 164 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 165 // nil eth1DataFetcher would panic if cached value not used 166 web3Service.eth1DataFetcher = nil 167 168 header := &gethTypes.Header{ 169 Number: big.NewInt(0), 170 } 171 172 err = web3Service.headerCache.AddHeader(header) 173 require.NoError(t, err) 174 175 exists, height, err := web3Service.BlockExists(context.Background(), header.Hash()) 176 require.NoError(t, err, "Could not get block hash with given height") 177 require.Equal(t, true, exists) 178 require.Equal(t, 0, height.Cmp(header.Number)) 179 } 180 181 func TestBlockExistsWithCache_UsesCachedHeaderInfo(t *testing.T) { 182 beaconDB := dbutil.SetupDB(t) 183 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 184 HttpEndpoints: []string{endpoint}, 185 BeaconDB: beaconDB, 186 }) 187 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 188 189 header := &gethTypes.Header{ 190 Number: big.NewInt(0), 191 } 192 193 err = web3Service.headerCache.AddHeader(header) 194 require.NoError(t, err) 195 196 exists, height, err := web3Service.BlockExistsWithCache(context.Background(), header.Hash()) 197 require.NoError(t, err, "Could not get block hash with given height") 198 require.Equal(t, true, exists) 199 require.Equal(t, 0, height.Cmp(header.Number)) 200 } 201 202 func TestBlockExistsWithCache_HeaderNotCached(t *testing.T) { 203 beaconDB := dbutil.SetupDB(t) 204 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 205 HttpEndpoints: []string{endpoint}, 206 BeaconDB: beaconDB, 207 }) 208 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 209 210 exists, height, err := web3Service.BlockExistsWithCache(context.Background(), common.BytesToHash([]byte("hash"))) 211 require.NoError(t, err, "Could not get block hash with given height") 212 require.Equal(t, false, exists) 213 require.Equal(t, (*big.Int)(nil), height) 214 } 215 216 func TestService_BlockNumberByTimestamp(t *testing.T) { 217 beaconDB := dbutil.SetupDB(t) 218 testAcc, err := contracts.Setup() 219 require.NoError(t, err, "Unable to set up simulated backend") 220 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 221 HttpEndpoints: []string{endpoint}, 222 BeaconDB: beaconDB, 223 }) 224 require.NoError(t, err) 225 web3Service = setDefaultMocks(web3Service) 226 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 227 228 for i := 0; i < 200; i++ { 229 testAcc.Backend.Commit() 230 } 231 ctx := context.Background() 232 hd, err := testAcc.Backend.HeaderByNumber(ctx, nil) 233 require.NoError(t, err) 234 web3Service.latestEth1Data.BlockTime = hd.Time 235 web3Service.latestEth1Data.BlockHeight = hd.Number.Uint64() 236 blk, err := web3Service.BlockByTimestamp(ctx, 1000 /* time */) 237 require.NoError(t, err) 238 if blk.Number.Cmp(big.NewInt(0)) == 0 { 239 t.Error("Returned a block with zero number, expected to be non zero") 240 } 241 } 242 243 func TestService_BlockNumberByTimestampLessTargetTime(t *testing.T) { 244 beaconDB := dbutil.SetupDB(t) 245 testAcc, err := contracts.Setup() 246 require.NoError(t, err, "Unable to set up simulated backend") 247 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 248 HttpEndpoints: []string{endpoint}, 249 BeaconDB: beaconDB, 250 }) 251 require.NoError(t, err) 252 web3Service = setDefaultMocks(web3Service) 253 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 254 255 for i := 0; i < 200; i++ { 256 testAcc.Backend.Commit() 257 } 258 ctx := context.Background() 259 hd, err := testAcc.Backend.HeaderByNumber(ctx, nil) 260 require.NoError(t, err) 261 web3Service.latestEth1Data.BlockTime = hd.Time 262 // Use extremely small deadline to illustrate that context deadlines are respected. 263 ctx, cancel := context.WithTimeout(ctx, 100*time.Nanosecond) 264 defer cancel() 265 266 // Provide an unattainable target time 267 _, err = web3Service.findLessTargetEth1Block(ctx, hd.Number, hd.Time/2) 268 require.ErrorContains(t, context.DeadlineExceeded.Error(), err) 269 270 // Provide an attainable target time 271 blk, err := web3Service.findLessTargetEth1Block(context.Background(), hd.Number, hd.Time-5) 272 require.NoError(t, err) 273 require.NotEqual(t, hd.Number.Uint64(), blk.Number.Uint64(), "retrieved block is not less than the head") 274 } 275 276 func TestService_BlockNumberByTimestampMoreTargetTime(t *testing.T) { 277 beaconDB := dbutil.SetupDB(t) 278 testAcc, err := contracts.Setup() 279 require.NoError(t, err, "Unable to set up simulated backend") 280 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 281 HttpEndpoints: []string{endpoint}, 282 BeaconDB: beaconDB, 283 }) 284 require.NoError(t, err) 285 web3Service = setDefaultMocks(web3Service) 286 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 287 288 for i := 0; i < 200; i++ { 289 testAcc.Backend.Commit() 290 } 291 ctx := context.Background() 292 hd, err := testAcc.Backend.HeaderByNumber(ctx, nil) 293 require.NoError(t, err) 294 web3Service.latestEth1Data.BlockTime = hd.Time 295 // Use extremely small deadline to illustrate that context deadlines are respected. 296 ctx, cancel := context.WithTimeout(ctx, 100*time.Nanosecond) 297 defer cancel() 298 299 // Provide an unattainable target time with respect to head 300 _, err = web3Service.findMoreTargetEth1Block(ctx, big.NewInt(0).Div(hd.Number, big.NewInt(2)), hd.Time) 301 require.ErrorContains(t, context.DeadlineExceeded.Error(), err) 302 303 // Provide an attainable target time with respect to head 304 blk, err := web3Service.findMoreTargetEth1Block(context.Background(), big.NewInt(0).Sub(hd.Number, big.NewInt(5)), hd.Time) 305 require.NoError(t, err) 306 require.Equal(t, hd.Number.Uint64(), blk.Number.Uint64(), "retrieved block is not equal to the head") 307 } 308 309 func TestService_BlockTimeByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) { 310 beaconDB := dbutil.SetupDB(t) 311 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 312 HttpEndpoints: []string{endpoint}, 313 BeaconDB: beaconDB, 314 }) 315 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 316 317 web3Service = setDefaultMocks(web3Service) 318 web3Service.eth1DataFetcher = nil 319 ctx := context.Background() 320 321 _, err = web3Service.BlockTimeByHeight(ctx, big.NewInt(0)) 322 require.ErrorContains(t, "nil eth1DataFetcher", err) 323 }