Skip to content

Commit

Permalink
Merge pull request #181 from xssnick/dev-v19
Browse files Browse the repository at this point in the history
HighloadV3 & better offline signing support & cleaner behaviour of RL…
  • Loading branch information
xssnick authored Apr 12, 2024
2 parents f87553a + 85a8f0d commit a1194b8
Show file tree
Hide file tree
Showing 22 changed files with 439 additions and 145 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img align="right" width="425px" src="https://github.com/xssnick/props/blob/master/logoimg.png?raw=true">

[![Based on TON][ton-svg]][ton]
![Coverage](https://img.shields.io/badge/Coverage-74.2%25-brightgreen)
![Coverage](https://img.shields.io/badge/Coverage-74.1%25-brightgreen)

Golang library for interacting with TON blockchain.

Expand Down
4 changes: 4 additions & 0 deletions adnl/adnl.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ func (a *ADNL) SetDisconnectHandler(handler func(addr string, key ed25519.Public
a.onDisconnect = handler
}

func (a *ADNL) GetDisconnectHandler() func(addr string, key ed25519.PublicKey) {
return a.onDisconnect
}

func (a *ADNL) SetChannelReadyHandler(handler func(ch *Channel)) {
a.onChannel = handler
}
Expand Down
4 changes: 4 additions & 0 deletions adnl/dht/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ type MockADNL struct {
close func()
}

func (m MockADNL) GetDisconnectHandler() func(addr string, key ed25519.PublicKey) {
return nil
}

func (m MockADNL) GetQueryHandler() func(msg *adnl.MessageQuery) error {
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions adnl/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
type Peer interface {
SetCustomMessageHandler(handler func(msg *MessageCustom) error)
SetQueryHandler(handler func(msg *MessageQuery) error)
GetDisconnectHandler() func(addr string, key ed25519.PublicKey)
SetDisconnectHandler(handler func(addr string, key ed25519.PublicKey))
SendCustomMessage(ctx context.Context, req tl.Serializable) error
Query(ctx context.Context, req, result tl.Serializable) error
Expand All @@ -44,6 +45,10 @@ type peerConn struct {
client adnlClient
}

func (p *peerConn) GetDisconnectHandler() func(addr string, key ed25519.PublicKey) {
return p.client.GetDisconnectHandler()
}

type srvProcessor struct {
lastPacketAt time.Time
processor func(buf []byte) error
Expand Down
5 changes: 5 additions & 0 deletions adnl/overlay/manager-adnl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type ADNL interface {
SetCustomMessageHandler(handler func(msg *adnl.MessageCustom) error)
SetQueryHandler(handler func(msg *adnl.MessageQuery) error)
SetDisconnectHandler(handler func(addr string, key ed25519.PublicKey))
GetDisconnectHandler() func(addr string, key ed25519.PublicKey)
SendCustomMessage(ctx context.Context, req tl.Serializable) error
Query(ctx context.Context, req, result tl.Serializable) error
Answer(ctx context.Context, queryID []byte, result tl.Serializable) error
Expand All @@ -43,6 +44,10 @@ type ADNLWrapper struct {
ADNL
}

func (a *ADNLWrapper) GetDisconnectHandler() func(addr string, key ed25519.PublicKey) {
return a.ADNL.GetDisconnectHandler()
}

func CreateExtendedADNL(adnl ADNL) *ADNLWrapper {
w := &ADNLWrapper{
ADNL: adnl,
Expand Down
11 changes: 9 additions & 2 deletions adnl/overlay/manager-rldp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package overlay

import (
"context"
"crypto/ed25519"
"encoding/hex"
"fmt"
"github.com/xssnick/tonutils-go/adnl/rldp"
Expand Down Expand Up @@ -36,7 +37,13 @@ func CreateExtendedRLDP(rldp RLDP) *RLDPWrapper {
overlays: map[string]*RLDPOverlayWrapper{},
}
w.RLDP.SetOnQuery(w.queryHandler)
w.RLDP.SetOnDisconnect(w.disconnectHandler)
prev := w.GetADNL().GetDisconnectHandler()
w.GetADNL().SetDisconnectHandler(func(addr string, key ed25519.PublicKey) {
if prev != nil {
prev(addr, key)
}
w.disconnectHandler(addr, key)
})

return w
}
Expand Down Expand Up @@ -86,7 +93,7 @@ func (r *RLDPWrapper) queryHandler(transferId []byte, query *rldp.Query) error {
return h(transferId, query)
}

func (r *RLDPWrapper) disconnectHandler() {
func (r *RLDPWrapper) disconnectHandler(addr string, key ed25519.PublicKey) {
var list []func()

r.mx.RLock()
Expand Down
15 changes: 6 additions & 9 deletions adnl/rldp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type ADNL interface {
GetID() []byte
SetCustomMessageHandler(handler func(msg *adnl.MessageCustom) error)
SetDisconnectHandler(handler func(addr string, key ed25519.PublicKey))
GetDisconnectHandler() func(addr string, key ed25519.PublicKey)
SendCustomMessage(ctx context.Context, req tl.Serializable) error
Close()
}
Expand Down Expand Up @@ -68,7 +69,6 @@ func NewClient(a ADNL) *RLDP {
}

a.SetCustomMessageHandler(r.handleMessage)
a.SetDisconnectHandler(r.handleADNLDisconnect)

return r
}
Expand All @@ -87,21 +87,18 @@ func (r *RLDP) SetOnQuery(handler func(transferId []byte, query *Query) error) {
r.onQuery = handler
}

// Deprecated: use GetADNL().SetDisconnectHandler
// WARNING: it overrides underlying adnl disconnect handler
func (r *RLDP) SetOnDisconnect(handler func()) {
r.onDisconnect = handler
r.adnl.SetDisconnectHandler(func(addr string, key ed25519.PublicKey) {
handler()
})
}

func (r *RLDP) Close() {
r.adnl.Close()
}

func (r *RLDP) handleADNLDisconnect(addr string, key ed25519.PublicKey) {
disc := r.onDisconnect
if disc != nil {
disc()
}
}

func (r *RLDP) handleMessage(msg *adnl.MessageCustom) error {
isV2 := true
switch m := msg.Data.(type) {
Expand Down
4 changes: 4 additions & 0 deletions adnl/rldp/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ func (m MockADNL) RemoteAddr() string {
panic("implement me")
}

func (m MockADNL) GetDisconnectHandler() func(addr string, key ed25519.PublicKey) {
return nil
}

func (m MockADNL) SetCustomMessageHandler(handler func(msg *adnl.MessageCustom) error) {
}

Expand Down
1 change: 1 addition & 0 deletions adnl/rldp/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type ADNL interface {
RemoteAddr() string
Query(ctx context.Context, req, result tl.Serializable) error
SetDisconnectHandler(handler func(addr string, key ed25519.PublicKey))
GetDisconnectHandler() func(addr string, key ed25519.PublicKey)
SetCustomMessageHandler(handler func(msg *adnl.MessageCustom) error)
SendCustomMessage(ctx context.Context, req tl.Serializable) error
SetQueryHandler(handler func(msg *adnl.MessageQuery) error)
Expand Down
4 changes: 4 additions & 0 deletions adnl/rldp/http/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type MockADNL struct {
close func()
}

func (m MockADNL) GetDisconnectHandler() func(addr string, key ed25519.PublicKey) {
return nil
}

func (m MockADNL) GetID() []byte {
//TODO implement me
panic("implement me")
Expand Down
36 changes: 26 additions & 10 deletions example/highload-wallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/base64"
"log"
"strings"
"time"

"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/liteclient"
Expand All @@ -16,11 +17,11 @@ import (
func main() {
client := liteclient.NewConnectionPool()

// connect to mainnet lite server
err := client.AddConnection(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
// connect to testnet lite server
configUrl := "https://ton-blockchain.github.io/testnet-global.config.json"
err := client.AddConnectionsFromConfigUrl(context.Background(), configUrl)
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
panic(err)
}

api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
Expand All @@ -29,7 +30,21 @@ func main() {
words := strings.Split("birth pattern then forest walnut then phrase walnut fan pumpkin pattern then cluster blossom verify then forest velvet pond fiction pattern collect then then", " ")

// initialize high-load wallet
w, err := wallet.FromSeed(api, words, wallet.HighloadV2R2)
w, err := wallet.FromSeed(api, words, wallet.ConfigHighloadV3{
MessageTTL: 60 * 5,
MessageBuilder: func(ctx context.Context, subWalletId uint32) (id uint32, createdAt int64, err error) {
// Due to specific of externals emulation on liteserver,
// we need to take something less than or equals to block time, as message creation time,
// otherwise external message will be rejected, because time will be > than emulation time
// hope it will be fixed in the next LS versions
createdAt = time.Now().Unix() - 30

// example query id which will allow you to send 1 tx per second
// but you better to implement your own iterator in database, then you can send unlimited
// but make sure id is less than 1 << 23, when it is higher start from 0 again
return uint32(createdAt % (1 << 23)), createdAt, nil
},
})
if err != nil {
log.Fatalln("FromSeed err:", err.Error())
return
Expand Down Expand Up @@ -65,13 +80,14 @@ func main() {
}

var messages []*wallet.Message
// generate message for each destination, in single transaction can be sent up to 254 messages
// generate message for each destination, in single batch can be sent up to 65k messages (but consider messages size, external size limit is 64kb)
for addrStr, amtStr := range receivers {
addr := address.MustParseAddr(addrStr)
messages = append(messages, &wallet.Message{
Mode: 1, // pay fee separately
Mode: 1 + 2, // pay fee separately, ignore action errors
InternalMessage: &tlb.InternalMessage{
Bounce: false, // force send, even to uninitialized wallets
DstAddr: address.MustParseAddr(addrStr),
Bounce: addr.IsBounceable(),
DstAddr: addr,
Amount: tlb.MustFromTON(amtStr),
Body: comment,
},
Expand All @@ -88,7 +104,7 @@ func main() {
}

log.Println("transaction sent, hash:", base64.StdEncoding.EncodeToString(txHash))
log.Println("explorer link: https://tonscan.org/tx/" + base64.URLEncoding.EncodeToString(txHash))
log.Println("explorer link: https://testnet.tonscan.org/tx/" + base64.URLEncoding.EncodeToString(txHash))
return
}

Expand Down
71 changes: 29 additions & 42 deletions example/wallet-cold-alike/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"encoding/base64"
"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/liteclient"
"github.com/xssnick/tonutils-go/tlb"
Expand Down Expand Up @@ -36,56 +37,42 @@ func main() {

log.Println("wallet address:", w.WalletAddress())

block, err := api.CurrentMasterchainInfo(ctx)
if err != nil {
log.Fatalln("CurrentMasterchainInfo err:", err.Error())
return
}

balance, err := w.GetBalance(ctx, block)
if err != nil {
log.Fatalln("GetBalance err:", err.Error())
return
}
addr := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")

if balance.Nano().Uint64() >= 3000000 {
addr := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")
log.Println("sending transaction...")

log.Println("sending transaction...")
// default message ttl is 3 minutes, it is time during which you can send it to blockchain
// if you need to set longer TTL, you could use this method
// w.GetSpec().(*wallet.SpecV3).SetMessagesTTL(uint32((10 * time.Minute) / time.Second))

// default message ttl is 3 minutes, it is time during which you can send it to blockchain
// if you need to set longer TTL, you could use this method
// w.GetSpec().(*wallet.SpecV3).SetMessagesTTL(uint32((10 * time.Minute) / time.Second))
w.GetSpec().(*wallet.SpecV3).SetSeqnoFetcher(func(ctx context.Context, sub uint32) (uint32, error) {
// Get seqno from your database here, this func will be called during BuildTransfer to get seqno for transaction
return 1, nil
})

// if destination wallet is not initialized you should set bounce = true
msg, err := w.BuildTransfer(addr, tlb.MustFromTON("0.003"), false, "Hello from tonutils-go!")
if err != nil {
log.Fatalln("BuildTransfer err:", err.Error())
return
}
comment, _ := wallet.CreateCommentCell("Hello from tonutils-go!")
withStateInit := true // if wallet is initialized, you may set false to not send additional data

// pack message to send later or from other place
ext, err := w.BuildExternalMessage(ctx, msg)
if err != nil {
log.Fatalln("BuildExternalMessage err:", err.Error())
return
}

// if you wish to send it from diff source, or later, you could serialize it to BoC
// msgCell, _ := ext.ToCell()
// log.Println(base64.StdEncoding.EncodeToString(msgCell.ToBOC()))

// send message to blockchain
err = api.SendExternalMessage(ctx, ext)
if err != nil {
log.Fatalln("Failed to send external message:", err.Error())
return
}
// if destination wallet is not initialized you should set bounce = true
ext, err := w.PrepareExternalMessageForMany(context.Background(), withStateInit, []*wallet.Message{
wallet.SimpleMessageAutoBounce(addr, tlb.MustFromTON("0.003"), comment),
})
if err != nil {
log.Fatalln("BuildTransfer err:", err.Error())
return
}

log.Println("transaction sent, we are not waiting for confirmation")
// if you wish to send message from diff source, or later, you could serialize it to BoC
msgCell, _ := tlb.ToCell(ext)
log.Println(base64.StdEncoding.EncodeToString(msgCell.ToBOC()))

// send message to blockchain
if err = api.SendExternalMessage(ctx, ext); err != nil {
log.Fatalln("Failed to send external message:", err.Error())
return
}

log.Println("not enough balance:", balance.String())
log.Println("transaction sent, we are not waiting for confirmation")

return
}
6 changes: 3 additions & 3 deletions example/wallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func main() {
client := liteclient.NewConnectionPool()

// get config
cfg, err := liteclient.GetConfigFromUrl(context.Background(), "https://ton.org/global.config.json")
cfg, err := liteclient.GetConfigFromUrl(context.Background(), "https://ton.org/testnet-global.config.json")
if err != nil {
log.Fatalln("get config err: ", err.Error())
return
Expand All @@ -31,14 +31,14 @@ func main() {
}

// api client with full proof checks
api := ton.NewAPIClient(client, ton.ProofCheckPolicySecure).WithRetry()
api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
api.SetTrustedBlockFromConfig(cfg)

// bound all requests to single ton node
ctx := client.StickyContext(context.Background())

// seed words of account, you can generate them with any wallet or using wallet.NewSeed() method
words := strings.Split("birth pattern then forest walnut then phrase walnut fan pumpkin pattern then cluster blossom verify then forest velvet pond fiction pattern collect then then", " ")
words := strings.Split("diet diet attack autumn expose honey skate lounge holiday opinion village priority major enroll romance famous motor pact hello rubber express warfare rose whisper", " ")

w, err := wallet.FromSeed(api, words, wallet.V4R2)
if err != nil {
Expand Down
Loading

0 comments on commit a1194b8

Please sign in to comment.