Skip to content
Merged

v1.16 #379

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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[![Based on TON][ton-svg]][ton]
[![Telegram Channel][tgc-svg]][tg-channel]
![Coverage](https://img.shields.io/badge/Coverage-70.2%25-brightgreen)
![Coverage](https://img.shields.io/badge/Coverage-70.0%25-brightgreen)

Golang library for interacting with TON blockchain.

Expand Down
2 changes: 1 addition & 1 deletion example/account-state/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func main() {
return
}
// initialize ton api lite connection wrapper
api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry().WithLSInfoInErrors()

// if we want to route all requests to the same node, we can use it
ctx := client.StickyContext(context.Background())
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ toolchain go1.24.3
require (
filippo.io/edwards25519 v1.1.0
github.com/xssnick/raptorq v1.3.0
golang.org/x/crypto v0.42.0
golang.org/x/crypto v0.45.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/xssnick/raptorq v1.3.0 h1:3GoaySKMg/i8rbjhIuqjxpTTO2l3Gs2/Gh7k3GAjvGo=
github.com/xssnick/raptorq v1.3.0/go.mod h1:kgEVVsZv2hP+IeV7C7985KIFsDdvYq2ARW234SBA9Q4=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
17 changes: 16 additions & 1 deletion liteclient/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ import (

const _StickyCtxKey = "_ton_node_sticky"
const _StickyCtxUsedNodesKey = "_ton_used_nodes_sticky"
const CtxLSInfoKey = "_ls_info"

type OnDisconnectCallback func(addr, key string)

type ADNLResponse struct {
Data tl.Serializable
}

type LSInfo struct {
Details string
}

type ADNLRequest struct {
QueryID []byte
Data any
Expand Down Expand Up @@ -241,8 +246,18 @@ func (c *ConnectionPool) QueryADNL(ctx context.Context, request tl.Serializable,
// wait for response
select {
case resp := <-ch:
took := time.Since(tm)
atomic.AddInt64(&node.weight, 1)
atomic.StoreInt64(&node.lastRespTime, int64(time.Since(tm)))
atomic.StoreInt64(&node.lastRespTime, int64(took))

if inf, ok := ctx.Value(CtxLSInfoKey).(*LSInfo); ok && inf != nil {
str := fmt.Sprintf("(%s, took: %d ms)", node.addr, took.Milliseconds())
if inf.Details != "" {
inf.Details += ", " + str
} else {
inf.Details = str
}
}

reflect.ValueOf(result).Elem().Set(reflect.ValueOf(resp.Data))
return nil
Expand Down
2 changes: 1 addition & 1 deletion tl/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func getStructInfoReferenceByShortName(name string) *structInfo {
func RegisterAllowedGroup(name string, names ...string) {
initMx.Lock() // we lock because init() methods in independent packages can be called in parallel
defer initMx.Unlock()

grp := _allowedGroup[name]
grp = append(grp, names...)
_allowedGroup[name] = grp
Expand Down
27 changes: 25 additions & 2 deletions ton/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package ton
import (
"context"
"fmt"
"github.com/xssnick/tonutils-go/liteclient"
"reflect"
"sync"
"time"

"github.com/xssnick/tonutils-go/liteclient"

"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/tl"
"github.com/xssnick/tonutils-go/tlb"
Expand Down Expand Up @@ -45,6 +46,8 @@ type ContractExecError struct {
type LSError struct {
Code int32 `tl:"int"`
Text string `tl:"string"`

Servers string `tl:"-"`
}

// Deprecated: use APIClientWrapped
Expand All @@ -56,6 +59,7 @@ type APIClientWrapped interface {
GetLibraries(ctx context.Context, list ...[]byte) ([]*cell.Cell, error)
LookupBlock(ctx context.Context, workchain int32, shard int64, seqno uint32) (*BlockIDExt, error)
GetBlockData(ctx context.Context, block *BlockIDExt) (*tlb.Block, error)
GetBlockHeader(ctx context.Context, block *BlockIDExt) (*tlb.BlockHeader, error)
GetBlockTransactionsV2(ctx context.Context, block *BlockIDExt, count uint32, after ...*TransactionID3) ([]TransactionShortInfo, bool, error)
GetBlockShardsInfo(ctx context.Context, master *BlockIDExt) ([]*BlockIDExt, error)
GetBlockchainConfig(ctx context.Context, block *BlockIDExt, onlyParams ...int32) (*BlockchainConfig, error)
Expand All @@ -73,10 +77,18 @@ type APIClientWrapped interface {
WaitForBlock(seqno uint32) APIClientWrapped
WithRetry(maxRetries ...int) APIClientWrapped
WithTimeout(timeout time.Duration) APIClientWrapped
WithLSInfoInErrors() APIClientWrapped
SetTrustedBlock(block *BlockIDExt)
SetTrustedBlockFromConfig(cfg *liteclient.GlobalConfig)
FindLastTransactionByInMsgHash(ctx context.Context, addr *address.Address, msgHash []byte, maxTxNumToScan ...int) (*tlb.Transaction, error)
FindLastTransactionByOutMsgHash(ctx context.Context, addr *address.Address, msgHash []byte, maxTxNumToScan ...int) (*tlb.Transaction, error)
FindLastTransactionByInMsgHashAfterTime(ctx context.Context, addr *address.Address, msgHash []byte, after time.Time) (*tlb.Transaction, error)
FindLastTransactionByOutMsgHashAfterTime(ctx context.Context, addr *address.Address, msgHash []byte, after time.Time) (*tlb.Transaction, error)

GetOutMsgQueueSizes(ctx context.Context, wc *int32, shard *int64) (*OutMsgQueueSizes, error)
GetBlockOutMsgQueueSize(ctx context.Context, block *BlockIDExt) (*BlockOutMsgQueueSize, error)
GetDispatchQueueInfo(ctx context.Context, block *BlockIDExt, afterAddr *address.Address, maxAccounts int) (*DispatchQueueInfo, error)
GetDispatchQueueMessages(ctx context.Context, block *BlockIDExt, addr *address.Address, afterLT uint64, maxMessages int, options ...func(*GetDispatchQueueMessages)) (*DispatchQueueMessages, error)
}

type APIClient struct {
Expand Down Expand Up @@ -146,7 +158,15 @@ func (c *APIClient) WithRetry(maxTries ...int) APIClientWrapped {
}
return &APIClient{
parent: c,
client: &retryClient{original: c.client, maxRetries: tries},
client: &retryClient{LiteClient: c.client, maxRetries: tries},
proofCheckPolicy: c.proofCheckPolicy,
}
}

func (c *APIClient) WithLSInfoInErrors() APIClientWrapped {
return &APIClient{
parent: c,
client: &nodeEnricherWrapper{LiteClient: c.client},
proofCheckPolicy: c.proofCheckPolicy,
}
}
Expand All @@ -168,6 +188,9 @@ func (c *APIClient) root() *APIClient {
}

func (e LSError) Error() string {
if e.Servers != "" {
return fmt.Sprintf("lite server error, code %d: [%s] %s", e.Code, e.Servers, e.Text)
}
return fmt.Sprintf("lite server error, code %d: %s", e.Code, e.Text)
}

Expand Down
98 changes: 92 additions & 6 deletions ton/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,17 @@ func init() {
tl.Register(BlockLinkBackward{}, "liteServer.blockLinkBack to_key_block:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt dest_proof:bytes proof:bytes state_proof:bytes = liteServer.BlockLink")
tl.Register(BlockLinkForward{}, "liteServer.blockLinkForward to_key_block:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt dest_proof:bytes config_proof:bytes signatures:liteServer.SignatureSet = liteServer.BlockLink")
tl.Register(SignatureSet{}, "liteServer.signatureSet validator_set_hash:int catchain_seqno:int signatures:(vector liteServer.signature) = liteServer.SignatureSet")
tl.Register(SignatureSetOrdinary{}, "liteServer.signatureSet.ordinary#f644a6e6 validator_set_hash:int catchain_seqno:int signatures:(vector liteServer.signature) = liteServer.SignatureSet")
tl.Register(SignatureSetSimplex{}, "liteServer.signatureSet.simplex cc_seqno:int validator_set_hash:int signatures:(vector liteServer.signature) session_id:int256 slot:int candidate:bytes = liteServer.SignatureSet")
tl.Register(Signature{}, "liteServer.signature node_id_short:int256 signature:bytes = liteServer.Signature")
tl.Register(BlockID{}, "ton.blockId root_cell_hash:int256 file_hash:int256 = ton.BlockId")
tl.Register(ConsensusDataToSign{}, "consensus.dataToSign session_id:int256 data:bytes = consensus.DataToSign")
tl.Register(ConsensusCandidateID{}, "consensus.candidateId slot:int hash:int256 = consensus.CandidateId")
tl.Register(ConsensusSimplexFinalizeVote{}, "consensus.simplex.finalizeVote id:consensus.CandidateId = consensus.simplex.UnsignedVote")
tl.Register(ConsensusCandidateParent{}, "consensus.candidateParent id:consensus.CandidateId = consensus.CandidateParent")
tl.Register(ConsensusCandidateWithoutParents{}, "consensus.candidateWithoutParents = consensus.CandidateParent")
tl.Register(ConsensusCandidateHashDataOrdinary{}, "consensus.candidateHashDataOrdinary block:tonNode.blockIdExt collated_file_hash:int256 parent:consensus.CandidateParent = consensus.CandidateHashData")
tl.Register(ConsensusCandidateHashDataEmpty{}, "consensus.candidateHashDataEmpty block:tonNode.blockIdExt parent:consensus.candidateId = consensus.CandidateHashData")

tl.Register(GetVersion{}, "liteServer.getVersion = liteServer.Version")
tl.Register(Version{}, "liteServer.version mode:# version:int capabilities:long now:int = liteServer.Version")
Expand Down Expand Up @@ -118,12 +127,12 @@ type BlockLinkBackward struct {
}

type BlockLinkForward struct {
ToKeyBlock bool `tl:"bool"`
From *BlockIDExt `tl:"struct"`
To *BlockIDExt `tl:"struct"`
DestProof []byte `tl:"bytes"`
ConfigProof []byte `tl:"bytes"`
SignatureSet *SignatureSet `tl:"struct boxed"`
ToKeyBlock bool `tl:"bool"`
From *BlockIDExt `tl:"struct"`
To *BlockIDExt `tl:"struct"`
DestProof []byte `tl:"bytes"`
ConfigProof []byte `tl:"bytes"`
SignatureSet any `tl:"struct boxed [liteServer.signatureSet,liteServer.signatureSet.ordinary,liteServer.signatureSet.simplex]"`
}

type SignatureSet struct {
Expand All @@ -132,11 +141,57 @@ type SignatureSet struct {
Signatures []Signature `tl:"vector struct"`
}

type SignatureSetOrdinary struct {
ValidatorSetHash int32 `tl:"int"`
CatchainSeqno int32 `tl:"int"`
Signatures []Signature `tl:"vector struct"`
}

type SignatureSetSimplex struct {
CCSeqno int32 `tl:"int"`
ValidatorSetHash int32 `tl:"int"`
Signatures []Signature `tl:"vector struct"`
SessionID []byte `tl:"int256"`
Slot int32 `tl:"int"`
Candidate []byte `tl:"bytes"`
}

type Signature struct {
NodeIDShort []byte `tl:"int256"`
Signature []byte `tl:"bytes"`
}

type ConsensusDataToSign struct {
SessionID []byte `tl:"int256"`
Data []byte `tl:"bytes"`
}

type ConsensusCandidateID struct {
Slot int32 `tl:"int"`
Hash []byte `tl:"int256"`
}

type ConsensusSimplexFinalizeVote struct {
ID any `tl:"struct boxed [consensus.candidateId]"`
}

type ConsensusCandidateParent struct {
ID any `tl:"struct boxed [consensus.candidateId]"`
}

type ConsensusCandidateWithoutParents struct{}

type ConsensusCandidateHashDataOrdinary struct {
Block BlockIDExt `tl:"struct"`
CollatedFileHash []byte `tl:"int256"`
Parent any `tl:"struct boxed [consensus.candidateParent,consensus.candidateWithoutParents]"`
}

type ConsensusCandidateHashDataEmpty struct {
Block BlockIDExt `tl:"struct"`
Parent ConsensusCandidateID `tl:"struct"`
}

type Object struct{}
type True struct{}

Expand Down Expand Up @@ -446,6 +501,37 @@ func (c *APIClient) GetBlockData(ctx context.Context, block *BlockIDExt) (*tlb.B
return &bData, nil
}

// GetBlockHeader - get block detailed information
func (c *APIClient) GetBlockHeader(ctx context.Context, block *BlockIDExt) (*tlb.BlockHeader, error) {
var resp tl.Serializable
err := c.client.QueryLiteserver(ctx, GetBlockHeader{ID: block}, &resp)
if err != nil {
return nil, err
}

switch t := resp.(type) {
case BlockHeader:
pl, err := cell.FromBOC(t.HeaderProof)
if err != nil {
return nil, err
}

pl, err = cell.UnwrapProof(pl, block.RootHash)
if err != nil {
return nil, fmt.Errorf("incorrect proof: %w", err)
}

var bData tlb.Block
if err = tlb.LoadFromCellAsProof(&bData, pl.BeginParse()); err != nil {
return nil, fmt.Errorf("failed to parse block data proof: %w", err)
}
return &bData.BlockInfo, nil
case LSError:
return nil, t
}
return nil, errUnexpectedResponse(resp)
}

// GetBlockDataAsCell - get block detailed information as a cell
func (c *APIClient) GetBlockDataAsCell(ctx context.Context, block *BlockIDExt) (*cell.Cell, error) {
var resp tl.Serializable
Expand Down
2 changes: 1 addition & 1 deletion ton/dns/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var api = func() ton.APIClientWrapped {
panic(err)
}

return ton.NewAPIClient(client).WithTimeout(5 * time.Second).WithRetry()
return ton.NewAPIClient(client).WithTimeout(5 * time.Second).WithRetry().WithLSInfoInErrors()
}()

func TestDNSClient_Resolve(t *testing.T) {
Expand Down
24 changes: 22 additions & 2 deletions ton/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var apiTestNet = func() APIClientWrapped {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err := client.AddConnectionsFromConfigUrl(ctx, "https://ton-blockchain.github.io/testnet-global.config.json")
err := client.AddConnection(ctx, "109.236.80.69:49913", "AxFZRHVD1qIO9Fyva52P4vC3tRvk8ac1KKOG0c6IVio=")
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -121,6 +121,26 @@ func TestAPIClient_GetBlockData(t *testing.T) {
// TODO: data check
}

func TestAPIClient_GetBlockHeader(t *testing.T) {
ctx := api.Client().StickyContext(context.Background())

b, err := api.CurrentMasterchainInfo(ctx)
if err != nil {
t.Fatal("get block err:", err.Error())
return
}

hdr, err := api.WaitForBlock(b.SeqNo).GetBlockHeader(ctx, b)
if err != nil {
t.Fatal("Get master block data err:", err.Error())
return
}

if hdr.SeqNo != b.SeqNo {
t.Fatal("not eq")
}
}

// commented because public archival LS works too bad to test
/*func TestAPIClient_GetOldBlockData(t *testing.T) {
client := liteclient.NewConnectionPool()
Expand Down Expand Up @@ -723,7 +743,7 @@ func TestAPIClient_GetLibraries(t *testing.T) {
defer cancel()
ctx := apiTestNet.Client().StickyContext(_ctx)

addr := address.MustParseAddr("EQBi-jwMXO2AlSdhun2Th8lDr2jgsijuqWdyyD-ec-K1SYY1")
addr := address.MustParseAddr("0QDSbmZlj51noKgXhUmrfcIcjJXXtLgDis2ydvx8uKKqXhHQ")

b, err := apiTestNet.CurrentMasterchainInfo(ctx)
if err != nil {
Expand Down
Loading