github.com/lukso-network/go-ethereum@v1.8.22/p2p/protocols/accounting_test.go (about)

     1  // Copyright 2018 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 protocols
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/ethereum/go-ethereum/p2p"
    23  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    24  	"github.com/ethereum/go-ethereum/rlp"
    25  )
    26  
    27  //dummy Balance implementation
    28  type dummyBalance struct {
    29  	amount int64
    30  	peer   *Peer
    31  }
    32  
    33  //dummy Prices implementation
    34  type dummyPrices struct{}
    35  
    36  //a dummy message which needs size based accounting
    37  //sender pays
    38  type perBytesMsgSenderPays struct {
    39  	Content string
    40  }
    41  
    42  //a dummy message which needs size based accounting
    43  //receiver pays
    44  type perBytesMsgReceiverPays struct {
    45  	Content string
    46  }
    47  
    48  //a dummy message which is paid for per unit
    49  //sender pays
    50  type perUnitMsgSenderPays struct{}
    51  
    52  //receiver pays
    53  type perUnitMsgReceiverPays struct{}
    54  
    55  //a dummy message which has zero as its price
    56  type zeroPriceMsg struct{}
    57  
    58  //a dummy message which has no accounting
    59  type nilPriceMsg struct{}
    60  
    61  //return the price for the defined messages
    62  func (d *dummyPrices) Price(msg interface{}) *Price {
    63  	switch msg.(type) {
    64  	//size based message cost, receiver pays
    65  	case *perBytesMsgReceiverPays:
    66  		return &Price{
    67  			PerByte: true,
    68  			Value:   uint64(100),
    69  			Payer:   Receiver,
    70  		}
    71  	//size based message cost, sender pays
    72  	case *perBytesMsgSenderPays:
    73  		return &Price{
    74  			PerByte: true,
    75  			Value:   uint64(100),
    76  			Payer:   Sender,
    77  		}
    78  		//unitary cost, receiver pays
    79  	case *perUnitMsgReceiverPays:
    80  		return &Price{
    81  			PerByte: false,
    82  			Value:   uint64(99),
    83  			Payer:   Receiver,
    84  		}
    85  		//unitary cost, sender pays
    86  	case *perUnitMsgSenderPays:
    87  		return &Price{
    88  			PerByte: false,
    89  			Value:   uint64(99),
    90  			Payer:   Sender,
    91  		}
    92  	case *zeroPriceMsg:
    93  		return &Price{
    94  			PerByte: false,
    95  			Value:   uint64(0),
    96  			Payer:   Sender,
    97  		}
    98  	case *nilPriceMsg:
    99  		return nil
   100  	}
   101  	return nil
   102  }
   103  
   104  //dummy accounting implementation, only stores values for later check
   105  func (d *dummyBalance) Add(amount int64, peer *Peer) error {
   106  	d.amount = amount
   107  	d.peer = peer
   108  	return nil
   109  }
   110  
   111  type testCase struct {
   112  	msg        interface{}
   113  	size       uint32
   114  	sendResult int64
   115  	recvResult int64
   116  }
   117  
   118  //lowest level unit test
   119  func TestBalance(t *testing.T) {
   120  	//create instances
   121  	balance := &dummyBalance{}
   122  	prices := &dummyPrices{}
   123  	//create the spec
   124  	spec := createTestSpec()
   125  	//create the accounting hook for the spec
   126  	acc := NewAccounting(balance, prices)
   127  	//create a peer
   128  	id := adapters.RandomNodeConfig().ID
   129  	p := p2p.NewPeer(id, "testPeer", nil)
   130  	peer := NewPeer(p, &dummyRW{}, spec)
   131  	//price depends on size, receiver pays
   132  	msg := &perBytesMsgReceiverPays{Content: "testBalance"}
   133  	size, _ := rlp.EncodeToBytes(msg)
   134  
   135  	testCases := []testCase{
   136  		{
   137  			msg,
   138  			uint32(len(size)),
   139  			int64(len(size) * 100),
   140  			int64(len(size) * -100),
   141  		},
   142  		{
   143  			&perBytesMsgSenderPays{Content: "testBalance"},
   144  			uint32(len(size)),
   145  			int64(len(size) * -100),
   146  			int64(len(size) * 100),
   147  		},
   148  		{
   149  			&perUnitMsgSenderPays{},
   150  			0,
   151  			int64(-99),
   152  			int64(99),
   153  		},
   154  		{
   155  			&perUnitMsgReceiverPays{},
   156  			0,
   157  			int64(99),
   158  			int64(-99),
   159  		},
   160  		{
   161  			&zeroPriceMsg{},
   162  			0,
   163  			int64(0),
   164  			int64(0),
   165  		},
   166  		{
   167  			&nilPriceMsg{},
   168  			0,
   169  			int64(0),
   170  			int64(0),
   171  		},
   172  	}
   173  	checkAccountingTestCases(t, testCases, acc, peer, balance, true)
   174  	checkAccountingTestCases(t, testCases, acc, peer, balance, false)
   175  }
   176  
   177  func checkAccountingTestCases(t *testing.T, cases []testCase, acc *Accounting, peer *Peer, balance *dummyBalance, send bool) {
   178  	for _, c := range cases {
   179  		var err error
   180  		var expectedResult int64
   181  		//reset balance before every check
   182  		balance.amount = 0
   183  		if send {
   184  			err = acc.Send(peer, c.size, c.msg)
   185  			expectedResult = c.sendResult
   186  		} else {
   187  			err = acc.Receive(peer, c.size, c.msg)
   188  			expectedResult = c.recvResult
   189  		}
   190  
   191  		checkResults(t, err, balance, peer, expectedResult)
   192  	}
   193  }
   194  
   195  func checkResults(t *testing.T, err error, balance *dummyBalance, peer *Peer, result int64) {
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	if balance.peer != peer {
   200  		t.Fatalf("expected Add to be called with peer %v, got %v", peer, balance.peer)
   201  	}
   202  	if balance.amount != result {
   203  		t.Fatalf("Expected balance to be %d but is %d", result, balance.amount)
   204  	}
   205  }
   206  
   207  //create a test spec
   208  func createTestSpec() *Spec {
   209  	spec := &Spec{
   210  		Name:       "test",
   211  		Version:    42,
   212  		MaxMsgSize: 10 * 1024,
   213  		Messages: []interface{}{
   214  			&perBytesMsgReceiverPays{},
   215  			&perBytesMsgSenderPays{},
   216  			&perUnitMsgReceiverPays{},
   217  			&perUnitMsgSenderPays{},
   218  			&zeroPriceMsg{},
   219  			&nilPriceMsg{},
   220  		},
   221  	}
   222  	return spec
   223  }