github.com/juliankolbe/go-ethereum@v1.9.992/les/vflux/server/balance_test.go (about)

     1  // Copyright 2019 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package server
    18  
    19  import (
    20  	"math"
    21  	"math/rand"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/juliankolbe/go-ethereum/common/mclock"
    27  	"github.com/juliankolbe/go-ethereum/ethdb/memorydb"
    28  	"github.com/juliankolbe/go-ethereum/les/utils"
    29  	"github.com/juliankolbe/go-ethereum/p2p/enode"
    30  	"github.com/juliankolbe/go-ethereum/p2p/enr"
    31  	"github.com/juliankolbe/go-ethereum/p2p/nodestate"
    32  )
    33  
    34  var (
    35  	testFlag     = testSetup.NewFlag("testFlag")
    36  	connAddrFlag = testSetup.NewField("connAddr", reflect.TypeOf(""))
    37  	btTestSetup  = NewBalanceTrackerSetup(testSetup)
    38  )
    39  
    40  func init() {
    41  	btTestSetup.Connect(connAddrFlag, ppTestSetup.CapacityField)
    42  }
    43  
    44  type zeroExpirer struct{}
    45  
    46  func (z zeroExpirer) SetRate(now mclock.AbsTime, rate float64)                 {}
    47  func (z zeroExpirer) SetLogOffset(now mclock.AbsTime, logOffset utils.Fixed64) {}
    48  func (z zeroExpirer) LogOffset(now mclock.AbsTime) utils.Fixed64               { return 0 }
    49  
    50  type balanceTestSetup struct {
    51  	clock *mclock.Simulated
    52  	ns    *nodestate.NodeStateMachine
    53  	bt    *BalanceTracker
    54  }
    55  
    56  func newBalanceTestSetup() *balanceTestSetup {
    57  	clock := &mclock.Simulated{}
    58  	ns := nodestate.NewNodeStateMachine(nil, nil, clock, testSetup)
    59  	db := memorydb.New()
    60  	bt := NewBalanceTracker(ns, btTestSetup, db, clock, zeroExpirer{}, zeroExpirer{})
    61  	ns.Start()
    62  	return &balanceTestSetup{
    63  		clock: clock,
    64  		ns:    ns,
    65  		bt:    bt,
    66  	}
    67  }
    68  
    69  func (b *balanceTestSetup) newNode(capacity uint64) *NodeBalance {
    70  	node := enode.SignNull(&enr.Record{}, enode.ID{})
    71  	b.ns.SetState(node, testFlag, nodestate.Flags{}, 0)
    72  	b.ns.SetField(node, btTestSetup.connAddressField, "")
    73  	if capacity != 0 {
    74  		b.ns.SetField(node, ppTestSetup.CapacityField, capacity)
    75  	}
    76  	n, _ := b.ns.GetField(node, btTestSetup.BalanceField).(*NodeBalance)
    77  	return n
    78  }
    79  
    80  func (b *balanceTestSetup) stop() {
    81  	b.bt.Stop()
    82  	b.ns.Stop()
    83  }
    84  
    85  func TestAddBalance(t *testing.T) {
    86  	b := newBalanceTestSetup()
    87  	defer b.stop()
    88  
    89  	node := b.newNode(1000)
    90  	var inputs = []struct {
    91  		delta     int64
    92  		expect    [2]uint64
    93  		total     uint64
    94  		expectErr bool
    95  	}{
    96  		{100, [2]uint64{0, 100}, 100, false},
    97  		{-100, [2]uint64{100, 0}, 0, false},
    98  		{-100, [2]uint64{0, 0}, 0, false},
    99  		{1, [2]uint64{0, 1}, 1, false},
   100  		{maxBalance, [2]uint64{0, 0}, 0, true},
   101  	}
   102  	for _, i := range inputs {
   103  		old, new, err := node.AddBalance(i.delta)
   104  		if i.expectErr {
   105  			if err == nil {
   106  				t.Fatalf("Expect get error but nil")
   107  			}
   108  			continue
   109  		} else if err != nil {
   110  			t.Fatalf("Expect get no error but %v", err)
   111  		}
   112  		if old != i.expect[0] || new != i.expect[1] {
   113  			t.Fatalf("Positive balance mismatch, got %v -> %v", old, new)
   114  		}
   115  		if b.bt.TotalTokenAmount() != i.total {
   116  			t.Fatalf("Total positive balance mismatch, want %v, got %v", i.total, b.bt.TotalTokenAmount())
   117  		}
   118  	}
   119  }
   120  
   121  func TestSetBalance(t *testing.T) {
   122  	b := newBalanceTestSetup()
   123  	defer b.stop()
   124  	node := b.newNode(1000)
   125  
   126  	var inputs = []struct {
   127  		pos, neg uint64
   128  	}{
   129  		{1000, 0},
   130  		{0, 1000},
   131  		{1000, 1000},
   132  	}
   133  
   134  	for _, i := range inputs {
   135  		node.SetBalance(i.pos, i.neg)
   136  		pos, neg := node.GetBalance()
   137  		if pos != i.pos {
   138  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.pos, pos)
   139  		}
   140  		if neg != i.neg {
   141  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.neg, neg)
   142  		}
   143  	}
   144  }
   145  
   146  func TestBalanceTimeCost(t *testing.T) {
   147  	b := newBalanceTestSetup()
   148  	defer b.stop()
   149  	node := b.newNode(1000)
   150  
   151  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   152  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   153  	node.SetBalance(uint64(time.Minute), 0) // 1 minute time allowance
   154  
   155  	var inputs = []struct {
   156  		runTime time.Duration
   157  		expPos  uint64
   158  		expNeg  uint64
   159  	}{
   160  		{time.Second, uint64(time.Second * 59), 0},
   161  		{0, uint64(time.Second * 59), 0},
   162  		{time.Second * 59, 0, 0},
   163  		{time.Second, 0, uint64(time.Second)},
   164  	}
   165  	for _, i := range inputs {
   166  		b.clock.Run(i.runTime)
   167  		if pos, _ := node.GetBalance(); pos != i.expPos {
   168  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
   169  		}
   170  		if _, neg := node.GetBalance(); neg != i.expNeg {
   171  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
   172  		}
   173  	}
   174  
   175  	node.SetBalance(uint64(time.Minute), 0) // Refill 1 minute time allowance
   176  	for _, i := range inputs {
   177  		b.clock.Run(i.runTime)
   178  		if pos, _ := node.GetBalance(); pos != i.expPos {
   179  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
   180  		}
   181  		if _, neg := node.GetBalance(); neg != i.expNeg {
   182  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
   183  		}
   184  	}
   185  }
   186  
   187  func TestBalanceReqCost(t *testing.T) {
   188  	b := newBalanceTestSetup()
   189  	defer b.stop()
   190  	node := b.newNode(1000)
   191  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   192  
   193  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   194  	node.SetBalance(uint64(time.Minute), 0) // 1 minute time serving time allowance
   195  	var inputs = []struct {
   196  		reqCost uint64
   197  		expPos  uint64
   198  		expNeg  uint64
   199  	}{
   200  		{uint64(time.Second), uint64(time.Second * 59), 0},
   201  		{0, uint64(time.Second * 59), 0},
   202  		{uint64(time.Second * 59), 0, 0},
   203  		{uint64(time.Second), 0, uint64(time.Second)},
   204  	}
   205  	for _, i := range inputs {
   206  		node.RequestServed(i.reqCost)
   207  		if pos, _ := node.GetBalance(); pos != i.expPos {
   208  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
   209  		}
   210  		if _, neg := node.GetBalance(); neg != i.expNeg {
   211  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
   212  		}
   213  	}
   214  }
   215  
   216  func TestBalanceToPriority(t *testing.T) {
   217  	b := newBalanceTestSetup()
   218  	defer b.stop()
   219  	node := b.newNode(1000)
   220  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   221  
   222  	var inputs = []struct {
   223  		pos      uint64
   224  		neg      uint64
   225  		priority int64
   226  	}{
   227  		{1000, 0, 1},
   228  		{2000, 0, 2}, // Higher balance, higher priority value
   229  		{0, 0, 0},
   230  		{0, 1000, -1000},
   231  	}
   232  	for _, i := range inputs {
   233  		node.SetBalance(i.pos, i.neg)
   234  		priority := node.Priority(b.clock.Now(), 1000)
   235  		if priority != i.priority {
   236  			t.Fatalf("Priority mismatch, want %v, got %v", i.priority, priority)
   237  		}
   238  	}
   239  }
   240  
   241  func TestEstimatedPriority(t *testing.T) {
   242  	b := newBalanceTestSetup()
   243  	defer b.stop()
   244  	node := b.newNode(1000000000)
   245  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   246  
   247  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   248  	node.SetBalance(uint64(time.Minute), 0)
   249  	var inputs = []struct {
   250  		runTime    time.Duration // time cost
   251  		futureTime time.Duration // diff of future time
   252  		reqCost    uint64        // single request cost
   253  		priority   int64         // expected estimated priority
   254  	}{
   255  		{time.Second, time.Second, 0, 58},
   256  		{0, time.Second, 0, 58},
   257  
   258  		// 2 seconds time cost, 1 second estimated time cost, 10^9 request cost,
   259  		// 10^9 estimated request cost per second.
   260  		{time.Second, time.Second, 1000000000, 55},
   261  
   262  		// 3 seconds time cost, 3 second estimated time cost, 10^9*2 request cost,
   263  		// 4*10^9 estimated request cost.
   264  		{time.Second, 3 * time.Second, 1000000000, 48},
   265  
   266  		// All positive balance is used up
   267  		{time.Second * 55, 0, 0, 0},
   268  
   269  		// 1 minute estimated time cost, 4/58 * 10^9 estimated request cost per sec.
   270  		{0, time.Minute, 0, -int64(time.Minute) - int64(time.Second)*120/29},
   271  	}
   272  	for _, i := range inputs {
   273  		b.clock.Run(i.runTime)
   274  		node.RequestServed(i.reqCost)
   275  		priority := node.EstMinPriority(b.clock.Now()+mclock.AbsTime(i.futureTime), 1000000000, false)
   276  		if priority != i.priority {
   277  			t.Fatalf("Estimated priority mismatch, want %v, got %v", i.priority, priority)
   278  		}
   279  	}
   280  }
   281  
   282  func TestPosBalanceMissing(t *testing.T) {
   283  	b := newBalanceTestSetup()
   284  	defer b.stop()
   285  	node := b.newNode(1000)
   286  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   287  
   288  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   289  	var inputs = []struct {
   290  		pos, neg uint64
   291  		priority int64
   292  		cap      uint64
   293  		after    time.Duration
   294  		expect   uint64
   295  	}{
   296  		{uint64(time.Second * 2), 0, 0, 1, time.Second, 0},
   297  		{uint64(time.Second * 2), 0, 0, 1, 2 * time.Second, 1},
   298  		{uint64(time.Second * 2), 0, int64(time.Second), 1, 2 * time.Second, uint64(time.Second) + 1},
   299  		{0, 0, int64(time.Second), 1, time.Second, uint64(2*time.Second) + 1},
   300  		{0, 0, -int64(time.Second), 1, time.Second, 1},
   301  	}
   302  	for _, i := range inputs {
   303  		node.SetBalance(i.pos, i.neg)
   304  		got := node.PosBalanceMissing(i.priority, i.cap, i.after)
   305  		if got != i.expect {
   306  			t.Fatalf("Missing budget mismatch, want %v, got %v", i.expect, got)
   307  		}
   308  	}
   309  }
   310  
   311  func TestPostiveBalanceCounting(t *testing.T) {
   312  	b := newBalanceTestSetup()
   313  	defer b.stop()
   314  
   315  	var nodes []*NodeBalance
   316  	for i := 0; i < 100; i += 1 {
   317  		node := b.newNode(1000000)
   318  		node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   319  		nodes = append(nodes, node)
   320  	}
   321  
   322  	// Allocate service token
   323  	var sum uint64
   324  	for i := 0; i < 100; i += 1 {
   325  		amount := int64(rand.Intn(100) + 100)
   326  		nodes[i].AddBalance(amount)
   327  		sum += uint64(amount)
   328  	}
   329  	if b.bt.TotalTokenAmount() != sum {
   330  		t.Fatalf("Invalid token amount")
   331  	}
   332  
   333  	// Change client status
   334  	for i := 0; i < 100; i += 1 {
   335  		if rand.Intn(2) == 0 {
   336  			b.ns.SetField(nodes[i].node, ppTestSetup.CapacityField, uint64(1))
   337  		}
   338  	}
   339  	if b.bt.TotalTokenAmount() != sum {
   340  		t.Fatalf("Invalid token amount")
   341  	}
   342  	for i := 0; i < 100; i += 1 {
   343  		if rand.Intn(2) == 0 {
   344  			b.ns.SetField(nodes[i].node, ppTestSetup.CapacityField, uint64(1))
   345  		}
   346  	}
   347  	if b.bt.TotalTokenAmount() != sum {
   348  		t.Fatalf("Invalid token amount")
   349  	}
   350  }
   351  
   352  func TestCallbackChecking(t *testing.T) {
   353  	b := newBalanceTestSetup()
   354  	defer b.stop()
   355  	node := b.newNode(1000000)
   356  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   357  
   358  	var inputs = []struct {
   359  		priority int64
   360  		expDiff  time.Duration
   361  	}{
   362  		{500, time.Millisecond * 500},
   363  		{0, time.Second},
   364  		{-int64(time.Second), 2 * time.Second},
   365  	}
   366  	node.SetBalance(uint64(time.Second), 0)
   367  	for _, i := range inputs {
   368  		diff, _ := node.timeUntil(i.priority)
   369  		if diff != i.expDiff {
   370  			t.Fatalf("Time difference mismatch, want %v, got %v", i.expDiff, diff)
   371  		}
   372  	}
   373  }
   374  
   375  func TestCallback(t *testing.T) {
   376  	b := newBalanceTestSetup()
   377  	defer b.stop()
   378  	node := b.newNode(1000)
   379  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   380  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   381  
   382  	callCh := make(chan struct{}, 1)
   383  	node.SetBalance(uint64(time.Minute), 0)
   384  	node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} })
   385  
   386  	b.clock.Run(time.Minute)
   387  	select {
   388  	case <-callCh:
   389  	case <-time.NewTimer(time.Second).C:
   390  		t.Fatalf("Callback hasn't been called yet")
   391  	}
   392  
   393  	node.SetBalance(uint64(time.Minute), 0)
   394  	node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} })
   395  	node.removeCallback(balanceCallbackZero)
   396  
   397  	b.clock.Run(time.Minute)
   398  	select {
   399  	case <-callCh:
   400  		t.Fatalf("Callback shouldn't be called")
   401  	case <-time.NewTimer(time.Millisecond * 100).C:
   402  	}
   403  }
   404  
   405  func TestBalancePersistence(t *testing.T) {
   406  	clock := &mclock.Simulated{}
   407  	ns := nodestate.NewNodeStateMachine(nil, nil, clock, testSetup)
   408  	db := memorydb.New()
   409  	posExp := &utils.Expirer{}
   410  	negExp := &utils.Expirer{}
   411  	posExp.SetRate(clock.Now(), math.Log(2)/float64(time.Hour*2)) // halves every two hours
   412  	negExp.SetRate(clock.Now(), math.Log(2)/float64(time.Hour))   // halves every hour
   413  	bt := NewBalanceTracker(ns, btTestSetup, db, clock, posExp, negExp)
   414  	ns.Start()
   415  	bts := &balanceTestSetup{
   416  		clock: clock,
   417  		ns:    ns,
   418  		bt:    bt,
   419  	}
   420  	var nb *NodeBalance
   421  	exp := func(expPos, expNeg uint64) {
   422  		pos, neg := nb.GetBalance()
   423  		if pos != expPos {
   424  			t.Fatalf("Positive balance incorrect, want %v, got %v", expPos, pos)
   425  		}
   426  		if neg != expNeg {
   427  			t.Fatalf("Positive balance incorrect, want %v, got %v", expPos, pos)
   428  		}
   429  	}
   430  	expTotal := func(expTotal uint64) {
   431  		total := bt.TotalTokenAmount()
   432  		if total != expTotal {
   433  			t.Fatalf("Total token amount incorrect, want %v, got %v", expTotal, total)
   434  		}
   435  	}
   436  
   437  	expTotal(0)
   438  	nb = bts.newNode(0)
   439  	expTotal(0)
   440  	nb.SetBalance(16000000000, 16000000000)
   441  	exp(16000000000, 16000000000)
   442  	expTotal(16000000000)
   443  	clock.Run(time.Hour * 2)
   444  	exp(8000000000, 4000000000)
   445  	expTotal(8000000000)
   446  	bt.Stop()
   447  	ns.Stop()
   448  
   449  	clock = &mclock.Simulated{}
   450  	ns = nodestate.NewNodeStateMachine(nil, nil, clock, testSetup)
   451  	posExp = &utils.Expirer{}
   452  	negExp = &utils.Expirer{}
   453  	posExp.SetRate(clock.Now(), math.Log(2)/float64(time.Hour*2)) // halves every two hours
   454  	negExp.SetRate(clock.Now(), math.Log(2)/float64(time.Hour))   // halves every hour
   455  	bt = NewBalanceTracker(ns, btTestSetup, db, clock, posExp, negExp)
   456  	ns.Start()
   457  	bts = &balanceTestSetup{
   458  		clock: clock,
   459  		ns:    ns,
   460  		bt:    bt,
   461  	}
   462  	expTotal(8000000000)
   463  	nb = bts.newNode(0)
   464  	exp(8000000000, 4000000000)
   465  	expTotal(8000000000)
   466  	clock.Run(time.Hour * 2)
   467  	exp(4000000000, 1000000000)
   468  	expTotal(4000000000)
   469  	bt.Stop()
   470  	ns.Stop()
   471  }