proximax-storage / go-xpx-chain-sdk Goto Github PK
View Code? Open in Web Editor NEWProximaX Sirius-Chain Golang SDK
License: Apache License 2.0
ProximaX Sirius-Chain Golang SDK
License: Apache License 2.0
When a handler is set to return true, it seems it will remove the function and the websocket will unsubscribe to channel if no more handler for same channel.
But if the websocket is closed right away after the handler execution, the unsubscription to channel will fail and results to panic.
conf, err := sdk.NewConfig([]string{"http://csddev1.xpxsirius.io:3000", "http://csddev2.xpxsirius.io:3000"}, sdk.PrivateTest, sdk.WebsocketReconnectionDefaultTimeout)
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*30)
//defer cancel()
wsClient, err := websocket.NewClient(ctx, conf)
if err != nil {
panic(err)
}
go wsClient.Listen()
address, err := sdk.NewAddressFromRaw("WAFSWQIF2UBLBHOFOYMIQJXZKRSTNONZIM6VYAZT")
if err != nil {
panic(err)
}
txn, err := sdk.NewTransferTransaction(
sdk.NewDeadline(time.Hour),
address,
[]*sdk.Mosaic{},
sdk.NewPlainMessage(fmt.Sprintf("hi there %d", rand.Int())),
sdk.PrivateTest,
)
if err != nil {
panic(err)
}
acct, err := sdk.NewAccountFromPrivateKey("b007b006c2178a8819c6b38dd3b49db1c2170483fdfcff21656f071c7541b5a9", sdk.PrivateTest)
if err != nil {
panic(err)
}
signedTxn, err := acct.Sign(txn)
if err != nil {
panic(err)
}
var wg sync.WaitGroup
wg.Add(1)
err = wsClient.AddUnconfirmedAddedHandlers(address, func(txn sdk.Transaction) bool {
fmt.Println("FOUND", txn.GetAbstractTransaction().Hash.String())
if txn.GetAbstractTransaction().Hash.String() == signedTxn.Hash.String() {
fmt.Println("FOUND HASH IT ON UNCONFIRMED", txn.GetAbstractTransaction().Hash.String())
wg.Done()
// ISSUE
return true
}
return false
})
if err != nil {
panic(err)
}
client := sdk.NewClient(nil, conf)
_, err = client.Transaction.Announce(context.Background(), signedTxn)
fmt.Println("ANNOUNCED", signedTxn.Hash)
if err != nil {
panic(err)
}
wg.Wait()
cancel()
When creating a new subscriber, we use a function that returns the necessary structure.
When registering, we send argument by value, and when deleting, send argument is by reference. This causes a problem when deleting, because the links do not match. We need to consider a mechanism by which we can accurately identify the subscription
Update made here
Multiplying very big uint64 on uint64 can lead to overflow cost of offer
Touches every WebSocket subscriber implementation.
All implementations have map of maps. Also, there is lock which defends wrapper map, but underlying maps are not backed by any lock, what causes Race Condition.
WARNING: DATA RACE
Write at 0x00c0000a64b0 by goroutine 74:
runtime.mapdelete_fast64()
/usr/local/Cellar/go/1.12/libexec/src/runtime/map_fast64.go:272 +0x0
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/subscribers.(*partialAddedImpl).RemoveHandlers()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/subscribers/partial_added.go:65 +0x252
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/handlers.(*partialAddedHandler).Handle.func1()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/handlers/partial_added.go:46 +0x13a
Previous read at 0x00c0000a64b0 by goroutine 84:
runtime.mapiternext()
/usr/local/Cellar/go/1.12/libexec/src/runtime/map.go:853 +0x0
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/handlers.(*partialAddedHandler).Handle()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/handlers/partial_added.go:35 +0x2cd
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket.(*messageRouter).RouteMessage()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/router.go:43 +0x19e
Goroutine 74 (running) created at:
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/handlers.(*partialAddedHandler).Handle()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/handlers/partial_added.go:37 +0x2bc
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket.(*messageRouter).RouteMessage()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/router.go:43 +0x19e
Goroutine 84 (running) created at:
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket.(*CatapultWebsocketClientImpl).Listen()
/go/pkg/mod/github.com/proximax-storage/[email protected]/sdk/websocket/client.go:168 +0x182
Whenever I tried Close(), it always fails with the error below.
panic: close tcp 192.168.20.11:64395->52.221.198.188:3000: use of closed network connection
goroutine 69 [running]:
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket.(*CatapultWebsocketClientImpl).Listen(0xc000388000)
C:/Users/Tyrone/go/src/github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/client.go:162 +0x24a
created by main.testWS
C:/Users/Tyrone/go/src/github.com/proximax-storage/csd-test-playground/test_websocket/main.go:56 +0x1a0
My understanding is there is a race condition on Close() and Listen(). Upon Close(), Listen() would immediately run the first case below.
for {
select {
case <-c.ctx.Done():
if c.conn != nil {
if err := c.conn.Close(); err != nil {
panic(err)
}
c.conn = nil
}
return
case msg := <-messagesChan:
go c.messageRouter.RouteMessage(msg)
}
}
My code:
conf, err := sdk.NewConfig([]string{"http://csddev1.xpxsirius.io:3000", "http://csddev2.xpxsirius.io:3000"}, sdk.PrivateTest, sdk.WebsocketReconnectionDefaultTimeout)
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*30)
wsClient, err := websocket.NewClient(ctx, conf)
if err != nil {
panic(err)
}
go wsClient.Listen()
address, err := sdk.NewAddressFromRaw("WAFSWQIF2UBLBHOFOYMIQJXZKRSTNONZIM6VYAZT")
if err != nil {
panic(err)
}
txn, err := sdk.NewTransferTransaction(
sdk.NewDeadline(time.Hour),
address,
[]*sdk.Mosaic{},
sdk.NewPlainMessage(fmt.Sprintf("hi there %d", rand.Int())),
sdk.PrivateTest,
)
if err != nil {
panic(err)
}
acct, err := sdk.NewAccountFromPrivateKey("b007b006c2178a8819c6b38dd3b49db1c2170483fdfcff21656f071c7541b5a9", sdk.PrivateTest)
if err != nil {
panic(err)
}
signedTxn, err := acct.Sign(txn)
if err != nil {
panic(err)
}
var wg sync.WaitGroup
wg.Add(1)
err = wsClient.AddUnconfirmedAddedHandlers(address, func(txn sdk.Transaction) bool {
fmt.Println("FOUND", txn.GetAbstractTransaction().Hash.String())
if txn.GetAbstractTransaction().Hash.String() == signedTxn.Hash.String() {
fmt.Println("FOUND HASH IT ON UNCONFIRMED", txn.GetAbstractTransaction().Hash.String())
wg.Done()
}
return false
})
if err != nil {
panic(err)
}
client := sdk.NewClient(nil, conf)
_, err = client.Transaction.Announce(context.Background(), signedTxn)
fmt.Println("ANNOUNCED", signedTxn.Hash)
if err != nil {
panic(err)
}
wg.Wait()
wsClient.Close()
My workaround right now is to use context's cancel function. It works most of the time but has a separate issue.
Listen() seems to spawn a goroutine for reading websocket messages.
However, when the websocket is closed, the goroutine sometimes fails with this panic error.
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0xc8 pc=0xa44116]
goroutine 2753 [running]:
github.com/gorilla/websocket.(*Conn).NextReader(0x0, 0xc00000ed28, 0xc000419f40, 0x404de5, 0xc00053e420, 0xc000419f90)
C:/Users/Tyrone/go/src/github.com/gorilla/websocket/conn.go:936 +0x26
github.com/gorilla/websocket.(*Conn).ReadMessage(0x0, 0xc000419f90, 0xc0003cac00, 0x331, 0x600, 0x0, 0x0)
C:/Users/Tyrone/go/src/github.com/gorilla/websocket/conn.go:1026 +0x2f
github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket.(*CatapultWebsocketClientImpl).Listen.func1(0xc000375900, 0xc00053e420)
C:/Users/Tyrone/go/src/github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/client.go:132 +0x116
created by github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket.(*CatapultWebsocketClientImpl).Listen
C:/Users/Tyrone/go/src/github.com/proximax-storage/go-xpx-catapult-sdk/sdk/websocket/client.go:127 +0x98
My understanding is due to the setting of conn
to nil
as shown on the line below.
for {
select {
case <-c.ctx.Done():
if c.conn != nil {
if err := c.conn.Close(); err != nil {
panic(err)
}
c.conn = nil
}
return
case msg := <-messagesChan:
go c.messageRouter.RouteMessage(msg)
}
}
Can we remove the c.conn = nil
to avoid panic?
For some reason, I can't replicate on a simple code the issue but on the app it always happens. The removal of above fixed my proble.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.