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 } } } }