Skip to content

Commit

Permalink
Merge pull request #132 from xssnick/improvements-182
Browse files Browse the repository at this point in the history
Fixed BoC index serialization rare issue, flag for adnl dns record, a…
  • Loading branch information
xssnick authored Sep 21, 2023
2 parents edf5e1c + dab0b95 commit a1b7609
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 69 deletions.
19 changes: 11 additions & 8 deletions example/account-state/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"sort"

"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/liteclient"
Expand All @@ -13,13 +14,12 @@ 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 mainnet lite servers
err := client.AddConnectionsFromConfigUrl(context.Background(), "https://ton.org/global.config.json")
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
}

// initialize ton api lite connection wrapper
api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()

Expand Down Expand Up @@ -69,14 +69,17 @@ func main() {
log.Printf("send err: %s", err.Error())
return
}
// set previous info from the oldest transaction in list
lastHash = list[0].PrevTxHash
lastLt = list[0].PrevTxLT

// reverse list to show the newest first
sort.Slice(list, func(i, j int) bool {
return list[i].LT > list[j].LT
})

// oldest = first in list
for _, t := range list {
fmt.Println(t.String())
}

// set previous info from the oldest transaction in list
lastHash = list[0].PrevTxHash
lastLt = list[0].PrevTxLT
}
}
2 changes: 1 addition & 1 deletion example/wallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func main() {
// 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", " ")

w, err := wallet.FromSeed(api, words, wallet.V3)
w, err := wallet.FromSeed(api, words, wallet.V4R2)
if err != nil {
log.Fatalln("FromSeed err:", err.Error())
return
Expand Down
16 changes: 11 additions & 5 deletions ton/dns/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,19 @@ func (d *Domain) BuildSetRecordPayload(name string, value *cell.Cell) *cell.Cell
}

func (d *Domain) BuildSetSiteRecordPayload(addr []byte, isStorage bool) *cell.Cell {
var cat uint64 = _CategoryADNLSite
var payload *cell.Cell
if isStorage {
cat = _CategoryStorageSite
payload = cell.BeginCell().MustStoreUInt(_CategoryStorageSite, 16).
MustStoreSlice(addr, 256).
EndCell()
} else {
payload = cell.BeginCell().MustStoreUInt(_CategoryADNLSite, 16).
MustStoreSlice(addr, 256).
MustStoreUInt(0, 8).
EndCell()
}

record := cell.BeginCell().MustStoreRef(cell.BeginCell().MustStoreUInt(cat, 16).MustStoreSlice(addr, 256).EndCell()).EndCell()
return d.BuildSetRecordPayload("site", record)
// https://github.com/ton-blockchain/TEPs/blob/master/text/0081-dns-standard.md#dns-records
return d.BuildSetRecordPayload("site", cell.BeginCell().MustStoreRef(payload).EndCell())
}

func (d *Domain) BuildSetWalletRecordPayload(addr *address.Address) *cell.Cell {
Expand Down
1 change: 1 addition & 0 deletions ton/dns/resolve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func TestDomain_GetRecords(t *testing.T) {
MustStoreSlice(h.Sum(nil), 256).MustStoreRef(cell.BeginCell().
MustStoreUInt(_CategoryADNLSite, 16).
MustStoreSlice(adnlAddr, 256).
MustStoreUInt(0, 8).
EndCell()).EndCell()

if !bytes.Equal(domain.BuildSetSiteRecordPayload(adnlAddr, false).Hash(), site.Hash()) {
Expand Down
6 changes: 3 additions & 3 deletions ton/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/xssnick/tonutils-go/tvm/cell"
)

var apiTestNet = func() *APIClient {
var apiTestNet = func() APIClientWrapped {
client := liteclient.NewConnectionPool()

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
Expand All @@ -26,7 +26,7 @@ var apiTestNet = func() *APIClient {
panic(err)
}

return NewAPIClient(client)
return NewAPIClient(client, ProofCheckPolicySecure)
}()

var api = func() APIClientWrapped {
Expand Down Expand Up @@ -205,7 +205,7 @@ func Test_ExternalMessage(t *testing.T) { // need to deploy contract on test-net
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

ctx = apiTestNet.client.StickyContext(ctx)
ctx = apiTestNet.Client().StickyContext(ctx)

b, err := apiTestNet.GetMasterchainInfo(ctx)
if err != nil {
Expand Down
54 changes: 42 additions & 12 deletions ton/payments/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"context"
"crypto/ed25519"
"crypto/rand"
"encoding/hex"
"fmt"
"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/tlb"
Expand Down Expand Up @@ -51,22 +50,25 @@ func NewPaymentChannelClient(api TonApi) *Client {
}
}

var ErrVerificationNotPassed = fmt.Errorf("verification not passed")

func (c *Client) GetAsyncChannel(ctx context.Context, block *ton.BlockIDExt, addr *address.Address, verify bool) (*AsyncChannel, error) {
acc, err := c.api.GetAccount(ctx, block, addr)
if err != nil {
return nil, fmt.Errorf("failed to get account: %w", err)
}

if !acc.IsActive {
if !acc.IsActive || !acc.State.IsValid || acc.State.Status != tlb.AccountStatusActive {
return nil, fmt.Errorf("channel account is not active")
}

if verify {
codeBoC, _ := hex.DecodeString(AsyncPaymentChannelCodeBoC)
code, _ := cell.FromBOC(codeBoC)
return c.ParseAsyncChannel(addr, acc.Code, acc.Data, verify)
}

if !bytes.Equal(acc.Code.Hash(), code.Hash()) {
return nil, fmt.Errorf("incorrect code hash")
func (c *Client) ParseAsyncChannel(addr *address.Address, code, data *cell.Cell, verify bool) (*AsyncChannel, error) {
if verify {
if !bytes.Equal(code.Hash(), AsyncPaymentChannelCodeHash) {
return nil, ErrVerificationNotPassed
}
}

Expand All @@ -76,20 +78,44 @@ func (c *Client) GetAsyncChannel(ctx context.Context, block *ton.BlockIDExt, add
Status: ChannelStatusUninitialized,
}

err = tlb.LoadFromCell(&ch.Storage, acc.Data.BeginParse())
err := tlb.LoadFromCell(&ch.Storage, data.BeginParse())
if err != nil {
return nil, fmt.Errorf("failed to load storage: %w", err)
}

if verify {
storageData := AsyncChannelStorageData{
KeyA: ch.Storage.KeyA,
KeyB: ch.Storage.KeyB,
ChannelID: ch.Storage.ChannelID,
ClosingConfig: ch.Storage.ClosingConfig,
Payments: ch.Storage.Payments,
}

data, err = tlb.ToCell(storageData)
if err != nil {
return nil, fmt.Errorf("failed to serialize storage data: %w", err)
}

si, err := tlb.ToCell(tlb.StateInit{
Code: AsyncPaymentChannelCode,
Data: data,
})
if err != nil {
return nil, fmt.Errorf("failed to serialize state init: %w", err)
}

if !bytes.Equal(si.Hash(), ch.addr.Data()) {
return nil, ErrVerificationNotPassed
}
}

ch.Status = ch.Storage.calcState()

return ch, nil
}

func (c *Client) GetDeployAsyncChannelParams(channelId ChannelID, isA bool, initialBalance tlb.Coins, ourKey ed25519.PrivateKey, theirKey ed25519.PublicKey, closingConfig ClosingConfig, paymentConfig PaymentConfig) (body, code, data *cell.Cell, err error) {
codeBoC, _ := hex.DecodeString(AsyncPaymentChannelCodeBoC)
code, _ = cell.FromBOC(codeBoC)

if len(channelId) != 16 {
return nil, nil, nil, fmt.Errorf("channelId len should be 16 bytes")
}
Expand Down Expand Up @@ -129,7 +155,11 @@ func (c *Client) GetDeployAsyncChannelParams(channelId ChannelID, isA bool, init
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to serialize message: %w", err)
}
return body, code, data, nil
return body, AsyncPaymentChannelCode, data, nil
}

func (c *AsyncChannel) Address() *address.Address {
return c.addr
}

// calcState - it repeats get_channel_state method of contract,
Expand Down
8 changes: 8 additions & 0 deletions ton/payments/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package payments

import (
"encoding/hex"
"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/tlb"
"github.com/xssnick/tonutils-go/tvm/cell"
Expand All @@ -9,6 +10,13 @@ import (
// AsyncPaymentChannelCodeBoC Taken from https://github.com/ton-blockchain/payment-channels/tree/master#compiled-code
const AsyncPaymentChannelCodeBoC = "B5EE9C72410230010007FB000114FF00F4A413F4BCF2C80B0102012002030201480405000AF26C21F0190202CB06070201202E2F020120080902012016170201200A0B0201200C0D0009D3610F80CC001D6B5007434C7FE8034C7CC1BC0FE19E0201580E0F0201201011002D3E11DBC4BE11DBC43232C7FE11DBC47E80B2C7F2407320008B083E1B7B51343480007E187E80007E18BE80007E18F4FFC07E1934FFC07E1974DFC07E19BC01887080A7F4C7C07E1A34C7C07E1A7D01007E1AB7807080E535007E1AF7BE1B2002012012130201201415008D3E13723E11BE117E113E10540132803E10BE80BE10FE8084F2FFC4B2FFF2DFFC02887080A7FE12BE127E121400F2C7C4B2C7FD0037807080E53E12C073253E1333C5B8B27B5520004D1C3C02FE106CFCB8193E803E800C3E1096283E18BE10C0683E18FE10BE10E8006EFCB819BC032000CF1D3C02FE106CFCB819348020C235C6083E4040E4BE1124BE117890CC3E443CB81974C7C060841A5B9A5D2EBCB81A3E118074DFD66EBCB81CBE803E800C3E1094882FBE10D4882FAC3CB819807E18BE18FE12F43E800C3E10BE10E80068006E7CB8199FFE187C0320004120843777222E9C20043232C15401B3C594013E808532DA84B2C7F2DFF2407EC02002012018190201D42B2C0201201A1B0201201E1F0201201C1D00E5473F00BD401D001D401D021F90102D31F01821043436D74BAF2E068F84601D37F59BAF2E072F844544355F910F8454330F910B0F2E065D33FD33F30F84822B9F84922B9B0F2E06C21F86820F869F84A6E915B8E19F84AD0D33FFA003171D721D33F305033BC02BCB1936DF86ADEE2F800F00C8006F3E12F43E800C7E903E900C3E09DBC41CBE10D62F24CC20C1B7BE10FE11963C03FE10BE11A04020BC03DC3E185C3E189C3E18DB7E1ABC032000B51D3C02F5007400750074087E4040B4C7C0608410DB1BDCEEBCB81A3E118074DFD66EBCB81CBE111510D57E443E1150CC3E442C3CB8197E80007E18BE80007E18F4CFF4CFCC3E1208AE7E1248AE6C3CB81B007E1A3E1A7E003C042001C1573F00BF84A6EF2E06AD2008308D71820F9012392F84492F845E24130F910F2E065D31F018210556E436CBAF2E068F84601D37F59BAF2E072D401D08308D71820F901F8444130F910F2E06501D430D08308D71820F901F8454130F910F2E06501820020120222301FED31F01821043685374BAF2E068F84601D37F59BAF2E072D33FFA00F404552003D200019AD401D0D33FFA00F40430937F206DE2303205D31F01821043685374BAF2E068F84601D37F59BAF2E072D33FFA00F404552003D200019AD401D0D33FFA00F40430937F206DE23032F8485280BEF8495250BEB0524BBE1AB0527ABE19210064B05215BE14B05248BE17B0F2E06970F82305C8CB3F5004FA0215F40015CB3F5004FA0212F400CB1F12CA00CA00C9F86AF00C01C31CFC02FE129BACFCB81AF48020C235C6083E4048E4BE1124BE1178904C3E443CB81974C7C0608410DA19D46EBCB81A3E118074DFD66EBCB81CB5007420C235C6083E407E11104C3E443CB81940750C3420C235C6083E407E11504C3E443CB81940602403F71CFC02FE129BACFCB81AF48020C235C6083E4048E4BE1124BE1178904C3E443CB81974C7C0608410DB10DBAEBCB81A3E118074DFD66EBCB81CBD010C3E12B434CFFE803D0134CFFE803D0134C7FE11DBC4148828083E08EE7CB81BBE11DBC4A83E08EF3CB81C34800C151D5A64D6D4C8F7A2B98E82A49B08B8C3816028292A01FCD31F01821043685374BAF2E068F84601D37F59BAF2E072D33FFA00F404552003D200019AD401D0D33FFA00F40430937F206DE2303205D31F01821043685374BAF2E068F84601D37F59BAF2E072D33FFA00F404552003D200019AD401D0D33FFA00F40430937F206DE230325339BE5381BEB0F8495250BEB0F8485290BEB02502FE5237BE16B05262BEB0F2E06927C20097F84918BEF2E0699137E222C20097F84813BEF2E0699132E2F84AD0D33FFA00F404D33FFA00F404D31FF8476F105220A0F823BCF2E06FD200D20030B3F2E073209C3537373A5274BC5263BC12B18E11323939395250BC5299BC18B14650134440E25319BAB3F2E06D9130E30D7F05C82627002496F8476F1114A098F8476F1117A00603E203003ECB3F5004FA0215F40012CB3F5004FA0213F400CB1F12CA00CA00C9F86AF00C00620A8020F4966FA5208E213050038020F4666FA1208E1001FA00ED1E15DA119450C3A00B9133E2923430E202926C21E2B31B000C3535075063140038C8CB3F5004FA0212F400CB3F5003FA0213F400CB1FCA00C9F86AF00C00D51D3C02FE129BACFCB81AFE12B434CFFE803D010C74CFFE803D010C74C7CC3E11DBC4283E11DBC4A83E08EE7CB81C7E003E10886808E87E18BE10D400E816287E18FE10F04026BE10BE10E83E189C3E18F7BE10B04026BE10FE10A83E18DC3E18F780693E1A293E1A7C042001F53B7EF4C7C8608419F1F4A06EA4CC7C037808608403818830AEA54C7C03B6CC780C882084155DD61FAEA54C3C0476CC780820841E6849BBEEA54C3C04B6CC7808208407C546B3EEA54C3C0576CC780820840223AA8CAEA54C3C05B6CC7808208419BDBC1A6EA54C3C05F6CC780C60840950CAA46EA53C0636CC78202D0008840FF2F00075BC7FE3A7805FC25E87D007D207D20184100D0CAF6A1EC7C217C21B7817C227C22B7817C237C23FC247C24B7817C2524C3B7818823881B22A021984008DBD0CABA7805FC20C8B870FC253748B8F07C256840206B90FD0018C020EB90FD0018B8EB90E98F987C23B7882908507C11DE491839707C23B788507C23B789507C11DE48B9F03A4331C4966"

var AsyncPaymentChannelCode = func() *cell.Cell {
codeBoC, _ := hex.DecodeString(AsyncPaymentChannelCodeBoC)
code, _ := cell.FromBOC(codeBoC)
return code
}()
var AsyncPaymentChannelCodeHash = AsyncPaymentChannelCode.Hash()

// Data types

type Signature struct {
Expand Down
7 changes: 6 additions & 1 deletion tvm/cell/cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ type Cell struct {
special bool
levelMask LevelMask
bitsSz uint
index int
data []byte

hashes []byte
Expand Down Expand Up @@ -173,6 +172,8 @@ func (c *Cell) dump(deep int, bin bool, limitLength int) string {

const _DataCellMaxLevel = 3

// Hash - calculates a hash of cell recursively
// Once calculated, it is cached and can be reused cheap.
func (c *Cell) Hash(level ...int) []byte {
if len(level) > 0 {
return c.getHash(level[0])
Expand All @@ -184,6 +185,10 @@ func (c *Cell) Sign(key ed25519.PrivateKey) []byte {
return ed25519.Sign(key, c.Hash())
}

func (c *Cell) Verify(key ed25519.PublicKey, signature []byte) bool {
return ed25519.Verify(key, c.Hash(), signature)
}

func (c *Cell) GetType() Type {
if !c.special {
return OrdinaryCellType
Expand Down
17 changes: 16 additions & 1 deletion tvm/cell/cell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestCell_HashSign(t *testing.T) {
}

pub, priv, _ := ed25519.GenerateKey(nil)
if !ed25519.Verify(pub, cc.Hash(), cc.Sign(priv)) {
if !cc.Verify(pub, cc.Sign(priv)) {
t.Fatal("sign not match")
}
}
Expand Down Expand Up @@ -239,3 +239,18 @@ func TestCell_ShardStateProof(t *testing.T) {
t.Fatal("wrong hash:", hex.EncodeToString(c.Hash(0)), "should be", hex.EncodeToString(trueHash))
}
}

func TestSameBocIndex(t *testing.T) {
r := BeginCell().MustStoreUInt(555, 32).EndCell()
c := BeginCell().MustStoreUInt(55, 64).MustStoreRef(r).MustStoreRef(r).MustStoreRef(r.copy()).EndCell()
println(hex.EncodeToString(c.ToBOC()))

c2, err := FromBOC(c.ToBOC())
if err != nil {
t.Fatal(err.Error())
}

if !bytes.Equal(c.Hash(), c2.Hash()) {
t.Fatal("wrong hash")
}
}
13 changes: 13 additions & 0 deletions tvm/cell/dict.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,26 @@ func (d *Dictionary) Set(key, value *Cell) error {
return fmt.Errorf("failed to set in dict, err: %w", err)
}

if value == nil {
delete(d.storage, hex.EncodeToString(data))
return nil
}

d.storage[hex.EncodeToString(data)] = &HashmapKV{
Key: key,
Value: value,
}
return nil
}

func (d *Dictionary) Delete(key *Cell) error {
return d.Set(key, nil)
}

func (d *Dictionary) DeleteIntKey(key *big.Int) error {
return d.SetIntKey(key, nil)
}

func (d *Dictionary) GetByIntKey(key *big.Int) *Cell {
return d.Get(BeginCell().MustStoreBigInt(key, d.keySz).EndCell())
}
Expand Down
18 changes: 18 additions & 0 deletions tvm/cell/dict_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,21 @@ func TestDict_CornerSame(t *testing.T) {
t.Fatal("invalid key")
}
}

func TestDict_Delete(t *testing.T) {
mm := NewDict(64)
mm.SetIntKey(big.NewInt(255), BeginCell().EndCell())
mm.SetIntKey(big.NewInt(777), BeginCell().EndCell())
hh, _ := mm.MustToCell().BeginParse().ToDict(64)

hh.DeleteIntKey(big.NewInt(255))
hh2, _ := hh.MustToCell().BeginParse().ToDict(64)

if hh2.GetByIntKey(big.NewInt(255)) != nil {
t.Fatal("invalid key")
}

if hh2.GetByIntKey(big.NewInt(777)) == nil {
t.Fatal("invalid key")
}
}
Loading

0 comments on commit a1b7609

Please sign in to comment.