odr_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package les
  17. import (
  18. "bytes"
  19. "context"
  20. "crypto/rand"
  21. "fmt"
  22. "math/big"
  23. "reflect"
  24. "testing"
  25. "time"
  26. "github.com/ethereum/go-ethereum/common"
  27. "github.com/ethereum/go-ethereum/common/math"
  28. "github.com/ethereum/go-ethereum/core"
  29. "github.com/ethereum/go-ethereum/core/rawdb"
  30. "github.com/ethereum/go-ethereum/core/state"
  31. "github.com/ethereum/go-ethereum/core/types"
  32. "github.com/ethereum/go-ethereum/core/vm"
  33. "github.com/ethereum/go-ethereum/ethdb"
  34. "github.com/ethereum/go-ethereum/light"
  35. "github.com/ethereum/go-ethereum/params"
  36. "github.com/ethereum/go-ethereum/rlp"
  37. )
  38. type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte
  39. func TestOdrGetBlockLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetBlock) }
  40. func TestOdrGetBlockLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetBlock) }
  41. func TestOdrGetBlockLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetBlock) }
  42. func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
  43. var block *types.Block
  44. if bc != nil {
  45. block = bc.GetBlockByHash(bhash)
  46. } else {
  47. block, _ = lc.GetBlockByHash(ctx, bhash)
  48. }
  49. if block == nil {
  50. return nil
  51. }
  52. rlp, _ := rlp.EncodeToBytes(block)
  53. return rlp
  54. }
  55. func TestOdrGetReceiptsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetReceipts) }
  56. func TestOdrGetReceiptsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetReceipts) }
  57. func TestOdrGetReceiptsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetReceipts) }
  58. func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
  59. var receipts types.Receipts
  60. if bc != nil {
  61. if number := rawdb.ReadHeaderNumber(db, bhash); number != nil {
  62. receipts = rawdb.ReadReceipts(db, bhash, *number, config)
  63. }
  64. } else {
  65. if number := rawdb.ReadHeaderNumber(db, bhash); number != nil {
  66. receipts, _ = light.GetBlockReceipts(ctx, lc.Odr(), bhash, *number)
  67. }
  68. }
  69. if receipts == nil {
  70. return nil
  71. }
  72. rlp, _ := rlp.EncodeToBytes(receipts)
  73. return rlp
  74. }
  75. func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrAccounts) }
  76. func TestOdrAccountsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrAccounts) }
  77. func TestOdrAccountsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrAccounts) }
  78. func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
  79. dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
  80. acc := []common.Address{bankAddr, userAddr1, userAddr2, dummyAddr}
  81. var (
  82. res []byte
  83. st *state.StateDB
  84. err error
  85. )
  86. for _, addr := range acc {
  87. if bc != nil {
  88. header := bc.GetHeaderByHash(bhash)
  89. st, err = state.New(header.Root, state.NewDatabase(db), nil)
  90. } else {
  91. header := lc.GetHeaderByHash(bhash)
  92. st = light.NewState(ctx, header, lc.Odr())
  93. }
  94. if err == nil {
  95. bal := st.GetBalance(addr)
  96. rlp, _ := rlp.EncodeToBytes(bal)
  97. res = append(res, rlp...)
  98. }
  99. }
  100. return res
  101. }
  102. func TestOdrContractCallLes2(t *testing.T) { testOdr(t, 2, 2, true, odrContractCall) }
  103. func TestOdrContractCallLes3(t *testing.T) { testOdr(t, 3, 2, true, odrContractCall) }
  104. func TestOdrContractCallLes4(t *testing.T) { testOdr(t, 4, 2, true, odrContractCall) }
  105. type callmsg struct {
  106. types.Message
  107. }
  108. func (callmsg) CheckNonce() bool { return false }
  109. func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
  110. data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
  111. var res []byte
  112. for i := 0; i < 3; i++ {
  113. data[35] = byte(i)
  114. if bc != nil {
  115. header := bc.GetHeaderByHash(bhash)
  116. statedb, err := state.New(header.Root, state.NewDatabase(db), nil)
  117. if err == nil {
  118. from := statedb.GetOrNewStateObject(bankAddr)
  119. from.SetBalance(math.MaxBig256)
  120. msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false)}
  121. context := core.NewEVMBlockContext(header, bc, nil)
  122. txContext := core.NewEVMTxContext(msg)
  123. vmenv := vm.NewEVM(context, txContext, statedb, statedb, config, vm.Config{})
  124. //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
  125. gp := new(core.GasPool).AddGas(math.MaxUint64)
  126. result, _ := core.ApplyMessage(vmenv, msg, gp)
  127. res = append(res, result.Return()...)
  128. }
  129. } else {
  130. header := lc.GetHeaderByHash(bhash)
  131. state := light.NewState(ctx, header, lc.Odr())
  132. state.SetBalance(bankAddr, math.MaxBig256)
  133. msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false)}
  134. context := core.NewEVMBlockContext(header, lc, nil)
  135. txContext := core.NewEVMTxContext(msg)
  136. vmenv := vm.NewEVM(context, txContext, state, state, config, vm.Config{})
  137. gp := new(core.GasPool).AddGas(math.MaxUint64)
  138. result, _ := core.ApplyMessage(vmenv, msg, gp)
  139. if state.Error() == nil {
  140. res = append(res, result.Return()...)
  141. }
  142. }
  143. }
  144. return res
  145. }
  146. func TestOdrTxStatusLes2(t *testing.T) { testOdr(t, 2, 1, false, odrTxStatus) }
  147. func TestOdrTxStatusLes3(t *testing.T) { testOdr(t, 3, 1, false, odrTxStatus) }
  148. func TestOdrTxStatusLes4(t *testing.T) { testOdr(t, 4, 1, false, odrTxStatus) }
  149. func odrTxStatus(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
  150. var txs types.Transactions
  151. if bc != nil {
  152. block := bc.GetBlockByHash(bhash)
  153. txs = block.Transactions()
  154. } else {
  155. if block, _ := lc.GetBlockByHash(ctx, bhash); block != nil {
  156. btxs := block.Transactions()
  157. txs = make(types.Transactions, len(btxs))
  158. for i, tx := range btxs {
  159. var err error
  160. txs[i], _, _, _, err = light.GetTransaction(ctx, lc.Odr(), tx.Hash())
  161. if err != nil {
  162. return nil
  163. }
  164. }
  165. }
  166. }
  167. rlp, _ := rlp.EncodeToBytes(txs)
  168. return rlp
  169. }
  170. // testOdr tests odr requests whose validation guaranteed by block headers.
  171. func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn odrTestFn) {
  172. // Assemble the test environment
  173. netconfig := testnetConfig{
  174. blocks: 4,
  175. protocol: protocol,
  176. connect: true,
  177. nopruning: true,
  178. }
  179. server, client, tearDown := newClientServerEnv(t, netconfig)
  180. defer tearDown()
  181. // Ensure the client has synced all necessary data.
  182. clientHead := client.handler.backend.blockchain.CurrentHeader()
  183. if clientHead.Number.Uint64() != 4 {
  184. t.Fatalf("Failed to sync the chain with server, head: %v", clientHead.Number.Uint64())
  185. }
  186. // Disable the mechanism that we will wait a few time for request
  187. // even there is no suitable peer to send right now.
  188. waitForPeers = 0
  189. test := func(expFail uint64) {
  190. // Mark this as a helper to put the failures at the correct lines
  191. t.Helper()
  192. for i := uint64(0); i <= server.handler.blockchain.CurrentHeader().Number.Uint64(); i++ {
  193. bhash := rawdb.ReadCanonicalHash(server.db, i)
  194. b1 := fn(light.NoOdr, server.db, server.handler.server.chainConfig, server.handler.blockchain, nil, bhash)
  195. // Set the timeout as 1 second here, ensure there is enough time
  196. // for travis to make the action.
  197. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  198. b2 := fn(ctx, client.db, client.handler.backend.chainConfig, nil, client.handler.backend.blockchain, bhash)
  199. cancel()
  200. eq := bytes.Equal(b1, b2)
  201. exp := i < expFail
  202. if exp && !eq {
  203. t.Fatalf("odr mismatch: have %x, want %x", b2, b1)
  204. }
  205. if !exp && eq {
  206. t.Fatalf("unexpected odr match")
  207. }
  208. }
  209. }
  210. // expect retrievals to fail (except genesis block) without a les peer
  211. client.handler.backend.peers.lock.Lock()
  212. client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return false }
  213. client.handler.backend.peers.lock.Unlock()
  214. test(expFail)
  215. // expect all retrievals to pass
  216. client.handler.backend.peers.lock.Lock()
  217. client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return true }
  218. client.handler.backend.peers.lock.Unlock()
  219. test(5)
  220. // still expect all retrievals to pass, now data should be cached locally
  221. if checkCached {
  222. client.handler.backend.peers.unregister(client.peer.speer.id)
  223. time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed
  224. test(5)
  225. }
  226. }
  227. func TestGetTxStatusFromUnindexedPeersLES4(t *testing.T) { testGetTxStatusFromUnindexedPeers(t, lpv4) }
  228. func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) {
  229. var (
  230. blocks = 8
  231. netconfig = testnetConfig{
  232. blocks: blocks,
  233. protocol: protocol,
  234. nopruning: true,
  235. }
  236. )
  237. server, client, tearDown := newClientServerEnv(t, netconfig)
  238. defer tearDown()
  239. // Iterate the chain, create the tx indexes locally
  240. var (
  241. testHash common.Hash
  242. testStatus light.TxStatus
  243. txs = make(map[common.Hash]*types.Transaction) // Transaction objects set
  244. blockNumbers = make(map[common.Hash]uint64) // Transaction hash to block number mappings
  245. blockHashes = make(map[common.Hash]common.Hash) // Transaction hash to block hash mappings
  246. intraIndex = make(map[common.Hash]uint64) // Transaction intra-index in block
  247. )
  248. for number := uint64(1); number < server.backend.Blockchain().CurrentBlock().NumberU64(); number++ {
  249. block := server.backend.Blockchain().GetBlockByNumber(number)
  250. if block == nil {
  251. t.Fatalf("Failed to retrieve block %d", number)
  252. }
  253. for index, tx := range block.Transactions() {
  254. txs[tx.Hash()] = tx
  255. blockNumbers[tx.Hash()] = number
  256. blockHashes[tx.Hash()] = block.Hash()
  257. intraIndex[tx.Hash()] = uint64(index)
  258. if testHash == (common.Hash{}) {
  259. testHash = tx.Hash()
  260. testStatus = light.TxStatus{
  261. Status: core.TxStatusIncluded,
  262. Lookup: &rawdb.LegacyTxLookupEntry{
  263. BlockHash: block.Hash(),
  264. BlockIndex: block.NumberU64(),
  265. Index: uint64(index),
  266. },
  267. }
  268. }
  269. }
  270. }
  271. // serveMsg processes incoming GetTxStatusMsg and sends the response back.
  272. serveMsg := func(peer *testPeer, txLookup uint64) error {
  273. msg, err := peer.app.ReadMsg()
  274. if err != nil {
  275. return err
  276. }
  277. if msg.Code != GetTxStatusMsg {
  278. return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, GetTxStatusMsg)
  279. }
  280. var r GetTxStatusPacket
  281. if err := msg.Decode(&r); err != nil {
  282. return err
  283. }
  284. stats := make([]light.TxStatus, len(r.Hashes))
  285. for i, hash := range r.Hashes {
  286. number, exist := blockNumbers[hash]
  287. if !exist {
  288. continue // Filter out unknown transactions
  289. }
  290. min := uint64(blocks) - txLookup
  291. if txLookup != txIndexUnlimited && (txLookup == txIndexDisabled || number < min) {
  292. continue // Filter out unindexed transactions
  293. }
  294. stats[i].Status = core.TxStatusIncluded
  295. stats[i].Lookup = &rawdb.LegacyTxLookupEntry{
  296. BlockHash: blockHashes[hash],
  297. BlockIndex: number,
  298. Index: intraIndex[hash],
  299. }
  300. }
  301. data, _ := rlp.EncodeToBytes(stats)
  302. reply := &reply{peer.app, TxStatusMsg, r.ReqID, data}
  303. reply.send(testBufLimit)
  304. return nil
  305. }
  306. var testspecs = []struct {
  307. peers int
  308. txLookups []uint64
  309. txs []common.Hash
  310. results []light.TxStatus
  311. }{
  312. // Retrieve mined transaction from the empty peerset
  313. {
  314. peers: 0,
  315. txLookups: []uint64{},
  316. txs: []common.Hash{testHash},
  317. results: []light.TxStatus{{}},
  318. },
  319. // Retrieve unknown transaction from the full peers
  320. {
  321. peers: 3,
  322. txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited},
  323. txs: []common.Hash{randomHash()},
  324. results: []light.TxStatus{{}},
  325. },
  326. // Retrieve mined transaction from the full peers
  327. {
  328. peers: 3,
  329. txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited},
  330. txs: []common.Hash{testHash},
  331. results: []light.TxStatus{testStatus},
  332. },
  333. // Retrieve mixed transactions from the full peers
  334. {
  335. peers: 3,
  336. txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited},
  337. txs: []common.Hash{randomHash(), testHash},
  338. results: []light.TxStatus{{}, testStatus},
  339. },
  340. // Retrieve mixed transactions from unindexed peer(but the target is still available)
  341. {
  342. peers: 3,
  343. txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2},
  344. txs: []common.Hash{randomHash(), testHash},
  345. results: []light.TxStatus{{}, testStatus},
  346. },
  347. // Retrieve mixed transactions from unindexed peer(but the target is not available)
  348. {
  349. peers: 3,
  350. txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2},
  351. txs: []common.Hash{randomHash(), testHash},
  352. results: []light.TxStatus{{}, {}},
  353. },
  354. }
  355. for _, testspec := range testspecs {
  356. // Create a bunch of server peers with different tx history
  357. var (
  358. serverPeers []*testPeer
  359. closeFns []func()
  360. )
  361. for i := 0; i < testspec.peers; i++ {
  362. peer, closePeer, _ := client.newRawPeer(t, fmt.Sprintf("server-%d", i), protocol, testspec.txLookups[i])
  363. serverPeers = append(serverPeers, peer)
  364. closeFns = append(closeFns, closePeer)
  365. // Create a one-time routine for serving message
  366. go func(i int, peer *testPeer) {
  367. serveMsg(peer, testspec.txLookups[i])
  368. }(i, peer)
  369. }
  370. // Send out the GetTxStatus requests, compare the result with
  371. // expected value.
  372. r := &light.TxStatusRequest{Hashes: testspec.txs}
  373. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  374. defer cancel()
  375. err := client.handler.backend.odr.RetrieveTxStatus(ctx, r)
  376. if err != nil {
  377. t.Errorf("Failed to retrieve tx status %v", err)
  378. } else {
  379. if !reflect.DeepEqual(testspec.results, r.Status) {
  380. t.Errorf("Result mismatch, diff")
  381. }
  382. }
  383. // Close all connected peers and start the next round
  384. for _, closeFn := range closeFns {
  385. closeFn()
  386. }
  387. }
  388. }
  389. // randomHash generates a random blob of data and returns it as a hash.
  390. func randomHash() common.Hash {
  391. var hash common.Hash
  392. if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
  393. panic(err)
  394. }
  395. return hash
  396. }