// Tests that subsequent signers can be promoted, each requiring half plus one // votes for it to pass through. func TestSignerPromotion(t *testing.T) { // Prefund a few accounts to authorize with and create the oracle keys := make([]*ecdsa.PrivateKey, 5) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() } key, oracle, sim := setupReleaseTest(t, keys...) // Gradually promote the keys, until all are authorized keys = append([]*ecdsa.PrivateKey{key}, keys...) for i := 1; i < len(keys); i++ { // Check that no votes are accepted from the not yet authed user if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil { t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err) } sim.Commit() pend, err := oracle.AuthProposals(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err) } if len(pend) != 0 { t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend)) } // Promote with half - 1 voters and check that the user's not yet authorized for j := 0; j < i/2; j++ { if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) } } sim.Commit() signers, err := oracle.Signers(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err) } if len(signers) != i { t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i) } // Promote with the last one needed to pass the promotion if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { t.Fatalf("Iter #%d: failed valid promotion completion attempt: %v", i, err) } sim.Commit() signers, err = oracle.Signers(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err) } if len(signers) != i+1 { t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i+1) } } }
// Tests that proposed versions can be nuked out of existence. func TestVersionNuking(t *testing.T) { // Prefund a few accounts to authorize with and create the oracle keys := make([]*ecdsa.PrivateKey, 9) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() } key, oracle, sim := setupReleaseTest(t, keys...) // Authorize all the keys as valid signers keys = append([]*ecdsa.PrivateKey{key}, keys...) for i := 1; i < len(keys); i++ { for j := 0; j <= i/2; j++ { if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) } } sim.Commit() } // Propose releases with more and more keys, always retaining enough users to nuke the proposals for i := 1; i < (len(keys)+1)/2; i++ { // Propose release with an initial set of signers for j := 0; j < i; j++ { if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { t.Fatalf("Iter #%d: failed valid proposal attempt: %v", i, err) } } sim.Commit() prop, err := oracle.ProposedVersion(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) } if len(prop.Pass) != i { t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want %d", i, len(prop.Pass), i) } // Nuke the release with half+1 voters for j := i; j <= i+(len(keys)+1)/2; j++ { if _, err := oracle.Nuke(bind.NewKeyedTransactor(keys[j])); err != nil { t.Fatalf("Iter #%d: failed valid nuke attempt: %v", i, err) } } sim.Commit() prop, err = oracle.ProposedVersion(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) } if len(prop.Pass) != 0 || len(prop.Fail) != 0 { t.Fatalf("Iter #%d: proposal vote count mismatch: have %d/%d pass/fail, want 0/0", i, len(prop.Pass), len(prop.Fail)) } } }
// Tests that demoting a signer will auto-nuke the currently pending release. func TestVersionAutoNuke(t *testing.T) { // Prefund a few accounts to authorize with and create the oracle keys := make([]*ecdsa.PrivateKey, 5) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() } key, oracle, sim := setupReleaseTest(t, keys...) // Authorize all the keys as valid signers keys = append([]*ecdsa.PrivateKey{key}, keys...) for i := 1; i < len(keys); i++ { for j := 0; j <= i/2; j++ { if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) } } sim.Commit() } // Make a release proposal and check it's existence if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{4}); err != nil { t.Fatalf("Failed valid proposal attempt: %v", err) } sim.Commit() prop, err := oracle.ProposedVersion(nil) if err != nil { t.Fatalf("Failed to retrieve active proposal: %v", err) } if len(prop.Pass) != 1 { t.Fatalf("Proposal vote count mismatch: have %d, want 1", len(prop.Pass)) } // Demote a signer and check release proposal deletion for i := 0; i <= len(keys)/2; i++ { if _, err := oracle.Demote(bind.NewKeyedTransactor(keys[i]), crypto.PubkeyToAddress(keys[len(keys)-1].PublicKey)); err != nil { t.Fatalf("Iter #%d: failed valid demotion attempt: %v", i, err) } } sim.Commit() prop, err = oracle.ProposedVersion(nil) if err != nil { t.Fatalf("Failed to retrieve active proposal: %v", err) } if len(prop.Pass) != 0 { t.Fatalf("Proposal vote count mismatch: have %d, want 0", len(prop.Pass)) } }
// setupReleaseTest creates a blockchain simulator and deploys a version oracle // contract for testing. func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.PrivateKey, *ReleaseOracle, *backends.SimulatedBackend) { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}} for _, key := range prefund { accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)}) } sim := backends.NewSimulatedBackend(accounts...) // Deploy a version oracle contract, commit and return _, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From}) if err != nil { t.Fatalf("Failed to deploy version contract: %v", err) } sim.Commit() return key, oracle, sim }
func TestDeploymentAndIntegration(t *testing.T) { // generate new key key := crypto.NewKey(rand.Reader) // init new simulated backend sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: key.Address, Balance: big.NewInt(10000000000)}) // created authenticator auth := bind.NewKeyedTransactor(key) // deploy the contract _, _, api, err := DeployEtherAPIs(auth, sim) // use a session based approach so that we do not need // to repass these settings all the time. session := &EtherAPIsSession{ Contract: api, CallOpts: bind.CallOpts{ Pending: true, }, TransactOpts: bind.TransactOpts{ From: auth.From, Signer: auth.Signer, GasLimit: big.NewInt(400000), }, } // add a new service _, err = session.AddService("etherapis", "https://etherapis.io", big.NewInt(0), big.NewInt(10), big.NewInt(432000)) if err != nil { t.Error(err) } // retrieve the service service, err := session.GetService(big.NewInt(0)) if err != nil { t.Fatal(err) } if service.Enabled { t.Error("expected service to be disabled") } // enable the service _, err = session.EnableService(big.NewInt(0)) if err != nil { t.Fatal(err) } service, err = session.GetService(big.NewInt(0)) if err != nil { t.Fatal(err) } if !service.Enabled { t.Error("expected service to be enabled") } // flag deletion _, err = session.DeleteService(big.NewInt(0)) if err != nil { t.Fatal(err) } service, err = session.GetService(big.NewInt(0)) if err != nil { t.Fatal(err) } if !service.Deleted { t.Error("expected service to be deleted") } }
// Tests that new versions can be released, honouring both voting rights as well // as the minimum required vote count. func TestVersionRelease(t *testing.T) { // Prefund a few accounts to authorize with and create the oracle keys := make([]*ecdsa.PrivateKey, 5) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() } key, oracle, sim := setupReleaseTest(t, keys...) // Track the "current release" var ( verMajor = uint32(0) verMinor = uint32(0) verPatch = uint32(0) verCommit = [20]byte{} ) // Gradually push releases, always requiring more signers than previously keys = append([]*ecdsa.PrivateKey{key}, keys...) for i := 1; i < len(keys); i++ { // Check that no votes are accepted from the not yet authed user if _, err := oracle.Release(bind.NewKeyedTransactor(keys[i]), 0, 0, 0, [20]byte{0}); err != nil { t.Fatalf("Iter #%d: failed invalid release attempt: %v", i, err) } sim.Commit() prop, err := oracle.ProposedVersion(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) } if len(prop.Pass) != 0 { t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want 0", i, len(prop.Pass)) } // Authorize the user to make releases for j := 0; j <= i/2; j++ { if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) } } sim.Commit() // Propose release with half voters and check that the release does not yet go through for j := 0; j < (i+1)/2; j++ { if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { t.Fatalf("Iter #%d: failed valid release attempt: %v", i, err) } } sim.Commit() ver, err := oracle.CurrentVersion(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err) } if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit { 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) } // Pass the release and check that it became the next version verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)} 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 { t.Fatalf("Iter #%d: failed valid release completion attempt: %v", i, err) } sim.Commit() ver, err = oracle.CurrentVersion(nil) if err != nil { t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err) } if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit { 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) } } }