How to subscribe to the event "incoming transaction successful"? - go

How can I subscribe to the event "incoming transaction successful". That is, I want to know that ether has come to my wallet. How to do it using subscription. I do not understand anything.
package main
import (
"context"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"log"
)
func main() {
client, err := ethclient.Dial("wss://mainnet.infura.io/ws/v3/XXXXXXXXXXXXXXXX")
if err != nil {
log.Fatal(err)
}
accs := map[string]string{
"0x92321477416e93Ea452f16015e2F2a13B3BDe8B7":"12e2cc06fb999fa29306f10db6b366e61a4946b9527286a0c56640c94cebd950",
}
keys := make([]common.Address, 0, len(accs))
for k := range accs {
keys = append(keys, common.HexToAddress(k))
}
var ch = make(chan types.Log)
sub, err := client.SubscribeFilterLogs(context.Background(), ethereum.FilterQuery{
BlockHash: nil,
FromBlock: nil,
ToBlock: nil,
Addresses: keys,
Topics: nil,
}, ch)
if err != nil {
log.Fatal(err)
}
defer sub.Unsubscribe()
for l := range ch {
// ???
}
}
Help me please. Where i can find example?
enter image description here

I'm looking through infura.io's API documentation and I'm not finding that they have an endpoint for payouts. The image that you linked above is their UI option for email notifications and has nothing to do with the API. In order to subscribe to an action, it would have to be initiated on their end. You would have to provide them with a callback to execute when that action occurs. Your callback would do the alerting, but they would call it when the trigger (a payout) occurred. Do they have a place to enter webhooks? If so, this would be your subscription.
While this option may not be a subscription, a possible workaround would be for you to poll the getBalance endpoint and compare the result to the previous result, and if there is an increase, alert you.

My solution
sub, err := client.SubscribeNewHead(context.Background(), ch)
get block
b, err := client.BlockByNumber(context.Background(), l.Number)
check all transactions
for _, tx := range b.Transactions() {
msg, err := tx.AsMessage(types.NewEIP155Signer(tx.ChainId()))
}
in msg.To() address )))

actually go-ethereum already provided a demo in their test script:
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/rpc"
)
const (
url = "https://mainnet.infura.io/v3/xxxxxxxx"
wss = "wss://eth-mainnet.alchemyapi.io/v2/xxxxxxxxxxx"
// wss = "wss://mainnet.infura.io/ws/v3/xxxxxxxx"
)
func watch() {
backend, err := ethclient.Dial(url)
if err != nil {
log.Printf("failed to dial: %v", err)
return
}
rpcCli, err := rpc.Dial(wss)
if err != nil {
log.Printf("failed to dial: %v", err)
return
}
gcli := gethclient.New(rpcCli)
txch := make(chan common.Hash, 100)
_, err = gcli.SubscribePendingTransactions(context.Background(), txch)
if err != nil {
log.Printf("failed to SubscribePendingTransactions: %v", err)
return
}
for {
select {
case txhash := <-txch:
tx, _, err := backend.TransactionByHash(context.Background(), txhash)
if err != nil {
continue
}
data, _ := tx.MarshalJSON()
log.Printf("tx: %v", string(data))
}
}
}
func DoTest() {
go watch()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
<-signalChan
}

Related

Golang: how to ensure redis subscribers receive all messages from redis pubsub?

I am trying to publish messages to dynamically generated channels in redis while subscribing all messages from the existing channels.
The following seems to work, but it fails to receive some messages depending on the timing of requests from the client (browser).
I tried "fan-in" for the two go channels in the select statement, but it did not work well.
package main
import (
...
"github.com/go-redis/redis/v8"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{}
var rd = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
var ctx = context.Background()
func echo(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("websocket connection err:", err)
return
}
defer conn.Close()
room := make(chan string)
start := make(chan string)
go func() {
loop:
for {
sub := rd.Subscribe(ctx)
defer sub.Close()
channels := []string{}
for {
select {
//it seems that messages are not received when executing this case sometimes
case channel := <-room:
log.Println("channel", channel)
channels = append(channels, channel)
sub = rd.Subscribe(ctx, channels...)
start <- "ok"
case msg := <-sub.Channel():
log.Println("msg", msg)
err := conn.WriteMessage(websocket.TextMessage, []byte(msg.Payload))
if err != nil {
log.Println("websocket write err:", err)
break loop
}
}
}
}
}()
for {
_, msg, err := conn.ReadMessage()
if err != nil {
log.Println("websocket read err:", err)
break
}
log.Println(string(msg))
chPrefix := strings.Split(string(msg), ":")[0]
ch := chPrefix + "-channel"
if string(msg) == "test" || string(msg) == "yeah" {
room <- ch
log.Println(ch)
log.Println(<-start)
}
if err := rd.Publish(ctx, ch, msg).Err(); err != nil {
log.Println("redis publish err:", err)
break
}
}
}
func main() {
http.Handle("/", http.FileServer(http.Dir("./js")))
http.HandleFunc("/ws", echo)
log.Println("server starting...", "http://localhost:5000")
log.Fatal(http.ListenAndServe("localhost:5000", nil))
}
If by all messages, you mean you do not wish to lose any messages, I would recommend using Redis Streams instead of pub/sub. This will ensure you are not missing messages and can go back on the stream history if necessary.
This is an example of using Go, Streams and websockets that should get you started in that direction

