prepare_test.go 9.5 KB

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