Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client.GetBlock() to verify hash of requested block #218

Merged
merged 1 commit into from
Dec 21, 2023
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
47 changes: 42 additions & 5 deletions liteapi/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ var (
ErrAccountNotFound = errors.New("account not found")
)

// ProofPolicy specifies a policy for proof checks.
// This feature is experimental and can be changed or removed in the future.
type ProofPolicy uint32

const (
// ProofPolicyUnsafe disables proof checks.
ProofPolicyUnsafe ProofPolicy = iota
ProofPolicyFast
)

// Client provides a convenient way to interact with TON blockchain.
//
// By default, it uses a single connection to a lite server.
Expand All @@ -53,7 +63,10 @@ var (
// To avoid this, you can use WithBlock() method to specify a target block for all requests.
type Client struct {
pool *pool.FailoverPool
// proofPolicy specifies a policy for proof checks.
proofPolicy ProofPolicy

// mu protects targetBlockID.
mu sync.RWMutex
targetBlockID *ton.BlockIDExt
}
Expand All @@ -66,6 +79,8 @@ type Options struct {
MaxConnections int
// InitCtx is used when opening a new connection to lite servers during the initialization.
InitCtx context.Context
// ProofPolicy specifies a policy for proof checks.
ProofPolicy ProofPolicy
}

type Option func(o *Options) error
Expand Down Expand Up @@ -95,6 +110,13 @@ func WithTimeout(timeout time.Duration) Option {
}
}

func WithProofPolicy(policy ProofPolicy) Option {
return func(o *Options) error {
o.ProofPolicy = policy
return nil
}
}

// WithInitializationContext specifies a context to be used
// when opening a new connection to lite servers during the initialization.
func WithInitializationContext(ctx context.Context) Option {
Expand Down Expand Up @@ -211,6 +233,7 @@ func NewClient(opts ...Option) (*Client, error) {
Timeout: 60 * time.Second,
MaxConnections: defaultMaxConnectionsNumber,
InitCtx: context.Background(),
ProofPolicy: ProofPolicyUnsafe,
}
for _, o := range opts {
if err := o(options); err != nil {
Expand Down Expand Up @@ -239,7 +262,8 @@ func NewClient(opts ...Option) (*Client, error) {
return nil, fmt.Errorf("all liteservers are unavailable")
}
client := Client{
pool: pool.NewFailoverPool(liteclients),
pool: pool.NewFailoverPool(liteclients),
proofPolicy: options.ProofPolicy,
}
go client.pool.Run(context.TODO())
return &client, nil
Expand Down Expand Up @@ -298,12 +322,25 @@ func (c *Client) GetBlock(ctx context.Context, blockID ton.BlockIDExt) (tlb.Bloc
if len(cells) != 1 {
return tlb.Block{}, boc.ErrNotSingleRoot
}
var data tlb.Block
err = tlb.NewDecoder().Unmarshal(cells[0], &data)
if err != nil {
decoder := tlb.NewDecoder()
var block tlb.Block
if err := decoder.Unmarshal(cells[0], &block); err != nil {
return tlb.Block{}, err
}
return data, nil
if c.proofPolicy == ProofPolicyUnsafe {
return block, nil
}
// this should be quite fast because
// when unmarshalling a block, we calculate hashes for transactions and messages.
// so most of the cells' hashes should be in the cache.
hash, err := decoder.Hasher().Hash(cells[0])
if err != nil {
return tlb.Block{}, fmt.Errorf("failed to calculate block hash: %w", err)
}
if !bytes.Equal(hash[:], blockID.RootHash[:]) {
return tlb.Block{}, fmt.Errorf("block hash mismatch")
}
return block, nil
}

func (c *Client) GetBlockRaw(ctx context.Context, blockID ton.BlockIDExt) (liteclient.LiteServerBlockDataC, error) {
Expand Down
2 changes: 1 addition & 1 deletion liteapi/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func TestGetAllShards(t *testing.T) {
}

func TestGetBlock(t *testing.T) {
api, err := NewClient(Mainnet(), FromEnvs())
api, err := NewClient(Mainnet(), FromEnvs(), WithProofPolicy(ProofPolicyFast))
if err != nil {
t.Fatal(err)
}
Expand Down
6 changes: 6 additions & 0 deletions tlb/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,9 @@ func decodeCell(c *boc.Cell, val reflect.Value) error {
func decodeBitString(c *boc.Cell, val reflect.Value) error {
return fmt.Errorf("bigString decoding not supported")
}

// Hasher returns boc.Hasher that is used to calculate hashes when decoding.
func (dec *Decoder) Hasher() *boc.Hasher {
return dec.hasher

}
Loading