contract_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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 release
  17. import (
  18. "crypto/ecdsa"
  19. "math/big"
  20. "testing"
  21. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  22. "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/core"
  25. "github.com/ethereum/go-ethereum/crypto"
  26. )
  27. // setupReleaseTest creates a blockchain simulator and deploys a version oracle
  28. // contract for testing.
  29. func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.PrivateKey, *ReleaseOracle, *backends.SimulatedBackend) {
  30. // Generate a new random account and a funded simulator
  31. key, _ := crypto.GenerateKey()
  32. auth := bind.NewKeyedTransactor(key)
  33. accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}}
  34. for _, key := range prefund {
  35. accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)})
  36. }
  37. sim := backends.NewSimulatedBackend(accounts...)
  38. // Deploy a version oracle contract, commit and return
  39. _, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From})
  40. if err != nil {
  41. t.Fatalf("Failed to deploy version contract: %v", err)
  42. }
  43. sim.Commit()
  44. return key, oracle, sim
  45. }
  46. // Tests that the version contract can be deployed and the creator is assigned
  47. // the sole authorized signer.
  48. func TestContractCreation(t *testing.T) {
  49. key, oracle, _ := setupReleaseTest(t)
  50. owner := crypto.PubkeyToAddress(key.PublicKey)
  51. signers, err := oracle.Signers(nil)
  52. if err != nil {
  53. t.Fatalf("Failed to retrieve list of signers: %v", err)
  54. }
  55. if len(signers) != 1 || signers[0] != owner {
  56. t.Fatalf("Initial signer mismatch: have %v, want %v", signers, owner)
  57. }
  58. }
  59. // Tests that subsequent signers can be promoted, each requiring half plus one
  60. // votes for it to pass through.
  61. func TestSignerPromotion(t *testing.T) {
  62. // Prefund a few accounts to authorize with and create the oracle
  63. keys := make([]*ecdsa.PrivateKey, 5)
  64. for i := 0; i < len(keys); i++ {
  65. keys[i], _ = crypto.GenerateKey()
  66. }
  67. key, oracle, sim := setupReleaseTest(t, keys...)
  68. // Gradually promote the keys, until all are authorized
  69. keys = append([]*ecdsa.PrivateKey{key}, keys...)
  70. for i := 1; i < len(keys); i++ {
  71. // Check that no votes are accepted from the not yet authed user
  72. if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil {
  73. t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err)
  74. }
  75. sim.Commit()
  76. pend, err := oracle.AuthProposals(nil)
  77. if err != nil {
  78. t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err)
  79. }
  80. if len(pend) != 0 {
  81. t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend))
  82. }
  83. // Promote with half - 1 voters and check that the user's not yet authorized
  84. for j := 0; j < i/2; j++ {
  85. if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  86. t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
  87. }
  88. }
  89. sim.Commit()
  90. signers, err := oracle.Signers(nil)
  91. if err != nil {
  92. t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err)
  93. }
  94. if len(signers) != i {
  95. t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i)
  96. }
  97. // Promote with the last one needed to pass the promotion
  98. if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  99. t.Fatalf("Iter #%d: failed valid promotion completion attempt: %v", i, err)
  100. }
  101. sim.Commit()
  102. signers, err = oracle.Signers(nil)
  103. if err != nil {
  104. t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err)
  105. }
  106. if len(signers) != i+1 {
  107. t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i+1)
  108. }
  109. }
  110. }
  111. // Tests that subsequent signers can be demoted, each requiring half plus one
  112. // votes for it to pass through.
  113. func TestSignerDemotion(t *testing.T) {
  114. // Prefund a few accounts to authorize with and create the oracle
  115. keys := make([]*ecdsa.PrivateKey, 5)
  116. for i := 0; i < len(keys); i++ {
  117. keys[i], _ = crypto.GenerateKey()
  118. }
  119. key, oracle, sim := setupReleaseTest(t, keys...)
  120. // Authorize all the keys as valid signers and verify cardinality
  121. keys = append([]*ecdsa.PrivateKey{key}, keys...)
  122. for i := 1; i < len(keys); i++ {
  123. for j := 0; j <= i/2; j++ {
  124. if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  125. t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
  126. }
  127. }
  128. sim.Commit()
  129. }
  130. signers, err := oracle.Signers(nil)
  131. if err != nil {
  132. t.Fatalf("Failed to retrieve list of signers: %v", err)
  133. }
  134. if len(signers) != len(keys) {
  135. t.Fatalf("Signer count mismatch: have %v, want %v", len(signers), len(keys))
  136. }
  137. // Gradually demote users until we run out of signers
  138. for i := len(keys) - 1; i >= 0; i-- {
  139. // Demote with half - 1 voters and check that the user's not yet dropped
  140. for j := 0; j < (i+1)/2; j++ {
  141. if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  142. t.Fatalf("Iter #%d: failed valid demotion attempt: %v", len(keys)-i, err)
  143. }
  144. }
  145. sim.Commit()
  146. signers, err := oracle.Signers(nil)
  147. if err != nil {
  148. t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err)
  149. }
  150. if len(signers) != i+1 {
  151. t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i+1)
  152. }
  153. // Demote with the last one needed to pass the demotion
  154. if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[(i+1)/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  155. t.Fatalf("Iter #%d: failed valid demotion completion attempt: %v", i, err)
  156. }
  157. sim.Commit()
  158. signers, err = oracle.Signers(nil)
  159. if err != nil {
  160. t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err)
  161. }
  162. if len(signers) != i {
  163. t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i)
  164. }
  165. // Check that no votes are accepted from the already demoted users
  166. if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil {
  167. t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err)
  168. }
  169. sim.Commit()
  170. pend, err := oracle.AuthProposals(nil)
  171. if err != nil {
  172. t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err)
  173. }
  174. if len(pend) != 0 {
  175. t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend))
  176. }
  177. }
  178. }
  179. // Tests that new versions can be released, honouring both voting rights as well
  180. // as the minimum required vote count.
  181. func TestVersionRelease(t *testing.T) {
  182. // Prefund a few accounts to authorize with and create the oracle
  183. keys := make([]*ecdsa.PrivateKey, 5)
  184. for i := 0; i < len(keys); i++ {
  185. keys[i], _ = crypto.GenerateKey()
  186. }
  187. key, oracle, sim := setupReleaseTest(t, keys...)
  188. // Track the "current release"
  189. var (
  190. verMajor = uint32(0)
  191. verMinor = uint32(0)
  192. verPatch = uint32(0)
  193. verCommit = [20]byte{}
  194. )
  195. // Gradually push releases, always requiring more signers than previously
  196. keys = append([]*ecdsa.PrivateKey{key}, keys...)
  197. for i := 1; i < len(keys); i++ {
  198. // Check that no votes are accepted from the not yet authed user
  199. if _, err := oracle.Release(bind.NewKeyedTransactor(keys[i]), 0, 0, 0, [20]byte{0}); err != nil {
  200. t.Fatalf("Iter #%d: failed invalid release attempt: %v", i, err)
  201. }
  202. sim.Commit()
  203. prop, err := oracle.ProposedVersion(nil)
  204. if err != nil {
  205. t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err)
  206. }
  207. if len(prop.Pass) != 0 {
  208. t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want 0", i, len(prop.Pass))
  209. }
  210. // Authorize the user to make releases
  211. for j := 0; j <= i/2; j++ {
  212. if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  213. t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
  214. }
  215. }
  216. sim.Commit()
  217. // Propose release with half voters and check that the release does not yet go through
  218. for j := 0; j < (i+1)/2; j++ {
  219. if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil {
  220. t.Fatalf("Iter #%d: failed valid release attempt: %v", i, err)
  221. }
  222. }
  223. sim.Commit()
  224. ver, err := oracle.CurrentVersion(nil)
  225. if err != nil {
  226. t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err)
  227. }
  228. if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit {
  229. t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit)
  230. }
  231. // Pass the release and check that it became the next version
  232. verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}
  233. if _, err = oracle.Release(bind.NewKeyedTransactor(keys[(i+1)/2]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil {
  234. t.Fatalf("Iter #%d: failed valid release completion attempt: %v", i, err)
  235. }
  236. sim.Commit()
  237. ver, err = oracle.CurrentVersion(nil)
  238. if err != nil {
  239. t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err)
  240. }
  241. if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit {
  242. t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit)
  243. }
  244. }
  245. }
  246. // Tests that proposed versions can be nuked out of existence.
  247. func TestVersionNuking(t *testing.T) {
  248. // Prefund a few accounts to authorize with and create the oracle
  249. keys := make([]*ecdsa.PrivateKey, 9)
  250. for i := 0; i < len(keys); i++ {
  251. keys[i], _ = crypto.GenerateKey()
  252. }
  253. key, oracle, sim := setupReleaseTest(t, keys...)
  254. // Authorize all the keys as valid signers
  255. keys = append([]*ecdsa.PrivateKey{key}, keys...)
  256. for i := 1; i < len(keys); i++ {
  257. for j := 0; j <= i/2; j++ {
  258. if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  259. t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
  260. }
  261. }
  262. sim.Commit()
  263. }
  264. // Propose releases with more and more keys, always retaining enough users to nuke the proposals
  265. for i := 1; i < (len(keys)+1)/2; i++ {
  266. // Propose release with an initial set of signers
  267. for j := 0; j < i; j++ {
  268. if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil {
  269. t.Fatalf("Iter #%d: failed valid proposal attempt: %v", i, err)
  270. }
  271. }
  272. sim.Commit()
  273. prop, err := oracle.ProposedVersion(nil)
  274. if err != nil {
  275. t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err)
  276. }
  277. if len(prop.Pass) != i {
  278. t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want %d", i, len(prop.Pass), i)
  279. }
  280. // Nuke the release with half+1 voters
  281. for j := i; j <= i+(len(keys)+1)/2; j++ {
  282. if _, err := oracle.Nuke(bind.NewKeyedTransactor(keys[j])); err != nil {
  283. t.Fatalf("Iter #%d: failed valid nuke attempt: %v", i, err)
  284. }
  285. }
  286. sim.Commit()
  287. prop, err = oracle.ProposedVersion(nil)
  288. if err != nil {
  289. t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err)
  290. }
  291. if len(prop.Pass) != 0 || len(prop.Fail) != 0 {
  292. t.Fatalf("Iter #%d: proposal vote count mismatch: have %d/%d pass/fail, want 0/0", i, len(prop.Pass), len(prop.Fail))
  293. }
  294. }
  295. }
  296. // Tests that demoting a signer will auto-nuke the currently pending release.
  297. func TestVersionAutoNuke(t *testing.T) {
  298. // Prefund a few accounts to authorize with and create the oracle
  299. keys := make([]*ecdsa.PrivateKey, 5)
  300. for i := 0; i < len(keys); i++ {
  301. keys[i], _ = crypto.GenerateKey()
  302. }
  303. key, oracle, sim := setupReleaseTest(t, keys...)
  304. // Authorize all the keys as valid signers
  305. keys = append([]*ecdsa.PrivateKey{key}, keys...)
  306. for i := 1; i < len(keys); i++ {
  307. for j := 0; j <= i/2; j++ {
  308. if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
  309. t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
  310. }
  311. }
  312. sim.Commit()
  313. }
  314. // Make a release proposal and check it's existence
  315. if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{4}); err != nil {
  316. t.Fatalf("Failed valid proposal attempt: %v", err)
  317. }
  318. sim.Commit()
  319. prop, err := oracle.ProposedVersion(nil)
  320. if err != nil {
  321. t.Fatalf("Failed to retrieve active proposal: %v", err)
  322. }
  323. if len(prop.Pass) != 1 {
  324. t.Fatalf("Proposal vote count mismatch: have %d, want 1", len(prop.Pass))
  325. }
  326. // Demote a signer and check release proposal deletion
  327. for i := 0; i <= len(keys)/2; i++ {
  328. if _, err := oracle.Demote(bind.NewKeyedTransactor(keys[i]), crypto.PubkeyToAddress(keys[len(keys)-1].PublicKey)); err != nil {
  329. t.Fatalf("Iter #%d: failed valid demotion attempt: %v", i, err)
  330. }
  331. }
  332. sim.Commit()
  333. prop, err = oracle.ProposedVersion(nil)
  334. if err != nil {
  335. t.Fatalf("Failed to retrieve active proposal: %v", err)
  336. }
  337. if len(prop.Pass) != 0 {
  338. t.Fatalf("Proposal vote count mismatch: have %d, want 0", len(prop.Pass))
  339. }
  340. }