commit_test.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // Copyright 2017 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 core
  17. import (
  18. "bytes"
  19. "math/big"
  20. "testing"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/consensus/istanbul"
  23. istanbulcommon "github.com/ethereum/go-ethereum/consensus/istanbul/common"
  24. ibfttypes "github.com/ethereum/go-ethereum/consensus/istanbul/ibft/types"
  25. "github.com/ethereum/go-ethereum/consensus/istanbul/validator"
  26. "github.com/ethereum/go-ethereum/crypto"
  27. )
  28. func TestHandleCommit(t *testing.T) {
  29. N := uint64(4)
  30. F := uint64(1)
  31. proposal := newTestProposal()
  32. expectedSubject := &istanbul.Subject{
  33. View: &istanbul.View{
  34. Round: big.NewInt(0),
  35. Sequence: proposal.Number(),
  36. },
  37. Digest: proposal.Hash(),
  38. }
  39. testCases := []struct {
  40. system *testSystem
  41. expectedErr error
  42. }{
  43. {
  44. // normal case
  45. func() *testSystem {
  46. sys := NewTestSystemWithBackend(N, F)
  47. for i, backend := range sys.backends {
  48. c := backend.engine
  49. c.valSet = backend.peers
  50. c.current = newTestRoundState(
  51. &istanbul.View{
  52. Round: big.NewInt(0),
  53. Sequence: big.NewInt(1),
  54. },
  55. c.valSet,
  56. )
  57. if i == 0 {
  58. // replica 0 is the proposer
  59. c.state = ibfttypes.StatePrepared
  60. }
  61. }
  62. return sys
  63. }(),
  64. nil,
  65. },
  66. {
  67. // future message
  68. func() *testSystem {
  69. sys := NewTestSystemWithBackend(N, F)
  70. for i, backend := range sys.backends {
  71. c := backend.engine
  72. c.valSet = backend.peers
  73. if i == 0 {
  74. // replica 0 is the proposer
  75. c.current = newTestRoundState(
  76. expectedSubject.View,
  77. c.valSet,
  78. )
  79. c.state = ibfttypes.StatePreprepared
  80. } else {
  81. c.current = newTestRoundState(
  82. &istanbul.View{
  83. Round: big.NewInt(2),
  84. Sequence: big.NewInt(3),
  85. },
  86. c.valSet,
  87. )
  88. }
  89. }
  90. return sys
  91. }(),
  92. istanbulcommon.ErrFutureMessage,
  93. },
  94. {
  95. // subject not match
  96. func() *testSystem {
  97. sys := NewTestSystemWithBackend(N, F)
  98. for i, backend := range sys.backends {
  99. c := backend.engine
  100. c.valSet = backend.peers
  101. if i == 0 {
  102. // replica 0 is the proposer
  103. c.current = newTestRoundState(
  104. expectedSubject.View,
  105. c.valSet,
  106. )
  107. c.state = ibfttypes.StatePreprepared
  108. } else {
  109. c.current = newTestRoundState(
  110. &istanbul.View{
  111. Round: big.NewInt(0),
  112. Sequence: big.NewInt(0),
  113. },
  114. c.valSet,
  115. )
  116. }
  117. }
  118. return sys
  119. }(),
  120. istanbulcommon.ErrOldMessage,
  121. },
  122. {
  123. // jump state
  124. func() *testSystem {
  125. sys := NewTestSystemWithBackend(N, F)
  126. for i, backend := range sys.backends {
  127. c := backend.engine
  128. c.valSet = backend.peers
  129. c.current = newTestRoundState(
  130. &istanbul.View{
  131. Round: big.NewInt(0),
  132. Sequence: proposal.Number(),
  133. },
  134. c.valSet,
  135. )
  136. // only replica0 stays at ibfttypes.StatePreprepared
  137. // other replicas are at ibfttypes.StatePrepared
  138. if i != 0 {
  139. c.state = ibfttypes.StatePrepared
  140. } else {
  141. c.state = ibfttypes.StatePreprepared
  142. }
  143. }
  144. return sys
  145. }(),
  146. nil,
  147. },
  148. // TODO: double send message
  149. }
  150. OUTER:
  151. for _, test := range testCases {
  152. test.system.Run(false)
  153. v0 := test.system.backends[0]
  154. r0 := v0.engine
  155. for i, v := range test.system.backends {
  156. validator := r0.valSet.GetByIndex(uint64(i))
  157. m, _ := ibfttypes.Encode(v.engine.current.Subject())
  158. if err := r0.handleCommit(&ibfttypes.Message{
  159. Code: ibfttypes.MsgCommit,
  160. Msg: m,
  161. Address: validator.Address(),
  162. Signature: []byte{},
  163. CommittedSeal: validator.Address().Bytes(), // small hack
  164. }, validator); err != nil {
  165. if err != test.expectedErr {
  166. t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
  167. }
  168. if r0.current.IsHashLocked() {
  169. t.Errorf("block should not be locked")
  170. }
  171. continue OUTER
  172. }
  173. }
  174. // prepared is normal case
  175. if r0.state != ibfttypes.StateCommitted {
  176. // There are not enough commit messages in core
  177. if r0.state != ibfttypes.StatePrepared {
  178. t.Errorf("state mismatch: have %v, want %v", r0.state, ibfttypes.StatePrepared)
  179. }
  180. if r0.current.Commits.Size() >= r0.QuorumSize() {
  181. t.Errorf("the size of commit messages should be less than %v", r0.QuorumSize())
  182. }
  183. if r0.current.IsHashLocked() {
  184. t.Errorf("block should not be locked")
  185. }
  186. continue
  187. }
  188. // core should have 2F+1 before Ceil2Nby3Block or Ceil(2N/3) prepare messages
  189. if r0.current.Commits.Size() < r0.QuorumSize() {
  190. t.Errorf("the size of commit messages should be larger than 2F+1 or Ceil(2N/3): size %v", r0.QuorumSize())
  191. }
  192. // check signatures large than F
  193. signedCount := 0
  194. committedSeals := v0.committedMsgs[0].committedSeals
  195. for _, validator := range r0.valSet.List() {
  196. for _, seal := range committedSeals {
  197. if bytes.Equal(validator.Address().Bytes(), seal[:common.AddressLength]) {
  198. signedCount++
  199. break
  200. }
  201. }
  202. }
  203. if signedCount <= r0.valSet.F() {
  204. t.Errorf("the expected signed count should be larger than %v, but got %v", r0.valSet.F(), signedCount)
  205. }
  206. if !r0.current.IsHashLocked() {
  207. t.Errorf("block should be locked")
  208. }
  209. }
  210. }
  211. // round is not checked for now
  212. func TestVerifyCommit(t *testing.T) {
  213. // for log purpose
  214. privateKey, _ := crypto.GenerateKey()
  215. peer := validator.New(getPublicKeyAddress(privateKey))
  216. valSet := validator.NewSet([]common.Address{peer.Address()}, istanbul.NewRoundRobinProposerPolicy())
  217. sys := NewTestSystemWithBackend(uint64(1), uint64(0))
  218. testCases := []struct {
  219. expected error
  220. commit *istanbul.Subject
  221. roundState *roundState
  222. }{
  223. {
  224. // normal case
  225. expected: nil,
  226. commit: &istanbul.Subject{
  227. View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
  228. Digest: newTestProposal().Hash(),
  229. },
  230. roundState: newTestRoundState(
  231. &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
  232. valSet,
  233. ),
  234. },
  235. {
  236. // old message
  237. expected: istanbulcommon.ErrInconsistentSubject,
  238. commit: &istanbul.Subject{
  239. View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
  240. Digest: newTestProposal().Hash(),
  241. },
  242. roundState: newTestRoundState(
  243. &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
  244. valSet,
  245. ),
  246. },
  247. {
  248. // different digest
  249. expected: istanbulcommon.ErrInconsistentSubject,
  250. commit: &istanbul.Subject{
  251. View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
  252. Digest: common.StringToHash("1234567890"),
  253. },
  254. roundState: newTestRoundState(
  255. &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
  256. valSet,
  257. ),
  258. },
  259. {
  260. // malicious package(lack of sequence)
  261. expected: istanbulcommon.ErrInconsistentSubject,
  262. commit: &istanbul.Subject{
  263. View: &istanbul.View{Round: big.NewInt(0), Sequence: nil},
  264. Digest: newTestProposal().Hash(),
  265. },
  266. roundState: newTestRoundState(
  267. &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
  268. valSet,
  269. ),
  270. },
  271. {
  272. // wrong prepare message with same sequence but different round
  273. expected: istanbulcommon.ErrInconsistentSubject,
  274. commit: &istanbul.Subject{
  275. View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)},
  276. Digest: newTestProposal().Hash(),
  277. },
  278. roundState: newTestRoundState(
  279. &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
  280. valSet,
  281. ),
  282. },
  283. {
  284. // wrong prepare message with same round but different sequence
  285. expected: istanbulcommon.ErrInconsistentSubject,
  286. commit: &istanbul.Subject{
  287. View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)},
  288. Digest: newTestProposal().Hash(),
  289. },
  290. roundState: newTestRoundState(
  291. &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
  292. valSet,
  293. ),
  294. },
  295. }
  296. for i, test := range testCases {
  297. c := sys.backends[0].engine
  298. c.current = test.roundState
  299. if err := c.verifyCommit(test.commit, peer); err != nil {
  300. if err != test.expected {
  301. t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected)
  302. }
  303. }
  304. }
  305. }