package sync import ( "bytes" "encoding/binary" "errors" "fmt" "math/big" "time" "unsafe" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" protobuf "github.com/golang/protobuf/proto" "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/p2p/stream/protocols/sync/message" syncpb "github.com/harmony-one/harmony/p2p/stream/protocols/sync/message" ) type testChainHelper struct{} func (tch *testChainHelper) getCurrentBlockNumber() uint64 { return 100 } func (tch *testChainHelper) getBlocksByNumber(bns []uint64) ([]*types.Block, error) { blocks := make([]*types.Block, 0, len(bns)) for _, bn := range bns { blocks = append(blocks, makeTestBlock(bn)) } return blocks, nil } func (tch *testChainHelper) getBlockHashes(bns []uint64) []common.Hash { hs := make([]common.Hash, 0, len(bns)) for _, bn := range bns { hs = append(hs, numberToHash(bn)) } return hs } func (tch *testChainHelper) getBlocksByHashes(hs []common.Hash) ([]*types.Block, error) { bs := make([]*types.Block, 0, len(hs)) for _, h := range hs { bn := hashToNumber(h) bs = append(bs, makeTestBlock(bn)) } return bs, nil } func (tch *testChainHelper) getNodeData(hs []common.Hash) ([][]byte, error) { data := makeTestNodeData(len(hs)) return data, nil } func (tch *testChainHelper) getReceipts(hs []common.Hash) ([]types.Receipts, error) { testReceipts := makeTestReceipts(len(hs), 3) receipts := make([]types.Receipts, len(hs)) for i, _ := range hs { receipts[i] = testReceipts } return receipts, nil } func (ch *testChainHelper) getAccountRange(root common.Hash, origin common.Hash, limit common.Hash, bytes uint64) ([]*message.AccountData, [][]byte, error) { testAccountRanges, testProofs := makeTestAccountRanges(2) return testAccountRanges, testProofs, nil } func (ch *testChainHelper) getStorageRanges(root common.Hash, accounts []common.Hash, origin common.Hash, limit common.Hash, bytes uint64) ([]*message.StoragesData, [][]byte, error) { testSlots, testProofs := makeTestStorageRanges(2) return testSlots, testProofs, nil } func (ch *testChainHelper) getByteCodes(hs []common.Hash, bytes uint64) ([][]byte, error) { testByteCodes := makeTestByteCodes(2) return testByteCodes, nil } func (ch *testChainHelper) getTrieNodes(root common.Hash, paths []*message.TrieNodePathSet, bytes uint64, start time.Time) ([][]byte, error) { testTrieNodes := makeTestTrieNodes(2) return testTrieNodes, nil } func checkGetReceiptsResult(b []byte, hs []common.Hash) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } bhResp, err := msg.GetReceiptsResponse() if err != nil { return err } if len(hs) != len(bhResp.Receipts) { return errors.New("unexpected size") } return nil } func checkGetNodeDataResult(b []byte, hs []common.Hash) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } bhResp, err := msg.GetNodeDataResponse() if err != nil { return err } if len(hs) != len(bhResp.DataBytes) { return errors.New("unexpected size") } return nil } func numberToHash(bn uint64) common.Hash { var h common.Hash binary.LittleEndian.PutUint64(h[:], bn) return h } func hashToNumber(h common.Hash) uint64 { return binary.LittleEndian.Uint64(h[:]) } func checkBlocksResult(bns []uint64, b []byte) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } gbResp, err := msg.GetBlocksByNumberResponse() if err != nil { return err } if len(gbResp.BlocksBytes) == 0 { return errors.New("nil response from GetBlocksByNumber") } blocks, err := decodeBlocksBytes(gbResp.BlocksBytes) if err != nil { return err } if len(blocks) != len(bns) { return errors.New("unexpected blocks number") } for i, bn := range bns { blk := blocks[i] if bn != blk.NumberU64() { return errors.New("unexpected number of a block") } } return nil } func makeTestBlock(bn uint64) *types.Block { header := testHeader.Copy() header.SetNumber(big.NewInt(int64(bn))) return types.NewBlockWithHeader(&block.Header{Header: header}) } // makeTestReceipts creates fake node data func makeTestNodeData(n int) [][]byte { testData := make([][]byte, n) for i := 0; i < n; i++ { testData[i] = types.EmptyRootHash.Bytes() } return testData } // makeTestReceipts creates fake receipts func makeTestReceipts(n int, nPerBlock int) []*types.Receipt { receipts := make([]*types.Receipt, nPerBlock) for i := 0; i < nPerBlock; i++ { receipts[i] = &types.Receipt{ Status: types.ReceiptStatusSuccessful, CumulativeGasUsed: 0x888888888, Logs: make([]*types.Log, 5), } } return receipts } func makeTestAccountRanges(n int) ([]*message.AccountData, [][]byte) { accounts := make([]*message.AccountData, n) proofs := make([][]byte, n) for i := 0; i < n; i++ { accounts[i] = &message.AccountData{ Hash: numberToHash(uint64(i * 2)).Bytes(), Body: numberToHash(uint64(i*2 + 1)).Bytes(), } } for i := 0; i < n; i++ { proofs[i] = numberToHash(uint64(i)).Bytes() } return accounts, proofs } func makeTestStorageRanges(n int) ([]*message.StoragesData, [][]byte) { slots := make([]*message.StoragesData, n) proofs := make([][]byte, n) for i := 0; i < n; i++ { slots[i] = &message.StoragesData{ Data: make([]*syncpb.StorageData, 2), } for j := 0; j < 2; j++ { slots[i].Data[j] = &message.StorageData{ Hash: numberToHash(uint64(i * 2)).Bytes(), Body: numberToHash(uint64(i*2 + 1)).Bytes(), } } } for i := 0; i < n; i++ { proofs[i] = numberToHash(uint64(i)).Bytes() } return slots, proofs } func makeTestByteCodes(n int) [][]byte { byteCodes := make([][]byte, n) for i := 0; i < n; i++ { byteCodes[i] = numberToHash(uint64(i)).Bytes() } return byteCodes } func makeTestTrieNodes(n int) [][]byte { trieNodes := make([][]byte, n) for i := 0; i < n; i++ { trieNodes[i] = numberToHash(uint64(i)).Bytes() } return trieNodes } func decodeBlocksBytes(bbs [][]byte) ([]*types.Block, error) { blocks := make([]*types.Block, 0, len(bbs)) for _, bb := range bbs { var block *types.Block if err := rlp.DecodeBytes(bb, &block); err != nil { return nil, err } blocks = append(blocks, block) } return blocks, nil } func decodeHashBytes(hs [][]byte) ([]common.Hash, error) { hashes := make([]common.Hash, 0) for _, h := range hs { var hash common.Hash if err := rlp.DecodeBytes(h, &hash); err != nil { return nil, err } hashes = append(hashes, hash) } return hashes, nil } func checkBlockNumberResult(b []byte) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } gnResp, err := msg.GetBlockNumberResponse() if err != nil { return err } if gnResp.Number != testCurBlockNumber { return fmt.Errorf("unexpected block number: %v / %v", gnResp.Number, testCurBlockNumber) } return nil } func checkBlockHashesResult(b []byte, bns []uint64) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } bhResp, err := msg.GetBlockHashesResponse() if err != nil { return err } got := bhResp.Hashes if len(got) != len(bns) { return errors.New("unexpected size") } for i, bn := range bns { expect := numberToHash(bn) if !bytes.Equal(expect[:], got[i]) { return errors.New("unexpected hash") } } return nil } func checkBlocksByHashesResult(b []byte, hs []common.Hash) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } bhResp, err := msg.GetBlocksByHashesResponse() if err != nil { return err } if len(hs) != len(bhResp.BlocksBytes) { return errors.New("unexpected size") } for i, h := range hs { num := hashToNumber(h) var blk *types.Block if err := rlp.DecodeBytes(bhResp.BlocksBytes[i], &blk); err != nil { return err } if blk.NumberU64() != num { return fmt.Errorf("unexpected number %v != %v", blk.NumberU64(), num) } } return nil } func checkAccountRangeResult(bytes uint64, b []byte) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } gbResp, err := msg.GetAccountRangesResponse() if err != nil { return err } if len(gbResp.Accounts) == 0 { return errors.New("nil response from GetAccountRanges") } if len(gbResp.Proof) != len(gbResp.Accounts) { return errors.New("unexpected proofs") } if len(b) > int(bytes) { return errors.New("unexpected data bytes") } return nil } func checkStorageRangesResult(accounts []common.Hash, bytes uint64, b []byte) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } gbResp, err := msg.GetStorageRangesResponse() if err != nil { return err } if len(gbResp.Slots) == 0 { return errors.New("nil response from GetStorageRanges") } if len(gbResp.Slots) != len(gbResp.Proof) { return errors.New("unexpected proofs") } sz := unsafe.Sizeof(gbResp.Slots) if sz > uintptr(bytes) { return errors.New("unexpected slot bytes") } return nil } func checkByteCodesResult(hs []common.Hash, bytes uint64, b []byte) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } gbResp, err := msg.GetByteCodesResponse() if err != nil { return err } if len(gbResp.Codes) == 0 { return errors.New("nil response from GetByteCodes") } if len(gbResp.Codes) != len(hs) { return errors.New("unexpected byte codes") } sz := len(hs) * common.HashLength if sz > int(bytes) { return errors.New("unexpected data bytes") } return nil } func checkTrieNodesResult(hs []common.Hash, bytes uint64, b []byte) error { var msg = &syncpb.Message{} if err := protobuf.Unmarshal(b, msg); err != nil { return err } gbResp, err := msg.GetTrieNodesResponse() if err != nil { return err } if len(gbResp.Nodes) == 0 { return errors.New("nil response from checkGetTrieNodes") } if len(gbResp.Nodes) != len(hs) { return errors.New("unexpected byte codes") } sz := len(hs) * common.HashLength if sz > int(bytes) { return errors.New("unexpected data bytes") } return nil }