Merge pull request #1236 from nspcc-dev/rpc/getrawmempool

rpc: update `getrawmempool` and `getrawtransaction` RPC-calls
This commit is contained in:
fyrchik 2020-07-29 10:18:51 +03:00 committed by GitHub
commit b187dfe3ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 120 additions and 86 deletions

View file

@ -378,7 +378,7 @@ func (bc *Blockchain) notificationDispatcher() {
for ch := range executionFeed {
ch <- aer
}
if aer.VMState == "HALT" {
if aer.VMState == vm.HaltState {
for i := range aer.Events {
for ch := range notificationFeed {
ch <- &aer.Events[i]

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert"
@ -316,7 +317,7 @@ func TestSubscriptions(t *testing.T) {
exec := <-executionCh
require.Equal(t, b.Hash(), exec.TxHash)
require.Equal(t, exec.VMState, "HALT")
require.Equal(t, exec.VMState, vm.HaltState)
// 3 burn events for every tx and 1 mint for primary node
require.True(t, len(notificationCh) >= 4)
@ -331,7 +332,7 @@ func TestSubscriptions(t *testing.T) {
require.Equal(t, txExpected, tx)
exec := <-executionCh
require.Equal(t, tx.Hash(), exec.TxHash)
if exec.VMState == "HALT" {
if exec.VMState == vm.HaltState {
notif := <-notificationCh
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
}

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
@ -122,14 +123,14 @@ func TestNativeContract_Invoke(t *testing.T) {
res, err := chain.GetAppExecResult(tx.Hash())
require.NoError(t, err)
require.Equal(t, "HALT", res.VMState)
require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, 1, len(res.Stack))
require.Equal(t, smartcontract.IntegerType, res.Stack[0].Type)
require.EqualValues(t, 42, res.Stack[0].Value)
res, err = chain.GetAppExecResult(tx2.Hash())
require.NoError(t, err)
require.Equal(t, "FAULT", res.VMState)
require.Equal(t, vm.FaultState, res.VMState)
require.NoError(t, chain.persist())
select {

View file

@ -12,6 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/stretchr/testify/require"
)
@ -253,7 +254,7 @@ func invokeNativePolicyMethod(chain *Blockchain, method string, args ...interfac
}
func checkResult(t *testing.T, result *state.AppExecResult, expected smartcontract.Parameter) {
require.Equal(t, "HALT", result.VMState)
require.Equal(t, vm.HaltState, result.VMState)
require.Equal(t, 1, len(result.Stack))
require.Equal(t, expected.Type, result.Stack[0].Type)
require.EqualValues(t, expected.Value, result.Stack[0].Value)

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
@ -23,7 +24,7 @@ type NotificationEvent struct {
type AppExecResult struct {
TxHash util.Uint256
Trigger trigger.Type
VMState string
VMState vm.State
GasConsumed int64
Stack []smartcontract.Parameter
Events []NotificationEvent
@ -56,7 +57,7 @@ func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(aer.TxHash[:])
w.WriteB(byte(aer.Trigger))
w.WriteString(aer.VMState)
w.WriteB(byte(aer.VMState))
w.WriteU64LE(uint64(aer.GasConsumed))
w.WriteArray(aer.Stack)
w.WriteArray(aer.Events)
@ -66,7 +67,7 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
r.ReadBytes(aer.TxHash[:])
aer.Trigger = trigger.Type(r.ReadB())
aer.VMState = r.ReadString()
aer.VMState = vm.State(r.ReadB())
aer.GasConsumed = int64(r.ReadU64LE())
r.ReadArray(&aer.Stack)
r.ReadArray(&aer.Events)

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
@ -23,7 +24,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
appExecResult := &AppExecResult{
TxHash: random.Uint256(),
Trigger: 1,
VMState: "Hault",
VMState: vm.HaltState,
GasConsumed: 10,
Stack: []smartcontract.Parameter{},
Events: []NotificationEvent{},

View file

@ -49,7 +49,7 @@ func NewApplicationLog(appExecRes *state.AppExecResult) ApplicationLog {
return ApplicationLog{
TxHash: appExecRes.TxHash,
Trigger: appExecRes.Trigger.String(),
VMState: appExecRes.VMState,
VMState: appExecRes.VMState.String(),
GasConsumed: appExecRes.GasConsumed,
Stack: appExecRes.Stack,
Events: events,

View file

@ -0,0 +1,10 @@
package result
import "github.com/nspcc-dev/neo-go/pkg/util"
// RawMempool represents a result of getrawmempool RPC call.
type RawMempool struct {
Height uint32 `json:"height"`
Verified []util.Uint256 `json:"verified"`
Unverified []util.Uint256 `json:"unverified"`
}

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util"
)
@ -22,10 +23,11 @@ type TransactionMetadata struct {
Blockhash util.Uint256 `json:"blockhash,omitempty"`
Confirmations int `json:"confirmations,omitempty"`
Timestamp uint64 `json:"blocktime,omitempty"`
VMState string `json:"vmstate"`
}
// NewTransactionOutputRaw returns a new ransactionOutputRaw object.
func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, chain blockchainer.Blockchainer) TransactionOutputRaw {
func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, appExecResult *state.AppExecResult, chain blockchainer.Blockchainer) TransactionOutputRaw {
// confirmations formula
confirmations := int(chain.BlockHeight() - header.Base.Index + 1)
return TransactionOutputRaw{
@ -34,6 +36,7 @@ func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header,
Blockhash: header.Hash(),
Confirmations: confirmations,
Timestamp: header.Timestamp,
VMState: appExecResult.VMState.String(),
},
}
}
@ -44,6 +47,7 @@ func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) {
Blockhash: t.Blockhash,
Confirmations: t.Confirmations,
Timestamp: t.Timestamp,
VMState: t.VMState,
})
if err != nil {
return nil, err
@ -75,6 +79,7 @@ func (t *TransactionOutputRaw) UnmarshalJSON(data []byte) error {
t.Blockhash = output.Blockhash
t.Confirmations = output.Confirmations
t.Timestamp = output.Timestamp
t.VMState = output.VMState
return json.Unmarshal(data, &t.Transaction)
}

View file

@ -463,13 +463,20 @@ func (s *Server) getPeers(_ request.Params) (interface{}, *response.Error) {
return peers, nil
}
func (s *Server) getRawMempool(_ request.Params) (interface{}, *response.Error) {
func (s *Server) getRawMempool(reqParams request.Params) (interface{}, *response.Error) {
verbose := reqParams.Value(0).GetBoolean()
mp := s.chain.GetMemPool()
hashList := make([]util.Uint256, 0)
for _, item := range mp.GetVerifiedTransactions() {
hashList = append(hashList, item.Hash())
}
return hashList, nil
if !verbose {
return hashList, nil
}
return result.RawMempool{
Height: s.chain.BlockHeight(),
Verified: hashList,
}, nil
}
func (s *Server) validateAddress(reqParams request.Params) (interface{}, *response.Error) {
@ -685,10 +692,13 @@ func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, *resp
_header := s.chain.GetHeaderHash(int(height))
header, err := s.chain.GetHeader(_header)
if err != nil {
resultsErr = response.NewInvalidParamsError(err.Error(), err)
} else {
results = result.NewTransactionOutputRaw(tx, header, s.chain)
return nil, response.NewInvalidParamsError(err.Error(), err)
}
st, err := s.chain.GetAppExecResult(txHash)
if err != nil {
return nil, response.NewRPCError("Unknown transaction", err.Error(), err)
}
results = result.NewTransactionOutputRaw(tx, header, st, s.chain)
} else {
results = hex.EncodeToString(tx.Bytes())
}
@ -873,7 +883,7 @@ func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *resu
vm.LoadScriptWithFlags(script, smartcontract.All)
_ = vm.Run()
result := &result.Invoke{
State: vm.State(),
State: vm.State().String(),
GasConsumed: vm.GasConsumed(),
Script: hex.EncodeToString(script),
Stack: vm.Estack().ToContractParameters(),

View file

@ -162,14 +162,14 @@ func testFile(t *testing.T, filename string) {
t.Run(ut.Tests[i].Name, func(t *testing.T) {
prog := []byte(test.Script)
vm := load(prog)
vm.state = breakState
vm.state = BreakState
vm.RegisterInteropGetter(getTestingInterop)
for i := range test.Steps {
execStep(t, vm, test.Steps[i])
result := test.Steps[i].Result
require.Equal(t, result.State, vm.state)
if result.State == faultState { // do not compare stacks on fault
if result.State == FaultState { // do not compare stacks on fault
continue
}

View file

@ -11,10 +11,14 @@ type State uint8
// Available States.
const (
noneState State = 0
haltState State = 1 << iota
faultState
breakState
// NoneState represents NONE VM state.
NoneState State = 0
// HaltState represents HALT VM state.
HaltState State = 1 << iota
// FaultState represents FAULT VM state.
FaultState
// BreakState represents BREAK VM state.
BreakState
)
// HasFlag checks for State flag presence.
@ -24,18 +28,18 @@ func (s State) HasFlag(f State) bool {
// String implements the stringer interface.
func (s State) String() string {
if s == noneState {
if s == NoneState {
return "NONE"
}
ss := make([]string, 0, 3)
if s.HasFlag(haltState) {
if s.HasFlag(HaltState) {
ss = append(ss, "HALT")
}
if s.HasFlag(faultState) {
if s.HasFlag(FaultState) {
ss = append(ss, "FAULT")
}
if s.HasFlag(breakState) {
if s.HasFlag(BreakState) {
ss = append(ss, "BREAK")
}
return strings.Join(ss, ", ")
@ -44,18 +48,18 @@ func (s State) String() string {
// StateFromString converts string into the VM State.
func StateFromString(s string) (st State, err error) {
if s = strings.TrimSpace(s); s == "NONE" {
return noneState, nil
return NoneState, nil
}
ss := strings.Split(s, ",")
for _, state := range ss {
switch state = strings.TrimSpace(state); state {
case "HALT":
st |= haltState
st |= HaltState
case "FAULT":
st |= faultState
st |= FaultState
case "BREAK":
st |= breakState
st |= BreakState
default:
return 0, errors.New("unknown state")
}

View file

@ -15,42 +15,42 @@ func TestStateFromString(t *testing.T) {
s, err = StateFromString("HALT")
assert.NoError(t, err)
assert.Equal(t, haltState, s)
assert.Equal(t, HaltState, s)
s, err = StateFromString("BREAK")
assert.NoError(t, err)
assert.Equal(t, breakState, s)
assert.Equal(t, BreakState, s)
s, err = StateFromString("FAULT")
assert.NoError(t, err)
assert.Equal(t, faultState, s)
assert.Equal(t, FaultState, s)
s, err = StateFromString("NONE")
assert.NoError(t, err)
assert.Equal(t, noneState, s)
assert.Equal(t, NoneState, s)
s, err = StateFromString("HALT, BREAK")
assert.NoError(t, err)
assert.Equal(t, haltState|breakState, s)
assert.Equal(t, HaltState|BreakState, s)
s, err = StateFromString("FAULT, BREAK")
assert.NoError(t, err)
assert.Equal(t, faultState|breakState, s)
assert.Equal(t, FaultState|BreakState, s)
_, err = StateFromString("HALT, KEK")
assert.Error(t, err)
}
func TestState_HasFlag(t *testing.T) {
assert.True(t, haltState.HasFlag(haltState))
assert.True(t, breakState.HasFlag(breakState))
assert.True(t, faultState.HasFlag(faultState))
assert.True(t, (haltState | breakState).HasFlag(haltState))
assert.True(t, (haltState | breakState).HasFlag(breakState))
assert.True(t, HaltState.HasFlag(HaltState))
assert.True(t, BreakState.HasFlag(BreakState))
assert.True(t, FaultState.HasFlag(FaultState))
assert.True(t, (HaltState | BreakState).HasFlag(HaltState))
assert.True(t, (HaltState | BreakState).HasFlag(BreakState))
assert.False(t, haltState.HasFlag(breakState))
assert.False(t, noneState.HasFlag(haltState))
assert.False(t, (faultState | breakState).HasFlag(haltState))
assert.False(t, HaltState.HasFlag(BreakState))
assert.False(t, NoneState.HasFlag(HaltState))
assert.False(t, (FaultState | BreakState).HasFlag(HaltState))
}
func TestState_MarshalJSON(t *testing.T) {
@ -59,11 +59,11 @@ func TestState_MarshalJSON(t *testing.T) {
err error
)
data, err = json.Marshal(haltState | breakState)
data, err = json.Marshal(HaltState | BreakState)
assert.NoError(t, err)
assert.Equal(t, data, []byte(`"HALT, BREAK"`))
data, err = json.Marshal(faultState)
data, err = json.Marshal(FaultState)
assert.NoError(t, err)
assert.Equal(t, data, []byte(`"FAULT"`))
}
@ -76,13 +76,13 @@ func TestState_UnmarshalJSON(t *testing.T) {
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
assert.NoError(t, err)
assert.Equal(t, haltState|breakState, s)
assert.Equal(t, HaltState|BreakState, s)
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
assert.NoError(t, err)
assert.Equal(t, faultState|breakState, s)
assert.Equal(t, FaultState|BreakState, s)
err = json.Unmarshal([]byte(`"NONE"`), &s)
assert.NoError(t, err)
assert.Equal(t, noneState, s)
assert.Equal(t, NoneState, s)
}

View file

@ -93,7 +93,7 @@ func New() *VM {
func NewWithTrigger(t trigger.Type) *VM {
vm := &VM{
getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage.
state: haltState,
state: HaltState,
istack: NewStack("invocation"),
refs: newRefCounter(),
keys: make(map[string]*keys.PublicKey),
@ -264,7 +264,7 @@ func (v *VM) Load(prog []byte) {
// Clear all stacks and state, it could be a reload.
v.istack.Clear()
v.estack.Clear()
v.state = noneState
v.state = NoneState
v.gasConsumed = 0
v.LoadScript(prog)
}
@ -330,9 +330,9 @@ func (v *VM) Stack(n string) string {
return string(b)
}
// State returns string representation of the state for the VM.
func (v *VM) State() string {
return v.state.String()
// State returns the state for the VM.
func (v *VM) State() State {
return v.state
}
// Ready returns true if the VM ready to execute the loaded program.
@ -344,37 +344,37 @@ func (v *VM) Ready() bool {
// Run starts the execution of the loaded program.
func (v *VM) Run() error {
if !v.Ready() {
v.state = faultState
v.state = FaultState
return errors.New("no program loaded")
}
if v.state.HasFlag(faultState) {
if v.state.HasFlag(FaultState) {
// VM already ran something and failed, in general its state is
// undefined in this case so we can't run anything.
return errors.New("VM has failed")
}
// haltState (the default) or breakState are safe to continue.
v.state = noneState
// HaltState (the default) or BreakState are safe to continue.
v.state = NoneState
for {
// check for breakpoint before executing the next instruction
ctx := v.Context()
if ctx != nil && ctx.atBreakPoint() {
v.state = breakState
v.state = BreakState
}
switch {
case v.state.HasFlag(faultState):
case v.state.HasFlag(FaultState):
// Should be caught and reported already by the v.Step(),
// but we're checking here anyway just in case.
return errors.New("VM has failed")
case v.state.HasFlag(haltState), v.state.HasFlag(breakState):
case v.state.HasFlag(HaltState), v.state.HasFlag(BreakState):
// Normal exit from this loop.
return nil
case v.state == noneState:
case v.state == NoneState:
if err := v.Step(); err != nil {
return err
}
default:
v.state = faultState
v.state = FaultState
return errors.New("unknown state")
}
}
@ -385,7 +385,7 @@ func (v *VM) Step() error {
ctx := v.Context()
op, param, err := ctx.Next()
if err != nil {
v.state = faultState
v.state = FaultState
return newError(ctx.ip, op, err)
}
return v.execute(ctx, op, param)
@ -397,7 +397,7 @@ func (v *VM) StepInto() error {
ctx := v.Context()
if ctx == nil {
v.state = haltState
v.state = HaltState
}
if v.HasStopped() {
@ -407,7 +407,7 @@ func (v *VM) StepInto() error {
if ctx != nil && ctx.prog != nil {
op, param, err := ctx.Next()
if err != nil {
v.state = faultState
v.state = FaultState
return newError(ctx.ip, op, err)
}
vErr := v.execute(ctx, op, param)
@ -418,7 +418,7 @@ func (v *VM) StepInto() error {
cctx := v.Context()
if cctx != nil && cctx.atBreakPoint() {
v.state = breakState
v.state = BreakState
}
return nil
}
@ -426,14 +426,14 @@ func (v *VM) StepInto() error {
// StepOut takes the debugger to the line where the current function was called.
func (v *VM) StepOut() error {
var err error
if v.state == breakState {
v.state = noneState
if v.state == BreakState {
v.state = NoneState
} else {
v.state = breakState
v.state = BreakState
}
expSize := v.istack.len
for v.state == noneState && v.istack.len >= expSize {
for v.state == NoneState && v.istack.len >= expSize {
err = v.StepInto()
}
return err
@ -447,22 +447,22 @@ func (v *VM) StepOver() error {
return err
}
if v.state == breakState {
v.state = noneState
if v.state == BreakState {
v.state = NoneState
} else {
v.state = breakState
v.state = BreakState
}
expSize := v.istack.len
for {
err = v.StepInto()
if !(v.state == noneState && v.istack.len > expSize) {
if !(v.state == NoneState && v.istack.len > expSize) {
break
}
}
if v.state == noneState {
v.state = breakState
if v.state == NoneState {
v.state = BreakState
}
return err
@ -471,22 +471,22 @@ func (v *VM) StepOver() error {
// HasFailed returns whether VM is in the failed state now. Usually used to
// check status after Run.
func (v *VM) HasFailed() bool {
return v.state.HasFlag(faultState)
return v.state.HasFlag(FaultState)
}
// HasStopped returns whether VM is in Halt or Failed state.
func (v *VM) HasStopped() bool {
return v.state.HasFlag(haltState) || v.state.HasFlag(faultState)
return v.state.HasFlag(HaltState) || v.state.HasFlag(FaultState)
}
// HasHalted returns whether VM is in Halt state.
func (v *VM) HasHalted() bool {
return v.state.HasFlag(haltState)
return v.state.HasFlag(HaltState)
}
// AtBreakpoint returns whether VM is at breakpoint.
func (v *VM) AtBreakpoint() bool {
return v.state.HasFlag(breakState)
return v.state.HasFlag(BreakState)
}
// GetInteropID converts instruction parameter to an interop ID.
@ -512,10 +512,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// each panic at a central point, putting the VM in a fault state and setting error.
defer func() {
if errRecover := recover(); errRecover != nil {
v.state = faultState
v.state = FaultState
err = newError(ctx.ip, op, errRecover)
} else if v.refs.size > MaxStackSize {
v.state = faultState
v.state = FaultState
err = newError(ctx.ip, op, "stack is too big")
}
}()
@ -1268,7 +1268,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.unloadContext(oldCtx)
if v.istack.Len() == 0 {
v.state = haltState
v.state = HaltState
break
}