+
Skip to content
Merged

v1.15 #356

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4bd54e0
Add support for Ledger compatible seed phrases.
mikalsande Aug 11, 2025
96b8cda
Accept address type as param in RunGetMethod & Added TL type groups f…
xssnick Aug 15, 2025
e9a074c
add bit_test.go test case
deepbean Sep 6, 2025
03c9d5b
Update bit_test.go
deepbean Sep 6, 2025
44a0c2c
Merge pull request #347 from mikalsande/ledger_compatible_seed_phrases
xssnick Sep 9, 2025
fff4d3d
refactor: use maps.Copy for cleaner map handling
fengyuchuanshen Sep 11, 2025
4444c46
Refactor stream cleanup timing and buffer cleanup in RLDP
andreypfau Sep 14, 2025
6fdaab2
Refactor stream handling to use parts slice
andreypfau Sep 14, 2025
27ff64f
Merge pull request #354 from andreypfau/patch-3
xssnick Sep 15, 2025
6fca5b0
Coins refactor & Flexible wallet init & Toncenter api & improvements …
xssnick Sep 16, 2025
07547cd
Add coverage for key conversion helpers
xssnick Sep 16, 2025
f51ad3c
Merge pull request #355 from xssnick/codex/add-tests-for-uncovered-areas
xssnick Sep 16, 2025
159428c
BlockID related refactoring
xssnick Sep 16, 2025
0233b0d
Merge pull request #352 from deepbean/dev-test
xssnick Sep 16, 2025
1ab4908
Merge pull request #353 from fengyuchuanshen/master
xssnick Sep 16, 2025
d0306bb
Merge 1ab4908e4c3fde319ab54ad8bed3774d49abeef4 into 74ec9ab05723a3383…
xssnick Sep 16, 2025
61fde3f
Updated coverage badge
Sep 16, 2025
7225ba8
Overlay small refactor
xssnick Sep 17, 2025
71d2393
Update README.md
xssnick Sep 17, 2025
5dba368
Merge 71d2393f732d6e056b71a41b926208f5bc89fb58 into 74ec9ab05723a3383…
xssnick Sep 17, 2025
9a39ae8
Updated coverage badge
Sep 17, 2025
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
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@

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

Golang library for interacting with TON blockchain.

This library is native golang implementation of ADNL and lite protocol. It works as connection pool and can be connected to multiple lite servers in the same time, balancing is done on lib side.

It is concurrent safe and can be used from multiple goroutines under high workloads.

All main TON protocols are implemented: ADNL, DHT, RLDP, Overlays, HTTP-RLDP, etc.
All main TON protocols are implemented: ADNL, DHT, RLDP, Overlays, etc.

------

If you love this library and want to support its development you can donate any amount of coins to this ton address ☺️
`EQBx6tZZWa2Tbv6BvgcvegoOQxkRrVaBVwBOoW85nbP37_Go`

