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  }