github.com/koko1123/flow-go-1@v0.29.6/module/epochs/machine_account_test.go (about) 1 package epochs 2 3 import ( 4 "testing" 5 6 "github.com/onflow/cadence" 7 "github.com/rs/zerolog" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 sdkcrypto "github.com/onflow/flow-go-sdk/crypto" 12 "github.com/koko1123/flow-go-1/model/flow" 13 "github.com/koko1123/flow-go-1/utils/unittest" 14 "github.com/onflow/flow-go/crypto" 15 ) 16 17 // TestMachineAccountChecking tests that CheckMachineAccount captures critical 18 // misconfigurations of the machine account correctly. 19 // 20 // In these tests, local refers to the machine account from the local file, 21 // remote refers to the machine account from on-chain. 22 func TestMachineAccountChecking(t *testing.T) { 23 conf := DefaultMachineAccountValidatorConfig() 24 25 t.Run("consistent machine account", func(t *testing.T) { 26 local, remote := unittest.MachineAccountFixture(t) 27 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 28 require.NoError(t, err) 29 }) 30 t.Run("inconsistent address", func(t *testing.T) { 31 local, remote := unittest.MachineAccountFixture(t) 32 remote.Address = unittest.RandomSDKAddressFixture() 33 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 34 require.Error(t, err) 35 }) 36 t.Run("inconsistent key", func(t *testing.T) { 37 local, remote := unittest.MachineAccountFixture(t) 38 randomKey := unittest.PrivateKeyFixture(crypto.ECDSAP256, unittest.DefaultSeedFixtureLength) 39 remote.Keys[0].PublicKey = randomKey.PublicKey() 40 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 41 require.Error(t, err) 42 }) 43 t.Run("inconsistent hash algo", func(t *testing.T) { 44 local, remote := unittest.MachineAccountFixture(t) 45 remote.Keys[0].HashAlgo = sdkcrypto.SHA2_384 46 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 47 require.Error(t, err) 48 }) 49 t.Run("inconsistent sig algo", func(t *testing.T) { 50 local, remote := unittest.MachineAccountFixture(t) 51 remote.Keys[0].SigAlgo = sdkcrypto.ECDSA_secp256k1 52 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 53 require.Error(t, err) 54 }) 55 t.Run("account without keys", func(t *testing.T) { 56 local, remote := unittest.MachineAccountFixture(t) 57 remote.Keys = nil 58 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 59 require.Error(t, err) 60 }) 61 t.Run("account with insufficient keys", func(t *testing.T) { 62 local, remote := unittest.MachineAccountFixture(t) 63 // increment key index so it doesn't match remote account 64 local.KeyIndex = local.KeyIndex + 1 65 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 66 require.Error(t, err) 67 }) 68 t.Run("invalid role", func(t *testing.T) { 69 local, remote := unittest.MachineAccountFixture(t) 70 for _, role := range flow.Roles() { 71 // skip valid roles 72 if role == flow.RoleCollection || role == flow.RoleConsensus { 73 continue 74 } 75 76 err := CheckMachineAccountInfo(zerolog.Nop(), conf, role, local, remote) 77 require.Error(t, err) 78 } 79 }) 80 81 t.Run("account with < hard minimum balance", func(t *testing.T) { 82 t.Run("collection", func(t *testing.T) { 83 local, remote := unittest.MachineAccountFixture(t) 84 remote.Balance = uint64(defaultHardMinBalanceLN) - 1 85 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleCollection, local, remote) 86 require.Error(t, err) 87 }) 88 t.Run("consensus", func(t *testing.T) { 89 local, remote := unittest.MachineAccountFixture(t) 90 remote.Balance = uint64(defaultHardMinBalanceSN) - 1 91 err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) 92 require.Error(t, err) 93 }) 94 }) 95 96 t.Run("disable balance checking", func(t *testing.T) { 97 minBalance, err := cadence.NewUFix64("0.001") 98 require.NoError(t, err) 99 100 balanceDisabledConfig := DefaultMachineAccountValidatorConfig() 101 WithoutBalanceChecks(&balanceDisabledConfig) 102 103 t.Run("collection", func(t *testing.T) { 104 local, remote := unittest.MachineAccountFixture(t) 105 remote.Balance = uint64(minBalance) 106 err := CheckMachineAccountInfo(zerolog.Nop(), balanceDisabledConfig, flow.RoleCollection, local, remote) 107 require.NoError(t, err) 108 }) 109 t.Run("consensus", func(t *testing.T) { 110 local, remote := unittest.MachineAccountFixture(t) 111 remote.Balance = uint64(minBalance) 112 err := CheckMachineAccountInfo(zerolog.Nop(), balanceDisabledConfig, flow.RoleConsensus, local, remote) 113 require.NoError(t, err) 114 }) 115 }) 116 117 // should log a warning when balance below soft minimum balance (but not 118 // below hard minimum balance) 119 t.Run("account with < soft minimum balance", func(t *testing.T) { 120 t.Run("collection", func(t *testing.T) { 121 local, remote := unittest.MachineAccountFixture(t) 122 remote.Balance = uint64(defaultSoftMinBalanceLN) - 1 123 log, hook := unittest.HookedLogger() 124 125 err := CheckMachineAccountInfo(log, conf, flow.RoleCollection, local, remote) 126 assert.NoError(t, err) 127 assert.Regexp(t, "machine account balance is below recommended balance", hook.Logs()) 128 }) 129 t.Run("consensus", func(t *testing.T) { 130 local, remote := unittest.MachineAccountFixture(t) 131 remote.Balance = uint64(defaultSoftMinBalanceSN) - 1 132 log, hook := unittest.HookedLogger() 133 134 err := CheckMachineAccountInfo(log, conf, flow.RoleConsensus, local, remote) 135 assert.NoError(t, err) 136 assert.Regexp(t, "machine account balance is below recommended balance", hook.Logs()) 137 }) 138 }) 139 140 // should log a warning when the local file deviates from defaults 141 t.Run("local file deviates from defaults", func(t *testing.T) { 142 t.Run("hash algo", func(t *testing.T) { 143 local, remote := unittest.MachineAccountFixture(t) 144 local.HashAlgorithm = sdkcrypto.SHA3_384 // non-standard hash algo 145 remote.Keys[0].HashAlgo = sdkcrypto.SHA3_384 // consistent between local/remote 146 log, hook := unittest.HookedLogger() 147 148 err := CheckMachineAccountInfo(log, conf, flow.RoleConsensus, local, remote) 149 assert.NoError(t, err) 150 assert.Regexp(t, "non-standard hash algo", hook.Logs()) 151 }) 152 t.Run("sig algo", func(t *testing.T) { 153 local, remote := unittest.MachineAccountFixture(t) 154 155 // non-standard sig algo 156 sk := unittest.PrivateKeyFixture(crypto.ECDSASecp256k1, unittest.DefaultSeedFixtureLength) 157 local.EncodedPrivateKey = sk.Encode() 158 local.SigningAlgorithm = sdkcrypto.ECDSA_secp256k1 159 // consistent between local/remote 160 remote.Keys[0].PublicKey = sk.PublicKey() 161 remote.Keys[0].SigAlgo = sdkcrypto.ECDSA_secp256k1 162 log, hook := unittest.HookedLogger() 163 164 err := CheckMachineAccountInfo(log, conf, flow.RoleConsensus, local, remote) 165 assert.NoError(t, err) 166 assert.Regexp(t, "non-standard signing algo", hook.Logs()) 167 }) 168 t.Run("key index", func(t *testing.T) { 169 local, remote := unittest.MachineAccountFixture(t) 170 local.KeyIndex = 1 // non-standard key index 171 remote.Keys = append(remote.Keys, remote.Keys[0]) // key with index exists on remote 172 remote.Keys[1].Index = 1 173 log, hook := unittest.HookedLogger() 174 175 err := CheckMachineAccountInfo(log, conf, flow.RoleConsensus, local, remote) 176 assert.NoError(t, err) 177 assert.Regexp(t, "non-standard key index", hook.Logs()) 178 }) 179 }) 180 } 181 182 // TestBackoff tests the backoff config behaves as expected. In particular, once 183 // we reach the cap duration, all future backoffs should be equal to the cap duration. 184 func TestMachineAccountValidatorBackoff_Overflow(t *testing.T) { 185 186 backoff := checkMachineAccountRetryBackoff() 187 188 // once the backoff reaches the maximum, it should remain in [(1-jitter)*max,(1+jitter*max)] 189 max := checkMachineAccountRetryMax + checkMachineAccountRetryMax*(checkMachineAccountRetryJitterPct+1)/100 190 min := checkMachineAccountRetryMax - checkMachineAccountRetryMax*(checkMachineAccountRetryJitterPct+1)/100 191 192 lastWait, stop := backoff.Next() 193 assert.False(t, stop) 194 for i := 0; i < 100; i++ { 195 wait, stop := backoff.Next() 196 assert.False(t, stop) 197 // the backoff value should either: 198 // * strictly increase, or 199 // * be within range of max duration + jitter 200 if wait < lastWait { 201 assert.Less(t, min, wait) 202 assert.Less(t, wait, max) 203 } 204 lastWait = wait 205 } 206 }