Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do unexpected mints checking in benchmark tests #2603

Merged
merged 13 commits into from Jul 13, 2023
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)]
}