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