You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
6.0 KiB

lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
  1. package lite
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/require"
  7. dbm "github.com/tendermint/tm-db"
  8. mockp "github.com/tendermint/tendermint/lite2/provider/mock"
  9. dbs "github.com/tendermint/tendermint/lite2/store/db"
  10. "github.com/tendermint/tendermint/types"
  11. )
  12. func TestClient_SequentialVerification(t *testing.T) {
  13. const (
  14. chainID = "sequential-verification"
  15. )
  16. var (
  17. keys = genPrivKeys(4)
  18. // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
  19. vals = keys.ToValidators(20, 10)
  20. bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
  21. header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals,
  22. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
  23. )
  24. testCases := []struct {
  25. otherHeaders map[int64]*types.SignedHeader // all except ^
  26. vals map[int64]*types.ValidatorSet
  27. initErr bool
  28. verifyErr bool
  29. }{
  30. // good
  31. {
  32. map[int64]*types.SignedHeader{
  33. // trusted header
  34. 1: header,
  35. // interim header (3/3 signed)
  36. 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
  37. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  38. // last header (3/3 signed)
  39. 3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
  40. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  41. },
  42. map[int64]*types.ValidatorSet{
  43. 1: vals,
  44. 2: vals,
  45. 3: vals,
  46. 4: vals,
  47. },
  48. false,
  49. false,
  50. },
  51. // bad: different first header
  52. {
  53. map[int64]*types.SignedHeader{
  54. // different header
  55. 1: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
  56. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  57. },
  58. map[int64]*types.ValidatorSet{
  59. 1: vals,
  60. },
  61. true,
  62. false,
  63. },
  64. // bad: 1/3 signed interim header
  65. {
  66. map[int64]*types.SignedHeader{
  67. // trusted header
  68. 1: header,
  69. // interim header (1/3 signed)
  70. 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
  71. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)),
  72. // last header (3/3 signed)
  73. 3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
  74. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  75. },
  76. map[int64]*types.ValidatorSet{
  77. 1: vals,
  78. 2: vals,
  79. 3: vals,
  80. 4: vals,
  81. },
  82. false,
  83. true,
  84. },
  85. // bad: 1/3 signed last header
  86. {
  87. map[int64]*types.SignedHeader{
  88. // trusted header
  89. 1: header,
  90. // interim header (3/3 signed)
  91. 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
  92. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  93. // last header (1/3 signed)
  94. 3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
  95. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)),
  96. },
  97. map[int64]*types.ValidatorSet{
  98. 1: vals,
  99. 2: vals,
  100. 3: vals,
  101. 4: vals,
  102. },
  103. false,
  104. true,
  105. },
  106. }
  107. for _, tc := range testCases {
  108. c, err := NewClient(
  109. chainID,
  110. TrustOptions{
  111. Period: 4 * time.Hour,
  112. Height: 1,
  113. Hash: header.Hash(),
  114. },
  115. mockp.New(
  116. chainID,
  117. tc.otherHeaders,
  118. tc.vals,
  119. ),
  120. dbs.New(dbm.NewMemDB(), chainID),
  121. SequentialVerification(),
  122. )
  123. if tc.initErr {
  124. require.Error(t, err)
  125. continue
  126. } else {
  127. require.NoError(t, err)
  128. }
  129. err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour))
  130. if tc.verifyErr {
  131. assert.Error(t, err)
  132. } else {
  133. assert.NoError(t, err)
  134. }
  135. }
  136. }
  137. func TestClient_SkippingVerification(t *testing.T) {
  138. const (
  139. chainID = "skipping-verification"
  140. )
  141. var (
  142. keys = genPrivKeys(4)
  143. // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
  144. vals = keys.ToValidators(20, 10)
  145. bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
  146. header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals,
  147. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
  148. )
  149. // required for 2nd test case
  150. newKeys := genPrivKeys(4)
  151. newVals := newKeys.ToValidators(10, 1)
  152. testCases := []struct {
  153. otherHeaders map[int64]*types.SignedHeader // all except ^
  154. vals map[int64]*types.ValidatorSet
  155. initErr bool
  156. verifyErr bool
  157. }{
  158. // good
  159. {
  160. map[int64]*types.SignedHeader{
  161. // trusted header
  162. 1: header,
  163. // last header (3/3 signed)
  164. 3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
  165. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  166. },
  167. map[int64]*types.ValidatorSet{
  168. 1: vals,
  169. 2: vals,
  170. 3: vals,
  171. 4: vals,
  172. },
  173. false,
  174. false,
  175. },
  176. // good, val set changes 100% at height 2
  177. {
  178. map[int64]*types.SignedHeader{
  179. // trusted header
  180. 1: header,
  181. // interim header (3/3 signed)
  182. 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals,
  183. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  184. // last header (0/4 of the original val set signed)
  185. 3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals,
  186. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(newKeys)),
  187. },
  188. map[int64]*types.ValidatorSet{
  189. 1: vals,
  190. 2: vals,
  191. 3: newVals,
  192. 4: newVals,
  193. },
  194. false,
  195. false,
  196. },
  197. }
  198. for _, tc := range testCases {
  199. c, err := NewClient(
  200. chainID,
  201. TrustOptions{
  202. Period: 4 * time.Hour,
  203. Height: 1,
  204. Hash: header.Hash(),
  205. },
  206. mockp.New(
  207. chainID,
  208. tc.otherHeaders,
  209. tc.vals,
  210. ),
  211. dbs.New(dbm.NewMemDB(), chainID),
  212. SkippingVerification(DefaultTrustLevel),
  213. )
  214. if tc.initErr {
  215. require.Error(t, err)
  216. continue
  217. } else {
  218. require.NoError(t, err)
  219. }
  220. err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour))
  221. if tc.verifyErr {
  222. assert.Error(t, err)
  223. } else {
  224. assert.NoError(t, err)
  225. }
  226. }
  227. }