Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Go

on:
push:
branches: [ "master" ]
branches: [ "master", "develop" ]
workflow_dispatch: {}

jobs:
Expand Down Expand Up @@ -67,4 +67,4 @@ jobs:
KEY: ${{ secrets.KEY }}
DB_URI: postgresql://pp_user:postgres@localhost:5432/payment_processor?sslmode=disable
run: |
go test -v $(go list ./...)
go test -v $(go list ./...)
130 changes: 89 additions & 41 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import (
"context"
"errors"
"fmt"
"math"
"math/big"
"sort"
"strings"
"time"

"github.com/gobicycle/bicycle/config"
"github.com/gobicycle/bicycle/core"
log "github.com/sirupsen/logrus"
Expand All @@ -20,11 +26,6 @@ import (
"github.com/xssnick/tonutils-go/ton/jetton"
"github.com/xssnick/tonutils-go/ton/wallet"
"github.com/xssnick/tonutils-go/tvm/cell"
"math"
"math/big"
"sort"
"strings"
"time"
)

type Connection struct {
Expand Down Expand Up @@ -52,6 +53,39 @@ type contract struct {
Data *boc.Cell
}

func retry[T any](f func() (T, error)) (T, error) {
if config.Config.LiteServerMaxRetries == 0 {
return f()
}
var result T
var err error
for i := 1; i <= config.Config.LiteServerMaxRetries; i++ {
result, err = f()
if err == nil {
return result, nil
}
time.Sleep(time.Duration(config.Config.LiteServerRetryDelay*i) * time.Millisecond)
}
return result, err
}

func retry2[T1 any, T2 any](fn func() (T1, T2, error)) (T1, T2, error) {
if config.Config.LiteServerMaxRetries == 0 {
return fn()
}
var res1 T1
var res2 T2
var err error
for i := 1; i <= config.Config.LiteServerMaxRetries; i++ {
res1, res2, err = fn()
if err == nil {
return res1, res2, nil
}
time.Sleep(time.Duration(config.Config.LiteServerRetryDelay*i) * time.Millisecond)
}
return res1, res2, err
}

// NewConnection creates new Blockchain connection
func NewConnection(addr, key string, rateLimit int) (*Connection, error) {

Expand Down Expand Up @@ -79,7 +113,7 @@ func NewConnection(addr, key string, rateLimit int) (*Connection, error) {
return nil, fmt.Errorf("get network config from url err: %s", err.Error())
}

wrappedClient = ton.NewAPIClient(limitedClient, ton.ProofCheckPolicySecure).WithRetry()
wrappedClient = ton.NewAPIClient(limitedClient, ton.ProofCheckPolicySecure)
wrappedClient.SetTrustedBlockFromConfig(cfg)

log.Infof("Fetching and checking proofs since config init block ...")
Expand All @@ -90,7 +124,7 @@ func NewConnection(addr, key string, rateLimit int) (*Connection, error) {
log.Infof("Proof checks are completed")

} else {
wrappedClient = ton.NewAPIClient(limitedClient, ton.ProofCheckPolicyUnsafe).WithRetry()
wrappedClient = ton.NewAPIClient(limitedClient, ton.ProofCheckPolicyUnsafe)
}

// TODO: replace after tonutils fix
Expand Down Expand Up @@ -140,8 +174,11 @@ func getConfigData(ctx context.Context, api ton.APIClientWrapped) (*address.Addr
if data == nil {
return nil, nil, fmt.Errorf("failed to get root address from blockchain config")
}

hash, err := data.BeginParse().LoadSlice(256)
slice, err := data.BeginParse()
if err != nil {
return nil, nil, fmt.Errorf("failed to get root address from blockchain config 2: %w", err)
}
hash, err := slice.LoadSlice(256)
if err != nil {
return nil, nil, fmt.Errorf("failed to get root address from blockchain config 4, failed to load hash: %w", err)
}
Expand Down Expand Up @@ -262,9 +299,12 @@ func getWalletRecord(d *dns.Domain) *address.Address {
if rec == nil {
return nil
}
p := rec.BeginParse()
p, err := rec.BeginParse()
if err != nil {
return nil
}

p, err := p.LoadRef()
p, err = p.LoadRef()
if err != nil {
return nil
}
Expand Down Expand Up @@ -326,7 +366,7 @@ func (c *Connection) GenerateDepositJettonWalletForProxy(
}

func (c *Connection) getContract(ctx context.Context, addr *address.Address) (contract, error) {
block, err := c.client.CurrentMasterchainInfo(ctx)
block, err := c.CurrentMasterchainInfo(ctx)
if err != nil {
return contract{}, err
}
Expand Down Expand Up @@ -435,7 +475,7 @@ func (c *Connection) GetJettonBalance(ctx context.Context, address core.Address,
// GetLastJettonBalance
// Returns jetton balance for last block in basic units
func (c *Connection) GetLastJettonBalance(ctx context.Context, address *address.Address) (*big.Int, error) {
masterID, err := c.client.CurrentMasterchainInfo(ctx)
masterID, err := c.CurrentMasterchainInfo(ctx)
if err != nil {
return nil, err
}
Expand All @@ -449,7 +489,7 @@ func (c *Connection) GetLastJettonBalance(ctx context.Context, address *address.
// GetAccountCurrentState
// Returns TON balance in nanoTONs and account status
func (c *Connection) GetAccountCurrentState(ctx context.Context, address *address.Address) (*big.Int, tlb.AccountStatus, error) {
masterID, err := c.client.CurrentMasterchainInfo(ctx)
masterID, err := c.CurrentMasterchainInfo(ctx)
if err != nil {
return nil, "", err
}
Expand All @@ -459,7 +499,7 @@ func (c *Connection) GetAccountCurrentState(ctx context.Context, address *addres
case <-ctx.Done():
return nil, "", core.ErrTimeoutExceeded
default:
account, err := c.client.GetAccount(ctx, masterID, address)
account, err := c.GetAccount(ctx, masterID, address)
if err != nil && isNotReadyError(err) {
time.Sleep(time.Millisecond * 200)
continue
Expand Down Expand Up @@ -504,7 +544,9 @@ func (c *Connection) GetTransactionIDsFromBlock(ctx context.Context, blockID *to
next = true
)
for next {
fetchedIDs, more, err := c.client.GetBlockTransactionsV2(ctx, blockID, 256, after)
fetchedIDs, more, err := retry2(func() ([]ton.TransactionShortInfo, bool, error) {
return c.client.GetBlockTransactionsV2(ctx, blockID, 256, after)
})
if err != nil {
return nil, err
}
Expand All @@ -525,7 +567,9 @@ func (c *Connection) GetTransactionIDsFromBlock(ctx context.Context, blockID *to
// GetTransactionFromBlock
// Gets transaction from block
func (c *Connection) GetTransactionFromBlock(ctx context.Context, blockID *ton.BlockIDExt, txID ton.TransactionShortInfo) (*tlb.Transaction, error) {
tx, err := c.client.GetTransaction(ctx, blockID, address.NewAddress(0, byte(blockID.Workchain), txID.Account), txID.LT)
tx, err := retry(func() (*tlb.Transaction, error) {
return c.client.GetTransaction(ctx, blockID, address.NewAddress(0, byte(blockID.Workchain), txID.Account), txID.LT)
})
if err != nil {
return nil, err
}
Expand All @@ -537,7 +581,9 @@ func inShard(addr core.Address, shard byte) bool {
}

func (c *Connection) getCurrentNodeTime(ctx context.Context) (time.Time, error) {
t, err := c.client.GetTime(ctx)
t, err := retry(func() (uint32, error) {
return c.client.GetTime(ctx)
})
if err != nil {
return time.Time{}, err
}
Expand Down Expand Up @@ -590,16 +636,11 @@ func (c *Connection) WaitStatus(ctx context.Context, addr *address.Address, stat

// GetAccount
// The method is being redefined for more stable operation.
// Gets account from prev block if impossible to get it from current block. Be careful with diff calculation between blocks.
func (c *Connection) GetAccount(ctx context.Context, block *ton.BlockIDExt, addr *address.Address) (*tlb.Account, error) {
res, err := c.client.GetAccount(ctx, block, addr)
if err != nil && isNotReadyError(err) {
prevBlock, err := c.client.LookupBlock(ctx, block.Workchain, block.Shard, block.SeqNo-1)
if err != nil {
return nil, err
}
return c.client.GetAccount(ctx, prevBlock, addr)
}
res, err := retry(func() (*tlb.Account, error) {
return c.client.GetAccount(ctx, block, addr)
})

return res, err
}

Expand All @@ -616,30 +657,29 @@ func (c *Connection) RunGetMethod(ctx context.Context, block *ton.BlockIDExt, ad
case <-ctx.Done():
return nil, core.ErrTimeoutExceeded
default:
res, err := c.client.RunGetMethod(ctx, block, addr, method, params...)
if err != nil && isNotReadyError(err) {
time.Sleep(time.Millisecond * 200)
continue
}
return res, err
return retry(func() (*ton.ExecutionResult, error) {
return c.client.RunGetMethod(ctx, block, addr, method, params...)
})
}
}
}

func (c *Connection) ListTransactions(ctx context.Context, addr *address.Address, num uint32, lt uint64, txHash []byte) ([]*tlb.Transaction, error) {
return c.client.ListTransactions(ctx, addr, num, lt, txHash)
}

func (c *Connection) Client() ton.LiteClient {
return c.client.Client()
return retry(func() ([]*tlb.Transaction, error) {
return c.client.ListTransactions(ctx, addr, num, lt, txHash)
})
}

func (c *Connection) CurrentMasterchainInfo(ctx context.Context) (*ton.BlockIDExt, error) {
return c.client.CurrentMasterchainInfo(ctx)
return retry(func() (*ton.BlockIDExt, error) {
return c.client.CurrentMasterchainInfo(ctx)
})
}

func (c *Connection) GetMasterchainInfo(ctx context.Context) (*ton.BlockIDExt, error) {
return c.client.GetMasterchainInfo(ctx)
return retry(func() (*ton.BlockIDExt, error) {
return c.client.GetMasterchainInfo(ctx)
})
}

func (c *Connection) SendExternalMessageWaitTransaction(ctx context.Context, ext *tlb.ExternalMessage) (*tlb.Transaction, *ton.BlockIDExt, []byte, error) {
Expand All @@ -659,7 +699,15 @@ func getBlockchainConfig(ctx context.Context, client ton.LiteClient, block *ton.

switch t := resp.(type) {
case ton.ConfigAll:
stateExtra, err := ton.CheckShardMcStateExtraProof(block, []*cell.Cell{t.StateProof, t.ConfigProof})
stateProof, err := cell.FromBOC(t.StateProof)
if err != nil {
return nil, err
}
configProof, err := cell.FromBOC(t.ConfigProof)
if err != nil {
return nil, err
}
stateExtra, err := ton.CheckShardMcStateExtraProof(block, []*cell.Cell{stateProof, configProof})
if err != nil {
return nil, fmt.Errorf("incorrect proof: %w", err)
}
Expand Down
Loading
Loading