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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subscriptions: add container hash to notification event #2192

Merged
merged 2 commits into from
Sep 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions docs/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Currently supported events:
Contents: transaction. Filters: sender and signer.
* notification generated during execution

Contents: container hash, contract script hash, stack item. Filters: contract script hash.
Contents: container hash, contract hash, notification name, stack item. Filters: contract hash, notification name.
* transaction executed

Contents: application execution result. Filters: VM state.
Expand Down Expand Up @@ -284,10 +284,10 @@ Example:

### `notification_from_execution` notification

Contains three parameters: contract script hash (hex-encoded LE Uint160
in a string), notification name and stack item (encoded the same way as
`state` field contents for notifications from `getapplicationlog`
response).
Contains four parameters: container hash (block's or transaction's hex-encoded LE
Uint256 hash in a string), contract hash (hex-encoded LE Uint160 in a string),
notification name and stack item (encoded the same way as `state` field contents
for notifications from `getapplicationlog` response).

Example:

Expand Down Expand Up @@ -329,6 +329,7 @@ Example:
},
"contract" : "0x1b4357bff5a01bdf2a6581247cf9ed1e24629176",
"name" : "transfer",
"container" : "0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7",
}
]
}
Expand Down
5 changes: 3 additions & 2 deletions internal/fakechain/fakechain.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
"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"
Expand Down Expand Up @@ -413,7 +414,7 @@ func (chain *FakeChain) SubscribeForExecutions(ch chan<- *state.AppExecResult) {
}

