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