How to get gRPC response limit

I have Go gRPC endpoint that returns an array of some items. I want to limit the number of items in response to make fit the maximum that gRPC allows to send.
My idea is to get the maximum in handler func, divide it by item size and voilĂ . But how can I get response max size?
I don't want to set max response size to make my class independent of grpc instantiation.
main.go
package main
import (
"log"
"main/api"
"main/router"
"net"
"google.golang.org/grpc"
)
func main() {
l, err := net.Listen("tcp", "0.0.0.0:138080")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
r := router.New()
api.RegisterTestGenerateServiceServer(grpcServer, r)
err = grpcServer.Serve(l)
if err != nil {
log.Fatalf("failed to run server: %v", err)
}
}
router.go
package router
import (
"context"
"main/api"
"main/model"
"unsafe"
)
type router struct {
}
func New() *router {
return &router{}
}
func (r *router) TestCall(context.Context, *api.TestCallRequest) (*api.TestCallResponse, error) {
items := somewhere.GetItems()
apiItems := transform.ToAPIItems(items)
itemSize := unsafe.Sizeof(model.Item{})
responseSize := someWonderFuncGetGrpcResponseMaxSize()
NItems := responseSize / itemSize
return &api.TestCallResponse{
Items: apiItems[:NItems],
}, nil
}
Try updating grpc.MaxCallSendMsgSize(s int) on your client to enable your client send larger message.
This worked for me, as example:
func main() {
// create connection
opts := []grpc.DialOption{
// grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), // uncomment for large amount of data returned
}
cc, err := grpc.Dial("localhost:9089", opts...)
if err != nil {
fmt.Printf("failed to dial: %v\n", err)
return
}
defer cc.Close()
client := pb.NewGrpcServiceClient(cc)
data, err := client.GetDataFromGRPCService()

Web server and listening nats at the same time

My code reads input from terminal and send those value to nats while it needs to have an http endpoint.
Separately it works but when I combine all of them it does not read from nats. If you could point me to a right direction I would appreciate.
package main
import (
"bufio"
"fmt"
nats "github.com/nats-io/nats.go"
"html/template"
"log"
"net/http"
"os"
)
func main() {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
tmpl := template.Must(template.ParseFiles(wd + "/template/main.html"))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := TodoPageData{
PageTitle: "Demo",
}
tmpl.Execute(w, data)
})
http.ListenAndServe(":8081", nil)
type message struct {
content string
}
var messages []message
nc, err := nats.Connect(
nats.DefaultURL,
)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Subscribe
if _, err := nc.Subscribe("updates", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
}); err != nil {
log.Fatal(err)
}
// io r/w
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if err := nc.Publish("updates", []byte(scanner.Text())); err != nil {
log.Fatal(err)
}
messages = append(messages, message{scanner.Text()})
for _, message := range messages {
fmt.Println(message.content)
}
}
if scanner.Err() != nil {
// handle error.
}
}
http.ListenAndServe is a blocking call. Start it on a new goroutine:
go http.ListenAndServe(":8081", nil)

golang: net.Conn: check conn status

