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.

279 lines
8.1 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. "fmt"
  4. "testing"
  5. "time"
  6. "github.com/stretchr/testify/assert"
  7. cmn "github.com/tendermint/tendermint/libs/common"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. func TestVerifyAdjustedHeaders(t *testing.T) {
  11. const (
  12. chainID = "TestVerifyAdjustedHeaders"
  13. lastHeight = 1
  14. nextHeight = 2
  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, lastHeight, bTime, nil, vals, vals,
  22. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
  23. )
  24. testCases := []struct {
  25. newHeader *types.SignedHeader
  26. newVals *types.ValidatorSet
  27. trustingPeriod time.Duration
  28. now time.Time
  29. expErr error
  30. expErrText string
  31. }{
  32. // same header -> no error
  33. 0: {
  34. header,
  35. vals,
  36. 3 * time.Hour,
  37. bTime.Add(2 * time.Hour),
  38. nil,
  39. "expected new header height 1 to be greater than one of old header 1",
  40. },
  41. // different chainID -> error
  42. 1: {
  43. keys.GenSignedHeader("different-chainID", nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
  44. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  45. vals,
  46. 3 * time.Hour,
  47. bTime.Add(2 * time.Hour),
  48. nil,
  49. "h2.ValidateBasic failed: signedHeader belongs to another chain 'different-chainID' not 'TestVerifyAdjustedHeaders'",
  50. },
  51. // 3/3 signed -> no error
  52. 2: {
  53. keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
  54. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  55. vals,
  56. 3 * time.Hour,
  57. bTime.Add(2 * time.Hour),
  58. nil,
  59. "",
  60. },
  61. // 2/3 signed -> no error
  62. 3: {
  63. keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
  64. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 1, len(keys)),
  65. vals,
  66. 3 * time.Hour,
  67. bTime.Add(2 * time.Hour),
  68. nil,
  69. "",
  70. },
  71. // 1/3 signed -> error
  72. 4: {
  73. keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
  74. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)),
  75. vals,
  76. 3 * time.Hour,
  77. bTime.Add(2 * time.Hour),
  78. types.ErrTooMuchChange{Got: 50, Needed: 93},
  79. "",
  80. },
  81. // vals does not match with what we have -> error
  82. 5: {
  83. keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, keys.ToValidators(10, 1), vals,
  84. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  85. keys.ToValidators(10, 1),
  86. 3 * time.Hour,
  87. bTime.Add(2 * time.Hour),
  88. nil,
  89. "to match those from new header",
  90. },
  91. // vals are inconsistent with newHeader -> error
  92. 6: {
  93. keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
  94. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  95. keys.ToValidators(10, 1),
  96. 3 * time.Hour,
  97. bTime.Add(2 * time.Hour),
  98. nil,
  99. "to match those that were supplied",
  100. },
  101. // old header has expired -> error
  102. 7: {
  103. keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
  104. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  105. keys.ToValidators(10, 1),
  106. 1 * time.Hour,
  107. bTime.Add(1 * time.Hour),
  108. nil,
  109. "old header has expired",
  110. },
  111. }
  112. for i, tc := range testCases {
  113. tc := tc
  114. t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
  115. err := Verify(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, DefaultTrustLevel)
  116. switch {
  117. case tc.expErr != nil && assert.Error(t, err):
  118. assert.Equal(t, tc.expErr, err)
  119. case tc.expErrText != "":
  120. assert.Contains(t, err.Error(), tc.expErrText)
  121. default:
  122. assert.NoError(t, err)
  123. }
  124. })
  125. }
  126. }
  127. func TestVerifyNonAdjustedHeaders(t *testing.T) {
  128. const (
  129. chainID = "TestVerifyNonAdjustedHeaders"
  130. lastHeight = 1
  131. )
  132. var (
  133. keys = genPrivKeys(4)
  134. // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
  135. vals = keys.ToValidators(20, 10)
  136. bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
  137. header = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals,
  138. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
  139. // 30, 40, 50
  140. twoThirds = keys[1:]
  141. twoThirdsVals = twoThirds.ToValidators(30, 10)
  142. // 50
  143. oneThird = keys[len(keys)-1:]
  144. oneThirdVals = oneThird.ToValidators(50, 10)
  145. // 20
  146. lessThanOneThird = keys[0:1]
  147. lessThanOneThirdVals = lessThanOneThird.ToValidators(20, 10)
  148. )
  149. testCases := []struct {
  150. newHeader *types.SignedHeader
  151. newVals *types.ValidatorSet
  152. trustingPeriod time.Duration
  153. now time.Time
  154. expErr error
  155. expErrText string
  156. }{
  157. // 3/3 new vals signed, 3/3 old vals present -> no error
  158. 0: {
  159. keys.GenSignedHeader(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals,
  160. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
  161. vals,
  162. 3 * time.Hour,
  163. bTime.Add(2 * time.Hour),
  164. nil,
  165. "",
  166. },
  167. // 2/3 new vals signed, 3/3 old vals present -> no error
  168. 1: {
  169. keys.GenSignedHeader(chainID, 4, bTime.Add(1*time.Hour), nil, vals, vals,
  170. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 1, len(keys)),
  171. vals,
  172. 3 * time.Hour,
  173. bTime.Add(2 * time.Hour),
  174. nil,
  175. "",
  176. },
  177. // 1/3 new vals signed, 3/3 old vals present -> error
  178. 2: {
  179. keys.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, vals, vals,
  180. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)),
  181. vals,
  182. 3 * time.Hour,
  183. bTime.Add(2 * time.Hour),
  184. types.ErrTooMuchChange{Got: 50, Needed: 93},
  185. "",
  186. },
  187. // 3/3 new vals signed, 2/3 old vals present -> no error
  188. 3: {
  189. twoThirds.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, twoThirdsVals, twoThirdsVals,
  190. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(twoThirds)),
  191. twoThirdsVals,
  192. 3 * time.Hour,
  193. bTime.Add(2 * time.Hour),
  194. nil,
  195. "",
  196. },
  197. // 3/3 new vals signed, 1/3 old vals present -> no error
  198. 4: {
  199. oneThird.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, oneThirdVals, oneThirdVals,
  200. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(oneThird)),
  201. oneThirdVals,
  202. 3 * time.Hour,
  203. bTime.Add(2 * time.Hour),
  204. nil,
  205. "",
  206. },
  207. // 3/3 new vals signed, less than 1/3 old vals present -> error
  208. 5: {
  209. lessThanOneThird.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, lessThanOneThirdVals, lessThanOneThirdVals,
  210. []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(lessThanOneThird)),
  211. lessThanOneThirdVals,
  212. 3 * time.Hour,
  213. bTime.Add(2 * time.Hour),
  214. types.ErrTooMuchChange{Got: 20, Needed: 46},
  215. "",
  216. },
  217. }
  218. for i, tc := range testCases {
  219. tc := tc
  220. t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
  221. err := Verify(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, DefaultTrustLevel)
  222. switch {
  223. case tc.expErr != nil && assert.Error(t, err):
  224. assert.Equal(t, tc.expErr, err)
  225. case tc.expErrText != "":
  226. assert.Contains(t, err.Error(), tc.expErrText)
  227. default:
  228. assert.NoError(t, err)
  229. }
  230. })
  231. }
  232. }
  233. func TestValidateTrustLevel(t *testing.T) {
  234. testCases := []struct {
  235. lvl cmn.Fraction
  236. valid bool
  237. }{
  238. // valid
  239. 0: {cmn.Fraction{Numerator: 1, Denominator: 1}, true},
  240. 1: {cmn.Fraction{Numerator: 1, Denominator: 3}, true},
  241. 2: {cmn.Fraction{Numerator: 2, Denominator: 3}, true},
  242. 3: {cmn.Fraction{Numerator: 3, Denominator: 3}, true},
  243. 4: {cmn.Fraction{Numerator: 4, Denominator: 5}, true},
  244. // invalid
  245. 5: {cmn.Fraction{Numerator: 6, Denominator: 5}, false},
  246. 6: {cmn.Fraction{Numerator: -1, Denominator: 3}, false},
  247. 7: {cmn.Fraction{Numerator: 0, Denominator: 1}, false},
  248. 8: {cmn.Fraction{Numerator: -1, Denominator: -3}, false},
  249. 9: {cmn.Fraction{Numerator: 0, Denominator: 0}, false},
  250. 10: {cmn.Fraction{Numerator: 1, Denominator: 0}, false},
  251. }
  252. for _, tc := range testCases {
  253. err := ValidateTrustLevel(tc.lvl)
  254. if !tc.valid {
  255. assert.Error(t, err)
  256. } else {
  257. assert.NoError(t, err)
  258. }
  259. }
  260. }