github.com/koko1123/flow-go-1@v0.29.6/model/flow/address_test.go (about) 1 package flow 2 3 import ( 4 "encoding/json" 5 "math/bits" 6 "math/rand" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 type addressWrapper struct { 15 Address Address 16 } 17 18 func TestHexToAddress(t *testing.T) { 19 20 type testCase struct { 21 literal string 22 value []byte 23 } 24 25 for _, test := range []testCase{ 26 {"123", []byte{0x1, 0x23}}, 27 {"1", []byte{0x1}}, 28 // leading zero 29 {"01", []byte{0x1}}, 30 {"001", []byte{0x1}}, 31 {"0001", []byte{0x1}}, 32 } { 33 34 expected := BytesToAddress(test.value) 35 36 assert.Equal(t, expected, HexToAddress(test.literal)) 37 assert.Equal(t, expected, HexToAddress("0x"+test.literal)) 38 } 39 } 40 41 func TestAddressJSON(t *testing.T) { 42 addr := Mainnet.Chain().ServiceAddress() 43 data, err := json.Marshal(addressWrapper{Address: addr}) 44 require.Nil(t, err) 45 46 t.Log(string(data)) 47 48 var out addressWrapper 49 err = json.Unmarshal(data, &out) 50 require.Nil(t, err) 51 assert.Equal(t, addr, out.Address) 52 } 53 54 func TestShort(t *testing.T) { 55 type testcase struct { 56 addr Address 57 expected string 58 } 59 60 cases := []testcase{ 61 { 62 addr: Mainnet.Chain().ServiceAddress(), 63 expected: Mainnet.Chain().ServiceAddress().Hex(), 64 }, 65 { 66 addr: HexToAddress("0000000002"), 67 expected: "02", 68 }, 69 { 70 addr: HexToAddress("1f10"), 71 expected: "1f10", 72 }, 73 { 74 addr: HexToAddress("0f10"), 75 expected: "0f10", 76 }, 77 } 78 79 for _, c := range cases { 80 assert.Equal(t, c.addr.Short(), c.expected) 81 } 82 } 83 84 func TestAccountAddress(t *testing.T) { 85 t.Run("address constants", testAddressConstants) 86 t.Run("address generation", testAddressGeneration) 87 t.Run("chain address intersections", testAddressesIntersection) 88 t.Run("index from address", testIndexFromAddress) 89 } 90 91 func testAddressConstants(t *testing.T) { 92 // check n and k fit in 8 and 6 bytes 93 assert.LessOrEqual(t, linearCodeN, 8*8) 94 assert.LessOrEqual(t, linearCodeK, 6*8) 95 96 // Test addresses for all type of networks 97 chainIDs := []ChainID{ 98 Mainnet, 99 Testnet, 100 Emulator, 101 Sandboxnet, 102 } 103 104 for _, chainID := range chainIDs { 105 106 chain := chainID.Chain() 107 if chainID != Emulator { 108 // check the Zero and Root constants 109 expected := uint64ToAddress(uint64(chainID.getChainCodeWord())) 110 111 assert.Equal(t, chain.zeroAddress(), expected) 112 expected = uint64ToAddress(generatorMatrixRows[0] ^ uint64(chainID.getChainCodeWord())) 113 assert.Equal(t, chain.ServiceAddress(), expected) 114 } 115 116 // check the transition from account zero to root 117 state := chain.NewAddressGenerator() 118 address, err := state.NextAddress() 119 require.NoError(t, err) 120 assert.Equal(t, address, chain.ServiceAddress()) 121 122 // check high state values: generation should fail for high value states 123 state = chain.newAddressGeneratorAtIndex(maxIndex - 1) 124 _, err = state.NextAddress() 125 assert.NoError(t, err) 126 _, err = state.NextAddress() 127 assert.Error(t, err) 128 129 // check zeroAddress() is an invalid address 130 z := chain.zeroAddress() 131 check := chain.IsValid(z) 132 assert.False(t, check, "should be invalid") 133 } 134 } 135 136 const invalidCodeWord = uint64(0xab2ae42382900010) 137 138 func testAddressGeneration(t *testing.T) { 139 // seed random generator 140 rand.Seed(time.Now().UnixNano()) 141 142 // loops in each test 143 const loop = 50 144 145 // Test addresses for all type of networks 146 chainIDs := []ChainID{ 147 Mainnet, 148 Testnet, 149 Emulator, 150 Sandboxnet, 151 } 152 153 for _, chainID := range chainIDs { 154 155 chain := chainID.Chain() 156 157 // sanity check of NextAddress function consistency 158 state := chain.NewAddressGenerator() 159 expectedIndex := uint64(0) 160 for i := 0; i < loop; i++ { 161 expectedIndex++ 162 address, err := state.NextAddress() 163 require.NoError(t, err) 164 expectedAddress, err := chain.AddressAtIndex(expectedIndex) 165 require.NoError(t, err) 166 assert.Equal(t, address, expectedAddress) 167 require.Equal(t, state.CurrentAddress(), expectedAddress) 168 } 169 170 // sanity check of addresses weights in Flow. 171 // All addresses hamming weights must be less than d. 172 // this is only a sanity check of the implementation and not an exhaustive proof 173 if chainID == Mainnet { 174 r := uint64(rand.Intn(maxIndex - loop)) 175 state = chain.newAddressGeneratorAtIndex(r) 176 for i := 0; i < loop; i++ { 177 address, err := state.NextAddress() 178 require.NoError(t, err) 179 weight := bits.OnesCount64(address.uint64()) 180 assert.LessOrEqual(t, linearCodeD, weight) 181 } 182 } 183 184 if chainID == Mainnet { 185 186 // sanity check of address distances. 187 // All distances between any two addresses must be less than d. 188 // this is only a sanity check of the implementation and not an exhaustive proof 189 r := uint64(rand.Intn(maxIndex - loop - 1)) 190 state = chain.newAddressGeneratorAtIndex(r) 191 refAddress, err := state.NextAddress() 192 require.NoError(t, err) 193 for i := 0; i < loop; i++ { 194 address, err := state.NextAddress() 195 require.NoError(t, err) 196 distance := bits.OnesCount64(address.uint64() ^ refAddress.uint64()) 197 assert.LessOrEqual(t, linearCodeD, distance) 198 } 199 200 } 201 202 // sanity check of valid account addresses. 203 // All valid addresses must pass IsValid. 204 r := uint64(rand.Intn(maxIndex - loop)) 205 state = chain.newAddressGeneratorAtIndex(r) 206 for i := 0; i < loop; i++ { 207 address, err := state.NextAddress() 208 require.NoError(t, err) 209 check := chain.IsValid(address) 210 assert.True(t, check, "account address format should be valid") 211 } 212 213 // sanity check of invalid account addresses. 214 // All invalid addresses must fail IsValid. 215 invalidAddress := uint64ToAddress(invalidCodeWord) 216 check := chain.IsValid(invalidAddress) 217 assert.False(t, check, "account address format should be invalid") 218 r = uint64(rand.Intn(maxIndex - loop)) 219 220 state = chain.newAddressGeneratorAtIndex(r) 221 for i := 0; i < loop; i++ { 222 address, err := state.NextAddress() 223 require.NoError(t, err) 224 invalidAddress = uint64ToAddress(address.uint64() ^ invalidCodeWord) 225 check := chain.IsValid(invalidAddress) 226 assert.False(t, check, "account address format should be invalid") 227 } 228 } 229 } 230 231 func testAddressesIntersection(t *testing.T) { 232 // seed random generator 233 rand.Seed(time.Now().UnixNano()) 234 235 // loops in each test 236 const loop = 25 237 238 // Test addresses for all type of networks 239 chainIDs := []ChainID{ 240 Mainnet, 241 Testnet, 242 Emulator, 243 Sandboxnet, 244 } 245 246 for _, chainID := range chainIDs { 247 248 chain := chainID.Chain() 249 250 // All valid test addresses must fail Flow Mainnet check 251 r := uint64(rand.Intn(maxIndex - loop)) 252 state := chain.newAddressGeneratorAtIndex(r) 253 for k := 0; k < loop; k++ { 254 address, err := state.NextAddress() 255 require.NoError(t, err) 256 for _, otherChain := range chainIDs { 257 if chainID != otherChain { 258 check := otherChain.Chain().IsValid(address) 259 assert.False(t, check, "test account address format should be invalid in Flow") 260 } else { 261 sameChainCheck := chain.IsValid(address) 262 require.True(t, sameChainCheck) 263 } 264 } 265 } 266 267 // sanity check: mainnet addresses must fail the test check 268 r = uint64(rand.Intn(maxIndex - loop)) 269 for k := 0; k < loop; k++ { 270 for _, otherChain := range chainIDs { 271 if chainID != otherChain { 272 invalidAddress, err := otherChain.Chain().newAddressGeneratorAtIndex(r).NextAddress() 273 require.NoError(t, err) 274 check := chain.IsValid(invalidAddress) 275 assert.False(t, check, "account address format should be invalid") 276 } 277 } 278 } 279 280 // sanity check of invalid account addresses in all networks 281 require.NotEqual(t, invalidCodeWord, uint64(0)) 282 invalidAddress := uint64ToAddress(invalidCodeWord) 283 check := chain.IsValid(invalidAddress) 284 assert.False(t, check, "account address format should be invalid") 285 r = uint64(rand.Intn(maxIndex - loop)) 286 287 state = chain.newAddressGeneratorAtIndex(r) 288 for k := 0; k < loop; k++ { 289 address, err := state.NextAddress() 290 require.NoError(t, err) 291 invalidAddress = uint64ToAddress(address.uint64() ^ invalidCodeWord) 292 293 // must fail test network check 294 check = chain.IsValid(invalidAddress) 295 assert.False(t, check, "account address format should be invalid") 296 } 297 } 298 } 299 300 func testIndexFromAddress(t *testing.T) { 301 // seed random generator 302 rand.Seed(time.Now().UnixNano()) 303 304 // loops in each test 305 const loop = 50 306 307 // Test addresses for all type of networks 308 chains := []Chain{ 309 mainnet, 310 testnet, 311 emulator, 312 sandboxnet, 313 } 314 315 for _, chain := range chains { 316 for i := 0; i < loop; i++ { 317 // check the correctness of IndexFromAddress 318 319 // random valid index 320 r := uint64(rand.Intn(maxIndex)) + 1 321 // generate the address 322 address := chain.newAddressGeneratorAtIndex(r).CurrentAddress() 323 // extract the index and compare 324 index, err := chain.IndexFromAddress(address) 325 assert.NoError(t, err) // address should be valid 326 assert.Equal(t, r, index, "wrong extracted address %d, should be %d", index, r) 327 328 // check for invalid addresses 329 330 // alter one bit in the address to obtain an invalid address 331 address[0] ^= 1 332 _, err = chain.IndexFromAddress(address) 333 assert.Error(t, err) 334 } 335 // check the zero address error 336 _, err := chain.IndexFromAddress(chain.zeroAddress()) 337 assert.Error(t, err) 338 } 339 } 340 341 func TestUint48(t *testing.T) { 342 // seed random generator 343 rand.Seed(time.Now().UnixNano()) 344 345 const loop = 50 346 // test consistensy of putUint48 and uint48 347 for i := 0; i < loop; i++ { 348 r := uint64(rand.Intn(1 << linearCodeK)) 349 b := make([]byte, addressIndexLength) 350 putUint48(b, r) 351 res := uint48(b) 352 assert.Equal(t, r, res) 353 } 354 }