I encountered a strange behavior of the conn.Read:
let's presume that I have a couple of functions for testing net.Conn:
package example
import (
"io"
"log"
"net"
"os"
"time"
)
func CheckConn(conn net.Conn) (net.Conn, error) {
conn.SetReadDeadline(time.Now())
var one = []byte{}
_, err := conn.Read(one)
if err != nil {
log.Println("Net err: ", err)
}
if err == io.EOF {
return conn, err
}
var zero time.Time
conn.SetReadDeadline(zero)
return conn, nil
}
func CheckConnWithTimeout(conn net.Conn) (net.Conn, error) {
ch := make(chan bool, 1)
defer func() {
ch <- true
}()
go func() {
select {
case <-ch:
case <-time.After(1 * time.Second):
log.Println("It works too long")
os.Exit(1)
}
}()
return CheckConn(conn)
}
And I want to implement tests for it, lets start with this one:
package example
import (
"io"
"net"
"testing"
)
func TestClosedConn(t *testing.T) {
server, client := net.Pipe()
client.Close()
defer server.Close()
_, err := CheckConn(server)
if err != io.EOF {
t.Errorf("Not equal:\nExpected: %v\nactual: %v", io.EOF, err)
}
}
this works pretty well, we will receive io.EOF from CheckConn function, lets add one more test:
func TestClosedConnAfterWrite(t *testing.T) {
server, client := net.Pipe()
go func() {
client.Write([]byte{0xb})
}()
client.Close()
defer server.Close()
_, err := CheckConn(server)
err = nil
if err != io.EOF {
t.Errorf("Not equal:\nExpected: %v\nactual: %v", io.EOF, err)
}
}
looks like the first test, but we wrote to the client before(?) it was closed.
And this will not pass!
conn.Read will return &errors.errorString{s:"EOF"}, instead of io.EOF, so CheckConn will return error == nil,
It looks so weird!
But let's continue the tests, now I want to check unclosed connections:
func TestActiveConn(t *testing.T) {
server, client := net.Pipe()
defer client.Close()
defer server.Close()
_, err := CheckConnWithTimeout(server)
if err != nil {
t.Errorf("Not equal:\nExpected: %v\nactual: %v", nil, err)
}
}
I think you noticed that I use the function with a timeout just because SetReadDeadline will not work in this case(I have no idea why!)
So what is going wrong in last two test cases? Is there a normal way to test the connection? Why SetReadDeadline is not working in this case?

go routine - why websocket reports the connection as closed?

I'm trying to create a client and a server using Go but for some reason the server reports the connection as "closed". As the code is trivial I can't think of anything wrong with my code. Any help is appreciated.
package main
import (
log "github.com/golang/glog"
"net/http"
"golang.org/x/net/websocket"
"time"
"flag"
)
type server struct {
payload chan string
}
// srv pushes the messages received via ws into srv.payload
func (srv *server) serve(ws *websocket.Conn) {
go func() {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Exit(err)
}
srv.payload <- msg
}()
return
}
// This example demonstrates a trivial client/ server.
func main() {
flag.Parse()
srv := server{payload: make(chan string, 10)}
http.Handle("/echo", websocket.Handler(srv.serve))
go func() {
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Errorf("ListenAndServe: " + err.Error())
}
}()
// give the server some time to start listening
time.Sleep(3 *time.Second)
//dial and test the response.
ws, err := websocket.Dial("ws://localhost:12345/echo", "", "http://localhost/?x=45")
if err != nil {
log.Exit(err)
}
ms := "test"
if err := websocket.Message.Send(ws, ms); err != nil {
log.Exit(err)
}
msg := <-srv.payload
if msg != ms{
log.Errorf("msg %v is not %v", ms)
}
}
Error
t.go:21] read tcp 127.0.0.1:12345->127.0.0.1:43135:
Edit:
After some try and error I've found that if I remove the go routine from the serve method it works but it doesn't make sense to me. Any idea why it doesn't work when websocket.Message.Receive is in a separate go routine?
package main
import (
log "github.com/golang/glog"
"net/http"
"golang.org/x/net/websocket"
"time"
"flag"
)
type server struct {
payload chan string
}
// srv pushes the messages received via ws into srv.payload
func (srv *server) serve(ws *websocket.Conn) {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Exit(err)
}
srv.payload <- msg
return
}
// This example demonstrates a trivial client/ server.
func main() {
flag.Parse()
srv := server{payload: make(chan string, 10)}
go func() {
http.Handle("/echo", websocket.Handler(srv.serve))
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Errorf("ListenAndServe: " + err.Error())
}
}()
// give the server some time to start listening
time.Sleep(3 *time.Second)
//dial and test the response.
ws, err := websocket.Dial("ws://localhost:12345/echo", "", "http://localhost/?x=45")
if err != nil {
log.Exit(err)
}
ms := "test"
if err := websocket.Message.Send(ws, ms); err != nil {
log.Exit(err)
}
msg := <-srv.payload
if msg != ms{
log.Errorf("msg %v is not %v", ms)
}
}
The websocket server closes the connection when the handler returns.
Removing the Go routine is the correct fix.

Resources