Skip to content

Commit

Permalink
Merge pull request #2603 from 0chain/bm-mints-check
Browse files Browse the repository at this point in the history
Do unexpected mints checking in benchmark tests
  • Loading branch information
dabasov committed Jul 13, 2023
2 parents f79495b + 5932cd0 commit 01eb911
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 62 deletions.
8 changes: 8 additions & 0 deletions code/go/0chain.net/chaincore/chain/state/state_context.go
@@ -1,6 +1,7 @@
package state

import (
"errors"
"fmt"
"reflect"
"sort"
Expand Down Expand Up @@ -211,6 +212,10 @@ func (sc *StateContext) GetTransaction() *transaction.Transaction {
func (sc *StateContext) AddTransfer(t *state.Transfer) error {
sc.mutex.Lock()
defer sc.mutex.Unlock()
if !encryption.IsHash(t.ToClientID) {
return errors.New("invalid transaction ToClientID")
}

if t.ClientID != sc.txn.ClientID && t.ClientID != sc.txn.ToClientID {
return state.ErrInvalidTransfer
}
Expand Down Expand Up @@ -402,6 +407,9 @@ func (sc *StateContext) GetClientState(clientID string) (*state.State, error) {
if err != util.ErrValueNotPresent {
return nil, err
}
if er := sc.SetStateContext(s); er != nil {
return s, er
}
return s, err
}
//TODO: should we apply the pending transfers?
Expand Down
3 changes: 3 additions & 0 deletions code/go/0chain.net/chaincore/transaction/entity.go
Expand Up @@ -204,6 +204,9 @@ func (t *Transaction) ValidateWrtTimeForBlock(ctx context.Context, ts common.Tim

/*Validate - Entity implementation */
func (t *Transaction) Validate(ctx context.Context) error {
if !encryption.IsHash(t.ToClientID) {
return errors.New("invalid to client id")
}
return t.ValidateWrtTime(ctx, common.Now())
}

Expand Down
17 changes: 17 additions & 0 deletions code/go/0chain.net/smartcontract/benchmark/main/cmd/mpt.go
Expand Up @@ -243,6 +243,11 @@ func setUpMpt(
nil,
)

initSCTokens := currency.Coin(viper.GetInt64(benchmark.StartTokens))
mustAddMockSCBalances(balances, storagesc.ADDRESS, initSCTokens)
mustAddMockSCBalances(balances, minersc.ADDRESS, initSCTokens)
mustAddMockSCBalances(balances, zcnsc.ADDRESS, initSCTokens)

log.Println("created balances\t", time.Since(timer))

var eventDb *event.EventDb
Expand Down Expand Up @@ -573,6 +578,18 @@ func setUpMpt(
return pMpt, balances.GetState().GetRoot(), &benchData
}

func mustAddMockSCBalances(balances cstate.StateContextI, scAddress string, amount currency.Coin) {
s, err := balances.GetClientState(scAddress)
if err != nil && err != util.ErrValueNotPresent {
panic(err)
}
s.Balance = amount
_, err = balances.SetClientState(scAddress, s)
if err != nil {
panic(err)
}
}

func getMockIdKeyPair() (string, string, error) {
id, pbk, _, err := createKey()
return id, pbk, err
Expand Down
74 changes: 45 additions & 29 deletions code/go/0chain.net/smartcontract/benchmark/main/cmd/state.go
@@ -1,6 +1,9 @@
package cmd

import (
"fmt"
"log"

cstate "0chain.net/chaincore/chain/state"
"0chain.net/chaincore/state"
"0chain.net/chaincore/transaction"
Expand All @@ -9,94 +12,107 @@ import (
"github.com/0chain/common/core/util"
)

func mockUpdateState(txn *transaction.Transaction, balances cstate.StateContextI) {
func mockUpdateState(
name string,
txn *transaction.Transaction,
balances cstate.StateContextI) {
_ = balances.AddTransfer(state.NewTransfer(
txn.ClientID, txn.ToClientID, txn.Value),
)
_ = balances.AddTransfer(state.NewTransfer(
txn.ClientID, minersc.ADDRESS, txn.Fee),
)

clientState := balances.GetState()
for _, transfer := range balances.GetTransfers() {
mockTransferAmount(
[]byte(transfer.ClientID),
[]byte(transfer.ToClientID),
name,
transfer.ClientID,
transfer.ToClientID,
transfer.Amount,
clientState,
balances,
)
}

for _, signedTransfer := range balances.GetSignedTransfers() {
mockTransferAmount(
[]byte(signedTransfer.ClientID),
[]byte(signedTransfer.ToClientID),
name,
signedTransfer.ClientID,
signedTransfer.ToClientID,
signedTransfer.Amount,
clientState,
balances,
)
}

for _, mint := range balances.GetMints() {
mockMint(
[]byte(mint.ToClientID),
mint.ToClientID,
mint.Amount,
clientState,
balances,
)
}
}

func mockMint(
to []byte,
to string,
amount currency.Coin,
clientState util.MerklePatriciaTrieI,
balances cstate.StateContextI,
) {
toState := state.State{}
err := clientState.GetNodeValue(util.Path(to), &toState)
if err != nil {
toState, err := balances.GetClientState(to)
if err != nil && err != util.ErrValueNotPresent {
log.Fatal(err)
return
}
_ = balances.SetStateContext(&toState)

newBal, err := currency.AddCoin(toState.Balance, amount)
if err != nil {
return
}
//fmt.Printf("mint %v to %s, new balance: %v\n", amount, to, newBal)
toState.Balance = newBal
_, _ = clientState.Insert(util.Path(to), &toState)
if _, err := balances.SetClientState(to, toState); err != nil {
log.Fatal(err)
return
}
}

func mockTransferAmount(
from, to []byte,
name, from, to string,
amount currency.Coin,
clientState util.MerklePatriciaTrieI,
balances cstate.StateContextI,
) {
fromState := state.State{}
err := clientState.GetNodeValue(util.Path(from), &fromState)
fromState, err := balances.GetClientState(from)
if err != nil && err != util.ErrValueNotPresent {
log.Fatal(err)
return
}
if err != util.ErrValueNotPresent {
_ = balances.SetStateContext(&fromState)
fromState.Balance -= amount
_, _ = clientState.Insert(util.Path(from), &fromState)

v, err := currency.MinusCoin(fromState.Balance, amount)
if err != nil {
fmt.Printf("transfer: %v: from: %v, balance: %v, to: %v, amount: %v\n",
name, from, fromState.Balance, to, amount)
panic(err)
}

toState := state.State{}
err = clientState.GetNodeValue(util.Path(to), &toState)
fromState.Balance = v
_, err = balances.SetClientState(from, fromState)
if err != nil {
log.Fatal(err)
return
}

toState, err := balances.GetClientState(to)
if err != nil && err != util.ErrValueNotPresent {
log.Fatal(err)
return
}
_ = balances.SetStateContext(&toState)

newBal, err := currency.AddCoin(toState.Balance, amount)
if err != nil {
return
}
toState.Balance = newBal
_, _ = clientState.Insert(util.Path(to), &toState)
_, err = balances.SetClientState(to, toState)
if err != nil {
log.Fatal(err)
}
}
89 changes: 85 additions & 4 deletions code/go/0chain.net/smartcontract/benchmark/main/cmd/suites.go
Expand Up @@ -10,14 +10,14 @@ import (
"0chain.net/chaincore/transaction"
"0chain.net/core/common"
"0chain.net/core/viper"
"0chain.net/smartcontract/benchmark/main/cmd/log"
"0chain.net/smartcontract/faucetsc"
"0chain.net/smartcontract/minersc"
"0chain.net/smartcontract/rest"
"0chain.net/smartcontract/storagesc"
"0chain.net/smartcontract/vestingsc"
"0chain.net/smartcontract/zcnsc"

"0chain.net/smartcontract/benchmark/main/cmd/log"
"github.com/0chain/common/core/currency"

"0chain.net/smartcontract/benchmark"
"github.com/0chain/common/core/util"
Expand Down Expand Up @@ -153,6 +153,16 @@ func runSuite(
) []benchmarkResults {
var benchmarkResult []benchmarkResults
var wg sync.WaitGroup
scAddresses := []string{
minersc.ADDRESS,
storagesc.ADDRESS,
faucetsc.ADDRESS,
zcnsc.ADDRESS,
}
clientsMap := make(map[string]struct{}, len(data.Clients))
for _, c := range append(data.Clients, scAddresses...) {
clientsMap[c] = struct{}{}
}

for _, bm := range suite.Benchmarks {
wg.Add(1)
Expand Down Expand Up @@ -181,11 +191,51 @@ func runSuite(
timedBalance := cstate.NewTimedQueryStateContext(balances, func() common.Timestamp {
return data.Now
})

// do the balances checking only once, otherwise it would slow down the tests too much
var totalBalanceBefore currency.Coin
if i == 0 {
// get client balances and all delegate pools' balances before running the test
// compare it
for _, c := range append(data.Clients, scAddresses...) {
bal, err := timedBalance.GetClientBalance(c)
if err != nil {
log.Fatal(err)
}
totalBalanceBefore += bal
}
}

b.StartTimer()
err = bm.Run(timedBalance, b)
b.StopTimer()
if err != nil {
mockUpdateState(bm.Transaction(), balances)
// data.Clients is subset of all clients, so we need to check if there are
// any unknown clients that minted to or transferred to
unknownMintTransferClients := make(map[string]struct{})
if err == nil {
ms := timedBalance.GetMints()
for _, m := range ms {
if _, ok := clientsMap[m.ToClientID]; !ok {
unknownMintTransferClients[m.ToClientID] = struct{}{}

}
}

for _, tt := range timedBalance.GetTransfers() {
if _, ok := clientsMap[tt.ToClientID]; !ok {
unknownMintTransferClients[tt.ToClientID] = struct{}{}
}
}

for c := range unknownMintTransferClients {
bl, err := balances.GetClientBalance(c)
if err != nil {
log.Fatal(err)
}
totalBalanceBefore += bl
}

mockUpdateState(bm.Name(), bm.Transaction(), balances)
}
runCount++
currMptHashRoot := util.ToHex(timedBalance.GetState().GetRoot())
Expand All @@ -197,6 +247,37 @@ func runSuite(
} else {
prevMptHashRoot = currMptHashRoot
}

if i == 0 {
// get balances after mints
unknownAddresses := make([]string, 0, len(unknownMintTransferClients))
for c := range unknownMintTransferClients {
unknownAddresses = append(unknownAddresses, c)
}
var totalBalanceAfter currency.Coin
for _, c := range append(append(data.Clients, scAddresses...),
unknownAddresses...) {
bal, err := timedBalance.GetClientBalance(c)
if err != nil {
log.Fatal(err)
}
totalBalanceAfter += bal
}

// get total mints
var mintTokens currency.Coin
for _, m := range timedBalance.GetMints() {
mintTokens += m.Amount
}

if totalBalanceBefore != totalBalanceAfter-mintTokens {
log.Fatal(fmt.Sprintf("name:%s\ntokens mint or burned unexpected\nbefore:%v\nafter:-minted:%v\nminted:%v\n",
bm.Name(),
totalBalanceBefore,
totalBalanceAfter-mintTokens, mintTokens))

}
}
}
})
log.Println(bm.Name(), "run count is:", runCount)
Expand Down
Expand Up @@ -121,7 +121,7 @@ func BenchmarkRestTests(
FuncName: "nodePoolStat",
Params: map[string]string{
"id": data.Miners[0],
"pool_id": getMinerDelegatePoolId(0, 0, spenum.Miner),
"pool_id": getMinerDelegatePoolId(0, 0, data.Clients),
},
Endpoint: mrh.getNodePoolStat,
},
Expand Down
13 changes: 5 additions & 8 deletions code/go/0chain.net/smartcontract/minersc/benchmark_setup.go
@@ -1,8 +1,6 @@
package minersc

import (
"strconv"

"0chain.net/smartcontract/benchmark/main/cmd/log"

"github.com/0chain/common/core/currency"
Expand All @@ -16,7 +14,6 @@ import (
cstate "0chain.net/chaincore/chain/state"
"0chain.net/chaincore/node"
"0chain.net/core/common"
"0chain.net/core/encryption"
"0chain.net/smartcontract/benchmark"
"github.com/rcrowley/go-metrics"
"github.com/spf13/viper"
Expand Down Expand Up @@ -81,7 +78,7 @@ func AddMockMiners(
publickKeys = append(publickKeys, newNode.PublicKey)
for j := 0; j < numDelegates; j++ {
dId := (i + j) % numNodes
poolId := getMinerDelegatePoolId(i, dId, spenum.Miner)
poolId := getMinerDelegatePoolId(i, dId, clients)
pool := stakepool.DelegatePool{
Balance: delegatePoolBalance,
Reward: delegateReward,
Expand Down Expand Up @@ -244,7 +241,7 @@ func AddMockSharders(
publickKeys = append(publickKeys, newNode.PublicKey)
for j := 0; j < numDelegates; j++ {
dId := (i + j) % numNodes
poolId := getMinerDelegatePoolId(i, dId, spenum.Sharder)
poolId := getMinerDelegatePoolId(i, dId, clients)
pool := stakepool.DelegatePool{
Balance: delegatePoolBalance,
Reward: delegateReward,
Expand Down Expand Up @@ -490,7 +487,7 @@ func AddPhaseNode(balances cstate.StateContextI) {
}
}

func getMinerDelegatePoolId(miner, delegate int, nodeType spenum.Provider) string {
return encryption.Hash("delegate pool" +
strconv.Itoa(miner) + strconv.Itoa(delegate) + strconv.Itoa(int(nodeType)))
func getMinerDelegatePoolId(miner, delegate int, clients []string) string {
index := viper.GetInt(benchmark.NumBlobberDelegates)*miner + delegate
return clients[index%len(clients)]
}

0 comments on commit 01eb911

Please sign in to comment.