From 0fba7b5f4cd7a72bddf73540d374e44fe7c7ab56 Mon Sep 17 00:00:00 2001 From: polkadev Date: Tue, 22 Dec 2020 11:55:38 +0800 Subject: [PATCH 1/2] add observer example --- api/watch.go | 18 ++-- examples/{ => api}/event_watcher/main.go | 0 .../{ => api}/event_watcher/query_event.go | 0 examples/{ => api}/push_extrinsic/main.go | 0 examples/{ => api}/push_extrinsic/push.go | 0 examples/observer/main.go | 88 +++++++++++++++++++ observer/observer.go | 10 ++- utils/log/log.go | 9 ++ utils/os.go | 14 +++ 9 files changed, 127 insertions(+), 12 deletions(-) rename examples/{ => api}/event_watcher/main.go (100%) rename examples/{ => api}/event_watcher/query_event.go (100%) rename examples/{ => api}/push_extrinsic/main.go (100%) rename examples/{ => api}/push_extrinsic/push.go (100%) create mode 100644 examples/observer/main.go create mode 100644 utils/os.go diff --git a/api/watch.go b/api/watch.go index 3fc5182..195ce4b 100644 --- a/api/watch.go +++ b/api/watch.go @@ -8,7 +8,7 @@ import ( "github.com/patractlabs/go-patract/utils/log" ) -type watcher struct { +type Watcher struct { wg sync.WaitGroup eventChann chan evtMsgInChann stat int @@ -18,10 +18,10 @@ type watcher struct { logger log.Logger } -func NewWatcher(logger log.Logger, url string) *watcher { +func NewWatcher(logger log.Logger, url string) *Watcher { scanner := NewScanner(logger, url) - return &watcher{ + return &Watcher{ scanner: scanner, cli: scanner.Cli(), logger: logger, @@ -29,25 +29,25 @@ func NewWatcher(logger log.Logger, url string) *watcher { } } -func (s *watcher) Cli() *Client { +func (s *Watcher) Cli() *Client { return s.cli } -func (w *watcher) nextStatStep() { +func (w *Watcher) nextStatStep() { w.mutex.Lock() defer w.mutex.Unlock() w.stat++ } -func (w *watcher) Status() int { +func (w *Watcher) Status() int { w.mutex.RLock() defer w.mutex.RUnlock() return w.stat } -func (w *watcher) Wait() { +func (w *Watcher) Wait() { w.logger.Debug("watcher start wait stopped") w.wg.Wait() @@ -55,7 +55,7 @@ func (w *watcher) Wait() { w.logger.Debug("watcher stopped") } -func (w *watcher) Watch(ctx context.Context, fromHeight uint64, h EventHandler) error { +func (w *Watcher) Watch(ctx context.Context, fromHeight uint64, h EventHandler) error { w.logger.Debug("start scanner first", "from", fromHeight) // init the client @@ -131,7 +131,7 @@ func (w *watcher) Watch(ctx context.Context, fromHeight uint64, h EventHandler) return nil } -func (w *watcher) stop() { +func (w *Watcher) stop() { w.logger.Debug("watcher start stop") close(w.eventChann) } diff --git a/examples/event_watcher/main.go b/examples/api/event_watcher/main.go similarity index 100% rename from examples/event_watcher/main.go rename to examples/api/event_watcher/main.go diff --git a/examples/event_watcher/query_event.go b/examples/api/event_watcher/query_event.go similarity index 100% rename from examples/event_watcher/query_event.go rename to examples/api/event_watcher/query_event.go diff --git a/examples/push_extrinsic/main.go b/examples/api/push_extrinsic/main.go similarity index 100% rename from examples/push_extrinsic/main.go rename to examples/api/push_extrinsic/main.go diff --git a/examples/push_extrinsic/push.go b/examples/api/push_extrinsic/push.go similarity index 100% rename from examples/push_extrinsic/push.go rename to examples/api/push_extrinsic/push.go diff --git a/examples/observer/main.go b/examples/observer/main.go new file mode 100644 index 0000000..1c5eb19 --- /dev/null +++ b/examples/observer/main.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "flag" + "io/ioutil" + + "github.com/centrifuge/go-substrate-rpc-client/types" + "github.com/patractlabs/go-patract/contracts/erc20" + "github.com/patractlabs/go-patract/metadata" + "github.com/patractlabs/go-patract/observer" + "github.com/patractlabs/go-patract/utils" + "github.com/patractlabs/go-patract/utils/log" +) + +var ( + flagURL = flag.String("url", "ws://localhost:9944", "url to chain node") + flagContractMetadata = flag.String("metadata", "", "metadata for contract") + flagCodeHash = flag.String("hash", "", "codeHash for contract") +) + +func main() { + if !flag.Parsed() { + flag.Parse() + } + + logger := log.NewLogger() + defer func() { + logger.Flush() + }() + + o := observer.New(logger, *flagURL) + ctx, cancelFunc := context.WithCancel(context.Background()) + + metaBz, err := ioutil.ReadFile(*flagContractMetadata) + if err != nil { + logger.Error("read metadata err", "err", err, "path", *flagContractMetadata) + return + } + + hash, err := types.HexDecodeString(*flagCodeHash) + if err != nil { + logger.Error("hex decode code hash err", "err", err, "path", *flagCodeHash) + return + } + + o = o.WithFromHeight(0).WithMetaDataBytes(types.NewHash(hash), metaBz) + + metaData := o.MetaData(types.NewHash(hash)) + + h := observer.NewEvtHandler() + h = h.WithContractExecution(func(l log.Logger, height uint64, evt types.EventContractsContractExecution) { + data := evt.Data + + l.Debug("handler contract execution", "height", height) + + typ := metadata.GetEvtTypeIdx(data) + switch typ { + case 0: + var transfer erc20.EventTransfer + err := metaData.Spec.Events.DecodeEvt(metaData.NewCtxForDecode(data).WithLogger(l), &transfer) + if err != nil { + logger.Error("evt decode transfer error", "err", err, "height", height) + } + logger.Info("transfer event", "evt", transfer) + case 1: + var approve erc20.EventApproval + err := metaData.Spec.Events.DecodeEvt(metaData.NewCtxForDecode(data).WithLogger(l), &approve) + if err != nil { + logger.Error("evt decode approve error", "err", err, "height", height) + } + logger.Info("approve event", "evt", approve) + } + }) + + if err := o.WatchEvent(ctx, h); err != nil { + logger.Error("watch event error", "err", err) + return + } + + utils.HoldToClose(func() { + cancelFunc() + o.Wait() + + logger.Info("observer stop") + logger.Flush() + }) +} diff --git a/observer/observer.go b/observer/observer.go index 8663988..485efeb 100644 --- a/observer/observer.go +++ b/observer/observer.go @@ -15,6 +15,7 @@ type ContractObserver struct { fromHeight uint64 contractIDs map[types.AccountID]bool metaDatas map[types.Hash]*metadata.Data + watcher *api.Watcher } func New(logger log.Logger, url string) *ContractObserver { @@ -58,9 +59,9 @@ func (w *ContractObserver) MetaData(codeHash types.Hash) *metadata.Data { } func (w *ContractObserver) WatchEvent(ctx context.Context, handler *EvtHandler) error { - watcher := api.NewWatcher(w.logger, w.url) + w.watcher = api.NewWatcher(w.logger, w.url) - return watcher.Watch(ctx, w.fromHeight, + return w.watcher.Watch(ctx, w.fromHeight, func(l log.Logger, height uint64, evt *types.EventRecords) error { if len(evt.Contracts_Instantiated)+ @@ -79,8 +80,11 @@ func (w *ContractObserver) WatchEvent(ctx context.Context, handler *EvtHandler) }) } -func (w *ContractObserver) logContractEvts(height uint64, evt *types.EventRecords) { +func (w *ContractObserver) Wait() { + w.watcher.Wait() +} +func (w *ContractObserver) logContractEvts(height uint64, evt *types.EventRecords) { w.logger.Debug("block event", "height", height) for _, e := range evt.Contracts_Instantiated { diff --git a/utils/log/log.go b/utils/log/log.go index b4c4414..5638c71 100644 --- a/utils/log/log.go +++ b/utils/log/log.go @@ -14,6 +14,8 @@ const ( // Logger a logger interface type Logger interface { + Flush() + Debug(msg string, args ...interface{}) Info(msg string, args ...interface{}) Warn(msg string, args ...interface{}) @@ -68,6 +70,13 @@ func tryProcessTypes(arg interface{}) (string, bool) { return "", false } +func (l *loggerByZap) Flush() { + err := l.l.Sync() + if err != nil { + fmt.Printf("logger flush error %s", err.Error()) + } +} + // Debug imp debug log func (l *loggerByZap) Debug(msg string, args ...interface{}) { if len(args) == 0 { diff --git a/utils/os.go b/utils/os.go new file mode 100644 index 0000000..34e7371 --- /dev/null +++ b/utils/os.go @@ -0,0 +1,14 @@ +package utils + +import ( + "os" + "os/signal" + "syscall" +) + +func HoldToClose(waitFunc func()) { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + <-c + waitFunc() +} From 9566a9339ce58388cf6e24c6274759b2ddadd495 Mon Sep 17 00:00:00 2001 From: polkadev Date: Tue, 22 Dec 2020 12:06:21 +0800 Subject: [PATCH 2/2] update README.md --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index 78a90c0..1c28c94 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,48 @@ Call Transfer: ) ``` +### Rest + +We can use rest to get unsigned raw byte datas for constract call, it can help to build a offline signaturer for contract. + +can use this for example: [rest](https://github.com/patractlabs/go-patract/blob/master/examples/rest/main.go) + +start the rest server: + +```bash +go run ./examples/rest +``` + +to get data: + +```bash +curl -X POST \ + 'http://localhost:8899/erc20/exec/transfer?isOffline=true&contract=5HKinTRKW9THEJxbQb22Nfyq9FPWNVZ9DQ2GEQ4Vg1LqTPuk' \ + -H 'content-type: application/json' \ + -d '{ + "nonce":1, + "chain_status":{ + "spec_version":1, + "tx_version":1, + "block_hash":"0xc20f241b61039e5685d118c7fbc8b27210153c21eee7686a9466f22e01281114", + "genesis_hash":"0xc20f241b61039e5685d118c7fbc8b27210153c21eee7686a9466f22e01281114" + }, + "contract":"5HKinTRKW9THEJxbQb22Nfyq9FPWNVZ9DQ2GEQ4Vg1LqTPuk", + "origin":"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "gas_limit":"500000000000", + "args":{ + "to":"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "value":"100000000" + } +}' +``` + +### observer + +For a contract, we need observer events for the contract, can use `observer` to build a contract events observer service: + +example: [observer](https://github.com/patractlabs/go-patract/blob/master/examples/observer/main.go) + ## Thanks - [Centrifuge's GSRPC](https://github.com/centrifuge/go-substrate-rpc-client)