From 37dfd358fcae851436439829b54eeb2f03b6a015 Mon Sep 17 00:00:00 2001 From: iam047801 Date: Mon, 19 Sep 2022 21:05:33 +0300 Subject: [PATCH 01/14] nft: get methods AtBlock and ContentFromSlice --- ton/nft/collection.go | 23 ++++++++++++++++++----- ton/nft/content.go | 6 +++++- ton/nft/integration_test.go | 3 ++- ton/nft/item-editable.go | 3 +++ ton/nft/item.go | 3 +++ 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/ton/nft/collection.go b/ton/nft/collection.go index 4d87ad77..5dba95c8 100644 --- a/ton/nft/collection.go +++ b/ton/nft/collection.go @@ -3,10 +3,11 @@ package nft import ( "context" "fmt" - "github.com/xssnick/tonutils-go/ton" "math/big" "math/rand" + "github.com/xssnick/tonutils-go/ton" + "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/tvm/cell" @@ -60,7 +61,10 @@ func (c *CollectionClient) GetNFTAddressByIndex(ctx context.Context, index *big. if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetNFTAddressByIndexAtBlock(ctx, index, b) +} +func (c *CollectionClient) GetNFTAddressByIndexAtBlock(ctx context.Context, index *big.Int, b *tlb.BlockInfo) (*address.Address, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_nft_address_by_index", index) if err != nil { return nil, fmt.Errorf("failed to run get_nft_address_by_index method: %w", err) @@ -84,7 +88,10 @@ func (c *CollectionClient) RoyaltyParams(ctx context.Context) (*CollectionRoyalt if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.RoyaltyParamsAtBlock(ctx, b) +} +func (c *CollectionClient) RoyaltyParamsAtBlock(ctx context.Context, b *tlb.BlockInfo) (*CollectionRoyaltyParams, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "royalty_params") if err != nil { return nil, fmt.Errorf("failed to run royalty_params method: %w", err) @@ -118,14 +125,17 @@ func (c *CollectionClient) RoyaltyParams(ctx context.Context) (*CollectionRoyalt } func (c *CollectionClient) GetNFTContent(ctx context.Context, index *big.Int, individualNFTContent ContentAny) (ContentAny, error) { - con, err := toNftContent(individualNFTContent) + b, err := c.api.CurrentMasterchainInfo(ctx) if err != nil { - return nil, fmt.Errorf("failed to convert nft content to cell: %w", err) + return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetNFTContentAtBlock(ctx, index, individualNFTContent, b) +} - b, err := c.api.CurrentMasterchainInfo(ctx) +func (c *CollectionClient) GetNFTContentAtBlock(ctx context.Context, index *big.Int, individualNFTContent ContentAny, b *tlb.BlockInfo) (ContentAny, error) { + con, err := toNftContent(individualNFTContent) if err != nil { - return nil, fmt.Errorf("failed to get masterchain info: %w", err) + return nil, fmt.Errorf("failed to convert nft content to cell: %w", err) } res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_nft_content", index, con) @@ -151,7 +161,10 @@ func (c *CollectionClient) GetCollectionData(ctx context.Context) (*CollectionDa if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetCollectionDataAtBlock(ctx, b) +} +func (c *CollectionClient) GetCollectionDataAtBlock(ctx context.Context, b *tlb.BlockInfo) (*CollectionData, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_collection_data") if err != nil { return nil, fmt.Errorf("failed to run get_collection_data method: %w", err) diff --git a/ton/nft/content.go b/ton/nft/content.go index 61cea2a7..6fb3bd09 100644 --- a/ton/nft/content.go +++ b/ton/nft/content.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "errors" "fmt" + "github.com/xssnick/tonutils-go/tvm/cell" ) @@ -29,7 +30,10 @@ type ContentSemichain struct { } func ContentFromCell(c *cell.Cell) (ContentAny, error) { - s := c.BeginParse() + return ContentFromSlice(c.BeginParse()) +} + +func ContentFromSlice(s *cell.Slice) (ContentAny, error) { if s.BitsLeft() < 8 { if s.RefsNum() == 0 { return nil, errors.New("invalid content") diff --git a/ton/nft/integration_test.go b/ton/nft/integration_test.go index c881522d..92d6ac3a 100644 --- a/ton/nft/integration_test.go +++ b/ton/nft/integration_test.go @@ -4,13 +4,14 @@ import ( "context" "encoding/hex" "fmt" - "github.com/xssnick/tonutils-go/address" "log" "os" "strings" "testing" "time" + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/liteclient" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" diff --git a/ton/nft/item-editable.go b/ton/nft/item-editable.go index c4048631..1f92943a 100644 --- a/ton/nft/item-editable.go +++ b/ton/nft/item-editable.go @@ -31,7 +31,10 @@ func (c *ItemEditableClient) GetEditor(ctx context.Context) (*address.Address, e if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetEditorAtBlock(ctx, b) +} +func (c *ItemEditableClient) GetEditorAtBlock(ctx context.Context, b *tlb.BlockInfo) (*address.Address, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_editor") if err != nil { return nil, fmt.Errorf("failed to run get_editor method: %w", err) diff --git a/ton/nft/item.go b/ton/nft/item.go index f045e75d..358a80cf 100644 --- a/ton/nft/item.go +++ b/ton/nft/item.go @@ -46,7 +46,10 @@ func (c *ItemClient) GetNFTData(ctx context.Context) (*ItemData, error) { if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetNFTDataAtBlock(ctx, b) +} +func (c *ItemClient) GetNFTDataAtBlock(ctx context.Context, b *tlb.BlockInfo) (*ItemData, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_nft_data") if err != nil { return nil, fmt.Errorf("failed to run get_collection_data method: %w", err) From f62a299e72fdcbd5c8073c2634e736f4a4cbc5ec Mon Sep 17 00:00:00 2001 From: iam047801 Date: Mon, 19 Sep 2022 21:06:38 +0300 Subject: [PATCH 02/14] wallet: add Version.String() --- ton/wallet/wallet.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ton/wallet/wallet.go b/ton/wallet/wallet.go index 74d6a71a..13c76776 100644 --- a/ton/wallet/wallet.go +++ b/ton/wallet/wallet.go @@ -7,11 +7,12 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/xssnick/tonutils-go/ton" "math/rand" "strings" "time" + "github.com/xssnick/tonutils-go/ton" + "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/tvm/cell" @@ -35,6 +36,22 @@ const ( Unknown Version = 0 ) +func (v Version) String() string { + if v == Unknown { + return "unknown" + } + if v/10 > 0 && v/10 < 10 { + return fmt.Sprintf("V%dR%d", v/10, v%10) + } + if v/100 == 1 { + return fmt.Sprintf("highload V%dR%d", v/100/10, v%10) + } + if v/100 == 2 { + return fmt.Sprintf("lockup") + } + return fmt.Sprintf("%d", v) +} + var ( walletCodeHex = map[Version]string{ V1R1: _V1R1CodeHex, V1R2: _V1R2CodeHex, V1R3: _V1R3CodeHex, From 1c6139b683e75258ae2c33e6d2b99f9e22c136e4 Mon Sep 17 00:00:00 2001 From: iam047801 Date: Mon, 19 Sep 2022 21:09:41 +0300 Subject: [PATCH 03/14] jetton: add AtBlock --- ton/jetton/jetton.go | 9 ++++++++- ton/jetton/wallet.go | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ton/jetton/jetton.go b/ton/jetton/jetton.go index 9b89be73..a15f83ff 100644 --- a/ton/jetton/jetton.go +++ b/ton/jetton/jetton.go @@ -3,12 +3,13 @@ package jetton import ( "context" "fmt" + "math/big" + "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" "github.com/xssnick/tonutils-go/ton/nft" "github.com/xssnick/tonutils-go/tvm/cell" - "math/big" ) type TonApi interface { @@ -49,7 +50,10 @@ func (c *Client) GetJettonWallet(ctx context.Context, ownerAddr *address.Address if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetJettonWalletAtBlock(ctx, ownerAddr, b) +} +func (c *Client) GetJettonWalletAtBlock(ctx context.Context, ownerAddr *address.Address, b *tlb.BlockInfo) (*WalletClient, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_wallet_address", cell.BeginCell().MustStoreAddr(ownerAddr).EndCell().BeginParse()) if err != nil { @@ -77,7 +81,10 @@ func (c *Client) GetJettonData(ctx context.Context) (*Data, error) { if err != nil { return nil, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetJettonDataAtBlock(ctx, b) +} +func (c *Client) GetJettonDataAtBlock(ctx context.Context, b *tlb.BlockInfo) (*Data, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_jetton_data") if err != nil { return nil, fmt.Errorf("failed to run get_jetton_data method: %w", err) diff --git a/ton/jetton/wallet.go b/ton/jetton/wallet.go index 3defe954..60ee3a97 100644 --- a/ton/jetton/wallet.go +++ b/ton/jetton/wallet.go @@ -3,11 +3,12 @@ package jetton import ( "context" "fmt" + "math/rand" + "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" "github.com/xssnick/tonutils-go/tvm/cell" - "math/rand" ) type TransferPayload struct { @@ -43,7 +44,10 @@ func (c *WalletClient) GetBalance(ctx context.Context) (tlb.Coins, error) { if err != nil { return tlb.Coins{}, fmt.Errorf("failed to get masterchain info: %w", err) } + return c.GetBalanceAtBlock(ctx, b) +} +func (c *WalletClient) GetBalanceAtBlock(ctx context.Context, b *tlb.BlockInfo) (tlb.Coins, error) { res, err := c.master.api.RunGetMethod(ctx, b, c.addr, "get_wallet_data") if err != nil { if cErr, ok := err.(ton.ContractExecError); ok && cErr.Code == ton.ErrCodeContractNotInitialized { From 4dd6a2e1e8786a128fab8f3499dafe345a424029 Mon Sep 17 00:00:00 2001 From: iam047801 Date: Mon, 19 Sep 2022 21:11:37 +0300 Subject: [PATCH 04/14] dns: add AtBlock --- ton/dns/resolve.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ton/dns/resolve.go b/ton/dns/resolve.go index 2d687eec..eca82962 100644 --- a/ton/dns/resolve.go +++ b/ton/dns/resolve.go @@ -4,12 +4,13 @@ import ( "context" "crypto/sha256" "fmt" + "strings" + "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" "github.com/xssnick/tonutils-go/ton/nft" "github.com/xssnick/tonutils-go/tvm/cell" - "strings" ) var ErrNoSuchRecord = fmt.Errorf("no such dns record") @@ -46,23 +47,26 @@ func NewDNSClient(api TonApi, root *address.Address) *Client { } func (c *Client) Resolve(ctx context.Context, domain string) (*Domain, error) { + b, err := c.api.CurrentMasterchainInfo(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get masterchain info: %w", err) + } + return c.ResolveAtBlock(ctx, domain, b) +} + +func (c *Client) ResolveAtBlock(ctx context.Context, domain string, b *tlb.BlockInfo) (*Domain, error) { chain := strings.Split(domain, ".") for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { // reverse array chain[i], chain[j] = chain[j], chain[i] } - return c.resolve(ctx, c.root, strings.Join(chain, "\x00")+"\x00") + return c.resolve(ctx, c.root, strings.Join(chain, "\x00")+"\x00", b) } -func (c *Client) resolve(ctx context.Context, contractAddr *address.Address, chain string) (*Domain, error) { - b, err := c.api.CurrentMasterchainInfo(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get masterchain info: %w", err) - } - +func (c *Client) resolve(ctx context.Context, contractAddr *address.Address, chain string, b *tlb.BlockInfo) (*Domain, error) { name := []byte(chain) nameCell := cell.BeginCell() - if err = nameCell.StoreSlice(name, uint(len(name)*8)); err != nil { + if err := nameCell.StoreSlice(name, uint(len(name)*8)); err != nil { return nil, fmt.Errorf("failed to pack domain name: %w", err) } @@ -110,7 +114,7 @@ func (c *Client) resolve(ctx context.Context, contractAddr *address.Address, cha return nil, fmt.Errorf("failed to load next root: %w", err) } - return c.resolve(ctx, nextRoot, chain[bytesResolved:]) + return c.resolve(ctx, nextRoot, chain[bytesResolved:], b) } records, err := s.ToDict(256) From 2482f2b7230876918b0298c997e57b8aee0628c0 Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Mon, 3 Oct 2022 00:32:08 +0300 Subject: [PATCH 05/14] get time api method --- ton/api.go | 2 ++ ton/gettime.go | 34 ++++++++++++++++++++++++++++++++++ ton/integration_test.go | 13 ++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 ton/gettime.go diff --git a/ton/api.go b/ton/api.go index 8b0ad100..9a7e92ce 100644 --- a/ton/api.go +++ b/ton/api.go @@ -24,6 +24,7 @@ const ( _ListBlockTransactions int32 = -1375942694 _LookupBlock int32 = -87492834 _WaitMasterchainSeqno int32 = -1159022446 + _GetTime int32 = 380459572 ) // responses @@ -37,6 +38,7 @@ const ( _BlockTransactions int32 = -1114854101 _BlockHeader int32 = 1965916697 _AllShardsInfo int32 = 160425773 + _CurrentTime int32 = -380436467 _BoolTrue int32 = -1720552011 _BoolFalse int32 = -1132882121 diff --git a/ton/gettime.go b/ton/gettime.go new file mode 100644 index 00000000..a9db7a70 --- /dev/null +++ b/ton/gettime.go @@ -0,0 +1,34 @@ +package ton + +import ( + "context" + "encoding/binary" + "errors" +) + +func (c *APIClient) GetTime(ctx context.Context) (uint32, error) { + + resp, err := c.client.Do(ctx, _GetTime, nil) + if err != nil { + return 0, err + } + + switch resp.TypeID { + case _CurrentTime: + if len(resp.Data) < 4 { + return 0, errors.New("not enough length") + } + time := binary.LittleEndian.Uint32(resp.Data) + return time, nil + + case _LSError: + var lsErr LSError + resp.Data, err = lsErr.Load(resp.Data) + if err != nil { + return 0, err + } + return 0, lsErr + } + + return 0, errors.New("unknown response type") +} diff --git a/ton/integration_test.go b/ton/integration_test.go index 9ebd00bf..331a800b 100644 --- a/ton/integration_test.go +++ b/ton/integration_test.go @@ -125,7 +125,7 @@ func Test_ExternalMessage(t *testing.T) { // need to deploy contract on test-net ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - ctx = apiTestNet.client.StickyContext(ctx) + ctx = apiTestNet.client.StickyContext(ctx) b, err := apiTestNet.GetMasterchainInfo(ctx) if err != nil { @@ -375,3 +375,14 @@ func TestAPIClient_WaitNextBlock(t *testing.T) { t.Fatal("it works with not master") } } + +func Test_GetTime(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + utime, err := api.GetTime(ctx) + if err != nil { + t.Fatal("get time err:", err.Error()) + } + log.Println("current node utime: ", time.Unix(int64(utime), 0)) +} From 49baea0f1344f17bb7032531efe2c2f8738234e4 Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Mon, 3 Oct 2022 15:19:52 +0300 Subject: [PATCH 06/14] BlockExtra tag fix --- tlb/block.go | 1 + ton/integration_test.go | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tlb/block.go b/tlb/block.go index 74c9502d..940cb14b 100644 --- a/tlb/block.go +++ b/tlb/block.go @@ -63,6 +63,7 @@ type McBlockExtra struct { } type BlockExtra struct { + _ Magic `tlb:"#4a33f6fd"` InMsgDesc *cell.Cell `tlb:"^"` OutMsgDesc *cell.Cell `tlb:"^"` ShardAccountBlocks *cell.Cell `tlb:"^"` diff --git a/ton/integration_test.go b/ton/integration_test.go index 9ebd00bf..f433754a 100644 --- a/ton/integration_test.go +++ b/ton/integration_test.go @@ -84,10 +84,24 @@ func TestAPIClient_GetBlockData(t *testing.T) { _, err = api.GetBlockData(ctx, b) if err != nil { - t.Fatal("GetBlockData err:", err.Error()) + t.Fatal("Get master block data err:", err.Error()) return } + shards, err := api.GetBlockShardsInfo(ctx, b) + if err != nil { + log.Fatalln("get shards err:", err.Error()) + return + } + + for _, shard := range shards { + _, err = api.GetBlockData(ctx, shard) + if err != nil { + t.Fatal("Get shard block data err:", err.Error()) + return + } + } + // TODO: data check } @@ -125,7 +139,7 @@ func Test_ExternalMessage(t *testing.T) { // need to deploy contract on test-net ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - ctx = apiTestNet.client.StickyContext(ctx) + ctx = apiTestNet.client.StickyContext(ctx) b, err := apiTestNet.GetMasterchainInfo(ctx) if err != nil { From 3e52b43627944e6c08523887684475c7c6741d90 Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Tue, 4 Oct 2022 16:38:00 +0300 Subject: [PATCH 07/14] block header decoding --- tlb/block.go | 213 +++++++++++++++++++++++++++++++++++++++- tlb/shard.go | 3 +- ton/integration_test.go | 7 +- 3 files changed, 220 insertions(+), 3 deletions(-) diff --git a/tlb/block.go b/tlb/block.go index 940cb14b..85b0a046 100644 --- a/tlb/block.go +++ b/tlb/block.go @@ -3,6 +3,7 @@ package tlb import ( "encoding/binary" "errors" + "fmt" "github.com/xssnick/tonutils-go/tvm/cell" ) @@ -75,7 +76,7 @@ type BlockExtra struct { type Block struct { _ Magic `tlb:"#11ef55aa"` GlobalID int32 `tlb:"## 32"` - BlockInfo *cell.Cell `tlb:"^"` + BlockInfo BlockHeader `tlb:"^"` ValueFlow *cell.Cell `tlb:"^"` StateUpdate StateUpdate `tlb:"^"` Extra *BlockExtra `tlb:"^"` @@ -84,3 +85,213 @@ type Block struct { type AllShardsInfo struct { ShardHashes *cell.Dictionary `tlb:"dict 32"` } + +type BlockHeader struct { // BlockInfo from block.tlb + blockInfoPart + GenSoftware *GlobalVersion + MasterRef *ExtBlkRef + PrevRef BlkPrevInfo + PrevVertRef *BlkPrevInfo +} + +type blockInfoPart struct { + _ Magic `tlb:"#9bc7a987"` + Version uint32 `tlb:"## 32"` + NotMaster bool `tlb:"bool"` + AfterMerge bool `tlb:"bool"` + BeforeSplit bool `tlb:"bool"` + AfterSplit bool `tlb:"bool"` + WantSplit bool `tlb:"bool"` + WantMerge bool `tlb:"bool"` + KeyBlock bool `tlb:"bool"` + VertSeqnoIncr bool `tlb:"bool"` + Flags uint32 `tlb:"## 8"` + SeqNo uint32 `tlb:"## 32"` + VertSeqNo uint32 `tlb:"## 32"` + Shard ShardIdent `tlb:"."` + GenUtime uint32 `tlb:"## 32"` + StartLt uint64 `tlb:"## 64"` + EndLt uint64 `tlb:"## 64"` + GenValidatorListHashShort uint32 `tlb:"## 32"` + GenCatchainSeqno uint32 `tlb:"## 32"` + MinRefMcSeqno uint32 `tlb:"## 32"` + PrevKeyBlockSeqno uint32 `tlb:"## 32"` +} + +type ExtBlkRef struct { + EndLt uint64 `tlb:"## 64"` + SeqNo uint32 `tlb:"## 32"` + RootHash []byte `tlb:"bits 256"` + FileHash []byte `tlb:"bits 256"` +} + +type GlobalVersion struct { + _ Magic `tlb:"#c4"` + Version uint32 `tlb:"## 32"` + Capabilities uint64 `tlb:"## 64"` +} + +type BlkPrevInfo struct { + Prev1 ExtBlkRef + Prev2 *ExtBlkRef +} + +func (h *BlockHeader) LoadFromCell(loader *cell.Slice) error { + var infoPart blockInfoPart + err := LoadFromCell(&infoPart, loader) + if err != nil { + return err + } + h.blockInfoPart = infoPart + + if infoPart.Flags&1 == 1 { + var globalVer GlobalVersion + err = LoadFromCell(&globalVer, loader) + if err != nil { + return err + } + h.GenSoftware = &globalVer + } + + if infoPart.NotMaster { + var masterRef ExtBlkRef + l, err := loader.LoadRef() // skip master_ref:not_master?^BlkMasterInfo + if err != nil { + return err + } + err = LoadFromCell(&masterRef, l) + if err != nil { + return err + } + h.MasterRef = &masterRef + } + + l, err := loader.LoadRef() + if err != nil { + return err + } + prevRef, err := loadBlkPrevInfo(l, infoPart.AfterMerge) + if err != nil { + return err + } + h.PrevRef = *prevRef + + if infoPart.VertSeqnoIncr { + l, err := loader.LoadRef() + if err != nil { + return err + } + prevVertRef, err := loadBlkPrevInfo(l, false) + if err != nil { + return err + } + h.PrevVertRef = prevVertRef + } + return nil +} + +func loadBlkPrevInfo(loader *cell.Slice, afterMerge bool) (*BlkPrevInfo, error) { + var res BlkPrevInfo + + if !afterMerge { + var blkRef ExtBlkRef + err := LoadFromCell(&blkRef, loader) + if err != nil { + return nil, err + } + res.Prev1 = blkRef + return &res, nil + } + + var blkRef1, blkRef2 ExtBlkRef + prev1, err := loader.LoadRef() + if err != nil { + return nil, err + } + prev2, err := loader.LoadRef() + if err != nil { + return nil, err + } + err = LoadFromCell(&blkRef1, prev1) + if err != nil { + return nil, err + } + err = LoadFromCell(&blkRef2, prev2) + if err != nil { + return nil, err + } + + res.Prev1 = blkRef1 + res.Prev2 = &blkRef2 + return &res, nil +} + +func convertShardIdent(si ShardIdent) (workchain int32, shard uint64) { + shard = si.ShardPrefix + pow2 := uint64(1) << (63 - si.PrefixBits) + shard |= pow2 + return si.WorkchainID, shard +} + +func shardChild(shard uint64, left bool) uint64 { + x := lowerBit64(shard) >> 1 + if left { + return shard - x + } + return shard + x +} + +func shardParent(shard uint64) uint64 { + x := lowerBit64(shard) + return (shard - x) | (x << 1) +} + +func lowerBit64(x uint64) uint64 { + return x & bitsNegate64(x) +} + +func bitsNegate64(x uint64) uint64 { + return ^x + 1 +} + +func (h *BlockHeader) GetParentBlocks() ([]*BlockInfo, error) { + var parents []*BlockInfo + workchain, shard := convertShardIdent(h.Shard) + + if !h.AfterMerge && !h.AfterSplit { + return []*BlockInfo{{ + Workchain: workchain, + SeqNo: h.PrevRef.Prev1.SeqNo, + RootHash: h.PrevRef.Prev1.RootHash, + FileHash: h.PrevRef.Prev1.FileHash, + Shard: int64(shard), + }}, nil + } else if !h.AfterMerge && h.AfterSplit { + return []*BlockInfo{{ + Workchain: workchain, + SeqNo: h.PrevRef.Prev1.SeqNo, + RootHash: h.PrevRef.Prev1.RootHash, + FileHash: h.PrevRef.Prev1.FileHash, + Shard: int64(shardParent(shard)), + }}, nil + } + + if h.PrevRef.Prev2 == nil { + return nil, fmt.Errorf("must be 2 parent blocks after merge") + } + parents = append(parents, &BlockInfo{ + Workchain: workchain, + SeqNo: h.PrevRef.Prev1.SeqNo, + RootHash: h.PrevRef.Prev1.RootHash, + FileHash: h.PrevRef.Prev1.FileHash, + Shard: int64(shardChild(shard, true)), + }) + parents = append(parents, &BlockInfo{ + Workchain: workchain, + SeqNo: h.PrevRef.Prev1.SeqNo, + RootHash: h.PrevRef.Prev1.RootHash, + FileHash: h.PrevRef.Prev1.FileHash, + Shard: int64(shardChild(shard, false)), + }) + return parents, nil +} diff --git a/tlb/shard.go b/tlb/shard.go index ac6f01a9..df70a242 100644 --- a/tlb/shard.go +++ b/tlb/shard.go @@ -16,7 +16,8 @@ type ShardState struct { } type ShardIdent struct { - PrefixBits int8 `tlb:"## 6"` + _ Magic `tlb:"$00"` + PrefixBits int8 `tlb:"## 6"` // #<= 60 WorkchainID int32 `tlb:"## 32"` ShardPrefix uint64 `tlb:"## 64"` } diff --git a/ton/integration_test.go b/ton/integration_test.go index f433754a..f11aa448 100644 --- a/ton/integration_test.go +++ b/ton/integration_test.go @@ -95,11 +95,16 @@ func TestAPIClient_GetBlockData(t *testing.T) { } for _, shard := range shards { - _, err = api.GetBlockData(ctx, shard) + data, err := api.GetBlockData(ctx, shard) if err != nil { t.Fatal("Get shard block data err:", err.Error()) return } + _, err = data.BlockInfo.GetParentBlocks() + if err != nil { + t.Fatal("Get block parents err:", err.Error()) + return + } } // TODO: data check From 9c8519bec791c7a69485c847a1321264524f792f Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Tue, 4 Oct 2022 17:07:16 +0300 Subject: [PATCH 08/14] comment fix --- tlb/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tlb/block.go b/tlb/block.go index 85b0a046..389a944b 100644 --- a/tlb/block.go +++ b/tlb/block.go @@ -155,7 +155,7 @@ func (h *BlockHeader) LoadFromCell(loader *cell.Slice) error { if infoPart.NotMaster { var masterRef ExtBlkRef - l, err := loader.LoadRef() // skip master_ref:not_master?^BlkMasterInfo + l, err := loader.LoadRef() if err != nil { return err } From 11a5ac49d64a064c89eff2aac1e1a54d14efb407 Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Wed, 5 Oct 2022 02:46:34 +0300 Subject: [PATCH 09/14] public shard converter --- tlb/block.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tlb/block.go b/tlb/block.go index 389a944b..d4ba4ae5 100644 --- a/tlb/block.go +++ b/tlb/block.go @@ -226,7 +226,7 @@ func loadBlkPrevInfo(loader *cell.Slice, afterMerge bool) (*BlkPrevInfo, error) return &res, nil } -func convertShardIdent(si ShardIdent) (workchain int32, shard uint64) { +func ConvertShardIdentToShard(si ShardIdent) (workchain int32, shard uint64) { shard = si.ShardPrefix pow2 := uint64(1) << (63 - si.PrefixBits) shard |= pow2 @@ -256,7 +256,7 @@ func bitsNegate64(x uint64) uint64 { func (h *BlockHeader) GetParentBlocks() ([]*BlockInfo, error) { var parents []*BlockInfo - workchain, shard := convertShardIdent(h.Shard) + workchain, shard := ConvertShardIdentToShard(h.Shard) if !h.AfterMerge && !h.AfterSplit { return []*BlockInfo{{ From 31dce5f654ddf8033f4c87802fa66a4152233b50 Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Thu, 6 Oct 2022 00:52:04 +0300 Subject: [PATCH 10/14] get parents fix --- tlb/block.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tlb/block.go b/tlb/block.go index d4ba4ae5..679c1984 100644 --- a/tlb/block.go +++ b/tlb/block.go @@ -288,9 +288,9 @@ func (h *BlockHeader) GetParentBlocks() ([]*BlockInfo, error) { }) parents = append(parents, &BlockInfo{ Workchain: workchain, - SeqNo: h.PrevRef.Prev1.SeqNo, - RootHash: h.PrevRef.Prev1.RootHash, - FileHash: h.PrevRef.Prev1.FileHash, + SeqNo: h.PrevRef.Prev2.SeqNo, + RootHash: h.PrevRef.Prev2.RootHash, + FileHash: h.PrevRef.Prev2.FileHash, Shard: int64(shardChild(shard, false)), }) return parents, nil From 200051cfc0652fce87763e394704fe6c58e2650c Mon Sep 17 00:00:00 2001 From: Alexey Kostenko Date: Thu, 6 Oct 2022 01:35:58 +0300 Subject: [PATCH 11/14] shard state fix --- tlb/shard.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- ton/getstate.go | 2 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tlb/shard.go b/tlb/shard.go index df70a242..ec5410fd 100644 --- a/tlb/shard.go +++ b/tlb/shard.go @@ -4,7 +4,7 @@ import ( "github.com/xssnick/tonutils-go/tvm/cell" ) -type ShardState struct { +type ShardStateUnsplit struct { _ Magic `tlb:"#9023afe2"` GlobalID int32 `tlb:"## 32"` ShardIdent ShardIdent `tlb:"."` @@ -15,6 +15,11 @@ type ShardState struct { } `tlb:"^"` } +type ShardState struct { + Left ShardStateUnsplit + Right *ShardStateUnsplit +} + type ShardIdent struct { _ Magic `tlb:"$00"` PrefixBits int8 `tlb:"## 6"` // #<= 60 @@ -41,3 +46,41 @@ type ShardDesc struct { MinRefMcSeqNo uint32 `tlb:"## 32"` GenUTime uint32 `tlb:"## 32"` } + +func (s *ShardState) LoadFromCell(loader *cell.Slice) error { + preloader := loader.Copy() + tag, err := preloader.LoadUInt(32) + if err != nil { + return err + } + switch tag { + case 0x5f327da5: + var left, right ShardStateUnsplit + leftRef, err := loader.LoadRef() + if err != nil { + return err + } + rightRef, err := loader.LoadRef() + if err != nil { + return err + } + err = LoadFromCell(&left, leftRef) + if err != nil { + return err + } + err = LoadFromCell(&right, rightRef) + if err != nil { + return err + } + s.Left = left + s.Right = &right + case 0x9023afe2: + var state ShardStateUnsplit + err = LoadFromCell(&state, loader) + if err != nil { + return err + } + s.Left = state + } + return nil +} diff --git a/ton/getstate.go b/ton/getstate.go index 39348fd2..607498f7 100644 --- a/ton/getstate.go +++ b/ton/getstate.go @@ -74,7 +74,7 @@ func (c *APIClient) GetAccount(ctx context.Context, block *tlb.BlockInfo, addr * return nil, fmt.Errorf("failed to load ref ShardStateUnsplit: %w", err) } - var shardState tlb.ShardState + var shardState tlb.ShardStateUnsplit err = tlb.LoadFromCell(&shardState, ssuRef) if err != nil { return nil, fmt.Errorf("failed to load ref ShardState: %w", err) From 4d498709b1ccf699ab882f686c3e7f10d02bc105 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Tue, 18 Oct 2022 14:27:55 +0300 Subject: [PATCH 12/14] GetConfig method + improvements and fixes --- example/block-scan/main.go | 31 ++++++ example/deploy-nft-collection/main.go | 8 +- tlb/loader.go | 2 +- tlb/shard.go | 45 +++++++++ ton/api.go | 3 + ton/getconfig.go | 132 ++++++++++++++++++++++++++ ton/integration_test.go | 48 ++++++++++ ton/nft/content.go | 16 ++++ ton/nft/item.go | 14 ++- tvm/cell/serialize.go | 19 +--- 10 files changed, 293 insertions(+), 25 deletions(-) create mode 100644 ton/getconfig.go diff --git a/example/block-scan/main.go b/example/block-scan/main.go index 58c07409..1e2e7e13 100644 --- a/example/block-scan/main.go +++ b/example/block-scan/main.go @@ -2,11 +2,13 @@ package main import ( "context" + "fmt" "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/liteclient" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" "log" + "time" ) func main() { @@ -32,6 +34,9 @@ func main() { // if it will go down, another lite server will be used ctx := api.Client().StickyContext(context.Background()) + // storage for last seen shard seqno + shardLastSeqno := map[string]uint32{} + for { log.Printf("scanning %d master block...\n", master.SeqNo) @@ -42,6 +47,32 @@ func main() { return } + // shards in master block may have holes, e.g. shard seqno 2756461, then 2756463, and no 2756462 in master chain + // thus we need to scan a bit back in case of discovering a hole, till last seen, to fill the misses. + var ext []*tlb.BlockInfo + for _, shard := range shards { + id := fmt.Sprintf("%d|%d", shard.Workchain, shard.Shard) + have, ok := shardLastSeqno[id] + if ok && have < shard.SeqNo-1 { + // find every shard from first missed till previous of new shard, and add them to scan list + for x := have + 1; x < shard.SeqNo; x++ { + for { + missed, err := api.LookupBlock(ctx, shard.Workchain, shard.Shard, x) + if err != nil { + log.Printf("lookupBlock old shards %d %d %d err: %v", shard.Workchain, shard.Shard, x, err.Error()) + time.Sleep(1 * time.Second) + continue + } + log.Printf("discovered missed shard from chain %d %d %d", shard.Workchain, shard.Shard, x) + ext = append(ext, missed) + break + } + } + } + shardLastSeqno[id] = shard.SeqNo + } + shards = append(shards, ext...) + var txList []*tlb.Transaction // for each shard block getting transactions diff --git a/example/deploy-nft-collection/main.go b/example/deploy-nft-collection/main.go index 20b4abd5..fcdc8e01 100644 --- a/example/deploy-nft-collection/main.go +++ b/example/deploy-nft-collection/main.go @@ -88,11 +88,13 @@ func getContractData(collectionOwnerAddr, royaltyAddr *address.Address) *cell.Ce MustStoreAddr(royaltyAddr). EndCell() - collectionContent := nft.ContentOffchain{URI: "https://tonutils.com"} + // collection data + collectionContent := nft.ContentOffchain{URI: "https://tonutils.com/collection.json"} collectionContentCell, _ := collectionContent.ContentCell() - commonContent := nft.ContentOffchain{URI: "https://tonutils.com/nft/"} - commonContentCell, _ := commonContent.ContentCell() + // prefix for NFTs data + uri := "https://tonutils.com/nft/" + commonContentCell := cell.BeginCell().MustStoreStringSnake(uri).EndCell() contentRef := cell.BeginCell(). MustStoreRef(collectionContentCell). diff --git a/tlb/loader.go b/tlb/loader.go index f1e3128e..82af2da2 100644 --- a/tlb/loader.go +++ b/tlb/loader.go @@ -211,7 +211,7 @@ func LoadFromCell(v any, loader *cell.Slice) error { } if ldMagic != uint64(magic) { - return fmt.Errorf("magic is not correct") + return fmt.Errorf("magic is not correct for %s, want %x, got %x", rv.Type().String(), magic, ldMagic) } continue } else if settings[0] == "dict" { diff --git a/tlb/shard.go b/tlb/shard.go index ac6f01a9..c9b93a88 100644 --- a/tlb/shard.go +++ b/tlb/shard.go @@ -1,6 +1,8 @@ package tlb import ( + "fmt" + "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tvm/cell" ) @@ -9,13 +11,34 @@ type ShardState struct { GlobalID int32 `tlb:"## 32"` ShardIdent ShardIdent `tlb:"."` Seqno uint32 `tlb:"## 32"` + VertSeqno uint32 `tlb:"## 32"` + GenUTime uint32 `tlb:"## 32"` + GenLT uint64 `tlb:"## 64"` + MinRefMCSeqno uint32 `tlb:"## 32"` OutMsgQueueInfo *cell.Cell `tlb:"^"` + BeforeSplit bool `tlb:"bool"` Accounts struct { ShardAccounts *cell.Dictionary `tlb:"dict 256"` } `tlb:"^"` + Stats *cell.Cell `tlb:"^"` + McStateExtra *McStateExtra `tlb:"maybe ^"` +} + +type McStateExtra struct { + _ Magic `tlb:"#cc26"` + ShardHashes *cell.Dictionary `tlb:"dict 32"` + ConfigParams ConfigParams `tlb:"."` + Info *cell.Cell `tlb:"^"` + GlobalBalance CurrencyCollection `tlb:"."` +} + +type ConfigParams struct { + ConfigAddr *address.Address + Config *cell.Dictionary } type ShardIdent struct { + _ Magic `tlb:"$00"` PrefixBits int8 `tlb:"## 6"` WorkchainID int32 `tlb:"## 32"` ShardPrefix uint64 `tlb:"## 64"` @@ -40,3 +63,25 @@ type ShardDesc struct { MinRefMcSeqNo uint32 `tlb:"## 32"` GenUTime uint32 `tlb:"## 32"` } + +func (p *ConfigParams) LoadFromCell(loader *cell.Slice) error { + addrBits, err := loader.LoadSlice(256) + if err != nil { + return fmt.Errorf("failed to load bits of config addr: %w", err) + } + + dictRef, err := loader.LoadRef() + if err != nil { + return fmt.Errorf("failed to load config dict ref: %w", err) + } + + dict, err := dictRef.ToDict(32) + if err != nil { + return fmt.Errorf("failed to load config dict: %w", err) + } + + p.ConfigAddr = address.NewAddress(0, 255, addrBits) + p.Config = dict + + return nil +} diff --git a/ton/api.go b/ton/api.go index 8b0ad100..ce2bb0d0 100644 --- a/ton/api.go +++ b/ton/api.go @@ -24,6 +24,8 @@ const ( _ListBlockTransactions int32 = -1375942694 _LookupBlock int32 = -87492834 _WaitMasterchainSeqno int32 = -1159022446 + _GetConfigParams int32 = 705764377 + _GetConfigAll int32 = -1860491593 ) // responses @@ -37,6 +39,7 @@ const ( _BlockTransactions int32 = -1114854101 _BlockHeader int32 = 1965916697 _AllShardsInfo int32 = 160425773 + _ConfigParams int32 = -1367660753 _BoolTrue int32 = -1720552011 _BoolFalse int32 = -1132882121 diff --git a/ton/getconfig.go b/ton/getconfig.go new file mode 100644 index 00000000..9a7774c9 --- /dev/null +++ b/ton/getconfig.go @@ -0,0 +1,132 @@ +package ton + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" + "math/big" +) + +type BlockchainConfig struct { + data map[int32]*cell.Cell +} + +func (c *APIClient) GetBlockchainConfig(ctx context.Context, block *tlb.BlockInfo, onlyParams ...int32) (*BlockchainConfig, error) { + data := make([]byte, 4) + binary.LittleEndian.PutUint32(data, 0) // mode + + data = append(data, block.Serialize()...) + + id := _GetConfigAll + + if len(onlyParams) > 0 { + id = _GetConfigParams + + ln := make([]byte, 4) + binary.LittleEndian.PutUint32(ln, uint32(len(onlyParams))) + + data = append(data, ln...) + for _, p := range onlyParams { + param := make([]byte, 4) + binary.LittleEndian.PutUint32(param, uint32(p)) + data = append(data, param...) + } + } + + resp, err := c.client.Do(ctx, id, data) + if err != nil { + return nil, err + } + + switch resp.TypeID { + case _ConfigParams: + _ = binary.LittleEndian.Uint32(resp.Data) + resp.Data = resp.Data[4:] + + b := new(tlb.BlockInfo) + resp.Data, err = b.Load(resp.Data) + if err != nil { + return nil, err + } + + var shardProof []byte + shardProof, resp.Data = loadBytes(resp.Data) + _ = shardProof + + var configProof []byte + configProof, resp.Data = loadBytes(resp.Data) + _ = configProof + + c, err := cell.FromBOC(configProof) + if err != nil { + return nil, err + } + + ref, err := c.BeginParse().LoadRef() + if err != nil { + return nil, err + } + + var state tlb.ShardState + err = tlb.LoadFromCell(&state, ref) + if err != nil { + return nil, err + } + + if state.McStateExtra == nil { + return nil, errors.New("no mc extra state found, something went wrong") + } + + result := &BlockchainConfig{data: map[int32]*cell.Cell{}} + + if len(onlyParams) > 0 { + // we need it because lite server adds some unwanted keys + for _, param := range onlyParams { + res := state.McStateExtra.ConfigParams.Config.GetByIntKey(big.NewInt(int64(param))) + if res == nil { + return nil, fmt.Errorf("config param %d not found", param) + } + + v, err := res.BeginParse().LoadRef() + if err != nil { + return nil, fmt.Errorf("failed to load config param %d, err: %w", param, err) + } + + result.data[param] = v.MustToCell() + } + } else { + for _, kv := range state.McStateExtra.ConfigParams.Config.All() { + v, err := kv.Value.BeginParse().LoadRef() + if err != nil { + return nil, fmt.Errorf("failed to load config param %d, err: %w", kv.Key.BeginParse().MustLoadInt(32), err) + } + + result.data[int32(kv.Key.BeginParse().MustLoadInt(32))] = v.MustToCell() + } + } + + return result, nil + case _LSError: + var lsErr LSError + resp.Data, err = lsErr.Load(resp.Data) + if err != nil { + return nil, err + } + return nil, lsErr + } + + return nil, errors.New("unknown response type") +} + +// TODO: add methods to BlockchainConfig to easily get gas price and etc + +func (b *BlockchainConfig) Get(id int32) *cell.Cell { + return b.data[id] +} + +func (b *BlockchainConfig) All() map[int32]*cell.Cell { + return b.data +} diff --git a/ton/integration_test.go b/ton/integration_test.go index 5b5eb270..7bd5e037 100644 --- a/ton/integration_test.go +++ b/ton/integration_test.go @@ -357,3 +357,51 @@ func TestAPIClient_WaitNextBlock(t *testing.T) { t.Fatal("it works with not master") } } + +func Test_GetConfigParamsAll(t *testing.T) { + ctx := api.client.StickyContext(context.Background()) + + b, err := api.GetMasterchainInfo(ctx) + if err != nil { + t.Fatal("get block 2 err:", err.Error()) + return + } + + conf, err := api.GetBlockchainConfig(ctx, b) + if err != nil { + t.Fatal("get block err:", err.Error()) + return + } + + if len(conf.All()) < 20 { + t.Fatal("bad config response, too short") + } + + if conf.Get(8).BeginParse().MustLoadUInt(8) != 0xC4 { + t.Fatal("bad config response for 8 param") + } +} + +func Test_GetConfigParams8(t *testing.T) { + ctx := api.client.StickyContext(context.Background()) + + b, err := api.GetMasterchainInfo(ctx) + if err != nil { + t.Fatal("get block 2 err:", err.Error()) + return + } + + conf, err := api.GetBlockchainConfig(ctx, b, 8) + if err != nil { + t.Fatal("get block err:", err.Error()) + return + } + + if len(conf.All()) != 1 { + t.Fatal("bad config response, bad length") + } + + if conf.Get(8).BeginParse().MustLoadUInt(8) != 0xC4 { + t.Fatal("bad config response for 8 param") + } +} diff --git a/ton/nft/content.go b/ton/nft/content.go index 61cea2a7..fe36bf3c 100644 --- a/ton/nft/content.go +++ b/ton/nft/content.go @@ -183,6 +183,22 @@ func (c *ContentOnchain) SetAttributeBinary(name string, value []byte) error { return nil } +func (c *ContentOnchain) SetAttributeCell(key string, cl *cell.Cell) error { + if c.attributes == nil { + c.attributes = cell.NewDict(256) + } + + h := sha256.New() + h.Write([]byte(key)) + + err := c.attributes.Set(cell.BeginCell().MustStoreSlice(h.Sum(nil), 256).EndCell(), cell.BeginCell().MustStoreRef(cl).EndCell()) + if err != nil { + return err + } + + return nil +} + func (c *ContentOnchain) GetAttribute(name string) string { return string(c.GetAttributeBinary(name)) } diff --git a/ton/nft/item.go b/ton/nft/item.go index f045e75d..1a50fd86 100644 --- a/ton/nft/item.go +++ b/ton/nft/item.go @@ -49,7 +49,7 @@ func (c *ItemClient) GetNFTData(ctx context.Context) (*ItemData, error) { res, err := c.api.RunGetMethod(ctx, b, c.addr, "get_nft_data") if err != nil { - return nil, fmt.Errorf("failed to run get_collection_data method: %w", err) + return nil, fmt.Errorf("failed to run get_nft_data method: %w", err) } init, err := res.Int(0) @@ -121,15 +121,23 @@ func (c *ItemClient) GetNFTData(ctx context.Context) (*ItemData, error) { }, nil } -func (c *ItemClient) BuildTransferPayload(newOwner *address.Address, amountForward tlb.Coins, payloadForward *cell.Cell) (*cell.Cell, error) { +func (c *ItemClient) BuildTransferPayload(newOwner *address.Address, amountForward tlb.Coins, payloadForward *cell.Cell, responseTo ...*address.Address) (*cell.Cell, error) { if payloadForward == nil { payloadForward = cell.BeginCell().EndCell() } + respTo := newOwner + if len(responseTo) == 1 { + respTo = responseTo[0] + } else if len(responseTo) > 1 { + // to protect from misunderstanding + panic("only 1 response destination is allowed") + } + body, err := tlb.ToCell(TransferPayload{ QueryID: rand.Uint64(), NewOwner: newOwner, - ResponseDestination: newOwner, + ResponseDestination: respTo, CustomPayload: nil, ForwardAmount: amountForward, ForwardPayload: payloadForward, diff --git a/tvm/cell/serialize.go b/tvm/cell/serialize.go index 75edc6b8..50fe10c1 100644 --- a/tvm/cell/serialize.go +++ b/tvm/cell/serialize.go @@ -2,7 +2,6 @@ package cell import ( "encoding/binary" - "encoding/hex" "errors" "hash/crc32" "math" @@ -57,7 +56,7 @@ func (c *Cell) ToBOCWithFlags(withCRC bool) []byte { data = append(data, sizeBytes) // cells num - data = append(data, dynamicIntBytes(uint64(calcCells(c)), uint(cellSizeBytes))...) + data = append(data, dynamicIntBytes(uint64(len(orderCells)), uint(cellSizeBytes))...) // roots num (only 1 supported for now) data = append(data, dynamicIntBytes(1, uint(cellSizeBytes))...) @@ -82,22 +81,6 @@ func (c *Cell) ToBOCWithFlags(withCRC bool) []byte { return data } -func calcCells(cell *Cell) int { - m := map[string]*Cell{} - // calc unique cells - uniqCells(m, cell) - - return len(m) -} - -func uniqCells(m map[string]*Cell, cell *Cell) { - m[hex.EncodeToString(cell.Hash())] = cell - - for _, ref := range cell.refs { - uniqCells(m, ref) - } -} - func (c *Cell) serialize(refIndexSzBytes uint, isHash bool) []byte { // copy payload := append([]byte{}, c.BeginParse().MustLoadSlice(c.bitsSz)...) From 52dfa7686486f7e8814ad551c499c87940527887 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Tue, 18 Oct 2022 15:07:55 +0300 Subject: [PATCH 13/14] Tests for block tlb --- tlb/block_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ ton/blockinfo.go | 2 ++ 2 files changed, 73 insertions(+) create mode 100644 tlb/block_test.go diff --git a/tlb/block_test.go b/tlb/block_test.go new file mode 100644 index 00000000..5417bc71 --- /dev/null +++ b/tlb/block_test.go @@ -0,0 +1,71 @@ +package tlb + +import ( + "bytes" + "encoding/hex" + "github.com/xssnick/tonutils-go/tvm/cell" + "math/rand" + "testing" +) + +func TestBlockMaster(t *testing.T) { + boc, _ := hex.DecodeString("b5ee9c72e2020152000100002a490000002400cc00ea0180026202fe033003520361037a03940404047404c0056805a8069a06b4075c079c0806087608c30a160a3a0a5e0b0a0b2a0b4a0b6a0b880ba60bc20bde0bfa0c160c320cd80d5c0d800da00dec0e380e580e780e980eb60ed60ef60f160f360f560f76102010a8110e119011ae11cc11ea120612aa132a13761442148f14ae154415621580159e15bc15da15f81616163416521670168e16ac16ca16d816e616f417021710171e172c173a1748175617641772178017cc17da17e817f6180418121820182e187a1888189618a418b218c018ce18dc18ea18f8190619521976199a19e71a921ab21ad21b1f1b6b1b8a1ba81bf51c411c5e1c7a1cc71d131d2e1d4a1d971de31dfe1e4b1e661f0c1f591fdc2029207b20c721122132217f21cb21ea220a22572276229422e12300234d236c238c23d923f82445249124b024fd251c25c62613269a26e7274c279927e5286628b328d0291d293a298729a429f12a3d2a582afc2b492bc82c152c612cad2ccc2cda2d272d442d912dae2dfb2e182e652e822ecf2eec2f392f562fa32fc0300d302a3077309430e130fe314b316831b531d2321f323c325a3308335533a1343634443491349e34eb34f835453552356035ad35f936063653366036ad36ba370737143722376f377c37c937d6382338d838e6399a3a4e3a5c3aa93ab63ac43ad23b1f3b2c3b793b863bd33be03c2d3c3a3c873c943ce13cee3d3b3d483d953da43df13e683f1c3f693f763f843fd1401d402a40774084409240df412b41384185419241df41ec41fa42ae43624416442244284476449e44f244ff4542454c463046484656466546744684472847d0487848844890491649d64a5c4a6e4b124bd34bdc4c624c7e4d2f4dd04ddc4de84e6e4f2e4fb44fc65086510c511e51c3523152f052f7537d538e54325493041011ef55aaffffff11000100020003000401a09bc7a9870000000004010173ed450000000100ffffffff0000000000000000634e93ea00001d3677b8338000001d3677b83384955d862e00058edb0173ed410173bfbec400000003000000000000002e00050211b8e48dfb4a0eebb004000600070a8a040a13051bcbbbdeccd56f979164b7da81b8e49732e7215334e2b8ce57c41888d03190bd932f59e8bcca9b92cd1032c316407ca6099409a8aedf4146f39e95ffec016e016e000b000c14892736daee89910b52d7041a889bf97c864cfc84eeafba291a1b5b2e931cc1b5e800084a33f6fd0be55a2d75c3eae367b5ba338705f0319041b70e23a5c9b65374cf2739898e58f78b372f9292751451def1be4dc7cf494ef17470574d85c253ef77746b8ea127c00123012401250126009800001d3677a8f1440173ed443de180887d5f5a84d44bd19c87cbb664b0561d5eb81da88c5782b0e36e9a07e4d7fd7d801561f54bffc0cb5c4ec4e855deeeeb6fdf26d4c99a086ffafb93580a022581fa7454b05a2ea2ac0fd3a2a5d348d295400800080008001d43b9aca00250775d8011954fc400080201200009000a0015be000003bcb355ab466ad00015bfffffffbcbd0efda563d0245b9023afe2ffffff1100ffffffff00000000000000000173ed4400000001634e93e700001d3677a8f1440173ed4160000d000e000f0010245b9023afe2ffffff1100ffffffff00000000000000000173ed4500000001634e93ea00001d3677b833840173ed416000110012001300142848010124871f46ee0eb1ae00a27d5c29f6cdbcc378c1f4f1380805ff2297c9ed9fcf2200013213a09776db739953220712f110cafb8c5d8dc0fab70e9391a4894fc7cc8706b210f3282d0d6691f0235fdd6b31911b4bd49619c99ab3dbcb80305cea71d75d2eb0016d00128207e9d152c168ba8ab00018009122330000000000000000ffffffffffffffff81fa7454b05a2ea2a8280091001634558d88cb7e0929c9a44ffc1cf3c5a230c0db9b7ba3f7e489ee00f8289579a5c967d13c5fdbab5e261b0b55885aa0a63abbc4487521903d912f3e35c296ad0eb23d001b0010cc26aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac23305e8350c57b37e003f008e004000410111000000000000000050001532139abefb5447ac011734d52324dd76a05aff0775b45c928601222624dbc2141bf2fa4c3e38ec56db5fc900c7c628d963f6fd5f016fdbf141f3f5ae91c3314e7b7f016d00128207e9d152e9a4694ab00072009122330000000000000000ffffffffffffffff81fa7454ba691a52a828009100162455cc26aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac23305e83a13cd8b7e0128008e00170041006bb0400000000000000000b9f6a280000e9b3bd478a1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc028480101de5adf45c03a745fc9d3a418d9e2ba096db9c1aaa77bc0898b6d9ebb611d2b40000532bfe179ee8495cd144a2f6d30950db2d48088fac5f9a778519dc0114219bf54cead2e7212dcdf398af721e9e425782d575b6c4026b7ec52ea1d3fe9dbb2bbe2aecf001a0010000100f59f3900058edb600003a6cef51e28880000e99c6da994200b9dfdf25ea78c41c94e4584ff2623b917b789f5d0e6a859861410542cb94e76fdc6efde77dc19b6aa8de0d4ad05651468c38cb9f64e1f8cf1351de6d5a7d98d56d6ded0be00bb00bc23130103f4e8a960b45d4558001900740091231301022a87a0b197c88778001a001b0091331367865aed64db08164a3138d816a8cacbb8b8fa46bc1fd1023ef5bd4e8fb6299ddc6201a0bfe76f22f36ca8ad0c54aa645c5739752b0a53bac3e57e9dda18febd0027000f01015ec2a32762fd21d800270028009122130100cbc4fd8a34cb65a8001c007822130100596b57d9c1932d880079001d221301003f0bad3989c46848001e007c221100e0b187aea6583a68007d001f221100e0a7528c0ef9512800200080220f00c141a6498c4d0800810021220f00c02225548664a800220084220f00c0221de12e910800850023220f4030085e7768002a00870024220f00c02170f2272c280025008a219dbceaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa818042d76318e19f365e6f7e0780eb2bc9f0c382940ef38b61bb6257ad617eb36fe8c9f56964f2800003a6cef51e28700262277cff55555555555555555555555555555555555555555555555555555555555555554085ac1288e0000000000000074d9dea3c5118042d76318e195d0008c008d2313010068a1a14c598fee380029002a009122130100f62101db096d33a80092002b28480101357b3e386bb95837e17d8fc7dd37f292efdc3301f6e109d8b33eb8a02afbb95d002228480101df3229c929cdae91378fd16242bda5a97246c54da4e9700d7427829825ee7b5d001922130100dd08f96dc461bcc8002c009522130100a6df074312541a08002d002e22130100902873121142a0c80098002f221100f6b6943101117948003900ae2213010090175c7b3161aee8009a0030221301008fb45fdc97d252080031009d221301008f689e5fb80803e80032009f221301008f677b3e09283ac800a00033221301008f677a70024cc70800a20034221301008f6779cbfeb8f188003500a521a1bcd99999999999999999999999999999999999999999999999999999999999982011ecef393af6d61933fe8eada66c79771a5de359b379b70bfe4414022dee90877585c9818f39dda000003a6cef51e2850036227bcff33333333333333333333333333333333333333333333333333333333333333334081ac1664bc000000000000074d9dea3c50e011ecef393af6d6196d000a700372355ec039e4242ff8cc69bf4260c44ddc7b820f838fa85ad1828d2b83ace409d6c02a3b89a505ac592d94a7c4d00a900aa00382179a0634dfa13634f7a130000800006226ee3dc107c1c7d42d68c14695c1d67204eb60151dc4d282d62c96ca53e26c0100ee542c8b882e30ec339334e5ca000ac221100f6a2a63eab3d4e48003a00b0221100ea5905b0b329bd88003b00b2221100ea58fcd0996ec14800b3003c220f00c035987df0cb08003d00b6219bbd62f8f7bea30f8ab5e9f16c3fb8642b118f56ed1bdc49600dbe5220c8b1af9e040c474f803d1a0544cba813425adf3253dd3727a789c9e418b5e788a4cab5df805caee92b00000e9b3bd478a1c0003e236fcff34517c7bdf5187c55af4f8b61fdc321588c7ab768dee24b006df29106458d7cf21881f48000000000000074d9dea3c5110311d3e017f000b800b900ba28480101db29f7a5808e1a673feb2258d777f3005642991b8025b1f92bcd7c498a8bd8ed000222bf000100f59f3900058edb600003a6cef335e0880000e99c6da994200b9dfdf25ea78c41c94e4584ff2623b917b789f5d0e6a859861410542cb94e76fdc6efde77dc19b6aa8de0d4ad05651468c38cb9f64e1f8cf1351de6d5a7d98d56d6ded0be0042004328480101b20e36a3b36a4cdee601106c642e90718b0a58daf200753dbb3189f956b494b600012213c3c000074d9de66bc12000bd004432014645ed4db913f636932b4bebb489b51112e1a415136d57d6c6735ffc4bd556606e33f960111aa97c043f6040c47d1298da52d6a46a0378304ba87f61f3dee9db0010000c20005100522211480000e9b3bccd782400bf00452211200003a6cef335e09000c100462211200003a6cef335e09000c3004722116200003a6cef335e0900c500482211200003a6cef335e09000c700492211200003a6cef335e09000c9004a2211200003a6cef335e09000cb004b2211000003a6cef335e09000cd004c2211400000e9b3bccd782400cf004d2211000003a6cef335e09000d1004e2211400000e9b3bccd782400d3004f2211400000e9b3bccd782400d500502211d000003a6cef335e0900d900da220120005300f822012000dd006722012000540055220120005600fc220120010f005f220120005700fe2201200058010022012000590102220120005a0104220120005b0106220120005c0108220120005d010a220120005e010c28480101f25a1e1d7f11115186543ff6eb95e3d9b98f71d2c959af6b0dad6b63cd1e6d6900012201200060011222012001130061220120011500622201200063011822012001190064220120011b0065220120011d006628480101a96f5d75bc79b8d1640e680704965baaa2245e4b3a5ad8e980ef5a3568409418000222012000df006822012000e10069220120006a00e422012000e5006b22012000e7006c22012000e9006d22012000eb006e220120006f00ee220120007000f0220120007100f2284801016f2780ba9d3cdce8eee34a23d893d90800da0ac1be8a973c513909136b7f636b000223130103f4e8a974d234a558007300740091231301022a87a0c5b59fe77800750076009128480101827773c365eccfd6cb46a3f783a09f1aba77ce1a0a4d62048569e9b845e955f8016b3313e844a4da57b7cf2b0b52b004cc5886c966478e37012ee2d9b473bf083e1e3072beee68024dd8cc7dffdfde9e114e4818dc10824b446d661453963fa117c1fcbf0027000f01015ec2a33b80d481d8008f0090009122130100cbc4fd8a34cb65a80077007822130100596b57d9c1932d880079007a28480101cf20bddca78403c3e40e2ab1b3eeb526c425b5efb531e6c5bd3d52019c25125c002628480101ff7081e66c7f0d6e868021316b0189b9e67b61284c176cd74f78fa6baa18a025001a221301003f0bad3989c46848007b007c221100e0b187aea6583a68007d007e284801011d818de56750d053b2a227f0da85ba37fb1869d0c774629d04e2668e1204c0a3001b28480101174c3878604468b08b7b76f5349c41201ae28b81ca1d94794ab11a692e4668250018221100e0a7528c0ef95128007f0080220f00c141a6498c4d080081008228480101e00721ae4b2be2ed708fa68e6b7bec2759a107504812069b3b8aa9acea346c66001528480101747c06e45f53ca1dc7d23f39db07d12f2e67fa595a36fa41d26683ab42d1a3040014220f00c02225548664a800830084220f00c0221de12e91080085008628480101eb38ef90c590bedb3ce31140d2d4176d43db6b7aab35df685afc4ccf2a383209000b2848010179a2e20b8a926ab2fe83108ff00f2fbced9958047008e5cb5fdf8c798aab63850010220f4030085e7768002a0087008828480101a248b81f22333cc28f6b6744e4298aefcd9b6f2dc5d7c99e1da1b28c37f3aa0c0007220f00c02170f2272c280089008a219dbceaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa818042d76318e1805a8c67376726b2dcf563d47e9c2ed6fa8fd993942535d6c8ed758902593e99400003a6cef706707008b284801010143b3d2dd671b2559543155e003f847022e510b3a57afabbca05d4069c327ef000d2277cff55555555555555555555555555555555555555555555555555555555555555554085ac1288e0000000000000074d9dee0ce118042d76318e195d0008c008d2848010164a43970f2007a1da6d6fc81773cc095d1cc270e81359e471f3b03469abeb7b5000c214900000027cbb9d1062954439a83a91f27835fb9d2e3e798910356650c3c493c946234646840008e28480101374e198a900e08edc634a5f2ad73e388b0a3019d24269fae8046024e437476b1001028480101903aa268fecbed38822a8972ba42eadb53c0972f11b8486f1534210245db4898002322130100f62101ef274493a80092009328480101a5a7d24057d8643b2527709d986cda3846adcb3eddc32d28ec21f69e17dbaaef0001284801012bd772e408a34578028922281a3e5b5384970a6a6dd741b1cfa3b80a3e5ec57d002322130100dd08f981e2391cc80094009522130100a6df0757302b7a080096009728480101b90ed7fc04a4971294b12a078ec8189e8fdba184de6e23043922a774ae403ee2002422130100902873262f1a00c800980099221100f6b694310111794800ad00ae28480101eda54e0b0237690499c3e159ab800469fdbcb3c162d42181c2c298acd4e98f3100152213010090175c8f4f390ee8009a009b28480101cc6ead611f9fa7c0598d8f88d658fe0b91f5f9c9635c872154234c16c722970c0014221301008fb45ff0b5a9b208009c009d221301008f689e73d5df63e8009e009f28480101c7c146bea2ced23475861d11146c0560a46c3d243563fda0e32bf8c34229d2670013221301008f677b5226ff9ac800a000a128480101ef1aa8b2068cf6a8eadef8197235a5d5976865a32a3ad1fe80db069ddb8cc2fe001128480101c2ef35325f62d0b4cc17d1f5d083894100c3c478504d70b6eb8d3cf26e604ff40011221301008f677a842024270800a200a3284801018a51fe69422dbf7e028fb1dcac5a62064eefeb4c080793e78a24ef22334b307c0010221301008f6779e01c90518800a400a521a1bcd99999999999999999999999999999999999999999999999999999999999982011ecef3bbeb1c21823653419f6ebabf10f7978dae7e33a12d3bbe9815917a62eb4fe902f2a96e88600003a6cef70670500a62848010150725eee52e86432f846698a08ac153a67bc9ad9c160130af907c3bef05f29480007227bcff33333333333333333333333333333333333333333333333333333333333333334081ac1664bc000000000000074d9dee0ce0e011ecef3bbeb1c2196d000a700a8284801016217f872c99fafcb870f2c11a362f59339be95095f70d00b9cff2f6dcd69d3dd000e2355ec039e4242ff8cc69bf4260c44ddc7b820f838fa85ad1828d2b83ace409d6c02a3b89a505ac592d94a7c4d00a900aa00ab28480101ff06225996392d9e78d92fef981828f3459892841111b2d352901236d506cb65000b28480101336df3bd068890e3f26c1a8f5e77c4bf7cc3c81fc88006ab614b6db43647262600072179a0634dfa13634f7a130000800006226ee3dc107c1c7d42d68c14695c1d67204eb60151dc4d282d62c96ca53e26c0100ee542c8b882e30ec389aaabdca000ac28480101b8ad45439ed0f9f1ffb12362a0c0a6f522734feed11dda077d5f6067f1305170000b221100f6a2a63eab3d4e4800af00b028480101e2a96bbff9be849635722263833d77a90f0a832b410f8b73bca56041fd7e21970016221100ea5905b0b329bd8800b100b22848010130dd0d5ef5796dc4c101fbf5b4b083599e509d0f738b07a8dbfad6b5ae53aecb0012221100ea58fcd0996ec14800b300b42848010130219e3c8c788af6da8a296da6f3e9925c909eed9821a0ae1911c38f56f7b37e000b28480101e2bc337ece7f3af5171f3265f44c612fc2fcba87f4b4563dc7fdc3285dd6a44d0008220f00c035987df0cb0800b500b6219bbd62f8f7bea30f8ab5e9f16c3fb8642b118f56ed1bdc49600dbe5220c8b1af9e040c474f804c5ac6247ef1e5d11d080c3d8b21135b54598a72e11fbc6ebe1fa0c4b2a7df0a00000e9b3bdc19c1c000b72848010118dd0a8040c21a2cfb6c0acf4ad636dc67ef3ab0a3e102f1b43ad500c55728d00007236fcff34517c7bdf5187c55af4f8b61fdc321588c7ab768dee24b006df29106458d7cf21881f48000000000000074d9dee0ce110311d3e017f000b800b900ba284801017269fb9feb45d719ebdbc3b0816b987bab06f43378dc84dc84d55727905482140002004811fd096c000000000000000000000000000000000000000000000000000000000000000028480101986c49971b96062e1fba4410e27249c8d73b0a9380f7ffd44640167e68b215e800032213c3c000074d9dea3c512000bd00be22012000db00dc28480101258d602eaa21d621634dcf86692aeae308ff3cf888f3edafc6a5b21848d732f900182211480000e9b3bd478a2400bf00c0284801014b01ebcf5425735461aa8b83bae89e70fa21e95d2ee85e57b05dad26c1d6d53000162211200003a6cef51e289000c100c22848010165b0a85a0fdea0c76a2a98445623ea62427099a6318624794dea416f1bdc6f5c00152211200003a6cef51e289000c300c428480101b5b64686c719580155341cb7347af0405dec7158c283ad30833b07325bdc48a5001422116200003a6cef51e28900c500c628480101fde4f74a9866e3de066d6d27e3b1fe107053ecce8b54d8b05ebf4a3b0789c26b00112211200003a6cef51e289000c700c828480101f7a4391731a8136b142d214311bd2f8c162938f27185d22de576a045a13b1e1600102211200003a6cef51e289000c900ca2848010187c846be2bc06a266ae017ae9a13c66cf156125edd95b8bd4f6cfe3c903e3b35000f2211200003a6cef51e289000cb00cc2848010122da148fcc6a6a317ae3c41ee888034019cbfa89e57f306b85601dd2045d6daa000e2211000003a6cef51e289000cd00ce2848010191c44865f6767ab41750fbf5117df2d8be3110925c7993aa2e03780673c31f32000d2211400000e9b3bd478a2400cf00d0284801016d16afa0d70d41df6abe49636527c0b566bd3b722b731eba03433d7efbcb3908000b2211000003a6cef51e289000d100d228480101d744ca7d3ce6fe4538b3fa6a138971ca129c227d8a6736a9cd1d33c2f1fd06cc000a2211400000e9b3bd478a2400d300d42848010175d211346d824c33aff56800c12e0b320854590aadfd85e3f909502cdb6ec3c100082211400000e9b3bd478a2400d500d628480101322f03bbddf42b900d602199315f5d4befa1a9282a2a6c845f3db6ccd2b6bfc000062211cc00003a6cef51e28900d700d82211000003a6cef335e09000d900da00a9d0000074d9dea3c51000003a6cef51e28802e7da887bc30110fabeb509a897a3390f976cc960ac3abd703b5118af0561c6dd340fc9affafb002ac3ea97ff8196b89d89d0abbdddd6dfbe4da9933410dff5f726b01528480101523e62a3a95932c2a65f2314a8a818f82f48644967cc31dcfda9954109d8b55100012848010177c2748c31a7f78c56862aa9d06df60981de7aaa59e67d4d0360a2903384fe1500013201032a5ac73da06a6b989d158bec539003d36dc087d663eda6337be5667c284f16310ee22bacedde5f1c215edbbbdf7a1c20c98ec248b7893266ebfceeb41817bd000f000c2000f700f822012000dd00de284801019deed5e9cd5995ad6c97a06276c939029a1d05a6de03b6c724a4b5567e9adb7a000e22012000df00e0284801016bc4ad2e5c909f6f452be243edc65694f7e6db5f2fc615f69756954a60a563a2000c22012000e100e2284801016925c827cdb72656785a860c0ed1b94c1ff9f0614b9e2ed1b0aa1ee8fbb395aa000c22012000e300e422012000e500e62848010197d9c97586b5cf9a93f5077cf1e13c91f7a4d5b240601e4d08030ab62cd17707000b28480101f613c63e75ce90bdb3aadf01297ba9a958588392473ea542ef8654f281d2854f000922012000e700e828480101d83f99b6b2deca33e45337ea0fa4788a5590c2a9f88654c24c1e4b5282ec7787000822012000e900ea28480101497deb7f82cc061521c9f6bf58ddd3043ecb1dbaea13352ecb73bb53236a9dd8000622012000eb00ec28480101e86bec3c2e5a0c5b9bad30e9b0efd5c74409fece4efd571f8fe02eccbbd0af1a000422012000ed00ee22012000ef00f02848010178a2f12e152f91343bff8aeda8ca7bab1039578fb6b03832c150f22786d0500c000422012000f100f228480101b26a0cc496805853f303d8a00ae9fc7f7b20dc7cab6d1d1c21f5b86469874a84000202012000f300f428480101a31f27b17ffa79bcaf0e47f55dffa054f825e019e447026255e7e1a8d7488701000200b1bcd91dbefdb40075ad92878e330bb79115bcfc28f3c5b9833df391ce8138514a3199fcd1800000000000006500000002b0d782ed8000003f2a3414d8b199f72d0000000000000040800000038a0ed0708000002d14497b814002014800f500f600afbc6827bcf8957c10b8a5694ecd7f0dd41e6a2cd906c77e5340983b618fb6fb0800000000000000000000000000000000000000000000000000000000c695ef7e000000000000007200000006efab3f64000000450742496300afbc66e5f2524ea28a3bde37c9b8f9e929de2e8e0ae9b0b84a7deeee8d71d424e8c69d27d400000000000000e2000000093197d6c2000000a8632502f4c69d1ba4000000000000008a000000116e3e81da0000006af0f2148122012000f900fa28480101912d60694234d59e4645f5d2ebd90e081979a3f6eaf4124bec3980e4547a5094000e22012000fb00fc220120010f011022012000fd00fe28480101348a81067d100edaf90feeb18db50c3c315a07c6c0944b52368c30e76a6f40df000b22012000ff0100284801018540d2166efad6f7a81289ddf3983d3ed177993dce47ccb150f2fcc287428d53000a220120010101022848010110b3b5e79df7c963efb443120853eb1bf9377e78020993bf79d5aaa9b02d1a6a000822012001030104284801016f610eec3a1e4dc9bdacbda0e586e7a8f6b4734b6599ecc0f8c5d0e9666d0ed3000822012001050106284801019100c451439a1cfdcf444d77bc78d03f19ca5e71b1f8fdae5e9e0ccf3e8214a000072201200107010828480101e0140ab9f7e276e1143af00713243e470dfc2c93c02b124622926fd33551a71b00062201200109010a284801014ef684da255649795b7830d1100f419d8f8a0eeb9ed6ed3610ba20b5d815deed0003220120010b010c2848010155cdb8f72801ef11ba562172ed2626c88208eddcf4a0c8f6d5447a785d02b790000202037820010d010e28480101b6eb72df89b91190ab85640f1ef9817bf00e49c5c11e8fd173b5b382ca4a104700010073dde8c69d27d40000000002e7da88000004baec525bbe000096f6a2fa0e38c69d27d4000000001598a6b2000004da46f3846200009e49d38f1eeb00afbbdc4b61f8041625a15bee3b094ff72034e12e69e8d71521ac6748fe6359832319afc8c8000000000000066800000033ffaa8b0000000419c4a9d68319afccd800000000000003d00000002e72705fc80000026a49c0209c284801018dfe3c99df194f8fec2b5b64b5ef08296b853794a29497c7c425ca62a44695e6000c2201200111011222012001130114284801010c275d6749b7c9102256e4abafdecda16a1697f20d26cd0e711bd9d58ef4a2f2000a28480101c169f7745c95d5f3f6b4e550c15978aaf563631f3a9e6ddaaa361ed4042e4f05000922012001150116284801017a3b4493fefcfd2275fa2f6ab01a8db5d70a443fb48dfb00e152545adfb97cbb0007220120011701182201200119011a28480101b13de2fa76c60764833d05264a2e1081609e2dfa9a04bf8d6c5e1162ac7d47cb000728480101027742b12159d2d1310044b4a94e1eea928905b045871a52b552b4b4841288400006220120011b011c28480101df4611dc79f46dc700809e0c3140796be5ca9572c3f3fab70ddbe6a5460bf4900003220120011d011e28480101d52b65c44fcc1a90bbbf8cc01e8ab9c7b6c51f95c2735d6de72a669c1135a8360003020162011f01200201200121012200b0bc885a77c249fb95f38bb13224853b7944942c4b10b84f1b99aa32892aabd4f46335f78a00000000000000bc00000003fb0146f100000074c1cd03f56335e64b0000000000000058000000040ca9eccb000000387d19d10000afbc60807ffe2b018ea1eb65ddc523f7772fc1e1f16e73ee8905b4b66c12275ca800000000000000000000000000000000000000000000000000000000c67fd5260000000000000096000000081b2d1d7000000064c8dda77100afbc799607d471065ad26b0a721946ed764b15a01cf341e11989f08863962a362800000000000000000000000000000000000000000000000000000000c69d27d4000000000000003e00000002165b2b1c0000002c0b7cc7ad0103802001270001021101918f8df47d89a592d9a8e2220276e210d49d789c174ab2b303917d71c6655837000782012f0317cca5687735940043b9aca00401280129012a0247a00f076afb8843d0d2618df1779691876f9ffd59f9b30f47df60f49496744dec67200610012e013b0103d040012b003fb000000000400000000000000021dcd650010ee6b280087735940043b9aca004010150012d01db500e3a26680b9f6a280000e9b3bd478a000000e9b3bd478a0d73fd8f873243316a5b55a0395be4d1c584d71a7c52116b6379a3e93f9649360375de02eb9865b5786e167c4411e5c415cf6064be75fe04b0f81f0c4b84c7260880002c7d7c00000000000000000b9f6a0b1a749f2a012c001343b9aca0021dcd650020020161012e013b0106460600013f020340400130013102037604013201330297bf955555555555555555555555555555555555555555555555555555555555555502aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad00000074d9dee0ce0c1014c014e0397beb33333333333333333333333333333333333333333333333333333333333333029999999999999999999999999999999999999999999999999999999999999999cf8000074d9dee0ce00400134013501360397be8517c7bdf5187c55af4f8b61fdc321588c7ab768dee24b006df29106458d7cf029a28be3defa8c3e2ad7a7c5b0fee190ac463d5bb46f71258036f9488322c6be7cf8000074d9dee0ce004001410142014301035040013701034040013b0082722f7566ede0ba3a333ac2ca4e9820a0eb28fa3c675e8c5b7378fbba7d487af6b6d8b330226ee7a4226c9a4e28167203a4dec229d3f51655422b56dd7122352fda03af7333333333333333333333333333333333333333333333333333333333333333300001d3677b8338199ff4756d3363cbb8d2ef1acd9bcdb85ff220a0116f74843bac2e4c0c79ceed000001d3677a8f142634e93ea0001408014d013801390082722f7566ede0ba3a333ac2ca4e9820a0eb28fa3c675e8c5b7378fbba7d487af6b69e73e012c2b93293818802ecda692b6a70c7bc140c3d6ba22159dd15949099300205203024013a015100a0431b9004c4b4000000000000000000960000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003af7333333333333333333333333333333333333333333333333333333333333333300001d3677b8338232a2e0d714f1820922c85912266b270b551f97fd88c9ce96b02fbe0b94fedd3300001d3677b83381634e93ea0001408013c013d013e0101a0013f0082729e73e012c2b93293818802ecda692b6a70c7bc140c3d6ba22159dd1594909930d8b330226ee7a4226c9a4e28167203a4dec229d3f51655422b56dd7122352fda020f0409283baec018110140015100ab69fe00000000000000000000000000000000000000000000000000000000000000013fccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccd283baec000000003a6cef706700c69d27d440009e42614c107ac0000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103504001440103504001470082723145f857768776495406acbcd9f6451e43b82a7cf2b787bdfcd66f54e8f61eb2f5fc1aa51cd06879f30dac067b3d17d0571b7c8ef15db6c57ce3e90f63e7d80803af734517c7bdf5187c55af4f8b61fdc321588c7ab768dee24b006df29106458d7cf00001d3677b833817a340a8997502684b5be64a7ba6e4f4f1393c8316bcf1149956bbf00b95dd25600001d3677a8f143634e93ea0001408014d014501460082723145f857768776495406acbcd9f6451e43b82a7cf2b787bdfcd66f54e8f61eb20d9c166ab6df5f0d47d18e86fc45c1e5f42681c1184337189ef2e4aa3f2552c10205203034014a014b03af734517c7bdf5187c55af4f8b61fdc321588c7ab768dee24b006df29106458d7cf00001d3677b83383a4dbec8658831b756fd060883f7d013972d9838f66cebcd2e28d66f2b2d6d46900001d3677b83381634e93ea0001408014d014801490082720d9c166ab6df5f0d47d18e86fc45c1e5f42681c1184337189ef2e4aa3f2552c1f5fc1aa51cd06879f30dac067b3d17d0571b7c8ef15db6c57ce3e90f63e7d8080205303034014a014b00a042665004c4b400000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069600000009600000004000600000000000519ae84f17b8f8b22026a975ff55f1ab19fde4a768744d2178dfa63bb533e107a409026bc03af7555555555555555555555555555555555555555555555555555555555555555500001d3677b83383f9b2f37bf03c07595e4f861c14a0779c5b0ddb12bd6b0bf59b7f464fab4b279400001d3677a8f143634e93ea0001408014d014e014f0001200082720ac47779e474df79ac188caf2308fa7fccf511a8be789a6502f15ca63fba64408669008ce4710e1108a5eee86c282b1d13feaf7634e0c592943ae844ddd4ca0c02053030240150015100a041297004c4b40000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000012d452da449e50b8cf7dd27861f146122afe1b546bb8b70fc8216f0c614139f8e04d7cef969") + c, _ := cell.FromBOC(boc) + + var block Block + if err := LoadFromCell(&block, c.BeginParse()); err != nil { + t.Fatal(err) + } + + parents, err := block.BlockInfo.GetParentBlocks() + if err != nil { + t.Fatal(err) + } + + println(len(parents)) +} + +func TestBlockNotMaster(t *testing.T) { + boc, _ := hex.DecodeString("b5ee9c72e1021c0100040b00001c00c400de0170020402a0033c036a037c0387039e03b6041c048204ce04ea0536055405a005ec060406200700077007bc080908100817041011ef55aaffffff110102030402a09bc7a98700000000840101c745200000000100000000000000000000000000634e94ec00001d367caaae4000001d367caaae419bbc68ac00058fb00173ed920173bfbec400000003000000000000002e05060211b8e48dfb43b9aca00407080a8a04250ec78adc9d082383679c3289edc662b628be0e34e51a8f7c412e98d24c8a5fb59960f376a6ad4dce93f406ce904add5a2aea140c99b877d02f67f1cd1e5f51021902190c0d03894a33f6fdb1c342502d7261843b4a3bfdbfb766c45705b7c4410af03c358431620ff05a79b1be0d76ede085c08726e04bad3c5779d949364eb56540f06c2c49b98d514111401a1b1b009800001d367c9b6c040173ed92b57df82537164b18661e22f620e1a7a15826a73d7402eef9433d55c030232370a7caa150ac8f2f4c74cb5c77e6671edb6f8accd65c683faf6e48a88720b2c72d009800001d367c9b6c0101c7451f78d2820caf6a5f100a444450ddab2f7754bbce7c6027dce5349269227866124a33b3efd318a7ec75c8f26844fd4dce5f581927f670a0087d7fec56658b487d720225826b977bb75290e16c135cbbddba94870b40080909000d0010ee6b2800080201200a0b0013be000003bc91627aea900013bfffffffbc8b96fc9c50235b9023afe2ffffff110000000000000000000000000001c7451f00000001634e94e900001d367c9b6c010173ed91200e0f10235b9023afe2ffffff110000000000000000000000000001c7452000000001634e94ec00001d367caaae410173ed9220141516284801017e49cb3c190a5033a93c907c6631d4459cf4bf71f57f041dd14270fb919423dc000122138209ae5deedd4a4385b011192848010125e39d851243cee82c062dd588cfa4587461b7869f68023bad26988d33bf8a24000223130104d72ef76ea521c2d81213192848010105a0d0f5cf8e9d2d98f032e935e8de2208463332de6c74af0b9d5cfc2bc2802102162848010157c418ac5021e527850e982354ed5a21fd7a0b0ac719e443fcd3c80f496dc4db003401110000000000000000501722138209ae5deedd4a4385b0181921d90000000000000000ffffffffffffffff826b977bb75290e16bb5f5e54ddd448c900001d367c9b6c040173ed92b57df82537164b18661e22f620e1a7a15826a73d7402eef9433d55c030232370a7caa150ac8f2f4c74cb5c77e6671edb6f8accd65c683faf6e48a88720b2c72d819006bb0400000000000000000b9f6c900000e9b3e4db601ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0284801012aa19c773967de4112363f58e8331a68fb2b3fcb1d55daf352b93c497a019ce4021728480101b3e9649d10ccb379368e81a3a7e8e49c8eb53f6acc69b0ba2ffa80082f70ee39000100030020000102b1e6b8f1") + c, _ := cell.FromBOC(boc) + + var block Block + if err := LoadFromCell(&block, c.BeginParse()); err != nil { + t.Fatal(err) + } + + parents, err := block.BlockInfo.GetParentBlocks() + if err != nil { + t.Fatal(err) + } + + println(len(parents)) +} + +func TestBlockInfo(t *testing.T) { + root := make([]byte, 32) + file := make([]byte, 32) + + rand.Read(root) + rand.Read(file) + + bi := BlockInfo{ + Workchain: 77, + Shard: -6672233, + SeqNo: 80000112, + RootHash: root, + FileHash: file, + } + + data := bi.Serialize() + + bi2 := BlockInfo{} + _, err := bi2.Load(data) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(bi2.Serialize(), data) { + t.Fatal("not same") + } +} diff --git a/ton/blockinfo.go b/ton/blockinfo.go index 6f7e8857..3121a6bd 100644 --- a/ton/blockinfo.go +++ b/ton/blockinfo.go @@ -3,6 +3,7 @@ package ton import ( "context" "encoding/binary" + "encoding/hex" "errors" "fmt" "github.com/xssnick/tonutils-go/tlb" @@ -128,6 +129,7 @@ func (c *APIClient) GetBlockData(ctx context.Context, block *tlb.BlockInfo) (*tl var payload []byte payload, resp.Data = loadBytes(resp.Data) + println(hex.EncodeToString(payload)) cl, err := cell.FromBOC(payload) if err != nil { return nil, fmt.Errorf("failed to parse block boc: %w", err) From e87f536a3fe51886f12731bf7f70a1c087085bcb Mon Sep 17 00:00:00 2001 From: Coverage Date: Tue, 18 Oct 2022 12:09:53 +0000 Subject: [PATCH 14/14] Updated coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83d25aa0..3510c825 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Based on TON][ton-svg]][ton] -![Coverage](https://img.shields.io/badge/Coverage-70.7%25-brightgreen) +![Coverage](https://img.shields.io/badge/Coverage-70.2%25-brightgreen) Golang library for interacting with TON blockchain.