frostfs-contract/tests/util.go

229 lines
5.9 KiB
Go

package tests
import (
"context"
"net/http"
"os"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/consensus"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/core/state"
corestate "github.com/nspcc-dev/neo-go/pkg/core/stateroot"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv"
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
type awaiter struct {
ctx context.Context
t *testing.T
rpc *rpcclient.Client
}
func (a awaiter) await(tx util.Uint256, vub uint32, err error) *state.AppExecResult {
require.NoError(a.t, err)
return await(a.ctx, a.t, a.rpc, tx, vub)
}
const nodeWallet = `
{
"version": "3.0",
"accounts": [
{
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
"key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY",
"label": "",
"contract": {
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isdefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}
`
func iteratorToArray(iter *storage.Iterator) []stackitem.Item {
stackItems := make([]stackitem.Item, 0)
for iter.Next() {
stackItems = append(stackItems, iter.Value())
}
return stackItems
}
func newExecutor(t *testing.T) *neotest.Executor {
bc, acc := chain.NewSingle(t)
return neotest.NewExecutor(t, bc, acc, acc)
}
func initTmpWallet(t *testing.T) string {
f, err := os.CreateTemp("", "tmp-neo-go-wallet")
require.NoError(t, err)
_, err = f.WriteString(nodeWallet)
require.NoError(t, err)
err = f.Close()
require.NoError(t, err)
return f.Name()
}
func await(ctx context.Context, t *testing.T, rpc *rpcclient.Client, tx util.Uint256, vub uint32) *state.AppExecResult {
waitCtx, waitCancel := context.WithTimeout(ctx, 5*time.Second)
defer waitCancel()
for {
select {
case <-waitCtx.Done():
require.NoError(t, waitCtx.Err())
return nil
default:
bc, err := rpc.GetBlockCount()
require.NoError(t, err)
tr := trigger.Application
if log, err := rpc.GetApplicationLog(tx, &tr); err == nil {
return &state.AppExecResult{
Container: log.Container,
Execution: log.Executions[0],
}
}
require.LessOrEqual(t, bc, vub, "vub is expired")
time.Sleep(100 * time.Millisecond)
}
}
}
func neogoCfg() config.Config {
return config.Config{
ProtocolConfiguration: config.ProtocolConfiguration{},
ApplicationConfiguration: config.ApplicationConfiguration{
RPC: config.RPC{
BasicService: config.BasicService{
Enabled: true,
// See how tests are done in the neo-go itself:
// https://github.com/nspcc-dev/neo-go/blob/5fc61be5f6c5349d8de8b61967380feee6b51c55/config/protocol.unit_testnet.single.yml#L61
Addresses: []string{"localhost:0"},
},
MaxGasInvoke: 200_000_000,
SessionEnabled: true,
MaxIteratorResultItems: 100,
SessionPoolSize: 20,
SessionExpirationTime: 15,
},
DBConfiguration: dbconfig.DBConfiguration{
Type: dbconfig.InMemoryDB,
},
},
}
}
func consensusCfg(chain *core.Blockchain, walletAddress string, srv *network.Server) consensus.Config {
return consensus.Config{
Logger: zap.NewExample(),
Broadcast: srv.BroadcastExtensible,
BlockQueue: srv.GetBlockQueue(),
Chain: chain,
ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration,
RequestTx: srv.RequestTx,
StopTxFlow: srv.StopTxFlow,
Wallet: config.Wallet{
Path: walletAddress,
Password: "one",
},
TimePerBlock: 100 * time.Millisecond,
}
}
func runRPC(ctx context.Context, t *testing.T, chain *core.Blockchain, walletPath string) string {
log := zap.NewExample()
cfg := neogoCfg()
srvCfg, err := network.NewServerConfig(cfg)
require.NoError(t, err)
srv, err := network.NewServer(srvCfg, chain, chain.GetStateSyncModule(), log)
require.NoError(t, err)
t.Cleanup(srv.Shutdown)
errCh := make(chan error)
go func() {
for err := range errCh {
require.NoError(t, err)
return
}
}()
srMod := chain.GetStateModule().(*corestate.Module)
sr, err := stateroot.New(srvCfg.StateRootCfg, srMod, log, chain, srv.BroadcastExtensible)
require.NoError(t, err)
srv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload)
consens, err := consensus.NewService(consensusCfg(chain, walletPath, srv))
require.NoError(t, err)
srv.AddConsensusService(consens, consens.OnPayload, consens.OnTransaction)
rpcSrv := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, srv, nil, log, errCh)
srv.AddService(&rpcSrv)
initialAddr := rpcSrv.Addresses()[0]
go srv.Start()
// wait until RPC server is started
startTimeout, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
var actualAddr string
for {
select {
case <-startTimeout.Done():
t.Fatalf("Waiting for server start: %v", startTimeout.Err())
case <-ticker.C:
if actualAddr == "" {
newAddr := rpcSrv.Addresses()[0]
if initialAddr == newAddr {
continue
}
actualAddr = newAddr
t.Logf("RPC server is listening at %s, checking health", actualAddr)
}
if _, err = http.Get("http://" + actualAddr); err == nil {
return actualAddr
}
}
}
}