github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/integration/governance/bonding_test.go (about)

     1  // +build integration
     2  
     3  package governance
     4  
     5  import (
     6  	"bytes"
     7  	"testing"
     8  
     9  	"github.com/hyperledger/burrow/bcm"
    10  	"github.com/hyperledger/burrow/config"
    11  	"github.com/hyperledger/burrow/core"
    12  	"github.com/hyperledger/burrow/integration"
    13  	"github.com/hyperledger/burrow/integration/rpctest"
    14  	"github.com/hyperledger/burrow/permission"
    15  	"github.com/hyperledger/burrow/txs/payload"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestBonding(t *testing.T) {
    20  	genesisAccounts := integration.MakePrivateAccounts("accounts", 6)
    21  	genesisConfigs := make([]*config.BurrowConfig, len(genesisAccounts))
    22  	genesisKernels := make([]*core.Kernel, len(genesisAccounts))
    23  	genesisDoc := integration.TestGenesisDoc(genesisAccounts, 0, 1, 2, 3)
    24  	genesisDoc.GlobalPermissions = permission.NewAccountPermissions(permission.Input)
    25  	genesisDoc.Accounts[4].Permissions = permission.ZeroAccountPermissions.Clone()
    26  	var err error
    27  
    28  	// we need at least one validator to start
    29  	for i, acc := range genesisAccounts {
    30  		genesisConfigs[i], err = newConfig(genesisDoc, acc, genesisAccounts...)
    31  		require.NoError(t, err)
    32  
    33  		genesisKernels[i], err = newKernelAndBoot(genesisConfigs[i], acc, genesisAccounts...)
    34  		require.NoError(t, err)
    35  		defer integration.Shutdown(genesisKernels[i])
    36  	}
    37  
    38  	connectAllKernels(genesisKernels)
    39  
    40  	t.Run("NoPermission", func(t *testing.T) {
    41  		localAddress := genesisKernels[4].GRPCListenAddress().String()
    42  		inputAccount := genesisAccounts[4].GetAddress()
    43  		tcli := rpctest.NewTransactClient(t, localAddress)
    44  		bondTx := payload.NewBondTx(inputAccount, uint64(1<<2))
    45  		_, err = payloadSync(tcli, bondTx)
    46  		require.Error(t, err)
    47  	})
    48  
    49  	t.Run("BondFromNonVal", func(t *testing.T) {
    50  		// lets do the bond tx from a non-validator node
    51  		valAccount := genesisAccounts[5]
    52  		valKernel := genesisKernels[5]
    53  
    54  		localAddress := valKernel.GRPCListenAddress().String()
    55  		inputAccount := valAccount.GetAddress()
    56  		tcli := rpctest.NewTransactClient(t, localAddress)
    57  		qcli := rpctest.NewQueryClient(t, localAddress)
    58  
    59  		accBefore := getAccount(t, qcli, inputAccount)
    60  		var power uint64 = 1 << 16
    61  
    62  		bondTx := payload.NewBondTx(inputAccount, power)
    63  		_, err = payloadSync(tcli, bondTx)
    64  		require.NoError(t, err)
    65  		accAfter := getAccount(t, qcli, inputAccount)
    66  		// ensure power is subtracted from original account balance
    67  		require.Equal(t, accBefore.GetBalance()-power, accAfter.GetBalance())
    68  
    69  		// make sure our new validator exists in the set
    70  		vsOut := getValidators(t, qcli)
    71  		require.Contains(t, vsOut, valAccount.GetAddress())
    72  		require.Equal(t, vsOut[valAccount.GetAddress()].GetPower(), power)
    73  
    74  		// wait for new validator to see themself in set
    75  		waitFor(3, valKernel.Blockchain)
    76  		vsOut = getValidators(t, qcli)
    77  		require.Contains(t, vsOut, valAccount.GetAddress())
    78  		require.Equal(t, vsOut[valAccount.GetAddress()].GetPower(), power)
    79  
    80  		// wait for validator to propose a block
    81  		waitFor(10, valKernel.Blockchain)
    82  		checkProposed(t, genesisKernels[0], valAccount.GetPublicKey().GetAddress().Bytes())
    83  
    84  		unbondTx := payload.NewUnbondTx(inputAccount, power)
    85  		_, err = payloadSync(tcli, unbondTx)
    86  		require.NoError(t, err)
    87  
    88  		waitFor(2, valKernel.Blockchain)
    89  		vsOut = getValidators(t, qcli)
    90  		require.NotContains(t, vsOut, valAccount.GetAddress())
    91  		accAfter = getAccount(t, qcli, inputAccount)
    92  		require.Equal(t, accBefore.GetBalance(), accAfter.GetBalance())
    93  	})
    94  
    95  	// TODO:
    96  	// - add / remove too quickly
    97  	// - only validator can unbond themselves
    98  }
    99  
   100  func checkProposed(t *testing.T, kern *core.Kernel, exp []byte) {
   101  	height := kern.Node.BlockStore().Height()
   102  	t.Logf("current height is %d", height)
   103  	for i := int64(1); i < height; i++ {
   104  		bm := kern.Node.BlockStore().LoadBlockMeta(i)
   105  		if bytes.Equal(bm.Header.ProposerAddress, exp) {
   106  			t.Logf("%X proposed block %d", exp, i)
   107  			return
   108  		}
   109  	}
   110  	require.Fail(t, "bonded validator did not propose any blocks")
   111  }
   112  
   113  func waitFor(height uint64, blockchain *bcm.Blockchain) {
   114  	until := blockchain.LastBlockHeight() + height
   115  	for h := uint64(0); h < until; h = blockchain.LastBlockHeight() {
   116  		continue
   117  	}
   118  }