github.com/core-coin/go-core/v2@v2.1.9/les/lespay/server/balance_test.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package server
    18  
    19  import (
    20  	"math/rand"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/core-coin/go-core/v2/xcbdb/memorydb"
    26  
    27  	"github.com/core-coin/go-core/v2/common/mclock"
    28  	"github.com/core-coin/go-core/v2/les/utils"
    29  	"github.com/core-coin/go-core/v2/p2p/enode"
    30  	"github.com/core-coin/go-core/v2/p2p/enr"
    31  	"github.com/core-coin/go-core/v2/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  	b.ns.SetField(node, ppTestSetup.CapacityField, capacity)
    74  	n, _ := b.ns.GetField(node, btTestSetup.BalanceField).(*NodeBalance)
    75  	return n
    76  }
    77  
    78  func (b *balanceTestSetup) stop() {
    79  	b.bt.Stop()
    80  	b.ns.Stop()
    81  }
    82  
    83  func TestAddBalance(t *testing.T) {
    84  	b := newBalanceTestSetup()
    85  	defer b.stop()
    86  
    87  	node := b.newNode(1000)
    88  	var inputs = []struct {
    89  		delta     int64
    90  		expect    [2]uint64
    91  		total     uint64
    92  		expectErr bool
    93  	}{
    94  		{100, [2]uint64{0, 100}, 100, false},
    95  		{-100, [2]uint64{100, 0}, 0, false},
    96  		{-100, [2]uint64{0, 0}, 0, false},
    97  		{1, [2]uint64{0, 1}, 1, false},
    98  		{maxBalance, [2]uint64{0, 0}, 0, true},
    99  	}
   100  	for _, i := range inputs {
   101  		old, new, err := node.AddBalance(i.delta)
   102  		if i.expectErr {
   103  			if err == nil {
   104  				t.Fatalf("Expect get error but nil")
   105  			}
   106  			continue
   107  		} else if err != nil {
   108  			t.Fatalf("Expect get no error but %v", err)
   109  		}
   110  		if old != i.expect[0] || new != i.expect[1] {
   111  			t.Fatalf("Positive balance mismatch, got %v -> %v", old, new)
   112  		}
   113  		if b.bt.TotalTokenAmount() != i.total {
   114  			t.Fatalf("Total positive balance mismatch, want %v, got %v", i.total, b.bt.TotalTokenAmount())
   115  		}
   116  	}
   117  }
   118  
   119  func TestSetBalance(t *testing.T) {
   120  	b := newBalanceTestSetup()
   121  	defer b.stop()
   122  	node := b.newNode(1000)
   123  
   124  	var inputs = []struct {
   125  		pos, neg uint64
   126  	}{
   127  		{1000, 0},
   128  		{0, 1000},
   129  		{1000, 1000},
   130  	}
   131  
   132  	for _, i := range inputs {
   133  		node.SetBalance(i.pos, i.neg)
   134  		pos, neg := node.GetBalance()
   135  		if pos != i.pos {
   136  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.pos, pos)
   137  		}
   138  		if neg != i.neg {
   139  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.neg, neg)
   140  		}
   141  	}
   142  }
   143  
   144  func TestBalanceTimeCost(t *testing.T) {
   145  	b := newBalanceTestSetup()
   146  	defer b.stop()
   147  	node := b.newNode(1000)
   148  
   149  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   150  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   151  	node.SetBalance(uint64(time.Minute), 0) // 1 minute time allowance
   152  
   153  	var inputs = []struct {
   154  		runTime time.Duration
   155  		expPos  uint64
   156  		expNeg  uint64
   157  	}{
   158  		{time.Second, uint64(time.Second * 59), 0},
   159  		{0, uint64(time.Second * 59), 0},
   160  		{time.Second * 59, 0, 0},
   161  		{time.Second, 0, uint64(time.Second)},
   162  	}
   163  	for _, i := range inputs {
   164  		b.clock.Run(i.runTime)
   165  		if pos, _ := node.GetBalance(); pos != i.expPos {
   166  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
   167  		}
   168  		if _, neg := node.GetBalance(); neg != i.expNeg {
   169  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
   170  		}
   171  	}
   172  
   173  	node.SetBalance(uint64(time.Minute), 0) // Refill 1 minute time allowance
   174  	for _, i := range inputs {
   175  		b.clock.Run(i.runTime)
   176  		if pos, _ := node.GetBalance(); pos != i.expPos {
   177  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
   178  		}
   179  		if _, neg := node.GetBalance(); neg != i.expNeg {
   180  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
   181  		}
   182  	}
   183  }
   184  
   185  func TestBalanceReqCost(t *testing.T) {
   186  	b := newBalanceTestSetup()
   187  	defer b.stop()
   188  	node := b.newNode(1000)
   189  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   190  
   191  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   192  	node.SetBalance(uint64(time.Minute), 0) // 1 minute time serving time allowance
   193  	var inputs = []struct {
   194  		reqCost uint64
   195  		expPos  uint64
   196  		expNeg  uint64
   197  	}{
   198  		{uint64(time.Second), uint64(time.Second * 59), 0},
   199  		{0, uint64(time.Second * 59), 0},
   200  		{uint64(time.Second * 59), 0, 0},
   201  		{uint64(time.Second), 0, uint64(time.Second)},
   202  	}
   203  	for _, i := range inputs {
   204  		node.RequestServed(i.reqCost)
   205  		if pos, _ := node.GetBalance(); pos != i.expPos {
   206  			t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
   207  		}
   208  		if _, neg := node.GetBalance(); neg != i.expNeg {
   209  			t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
   210  		}
   211  	}
   212  }
   213  
   214  func TestBalanceToPriority(t *testing.T) {
   215  	b := newBalanceTestSetup()
   216  	defer b.stop()
   217  	node := b.newNode(1000)
   218  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   219  
   220  	var inputs = []struct {
   221  		pos      uint64
   222  		neg      uint64
   223  		priority int64
   224  	}{
   225  		{1000, 0, 1},
   226  		{2000, 0, 2}, // Higher balance, higher priority value
   227  		{0, 0, 0},
   228  		{0, 1000, -1000},
   229  	}
   230  	for _, i := range inputs {
   231  		node.SetBalance(i.pos, i.neg)
   232  		priority := node.Priority(b.clock.Now(), 1000)
   233  		if priority != i.priority {
   234  			t.Fatalf("Priority mismatch, want %v, got %v", i.priority, priority)
   235  		}
   236  	}
   237  }
   238  
   239  func TestEstimatedPriority(t *testing.T) {
   240  	b := newBalanceTestSetup()
   241  	defer b.stop()
   242  	node := b.newNode(1000000000)
   243  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   244  
   245  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   246  	node.SetBalance(uint64(time.Minute), 0)
   247  	var inputs = []struct {
   248  		runTime    time.Duration // time cost
   249  		futureTime time.Duration // diff of future time
   250  		reqCost    uint64        // single request cost
   251  		priority   int64         // expected estimated priority
   252  	}{
   253  		{time.Second, time.Second, 0, 58},
   254  		{0, time.Second, 0, 58},
   255  
   256  		// 2 seconds time cost, 1 second estimated time cost, 10^9 request cost,
   257  		// 10^9 estimated request cost per second.
   258  		{time.Second, time.Second, 1000000000, 55},
   259  
   260  		// 3 seconds time cost, 3 second estimated time cost, 10^9*2 request cost,
   261  		// 4*10^9 estimated request cost.
   262  		{time.Second, 3 * time.Second, 1000000000, 48},
   263  
   264  		// All positive balance is used up
   265  		{time.Second * 55, 0, 0, 0},
   266  
   267  		// 1 minute estimated time cost, 4/58 * 10^9 estimated request cost per sec.
   268  		{0, time.Minute, 0, -int64(time.Minute) - int64(time.Second)*120/29},
   269  	}
   270  	for _, i := range inputs {
   271  		b.clock.Run(i.runTime)
   272  		node.RequestServed(i.reqCost)
   273  		priority := node.EstMinPriority(b.clock.Now()+mclock.AbsTime(i.futureTime), 1000000000, false)
   274  		if priority != i.priority {
   275  			t.Fatalf("Estimated priority mismatch, want %v, got %v", i.priority, priority)
   276  		}
   277  	}
   278  }
   279  
   280  func TestPosBalanceMissing(t *testing.T) {
   281  	b := newBalanceTestSetup()
   282  	defer b.stop()
   283  	node := b.newNode(1000)
   284  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   285  
   286  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   287  	var inputs = []struct {
   288  		pos, neg uint64
   289  		priority int64
   290  		cap      uint64
   291  		after    time.Duration
   292  		expect   uint64
   293  	}{
   294  		{uint64(time.Second * 2), 0, 0, 1, time.Second, 0},
   295  		{uint64(time.Second * 2), 0, 0, 1, 2 * time.Second, 1},
   296  		{uint64(time.Second * 2), 0, int64(time.Second), 1, 2 * time.Second, uint64(time.Second) + 1},
   297  		{0, 0, int64(time.Second), 1, time.Second, uint64(2*time.Second) + 1},
   298  		{0, 0, -int64(time.Second), 1, time.Second, 1},
   299  	}
   300  	for _, i := range inputs {
   301  		node.SetBalance(i.pos, i.neg)
   302  		got := node.PosBalanceMissing(i.priority, i.cap, i.after)
   303  		if got != i.expect {
   304  			t.Fatalf("Missing budget mismatch, want %v, got %v", i.expect, got)
   305  		}
   306  	}
   307  }
   308  
   309  func TestPostiveBalanceCounting(t *testing.T) {
   310  	b := newBalanceTestSetup()
   311  	defer b.stop()
   312  
   313  	var nodes []*NodeBalance
   314  	for i := 0; i < 100; i += 1 {
   315  		node := b.newNode(1000000)
   316  		node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   317  		nodes = append(nodes, node)
   318  	}
   319  
   320  	// Allocate service token
   321  	var sum uint64
   322  	for i := 0; i < 100; i += 1 {
   323  		amount := int64(rand.Intn(100) + 100)
   324  		nodes[i].AddBalance(amount)
   325  		sum += uint64(amount)
   326  	}
   327  	if b.bt.TotalTokenAmount() != sum {
   328  		t.Fatalf("Invalid token amount")
   329  	}
   330  
   331  	// Change client status
   332  	for i := 0; i < 100; i += 1 {
   333  		if rand.Intn(2) == 0 {
   334  			b.ns.SetField(nodes[i].node, ppTestSetup.CapacityField, uint64(1))
   335  		}
   336  	}
   337  	if b.bt.TotalTokenAmount() != sum {
   338  		t.Fatalf("Invalid token amount")
   339  	}
   340  	for i := 0; i < 100; i += 1 {
   341  		if rand.Intn(2) == 0 {
   342  			b.ns.SetField(nodes[i].node, ppTestSetup.CapacityField, uint64(1))
   343  		}
   344  	}
   345  	if b.bt.TotalTokenAmount() != sum {
   346  		t.Fatalf("Invalid token amount")
   347  	}
   348  }
   349  
   350  func TestCallbackChecking(t *testing.T) {
   351  	b := newBalanceTestSetup()
   352  	defer b.stop()
   353  	node := b.newNode(1000000)
   354  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   355  
   356  	var inputs = []struct {
   357  		priority int64
   358  		expDiff  time.Duration
   359  	}{
   360  		{500, time.Millisecond * 500},
   361  		{0, time.Second},
   362  		{-int64(time.Second), 2 * time.Second},
   363  	}
   364  	node.SetBalance(uint64(time.Second), 0)
   365  	for _, i := range inputs {
   366  		diff, _ := node.timeUntil(i.priority)
   367  		if diff != i.expDiff {
   368  			t.Fatalf("Time difference mismatch, want %v, got %v", i.expDiff, diff)
   369  		}
   370  	}
   371  }
   372  
   373  func TestCallback(t *testing.T) {
   374  	b := newBalanceTestSetup()
   375  	defer b.stop()
   376  	node := b.newNode(1000)
   377  	node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
   378  	b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1))
   379  
   380  	callCh := make(chan struct{}, 1)
   381  	node.SetBalance(uint64(time.Minute), 0)
   382  	node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} })
   383  
   384  	b.clock.Run(time.Minute)
   385  	select {
   386  	case <-callCh:
   387  	case <-time.NewTimer(time.Second).C:
   388  		t.Fatalf("Callback hasn't been called yet")
   389  	}
   390  
   391  	node.SetBalance(uint64(time.Minute), 0)
   392  	node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} })
   393  	node.removeCallback(balanceCallbackZero)
   394  
   395  	b.clock.Run(time.Minute)
   396  	select {
   397  	case <-callCh:
   398  		t.Fatalf("Callback shouldn't be called")
   399  	case <-time.NewTimer(time.Millisecond * 100).C:
   400  	}
   401  }