github.com/evdatsion/aphelion-dpos-bft@v0.32.1/p2p/pex/addrbook_test.go (about)

     1  package pex
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"math"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    15  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    16  	"github.com/evdatsion/aphelion-dpos-bft/p2p"
    17  )
    18  
    19  func TestAddrBookPickAddress(t *testing.T) {
    20  	fname := createTempFileName("addrbook_test")
    21  	defer deleteTempFile(fname)
    22  
    23  	// 0 addresses
    24  	book := NewAddrBook(fname, true)
    25  	book.SetLogger(log.TestingLogger())
    26  	assert.Zero(t, book.Size())
    27  
    28  	addr := book.PickAddress(50)
    29  	assert.Nil(t, addr, "expected no address")
    30  
    31  	randAddrs := randNetAddressPairs(t, 1)
    32  	addrSrc := randAddrs[0]
    33  	book.AddAddress(addrSrc.addr, addrSrc.src)
    34  
    35  	// pick an address when we only have new address
    36  	addr = book.PickAddress(0)
    37  	assert.NotNil(t, addr, "expected an address")
    38  	addr = book.PickAddress(50)
    39  	assert.NotNil(t, addr, "expected an address")
    40  	addr = book.PickAddress(100)
    41  	assert.NotNil(t, addr, "expected an address")
    42  
    43  	// pick an address when we only have old address
    44  	book.MarkGood(addrSrc.addr.ID)
    45  	addr = book.PickAddress(0)
    46  	assert.NotNil(t, addr, "expected an address")
    47  	addr = book.PickAddress(50)
    48  	assert.NotNil(t, addr, "expected an address")
    49  
    50  	// in this case, nNew==0 but we biased 100% to new, so we return nil
    51  	addr = book.PickAddress(100)
    52  	assert.Nil(t, addr, "did not expected an address")
    53  }
    54  
    55  func TestAddrBookSaveLoad(t *testing.T) {
    56  	fname := createTempFileName("addrbook_test")
    57  	defer deleteTempFile(fname)
    58  
    59  	// 0 addresses
    60  	book := NewAddrBook(fname, true)
    61  	book.SetLogger(log.TestingLogger())
    62  	book.saveToFile(fname)
    63  
    64  	book = NewAddrBook(fname, true)
    65  	book.SetLogger(log.TestingLogger())
    66  	book.loadFromFile(fname)
    67  
    68  	assert.Zero(t, book.Size())
    69  
    70  	// 100 addresses
    71  	randAddrs := randNetAddressPairs(t, 100)
    72  
    73  	for _, addrSrc := range randAddrs {
    74  		book.AddAddress(addrSrc.addr, addrSrc.src)
    75  	}
    76  
    77  	assert.Equal(t, 100, book.Size())
    78  	book.saveToFile(fname)
    79  
    80  	book = NewAddrBook(fname, true)
    81  	book.SetLogger(log.TestingLogger())
    82  	book.loadFromFile(fname)
    83  
    84  	assert.Equal(t, 100, book.Size())
    85  }
    86  
    87  func TestAddrBookLookup(t *testing.T) {
    88  	fname := createTempFileName("addrbook_test")
    89  	defer deleteTempFile(fname)
    90  
    91  	randAddrs := randNetAddressPairs(t, 100)
    92  
    93  	book := NewAddrBook(fname, true)
    94  	book.SetLogger(log.TestingLogger())
    95  	for _, addrSrc := range randAddrs {
    96  		addr := addrSrc.addr
    97  		src := addrSrc.src
    98  		book.AddAddress(addr, src)
    99  
   100  		ka := book.addrLookup[addr.ID]
   101  		assert.NotNil(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr)
   102  
   103  		if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) {
   104  			t.Fatalf("KnownAddress doesn't match addr & src")
   105  		}
   106  	}
   107  }
   108  
   109  func TestAddrBookPromoteToOld(t *testing.T) {
   110  	fname := createTempFileName("addrbook_test")
   111  	defer deleteTempFile(fname)
   112  
   113  	randAddrs := randNetAddressPairs(t, 100)
   114  
   115  	book := NewAddrBook(fname, true)
   116  	book.SetLogger(log.TestingLogger())
   117  	for _, addrSrc := range randAddrs {
   118  		book.AddAddress(addrSrc.addr, addrSrc.src)
   119  	}
   120  
   121  	// Attempt all addresses.
   122  	for _, addrSrc := range randAddrs {
   123  		book.MarkAttempt(addrSrc.addr)
   124  	}
   125  
   126  	// Promote half of them
   127  	for i, addrSrc := range randAddrs {
   128  		if i%2 == 0 {
   129  			book.MarkGood(addrSrc.addr.ID)
   130  		}
   131  	}
   132  
   133  	// TODO: do more testing :)
   134  
   135  	selection := book.GetSelection()
   136  	t.Logf("selection: %v", selection)
   137  
   138  	if len(selection) > book.Size() {
   139  		t.Errorf("selection could not be bigger than the book")
   140  	}
   141  
   142  	selection = book.GetSelectionWithBias(30)
   143  	t.Logf("selection: %v", selection)
   144  
   145  	if len(selection) > book.Size() {
   146  		t.Errorf("selection with bias could not be bigger than the book")
   147  	}
   148  
   149  	assert.Equal(t, book.Size(), 100, "expecting book size to be 100")
   150  }
   151  
   152  func TestAddrBookHandlesDuplicates(t *testing.T) {
   153  	fname := createTempFileName("addrbook_test")
   154  	defer deleteTempFile(fname)
   155  
   156  	book := NewAddrBook(fname, true)
   157  	book.SetLogger(log.TestingLogger())
   158  
   159  	randAddrs := randNetAddressPairs(t, 100)
   160  
   161  	differentSrc := randIPv4Address(t)
   162  	for _, addrSrc := range randAddrs {
   163  		book.AddAddress(addrSrc.addr, addrSrc.src)
   164  		book.AddAddress(addrSrc.addr, addrSrc.src)  // duplicate
   165  		book.AddAddress(addrSrc.addr, differentSrc) // different src
   166  	}
   167  
   168  	assert.Equal(t, 100, book.Size())
   169  }
   170  
   171  type netAddressPair struct {
   172  	addr *p2p.NetAddress
   173  	src  *p2p.NetAddress
   174  }
   175  
   176  func randNetAddressPairs(t *testing.T, n int) []netAddressPair {
   177  	randAddrs := make([]netAddressPair, n)
   178  	for i := 0; i < n; i++ {
   179  		randAddrs[i] = netAddressPair{addr: randIPv4Address(t), src: randIPv4Address(t)}
   180  	}
   181  	return randAddrs
   182  }
   183  
   184  func randIPv4Address(t *testing.T) *p2p.NetAddress {
   185  	for {
   186  		ip := fmt.Sprintf("%v.%v.%v.%v",
   187  			cmn.RandIntn(254)+1,
   188  			cmn.RandIntn(255),
   189  			cmn.RandIntn(255),
   190  			cmn.RandIntn(255),
   191  		)
   192  		port := cmn.RandIntn(65535-1) + 1
   193  		id := p2p.ID(hex.EncodeToString(cmn.RandBytes(p2p.IDByteLength)))
   194  		idAddr := p2p.IDAddressString(id, fmt.Sprintf("%v:%v", ip, port))
   195  		addr, err := p2p.NewNetAddressString(idAddr)
   196  		assert.Nil(t, err, "error generating rand network address")
   197  		if addr.Routable() {
   198  			return addr
   199  		}
   200  	}
   201  }
   202  
   203  func TestAddrBookRemoveAddress(t *testing.T) {
   204  	fname := createTempFileName("addrbook_test")
   205  	defer deleteTempFile(fname)
   206  
   207  	book := NewAddrBook(fname, true)
   208  	book.SetLogger(log.TestingLogger())
   209  
   210  	addr := randIPv4Address(t)
   211  	book.AddAddress(addr, addr)
   212  	assert.Equal(t, 1, book.Size())
   213  
   214  	book.RemoveAddress(addr)
   215  	assert.Equal(t, 0, book.Size())
   216  
   217  	nonExistingAddr := randIPv4Address(t)
   218  	book.RemoveAddress(nonExistingAddr)
   219  	assert.Equal(t, 0, book.Size())
   220  }
   221  
   222  func TestAddrBookGetSelectionWithOneMarkedGood(t *testing.T) {
   223  	// create a book with 10 addresses, 1 good/old and 9 new
   224  	book, fname := createAddrBookWithMOldAndNNewAddrs(t, 1, 9)
   225  	defer deleteTempFile(fname)
   226  
   227  	addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   228  	assert.NotNil(t, addrs)
   229  	assertMOldAndNNewAddrsInSelection(t, 1, 9, addrs, book)
   230  }
   231  
   232  func TestAddrBookGetSelectionWithOneNotMarkedGood(t *testing.T) {
   233  	// create a book with 10 addresses, 9 good/old and 1 new
   234  	book, fname := createAddrBookWithMOldAndNNewAddrs(t, 9, 1)
   235  	defer deleteTempFile(fname)
   236  
   237  	addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   238  	assert.NotNil(t, addrs)
   239  	assertMOldAndNNewAddrsInSelection(t, 9, 1, addrs, book)
   240  }
   241  
   242  func TestAddrBookGetSelectionReturnsNilWhenAddrBookIsEmpty(t *testing.T) {
   243  	book, fname := createAddrBookWithMOldAndNNewAddrs(t, 0, 0)
   244  	defer deleteTempFile(fname)
   245  
   246  	addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   247  	assert.Nil(t, addrs)
   248  }
   249  
   250  func TestAddrBookGetSelection(t *testing.T) {
   251  	fname := createTempFileName("addrbook_test")
   252  	defer deleteTempFile(fname)
   253  
   254  	book := NewAddrBook(fname, true)
   255  	book.SetLogger(log.TestingLogger())
   256  
   257  	// 1) empty book
   258  	assert.Empty(t, book.GetSelection())
   259  
   260  	// 2) add one address
   261  	addr := randIPv4Address(t)
   262  	book.AddAddress(addr, addr)
   263  
   264  	assert.Equal(t, 1, len(book.GetSelection()))
   265  	assert.Equal(t, addr, book.GetSelection()[0])
   266  
   267  	// 3) add a bunch of addresses
   268  	randAddrs := randNetAddressPairs(t, 100)
   269  	for _, addrSrc := range randAddrs {
   270  		book.AddAddress(addrSrc.addr, addrSrc.src)
   271  	}
   272  
   273  	// check there is no duplicates
   274  	addrs := make(map[string]*p2p.NetAddress)
   275  	selection := book.GetSelection()
   276  	for _, addr := range selection {
   277  		if dup, ok := addrs[addr.String()]; ok {
   278  			t.Fatalf("selection %v contains duplicates %v", selection, dup)
   279  		}
   280  		addrs[addr.String()] = addr
   281  	}
   282  
   283  	if len(selection) > book.Size() {
   284  		t.Errorf("selection %v could not be bigger than the book", selection)
   285  	}
   286  }
   287  
   288  func TestAddrBookGetSelectionWithBias(t *testing.T) {
   289  	const biasTowardsNewAddrs = 30
   290  
   291  	fname := createTempFileName("addrbook_test")
   292  	defer deleteTempFile(fname)
   293  
   294  	book := NewAddrBook(fname, true)
   295  	book.SetLogger(log.TestingLogger())
   296  
   297  	// 1) empty book
   298  	selection := book.GetSelectionWithBias(biasTowardsNewAddrs)
   299  	assert.Empty(t, selection)
   300  
   301  	// 2) add one address
   302  	addr := randIPv4Address(t)
   303  	book.AddAddress(addr, addr)
   304  
   305  	selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
   306  	assert.Equal(t, 1, len(selection))
   307  	assert.Equal(t, addr, selection[0])
   308  
   309  	// 3) add a bunch of addresses
   310  	randAddrs := randNetAddressPairs(t, 100)
   311  	for _, addrSrc := range randAddrs {
   312  		book.AddAddress(addrSrc.addr, addrSrc.src)
   313  	}
   314  
   315  	// check there is no duplicates
   316  	addrs := make(map[string]*p2p.NetAddress)
   317  	selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
   318  	for _, addr := range selection {
   319  		if dup, ok := addrs[addr.String()]; ok {
   320  			t.Fatalf("selection %v contains duplicates %v", selection, dup)
   321  		}
   322  		addrs[addr.String()] = addr
   323  	}
   324  
   325  	if len(selection) > book.Size() {
   326  		t.Fatalf("selection %v could not be bigger than the book", selection)
   327  	}
   328  
   329  	// 4) mark 80% of the addresses as good
   330  	randAddrsLen := len(randAddrs)
   331  	for i, addrSrc := range randAddrs {
   332  		if int((float64(i)/float64(randAddrsLen))*100) >= 20 {
   333  			book.MarkGood(addrSrc.addr.ID)
   334  		}
   335  	}
   336  
   337  	selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
   338  
   339  	// check that ~70% of addresses returned are good
   340  	good := 0
   341  	for _, addr := range selection {
   342  		if book.IsGood(addr) {
   343  			good++
   344  		}
   345  	}
   346  
   347  	got, expected := int((float64(good)/float64(len(selection)))*100), (100 - biasTowardsNewAddrs)
   348  
   349  	// compute some slack to protect against small differences due to rounding:
   350  	slack := int(math.Round(float64(100) / float64(len(selection))))
   351  	if got > expected+slack {
   352  		t.Fatalf("got more good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)", got, expected, good, len(selection))
   353  	}
   354  	if got < expected-slack {
   355  		t.Fatalf("got fewer good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)", got, expected, good, len(selection))
   356  	}
   357  }
   358  
   359  func TestAddrBookHasAddress(t *testing.T) {
   360  	fname := createTempFileName("addrbook_test")
   361  	defer deleteTempFile(fname)
   362  
   363  	book := NewAddrBook(fname, true)
   364  	book.SetLogger(log.TestingLogger())
   365  	addr := randIPv4Address(t)
   366  	book.AddAddress(addr, addr)
   367  
   368  	assert.True(t, book.HasAddress(addr))
   369  
   370  	book.RemoveAddress(addr)
   371  
   372  	assert.False(t, book.HasAddress(addr))
   373  }
   374  
   375  func testCreatePrivateAddrs(t *testing.T, numAddrs int) ([]*p2p.NetAddress, []string) {
   376  	addrs := make([]*p2p.NetAddress, numAddrs)
   377  	for i := 0; i < numAddrs; i++ {
   378  		addrs[i] = randIPv4Address(t)
   379  	}
   380  
   381  	private := make([]string, numAddrs)
   382  	for i, addr := range addrs {
   383  		private[i] = string(addr.ID)
   384  	}
   385  	return addrs, private
   386  }
   387  
   388  func TestAddrBookEmpty(t *testing.T) {
   389  	fname := createTempFileName("addrbook_test")
   390  	defer deleteTempFile(fname)
   391  
   392  	book := NewAddrBook(fname, true)
   393  	book.SetLogger(log.TestingLogger())
   394  	// Check that empty book is empty
   395  	require.True(t, book.Empty())
   396  	// Check that book with our address is empty
   397  	book.AddOurAddress(randIPv4Address(t))
   398  	require.True(t, book.Empty())
   399  	// Check that book with private addrs is empty
   400  	_, privateIds := testCreatePrivateAddrs(t, 5)
   401  	book.AddPrivateIDs(privateIds)
   402  	require.True(t, book.Empty())
   403  
   404  	// Check that book with address is not empty
   405  	book.AddAddress(randIPv4Address(t), randIPv4Address(t))
   406  	require.False(t, book.Empty())
   407  }
   408  
   409  func TestPrivatePeers(t *testing.T) {
   410  	fname := createTempFileName("addrbook_test")
   411  	defer deleteTempFile(fname)
   412  
   413  	book := NewAddrBook(fname, true)
   414  	book.SetLogger(log.TestingLogger())
   415  
   416  	addrs, private := testCreatePrivateAddrs(t, 10)
   417  	book.AddPrivateIDs(private)
   418  
   419  	// private addrs must not be added
   420  	for _, addr := range addrs {
   421  		err := book.AddAddress(addr, addr)
   422  		if assert.Error(t, err) {
   423  			_, ok := err.(ErrAddrBookPrivate)
   424  			assert.True(t, ok)
   425  		}
   426  	}
   427  
   428  	// addrs coming from private peers must not be added
   429  	err := book.AddAddress(randIPv4Address(t), addrs[0])
   430  	if assert.Error(t, err) {
   431  		_, ok := err.(ErrAddrBookPrivateSrc)
   432  		assert.True(t, ok)
   433  	}
   434  }
   435  
   436  func testAddrBookAddressSelection(t *testing.T, bookSize int) {
   437  	// generate all combinations of old (m) and new addresses
   438  	for nBookOld := 0; nBookOld <= bookSize; nBookOld++ {
   439  		nBookNew := bookSize - nBookOld
   440  		dbgStr := fmt.Sprintf("book of size %d (new %d, old %d)", bookSize, nBookNew, nBookOld)
   441  
   442  		// create book and get selection
   443  		book, fname := createAddrBookWithMOldAndNNewAddrs(t, nBookOld, nBookNew)
   444  		defer deleteTempFile(fname)
   445  		addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   446  		assert.NotNil(t, addrs, "%s - expected a non-nil selection", dbgStr)
   447  		nAddrs := len(addrs)
   448  		assert.NotZero(t, nAddrs, "%s - expected at least one address in selection", dbgStr)
   449  
   450  		// check there's no nil addresses
   451  		for _, addr := range addrs {
   452  			if addr == nil {
   453  				t.Fatalf("%s - got nil address in selection %v", dbgStr, addrs)
   454  			}
   455  		}
   456  
   457  		// XXX: shadowing
   458  		nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
   459  
   460  		// Given:
   461  		// n - num new addrs, m - num old addrs
   462  		// k - num new addrs expected in the beginning (based on bias %)
   463  		// i=min(n, max(k,r-m)), aka expNew
   464  		// j=min(m, r-i), aka expOld
   465  		//
   466  		// We expect this layout:
   467  		// indices:      0...i-1   i...i+j-1
   468  		// addresses:    N0..Ni-1  O0..Oj-1
   469  		//
   470  		// There is at least one partition and at most three.
   471  		var (
   472  			k      = percentageOfNum(biasToSelectNewPeers, nAddrs)
   473  			expNew = cmn.MinInt(nNew, cmn.MaxInt(k, nAddrs-nBookOld))
   474  			expOld = cmn.MinInt(nOld, nAddrs-expNew)
   475  		)
   476  
   477  		// Verify that the number of old and new addresses are as expected
   478  		if nNew != expNew {
   479  			t.Fatalf("%s - expected new addrs %d, got %d", dbgStr, expNew, nNew)
   480  		}
   481  		if nOld != expOld {
   482  			t.Fatalf("%s - expected old addrs %d, got %d", dbgStr, expOld, nOld)
   483  		}
   484  
   485  		// Verify that the order of addresses is as expected
   486  		// Get the sequence types and lengths of the selection
   487  		seqLens, seqTypes, err := analyseSelectionLayout(book, addrs)
   488  		assert.NoError(t, err, "%s", dbgStr)
   489  
   490  		// Build a list with the expected lengths of partitions and another with the expected types, e.g.:
   491  		// expSeqLens = [10, 22], expSeqTypes = [1, 2]
   492  		// means we expect 10 new (type 1) addresses followed by 22 old (type 2) addresses.
   493  		var expSeqLens []int
   494  		var expSeqTypes []int
   495  
   496  		switch {
   497  		case expOld == 0: // all new addresses
   498  			expSeqLens = []int{nAddrs}
   499  			expSeqTypes = []int{1}
   500  		case expNew == 0: // all old addresses
   501  			expSeqLens = []int{nAddrs}
   502  			expSeqTypes = []int{2}
   503  		case nAddrs-expNew-expOld == 0: // new addresses, old addresses
   504  			expSeqLens = []int{expNew, expOld}
   505  			expSeqTypes = []int{1, 2}
   506  		}
   507  
   508  		assert.Equal(t, expSeqLens, seqLens,
   509  			"%s - expected sequence lengths of old/new %v, got %v",
   510  			dbgStr, expSeqLens, seqLens)
   511  		assert.Equal(t, expSeqTypes, seqTypes,
   512  			"%s - expected sequence types (1-new, 2-old) was %v, got %v",
   513  			dbgStr, expSeqTypes, seqTypes)
   514  	}
   515  }
   516  
   517  func TestMultipleAddrBookAddressSelection(t *testing.T) {
   518  	// test books with smaller size, < N
   519  	const N = 32
   520  	for bookSize := 1; bookSize < N; bookSize++ {
   521  		testAddrBookAddressSelection(t, bookSize)
   522  	}
   523  
   524  	// Test for two books with sizes from following ranges
   525  	ranges := [...][]int{{33, 100}, {100, 175}}
   526  	bookSizes := make([]int, 0, len(ranges))
   527  	for _, r := range ranges {
   528  		bookSizes = append(bookSizes, cmn.RandIntn(r[1]-r[0])+r[0])
   529  	}
   530  	t.Logf("Testing address selection for the following book sizes %v\n", bookSizes)
   531  	for _, bookSize := range bookSizes {
   532  		testAddrBookAddressSelection(t, bookSize)
   533  	}
   534  }
   535  
   536  func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) {
   537  	nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
   538  	assert.Equal(t, m, nOld, "old addresses")
   539  	assert.Equal(t, n, nNew, "new addresses")
   540  }
   541  
   542  func createTempFileName(prefix string) string {
   543  	f, err := ioutil.TempFile("", prefix)
   544  	if err != nil {
   545  		panic(err)
   546  	}
   547  	fname := f.Name()
   548  	err = f.Close()
   549  	if err != nil {
   550  		panic(err)
   551  	}
   552  	return fname
   553  }
   554  
   555  func deleteTempFile(fname string) {
   556  	err := os.Remove(fname)
   557  	if err != nil {
   558  		panic(err)
   559  	}
   560  }
   561  
   562  func createAddrBookWithMOldAndNNewAddrs(t *testing.T, nOld, nNew int) (book *addrBook, fname string) {
   563  	fname = createTempFileName("addrbook_test")
   564  
   565  	book = NewAddrBook(fname, true)
   566  	book.SetLogger(log.TestingLogger())
   567  	assert.Zero(t, book.Size())
   568  
   569  	randAddrs := randNetAddressPairs(t, nOld)
   570  	for _, addr := range randAddrs {
   571  		book.AddAddress(addr.addr, addr.src)
   572  		book.MarkGood(addr.addr.ID)
   573  	}
   574  
   575  	randAddrs = randNetAddressPairs(t, nNew)
   576  	for _, addr := range randAddrs {
   577  		book.AddAddress(addr.addr, addr.src)
   578  	}
   579  
   580  	return
   581  }
   582  
   583  func countOldAndNewAddrsInSelection(addrs []*p2p.NetAddress, book *addrBook) (nOld, nNew int) {
   584  	for _, addr := range addrs {
   585  		if book.IsGood(addr) {
   586  			nOld++
   587  		} else {
   588  			nNew++
   589  		}
   590  	}
   591  	return
   592  }
   593  
   594  // Analyse the layout of the selection specified by 'addrs'
   595  // Returns:
   596  // - seqLens - the lengths of the sequences of addresses of same type
   597  // - seqTypes - the types of sequences in selection
   598  func analyseSelectionLayout(book *addrBook, addrs []*p2p.NetAddress) (seqLens, seqTypes []int, err error) {
   599  	// address types are: 0 - nil, 1 - new, 2 - old
   600  	var (
   601  		prevType      = 0
   602  		currentSeqLen = 0
   603  	)
   604  
   605  	for _, addr := range addrs {
   606  		addrType := 0
   607  		if book.IsGood(addr) {
   608  			addrType = 2
   609  		} else {
   610  			addrType = 1
   611  		}
   612  		if addrType != prevType && prevType != 0 {
   613  			seqLens = append(seqLens, currentSeqLen)
   614  			seqTypes = append(seqTypes, prevType)
   615  			currentSeqLen = 0
   616  		}
   617  		currentSeqLen++
   618  		prevType = addrType
   619  	}
   620  
   621  	seqLens = append(seqLens, currentSeqLen)
   622  	seqTypes = append(seqTypes, prevType)
   623  
   624  	return
   625  }