code.vegaprotocol.io/vega@v0.79.0/core/staking/account_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package staking_test
    17  
    18  import (
    19  	"testing"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/core/staking"
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  )
    28  
    29  const (
    30  	testParty = "bob"
    31  )
    32  
    33  func TestStakingAccount(t *testing.T) {
    34  	t.Run("account with no event have a zero balance", testGetZeroBalanceWithNoEvent)
    35  	t.Run("event error validation", testEventErrorValidation)
    36  	t.Run("test events sorting", testEventSorting)
    37  	t.Run("get available balance at", testGetAvailableBalanceAt)
    38  	t.Run("get available balance in range", testGetAvailableBalanceInRange)
    39  }
    40  
    41  func testGetAvailableBalanceInRange(t *testing.T) {
    42  	acc := staking.NewStakingAccount(testParty)
    43  	cases := []struct {
    44  		evt    types.StakeLinking
    45  		expect error
    46  	}{
    47  		{
    48  			evt: types.StakeLinking{
    49  				ID:     "someid1",
    50  				Type:   types.StakeLinkingTypeDeposited,
    51  				TS:     100,
    52  				Party:  testParty,
    53  				Amount: num.NewUint(10),
    54  			},
    55  			expect: nil,
    56  		},
    57  		{
    58  			evt: types.StakeLinking{
    59  				ID:     "someid2",
    60  				Type:   types.StakeLinkingTypeRemoved,
    61  				TS:     110,
    62  				Party:  testParty,
    63  				Amount: num.NewUint(1),
    64  			},
    65  			expect: nil,
    66  		},
    67  		{
    68  			evt: types.StakeLinking{
    69  				ID:     "someid3",
    70  				Type:   types.StakeLinkingTypeDeposited,
    71  				TS:     120,
    72  				Party:  testParty,
    73  				Amount: num.NewUint(5),
    74  			},
    75  			expect: nil,
    76  		},
    77  		{
    78  			evt: types.StakeLinking{
    79  				ID:     "someid4",
    80  				Type:   types.StakeLinkingTypeRemoved,
    81  				TS:     125,
    82  				Party:  testParty,
    83  				Amount: num.NewUint(6),
    84  			},
    85  			expect: nil,
    86  		},
    87  	}
    88  
    89  	for _, c := range cases {
    90  		c := c
    91  		err := acc.AddEvent(&c.evt)
    92  		assert.Equal(t, c.expect, err)
    93  	}
    94  
    95  	balance, err := acc.GetAvailableBalanceInRange(
    96  		time.Unix(0, 10), time.Unix(0, 20))
    97  	assert.NoError(t, err)
    98  	assert.Equal(t, num.NewUint(0), balance)
    99  
   100  	balance, err = acc.GetAvailableBalanceInRange(
   101  		time.Unix(0, 10), time.Unix(0, 110))
   102  	assert.NoError(t, err)
   103  	assert.Equal(t, num.NewUint(0), balance)
   104  
   105  	balance, err = acc.GetAvailableBalanceInRange(
   106  		time.Unix(0, 101), time.Unix(0, 109))
   107  	assert.NoError(t, err)
   108  	assert.Equal(t, num.NewUint(10), balance)
   109  
   110  	balance, err = acc.GetAvailableBalanceInRange(
   111  		time.Unix(0, 101), time.Unix(0, 111))
   112  	assert.NoError(t, err)
   113  	assert.Equal(t, num.NewUint(9), balance)
   114  
   115  	balance, err = acc.GetAvailableBalanceInRange(
   116  		time.Unix(0, 101), time.Unix(0, 121))
   117  	assert.NoError(t, err)
   118  	assert.Equal(t, num.NewUint(9), balance)
   119  
   120  	balance, err = acc.GetAvailableBalanceInRange(
   121  		time.Unix(0, 101), time.Unix(0, 126))
   122  	assert.NoError(t, err)
   123  	assert.Equal(t, num.NewUint(8), balance)
   124  }
   125  
   126  func testGetAvailableBalanceAt(t *testing.T) {
   127  	acc := staking.NewStakingAccount(testParty)
   128  	cases := []struct {
   129  		evt    types.StakeLinking
   130  		expect error
   131  	}{
   132  		{
   133  			evt: types.StakeLinking{
   134  				ID:     "someid1",
   135  				Type:   types.StakeLinkingTypeDeposited,
   136  				TS:     100,
   137  				Party:  testParty,
   138  				Amount: num.NewUint(10),
   139  			},
   140  			expect: nil,
   141  		},
   142  		{
   143  			evt: types.StakeLinking{
   144  				ID:     "someid2",
   145  				Type:   types.StakeLinkingTypeRemoved,
   146  				TS:     110,
   147  				Party:  testParty,
   148  				Amount: num.NewUint(1),
   149  			},
   150  			expect: nil,
   151  		},
   152  		{
   153  			evt: types.StakeLinking{
   154  				ID:     "someid3",
   155  				Type:   types.StakeLinkingTypeDeposited,
   156  				TS:     120,
   157  				Party:  testParty,
   158  				Amount: num.NewUint(5),
   159  			},
   160  			expect: nil,
   161  		},
   162  	}
   163  
   164  	for _, c := range cases {
   165  		c := c
   166  		err := acc.AddEvent(&c.evt)
   167  		assert.Equal(t, c.expect, err)
   168  	}
   169  
   170  	balance, err := acc.GetAvailableBalanceAt(time.Unix(0, 10))
   171  	assert.NoError(t, err)
   172  	assert.Equal(t, num.NewUint(0), balance)
   173  	balance, err = acc.GetAvailableBalanceAt(time.Unix(0, 120))
   174  	assert.NoError(t, err)
   175  	assert.Equal(t, num.NewUint(14), balance)
   176  	balance, err = acc.GetAvailableBalanceAt(time.Unix(0, 115))
   177  	assert.NoError(t, err)
   178  	assert.Equal(t, num.NewUint(9), balance)
   179  }
   180  
   181  func testEventSorting(t *testing.T) {
   182  	acc := staking.NewStakingAccount(testParty)
   183  
   184  	// we have 2 events added for the same timestamp
   185  	// although the first one is a remove
   186  	// second one is a deposit
   187  	// so we would expect at the end for the events to
   188  	// be sorted and return the actual balance
   189  	evts := []types.StakeLinking{
   190  		{
   191  			ID:     "someid2",
   192  			Type:   types.StakeLinkingTypeRemoved,
   193  			TS:     100,
   194  			Party:  testParty,
   195  			Amount: num.NewUint(1),
   196  		},
   197  		{
   198  			ID:     "someid1",
   199  			Type:   types.StakeLinkingTypeDeposited,
   200  			TS:     100,
   201  			Party:  testParty,
   202  			Amount: num.NewUint(100),
   203  		},
   204  	}
   205  
   206  	err := acc.AddEvent(&evts[0])
   207  	assert.EqualError(t, staking.ErrNegativeBalance, err.Error())
   208  	err = acc.AddEvent(&evts[1])
   209  	assert.NoError(t, err)
   210  	// now assert the final balance
   211  	assert.Equal(t, num.NewUint(99), acc.GetAvailableBalance())
   212  }
   213  
   214  func testGetZeroBalanceWithNoEvent(t *testing.T) {
   215  	acc := staking.NewStakingAccount(testParty)
   216  	assert.Equal(t, num.UintZero(), acc.GetAvailableBalance())
   217  }
   218  
   219  func testEventErrorValidation(t *testing.T) {
   220  	acc := staking.NewStakingAccount(testParty)
   221  
   222  	cases := []struct {
   223  		evt    types.StakeLinking
   224  		expect error
   225  	}{
   226  		{ // invalid id
   227  			evt: types.StakeLinking{
   228  				ID:     "",
   229  				Type:   types.StakeLinkingTypeDeposited,
   230  				TS:     100,
   231  				Party:  testParty,
   232  				Amount: num.NewUint(1),
   233  			},
   234  			expect: staking.ErrMissingEventID,
   235  		},
   236  		{
   237  			evt: types.StakeLinking{
   238  				ID:     "someid",
   239  				Type:   10,
   240  				TS:     100,
   241  				Party:  testParty,
   242  				Amount: num.NewUint(1),
   243  			},
   244  			expect: staking.ErrInvalidEventKind,
   245  		},
   246  		{
   247  			evt: types.StakeLinking{
   248  				ID:     "someid",
   249  				Type:   types.StakeLinkingTypeDeposited,
   250  				TS:     0,
   251  				Party:  testParty,
   252  				Amount: num.NewUint(1),
   253  			},
   254  			expect: staking.ErrMissingTimestamp,
   255  		},
   256  		{
   257  			evt: types.StakeLinking{
   258  				ID:     "someid",
   259  				Type:   types.StakeLinkingTypeDeposited,
   260  				TS:     100,
   261  				Party:  testParty,
   262  				Amount: num.UintZero(),
   263  			},
   264  			expect: staking.ErrInvalidAmount,
   265  		},
   266  		{
   267  			evt: types.StakeLinking{
   268  				ID:     "someid",
   269  				Type:   types.StakeLinkingTypeDeposited,
   270  				TS:     100,
   271  				Party:  "not-a-party",
   272  				Amount: num.NewUint(10),
   273  			},
   274  			expect: staking.ErrInvalidParty,
   275  		},
   276  		{
   277  			evt: types.StakeLinking{
   278  				ID:     "someid",
   279  				Type:   types.StakeLinkingTypeDeposited,
   280  				TS:     100,
   281  				Party:  testParty,
   282  				Amount: num.NewUint(1),
   283  			},
   284  			expect: nil,
   285  		},
   286  		{
   287  			evt: types.StakeLinking{
   288  				ID:     "someid",
   289  				Type:   types.StakeLinkingTypeDeposited,
   290  				TS:     100,
   291  				Party:  testParty,
   292  				Amount: num.NewUint(1),
   293  			},
   294  			expect: staking.ErrEventAlreadyExists,
   295  		},
   296  	}
   297  
   298  	for _, c := range cases {
   299  		c := c
   300  		err := acc.AddEvent(&c.evt)
   301  		assert.Equal(t, c.expect, err)
   302  	}
   303  }