You can find many usage examples in **[example](https://github.com/xssnick/tonutils-go/tree/master/example)** directory

### How to use
- [Connection](#Connection)
- [Wallet](#Wallet)
Expand Down Expand Up @@ -56,14 +58,9 @@ If you love this library and want to support its development you can donate any
- [Proof creation](#Proofs)
- [Network](https://github.com/xssnick/tonutils-go/tree/master/adnl)
- [ADNL UDP](https://github.com/xssnick/tonutils-go/blob/master/adnl/adnl_test.go)
- [TON Site request](https://github.com/xssnick/tonutils-go/blob/master/example/site-request/main.go)
- [RLDP-HTTP Client-Server](https://github.com/xssnick/tonutils-go/blob/master/example/http-rldp-highload-test/main.go)
- [Custom reconnect policy](#Custom-reconnect-policy)
- [Features to implement](#Features-to-implement)


You can find usage examples in **[example](https://github.com/xssnick/tonutils-go/tree/master/example)** directory

You could also join our **[Telegram channel](https://t.me/tonutilsnews)** and **[group](https://t.me/tonutils)**, feel free ask any questions :)

### Connection
Expand Down Expand Up @@ -502,7 +499,7 @@ client.SetOnDisconnect(func(addr, serverKey string) {
* ✅ Payment channels
* ✅ Liteserver proofs automatic validation
* DHT Server
* TVM
* TVM (Contract execution emulation)

<!-- Badges -->
[ton-svg]: https://img.shields.io/badge/Based%20on-TON-blue
Expand Down
13 changes: 5 additions & 8 deletions address/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/xssnick/tonutils-go/crc16"
"strconv"
"strings"

"github.com/sigurn/crc16"
)

type AddrType int
Expand Down Expand Up @@ -85,8 +84,6 @@ func (a *Address) BitsLen() uint {
return a.bitsLen
}

var crcTable = crc16.MakeTable(crc16.CRC16_XMODEM)

func (a *Address) StringRaw() string {
switch a.addrType {
case NoneAddress:
Expand All @@ -105,7 +102,7 @@ func (a *Address) String() string {
case StdAddress:
var address [36]byte
copy(address[0:34], a.prepareChecksumData())
binary.BigEndian.PutUint16(address[34:], crc16.Checksum(address[:34], crcTable))
binary.BigEndian.PutUint16(address[34:], crc16.ChecksumXMODEM(address[:34]))
return base64.RawURLEncoding.EncodeToString(address[:])
case ExtAddress:
address := make([]byte, 1+4+len(a.data))
Expand Down Expand Up @@ -232,11 +229,11 @@ func ParseAddr(addr string) (*Address, error) {
}

if len(data) != 36 {
return nil, errors.New("incorrect address data")
return nil, errors.New("incorrect address data " + addr)
}

checksum := data[len(data)-2:]
if crc16.Checksum(data[:len(data)-2], crc16.MakeTable(crc16.CRC16_XMODEM)) != binary.BigEndian.Uint16(checksum) {
if crc16.ChecksumXMODEM(data[:len(data)-2]) != binary.BigEndian.Uint16(checksum) {
return nil, errors.New("invalid address")
}

Expand Down Expand Up @@ -267,7 +264,7 @@ func ParseRawAddr(addr string) (*Address, error) {
}

func (a *Address) Checksum() uint16 {
return crc16.Checksum(a.prepareChecksumData(), crc16.MakeTable(crc16.CRC16_XMODEM))
return crc16.ChecksumXMODEM(a.prepareChecksumData())
}

func (a *Address) prepareChecksumData() []byte {
Expand Down
11 changes: 9 additions & 2 deletions address/bit.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package address

// TODO add length checks and panic on errors

func setBit(n *byte, pos uint) {
if pos > 7 {
panic("bit position out of range [0..7]")
}
*n |= 1 << pos
}

func clearBit(n *byte, pos uint) {
if pos > 7 {
panic("bit position out of range [0..7]")
}
mask := ^(1 << pos)
*n &= byte(mask)
}

func hasBit(n byte, pos uint) bool {
if pos > 7 {
panic("bit position out of range [0..7]")
}
val := n & (1 << pos)
return val > 0
}
26 changes: 24 additions & 2 deletions address/bit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,26 @@ func TestClearBit(t *testing.T) {
n *byte
pos uint
}
bytePtr := func(v byte) *byte { b := v; return &b }
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"0", args{bytePtr(0b00000001), 0}},
{"1", args{bytePtr(0b00000010), 1}},
{"2", args{bytePtr(0b00000100), 2}},
{"3", args{bytePtr(0b00001000), 3}},
{"4", args{bytePtr(0b00010000), 4}},
{"5", args{bytePtr(0b00100000), 5}},
{"6", args{bytePtr(0b01000000), 6}},
{"7", args{bytePtr(0b10000000), 7}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
clearBit(tt.args.n, tt.args.pos)
if *tt.args.n != 0 {
t.Errorf("ClearBit() = %v, n = %v", tt.name, *tt.args.n)
}
})
}
}
Expand Down Expand Up @@ -54,15 +65,26 @@ func TestSetBit(t *testing.T) {
n *byte
pos uint
}
bytePtr := func(v byte) *byte { b := v; return &b }
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"0", args{bytePtr(0b00000000), 0}},
{"1", args{bytePtr(0b00000000), 1}},
{"2", args{bytePtr(0b00000000), 2}},
{"3", args{bytePtr(0b00000000), 3}},
{"4", args{bytePtr(0b00000000), 4}},
{"5", args{bytePtr(0b00000000), 5}},
{"6", args{bytePtr(0b00000000), 6}},
{"7", args{bytePtr(0b00000000), 7}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
setBit(tt.args.n, tt.args.pos)
if *tt.args.n == 0 {
t.Errorf("SetBit() = %v, n = %v", tt.name, *tt.args.n)
}
})
}
}
149 changes: 149 additions & 0 deletions adnl/keys/crypto_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package keys

import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/ed25519"
"reflect"
"testing"
Expand Down Expand Up @@ -42,6 +45,19 @@ func Test_sharedKey(t *testing.T) {
},
wantErr: false,
},
{
name: "invalid server key",
args: args{
ourKey: ed25519.NewKeyFromSeed([]byte{
175, 46, 138, 194, 124, 100, 226,
85, 88, 44, 196, 159, 130, 167,
223, 23, 125, 231, 145, 177, 104,
171, 189, 252, 16, 143, 108, 237,
99, 32, 104, 10}),
serverKey: []byte{1, 2, 3},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -56,3 +72,136 @@ func Test_sharedKey(t *testing.T) {
})
}
}

func TestBuildSharedCipher(t *testing.T) {
key := []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}
checksum := []byte{
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
}

stream, err := BuildSharedCipher(key, checksum)
if err != nil {
t.Fatalf("BuildSharedCipher() error = %v", err)
}

plaintext := []byte{
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
}
got := make([]byte, len(plaintext))
stream.XORKeyStream(got, plaintext)

expectedKey := make([]byte, 32)
copy(expectedKey, key[:16])
copy(expectedKey[16:], checksum[16:])
expectedIV := make([]byte, 16)
copy(expectedIV, checksum[:4])
copy(expectedIV[4:], key[20:])
block, err := aes.NewCipher(expectedKey)
if err != nil {
t.Fatalf("failed to init AES cipher: %v", err)
}
expectedStream := cipher.NewCTR(block, expectedIV)
want := make([]byte, len(plaintext))
expectedStream.XORKeyStream(want, plaintext)

if !bytes.Equal(got, want) {
t.Errorf("BuildSharedCipher() produced %x, want %x", got, want)
}
}

func TestNewCipherCtr(t *testing.T) {
key := []byte{
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
}
iv := []byte{
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
}
stream, err := NewCipherCtr(key, iv)
if err != nil {
t.Fatalf("NewCipherCtr() error = %v", err)
}

plaintext := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
got := make([]byte, len(plaintext))
stream.XORKeyStream(got, plaintext)

block, err := aes.NewCipher(key)
if err != nil {
t.Fatalf("failed to init AES cipher: %v", err)
}
expectedStream := cipher.NewCTR(block, iv)
want := make([]byte, len(plaintext))
expectedStream.XORKeyStream(want, plaintext)

if !bytes.Equal(got, want) {
t.Errorf("NewCipherCtr() produced %x, want %x", got, want)
}

if _, err := NewCipherCtr(key[:15], iv); err == nil {
t.Fatal("NewCipherCtr() expected error for invalid key length")
}
}

func TestEd25519PrivateToX25519(t *testing.T) {
seed := []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}
priv := ed25519.NewKeyFromSeed(seed)
got := Ed25519PrivateToX25519(priv)
want := []byte{
0x38, 0x94, 0xee, 0xa4, 0x9c, 0x58, 0x0a, 0xef,
0x81, 0x69, 0x35, 0x76, 0x2b, 0xe0, 0x49, 0x55,
0x9d, 0x6d, 0x14, 0x40, 0xde, 0xde, 0x12, 0xe6,
0xa1, 0x25, 0xf1, 0x84, 0x1f, 0xff, 0x8e, 0x6f,
}

if !bytes.Equal(got, want) {
t.Errorf("Ed25519PrivateToX25519() = %x, want %x", got, want)
}
}

func TestEd25519PubToX25519(t *testing.T) {
seed := []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}
priv := ed25519.NewKeyFromSeed(seed)
got, err := Ed25519PubToX25519(priv.Public().(ed25519.PublicKey))
if err != nil {
t.Fatalf("Ed25519PubToX25519() error = %v", err)
}
want := []byte{
0x47, 0x01, 0xd0, 0x84, 0x88, 0x45, 0x1f, 0x54,
0x5a, 0x40, 0x9f, 0xb5, 0x8a, 0xe3, 0xe5, 0x85,
0x81, 0xca, 0x40, 0xac, 0x3f, 0x7f, 0x11, 0x46,
0x98, 0xcd, 0x71, 0xde, 0xac, 0x73, 0xca, 0x01,
}

if !bytes.Equal(got, want) {
t.Errorf("Ed25519PubToX25519() = %x, want %x", got, want)
}
}

func TestEd25519PubToX25519_InvalidKey(t *testing.T) {
if _, err := Ed25519PubToX25519(ed25519.PublicKey(make([]byte, 31))); err == nil {
t.Fatal("Ed25519PubToX25519() expected error for invalid key length")
}
}
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载