// SubscribeForNotifications implements Blockchainer interface.
func (chain *FakeChain) SubscribeForNotifications(ch chan<- *state.NotificationEvent) {
func (chain *FakeChain) SubscribeForNotifications(ch chan<- *subscriptions.NotificationEvent) {
panic("TODO")
}

Expand Down Expand Up @@ -453,7 +454,7 @@ func (chain *FakeChain) UnsubscribeFromExecutions(ch chan<- *state.AppExecResult
}

// UnsubscribeFromNotifications implements Blockchainer interface.
func (chain *FakeChain) UnsubscribeFromNotifications(ch chan<- *state.NotificationEvent) {
func (chain *FakeChain) UnsubscribeFromNotifications(ch chan<- *subscriptions.NotificationEvent) {
panic("TODO")
}

Expand Down
26 changes: 18 additions & 8 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
Expand Down Expand Up @@ -647,7 +648,7 @@ func (bc *Blockchain) notificationDispatcher() {
// expected, but maps are convenient for adding/deleting elements).
blockFeed = make(map[chan<- *block.Block]bool)
txFeed = make(map[chan<- *transaction.Transaction]bool)
notificationFeed = make(map[chan<- *state.NotificationEvent]bool)
notificationFeed = make(map[chan<- *subscriptions.NotificationEvent]bool)
executionFeed = make(map[chan<- *state.AppExecResult]bool)
)
for {
Expand All @@ -660,7 +661,7 @@ func (bc *Blockchain) notificationDispatcher() {
blockFeed[ch] = true
case chan<- *transaction.Transaction:
txFeed[ch] = true
case chan<- *state.NotificationEvent:
case chan<- *subscriptions.NotificationEvent:
notificationFeed[ch] = true
case chan<- *state.AppExecResult:
executionFeed[ch] = true
Expand All @@ -673,7 +674,7 @@ func (bc *Blockchain) notificationDispatcher() {
delete(blockFeed, ch)
case chan<- *transaction.Transaction:
delete(txFeed, ch)
case chan<- *state.NotificationEvent:
case chan<- *subscriptions.NotificationEvent:
delete(notificationFeed, ch)
case chan<- *state.AppExecResult:
delete(executionFeed, ch)
Expand All @@ -693,7 +694,10 @@ func (bc *Blockchain) notificationDispatcher() {
}
for i := range aer.Events {
for ch := range notificationFeed {
ch <- &aer.Events[i]
ch <- &subscriptions.NotificationEvent{
Container: aer.Container,
NotificationEvent: aer.Events[i],
}
}
}

Expand All @@ -710,7 +714,10 @@ func (bc *Blockchain) notificationDispatcher() {
if aer.VMState == vm.HaltState {
for i := range aer.Events {
for ch := range notificationFeed {
ch <- &aer.Events[i]
ch <- &subscriptions.NotificationEvent{
Container: aer.Container,
NotificationEvent: aer.Events[i],
}
}
}
}
Expand All @@ -728,7 +735,10 @@ func (bc *Blockchain) notificationDispatcher() {
}
for i := range aer.Events {
for ch := range notificationFeed {
ch <- &aer.Events[i]
ch <- &subscriptions.NotificationEvent{
Container: aer.Container,
NotificationEvent: aer.Events[i],
}
}
}
}
Expand Down Expand Up @@ -1653,7 +1663,7 @@ func (bc *Blockchain) SubscribeForTransactions(ch chan<- *transaction.Transactio
// transactions use SubscribeForExecutions instead. Make sure this channel is
// read from regularly as not reading these events might affect other Blockchain
// functions.
func (bc *Blockchain) SubscribeForNotifications(ch chan<- *state.NotificationEvent) {
func (bc *Blockchain) SubscribeForNotifications(ch chan<- *subscriptions.NotificationEvent) {
bc.subCh <- ch
}

Expand Down Expand Up @@ -1681,7 +1691,7 @@ func (bc *Blockchain) UnsubscribeFromTransactions(ch chan<- *transaction.Transac
// UnsubscribeFromNotifications unsubscribes given channel from new
// execution-generated notifications, you can close it afterwards. Passing
// non-subscribed channel is a no-op.
func (bc *Blockchain) UnsubscribeFromNotifications(ch chan<- *state.NotificationEvent) {
func (bc *Blockchain) UnsubscribeFromNotifications(ch chan<- *subscriptions.NotificationEvent) {
bc.unsubCh <- ch
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
Expand Down Expand Up @@ -1397,7 +1398,7 @@ func TestSubscriptions(t *testing.T) {
const chBufSize = 16
blockCh := make(chan *block.Block, chBufSize)
txCh := make(chan *transaction.Transaction, chBufSize)
notificationCh := make(chan *state.NotificationEvent, chBufSize)
notificationCh := make(chan *subscriptions.NotificationEvent, chBufSize)
executionCh := make(chan *state.AppExecResult, chBufSize)

bc := newTestChain(t)
Expand Down
5 changes: 3 additions & 2 deletions pkg/core/blockchainer/blockchainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
"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"
Expand Down Expand Up @@ -69,13 +70,13 @@ type Blockchainer interface {
SetNotary(mod services.Notary)
SubscribeForBlocks(ch chan<- *block.Block)
SubscribeForExecutions(ch chan<- *state.AppExecResult)
SubscribeForNotifications(ch chan<- *state.NotificationEvent)
SubscribeForNotifications(ch chan<- *subscriptions.NotificationEvent)
SubscribeForTransactions(ch chan<- *transaction.Transaction)
VerifyTx(*transaction.Transaction) error
VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) error
GetMemPool() *mempool.Pool
UnsubscribeFromBlocks(ch chan<- *block.Block)
UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)
UnsubscribeFromNotifications(ch chan<- *state.NotificationEvent)
UnsubscribeFromNotifications(ch chan<- *subscriptions.NotificationEvent)
UnsubscribeFromTransactions(ch chan<- *transaction.Transaction)
}
9 changes: 5 additions & 4 deletions pkg/rpc/client/wsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
"github.com/nspcc-dev/neo-go/pkg/util"
)

Expand All @@ -38,8 +39,8 @@ type WSClient struct {
}

// Notification represents server-generated notification for client subscriptions.
// Value can be one of block.Block, state.AppExecResult, state.NotificationEvent
// transaction.Transaction or response.NotaryRequestEvent based on Type.
// Value can be one of block.Block, state.AppExecResult, subscriptions.NotificationEvent
// transaction.Transaction or subscriptions.NotaryRequestEvent based on Type.
type Notification struct {
Type response.EventID
Value interface{}
Expand Down Expand Up @@ -146,11 +147,11 @@ readloop:
case response.TransactionEventID:
val = &transaction.Transaction{}
case response.NotificationEventID:
val = new(state.NotificationEvent)
val = new(subscriptions.NotificationEvent)
case response.ExecutionEventID:
val = new(state.AppExecResult)
case response.NotaryRequestEventID:
val = new(response.NotaryRequestEvent)
val = new(subscriptions.NotaryRequestEvent)
case response.MissedEventID:
// No value.
default:
Expand Down
2 changes: 1 addition & 1 deletion pkg/rpc/client/wsclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func TestWSClientEvents(t *testing.T) {
// Events from RPC server test chain.
var events = []string{
`{"jsonrpc":"2.0","method":"transaction_executed","params":[{"container":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","trigger":"Application","vmstate":"HALT","gasconsumed":"22910000","stack":[],"notifications":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}},{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"transfer","state":{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}}]}]}`,
`{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}}]}`,
`{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"container":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}}]}`,
`{"jsonrpc":"2.0","method":"transaction_executed","params":[{"container":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","trigger":"Application","vmstate":"HALT","gasconsumed":"6042610","stack":[],"notifications":[{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteString","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}]}},{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","eventname":"transfer","state":{"type":"Array","value":[{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteString","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}}]}]}`,
fmt.Sprintf(`{"jsonrpc":"2.0","method":"block_added","params":[%s]}`, b1Verbose),
`{"jsonrpc":"2.0","method":"event_missed","params":[]}`,
Expand Down
15 changes: 2 additions & 13 deletions pkg/rpc/response/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,10 @@ package response
import (
"encoding/json"
"errors"

"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
)

type (
// EventID represents an event type happening on the chain.
EventID byte
// NotaryRequestEvent represents P2PNotaryRequest event either added or removed
// from notary payload pool.
NotaryRequestEvent struct {
Type mempoolevent.Type `json:"type"`
NotaryRequest *payload.P2PNotaryRequest `json:"notaryrequest"`
}
)
// EventID represents an event type happening on the chain.
type EventID byte

const (
// InvalidEventID is an invalid event id that is the default value of
Expand Down
13 changes: 13 additions & 0 deletions pkg/rpc/response/result/subscriptions/notary_request_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package subscriptions

import (
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
)

// NotaryRequestEvent represents P2PNotaryRequest event either added or removed
// from notary payload pool.
type NotaryRequestEvent struct {
Type mempoolevent.Type `json:"type"`
NotaryRequest *payload.P2PNotaryRequest `json:"notaryrequest"`
}
56 changes: 56 additions & 0 deletions pkg/rpc/response/result/subscriptions/notification_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package subscriptions

import (
"encoding/json"
"errors"
"fmt"

"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/util"
)

// NotificationEvent represents wrapper for notification from script execution.
type NotificationEvent struct {
// Container hash is the hash of script container which is either a block or a transaction.
Container util.Uint256
state.NotificationEvent
}

// notificationEventAux is an auxiliary struct for JSON marshalling.
type notificationEventAux struct {
Container util.Uint256 `json:"container"`
}

// MarshalJSON implements implements json.Marshaler interface.
func (ne *NotificationEvent) MarshalJSON() ([]byte, error) {
h, err := json.Marshal(&notificationEventAux{
Container: ne.Container,
})
if err != nil {
return nil, fmt.Errorf("failed to marshal hash: %w", err)
}
exec, err := json.Marshal(ne.NotificationEvent)
if err != nil {
return nil, fmt.Errorf("failed to marshal execution: %w", err)
}

if h[len(h)-1] != '}' || exec[0] != '{' {
return nil, errors.New("can't merge internal jsons")
}
h[len(h)-1] = ','
h = append(h, exec[1:]...)
return h, nil
}

// UnmarshalJSON implements implements json.Unmarshaler interface.
func (ne *NotificationEvent) UnmarshalJSON(data []byte) error {
aux := new(notificationEventAux)
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if err := json.Unmarshal(data, &ne.NotificationEvent); err != nil {
return err
}
ne.Container = aux.Container
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package subscriptions

import (
"testing"

"github.com/nspcc-dev/neo-go/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)

func TestNotificationEvent_MarshalUnmarshalJSON(t *testing.T) {
testserdes.MarshalUnmarshalJSON(t, &NotificationEvent{
Container: util.Uint256{1, 2, 3},
NotificationEvent: state.NotificationEvent{
ScriptHash: util.Uint160{4, 5, 6},
Name: "alarm",
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("qwerty"))}),
},
}, new(NotificationEvent))
}
7 changes: 4 additions & 3 deletions pkg/rpc/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
"github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
Expand Down Expand Up @@ -69,7 +70,7 @@ type (
notaryRequestSubs int
blockCh chan *block.Block
executionCh chan *state.AppExecResult
notificationCh chan *state.NotificationEvent
notificationCh chan *subscriptions.NotificationEvent
transactionCh chan *transaction.Transaction
notaryRequestCh chan mempoolevent.Event
}
Expand Down Expand Up @@ -181,7 +182,7 @@ func New(chain blockchainer.Blockchainer, conf rpc.Config, coreServer *network.S
// These are NOT buffered to preserve original order of events.
blockCh: make(chan *block.Block),
executionCh: make(chan *state.AppExecResult),
notificationCh: make(chan *state.NotificationEvent),
notificationCh: make(chan *subscriptions.NotificationEvent),
transactionCh: make(chan *transaction.Transaction),
notaryRequestCh: make(chan mempoolevent.Event),
}
Expand Down Expand Up @@ -1704,7 +1705,7 @@ chloop:
resp.Payload[0] = tx
case e := <-s.notaryRequestCh:
resp.Event = response.NotaryRequestEventID
resp.Payload[0] = &response.NotaryRequestEvent{
resp.Payload[0] = &subscriptions.NotaryRequestEvent{
Type: e.Type,
NotaryRequest: e.Data.(*payload.P2PNotaryRequest),
}
Expand Down
Loading