Skip to content

Commit

Permalink
Merge pull request #255 from tonkeeper/get-validators
Browse files Browse the repository at this point in the history
Implement /v2/blockchain/validators
  • Loading branch information
mr-tron authored Nov 23, 2023
2 parents ce6fbda + 6883db4 commit e0ae116
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 65 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
### 2023-10-21
- `/v2/blockchain/masterchain/{masterchain_seqno}/config` method which returns a blockchain config for a specific block.
- `/v2/blockchain/masterchain/{masterchain_seqno}/config/raw` method which returns a raw blockchain config for a specific block.

### 2023-10-22
- `/v2/blockchain/validators` method which returns the current validators and theirs stakes.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/sourcegraph/conc v0.3.0
github.com/stretchr/testify v1.8.4
github.com/tonkeeper/scam_backoffice_rules v0.0.0-20231030143526-f717a9954e0a
github.com/tonkeeper/tongo v1.4.1
github.com/tonkeeper/tongo v1.4.2-0.20231123114202-e8c3d9033f8f
go.opentelemetry.io/otel v1.19.0
go.opentelemetry.io/otel/metric v1.19.0
go.opentelemetry.io/otel/trace v1.19.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tonkeeper/scam_backoffice_rules v0.0.0-20231030143526-f717a9954e0a h1:yYAU4WwiNTi4Xddyids0ByjudmDELHtO+wAFTYnFXJU=
github.com/tonkeeper/scam_backoffice_rules v0.0.0-20231030143526-f717a9954e0a/go.mod h1:SqZXYO9vbID8ku+xnnaKXeNGmehxigODGrk5V1KqbRA=
github.com/tonkeeper/tongo v1.4.1 h1:QMIya/ongFuOyHgVOFmHF+Cu4nt23KumPRLApmp90zE=
github.com/tonkeeper/tongo v1.4.1/go.mod h1:LdOBjpUz6vLp1EdX3E0XLNks9YI5XMSqaQahfOMrBEY=
github.com/tonkeeper/tongo v1.4.2-0.20231123114202-e8c3d9033f8f h1:X4f9DsGTm2zrz5GoctPhPaYkylfsUgLfFkLYH3r724E=
github.com/tonkeeper/tongo v1.4.2-0.20231123114202-e8c3d9033f8f/go.mod h1:LdOBjpUz6vLp1EdX3E0XLNks9YI5XMSqaQahfOMrBEY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
Expand Down
148 changes: 86 additions & 62 deletions pkg/api/blockchain_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"net/http"

"github.com/tonkeeper/tongo/contract/elector"
"github.com/tonkeeper/tongo/tvm"

"github.com/tonkeeper/opentonapi/internal/g"
Expand Down Expand Up @@ -227,96 +228,119 @@ func (h *Handler) GetRawBlockchainConfigFromBlock(ctx context.Context, params oa
}

func (h *Handler) GetBlockchainValidators(ctx context.Context) (*oas.Validators, error) {
return nil, toError(http.StatusNotImplemented, fmt.Errorf("not implemented"))
mcInfoExtra, err := h.storage.GetMasterchainInfoExtRaw(ctx, 0)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
blockHeader, err := h.storage.GetBlockHeader(ctx, ton.BlockID{
next := ton.BlockID{
Workchain: int32(mcInfoExtra.Last.Workchain),
Shard: mcInfoExtra.Last.Shard,
Seqno: mcInfoExtra.Last.Seqno,
})
}
blockHeader, err := h.storage.GetBlockHeader(ctx, next)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
configBlockID := next
if !blockHeader.IsKeyBlock {
blockHeader, err = h.storage.GetBlockHeader(ctx, ton.BlockID{
Workchain: int32(mcInfoExtra.Last.Workchain),
Shard: mcInfoExtra.Last.Shard,
Seqno: uint32(blockHeader.PrevKeyBlockSeqno),
})
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
} //todo: fix this shit. find block by better algo
blockHeader, err = h.storage.GetBlockHeader(ctx, ton.BlockID{
Workchain: int32(mcInfoExtra.Last.Workchain),
Shard: mcInfoExtra.Last.Shard,
Seqno: uint32(blockHeader.PrevKeyBlockSeqno),
})
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
configBlockID.Seqno = uint32(blockHeader.PrevKeyBlockSeqno)
}
rawConfig, err := h.storage.GetConfigFromBlock(ctx, blockHeader.BlockID)
rawConfig, err := h.storage.GetConfigFromBlock(ctx, configBlockID)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}

config, err := ton.ConvertBlockchainConfig(rawConfig)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
bConfig, _ := h.storage.TrimmedConfigBase64()
configCell, _ := boc.DeserializeSinglRootBase64(bConfig)

electorAddr, _ := config.ElectorAddr()
electorState, err := h.storage.GetAccountStateRaw(ctx, electorAddr, &blockHeader.BlockIDExt)
bConfig, err := h.storage.TrimmedConfigBase64()
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
electorStateCell, err := boc.DeserializeBoc(electorState.State)
configCell, err := boc.DeserializeSinglRootBase64(bConfig)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}

var acc tlb.Account
err = tlb.Unmarshal(electorStateCell[0], &acc)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
electorAddr, ok := config.ElectorAddr()
if !ok {
return nil, toError(http.StatusInternalServerError, fmt.Errorf("can't get elector address"))
}
init := acc.Account.Storage.State.AccountActive.StateInit
code := init.Code.Value.Value
data := init.Data.Value.Value
iterations := 0
for {
iterations += 1
if iterations > 100 {
return nil, toError(http.StatusInternalServerError, fmt.Errorf("can't find block with elections"))
}
blockHeader, err := h.storage.GetBlockHeader(ctx, next)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
next.Seqno = uint32(blockHeader.PrevKeyBlockSeqno)
if !blockHeader.IsKeyBlock {
continue
}
electorState, err := h.storage.GetAccountStateRaw(ctx, electorAddr, &blockHeader.BlockIDExt)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
electorStateCell, err := boc.DeserializeBoc(electorState.State)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
var acc tlb.Account
err = tlb.Unmarshal(electorStateCell[0], &acc)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
init := acc.Account.Storage.State.AccountActive.StateInit
code := init.Code.Value.Value
data := init.Data.Value.Value

emulator, err := tvm.NewEmulator(&code, &data, configCell)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
emulator.SetGasLimit(10_000_000)
emulator, err := tvm.NewEmulator(&code, &data, configCell)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
if err := emulator.SetGasLimit(10_000_000); err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
list, err := elector.GetParticipantListExtended(ctx, electorAddr, emulator)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
if config.ConfigParam34 == nil {
return nil, toError(http.StatusInternalServerError, fmt.Errorf("there is no current validators set in blockchain config"))
}

status, result, err := emulator.RunSmcMethod(ctx, electorAddr, "participant_list_extended", nil)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
if status != 0 {
return nil, toError(http.StatusInternalServerError, fmt.Errorf("emulator status: %d", status))
}
var partList struct {
ElectAt int64
ElectClose int64
MinStake int64
TotalStake int64
Validators []struct {
Stake int64
MaxFactor int64
Address tlb.Int256
AdnlAddr string
validatorSet := config.ConfigParam34.CurValidators
var utimeSince uint32
switch validatorSet.SumType {
case "Validators":
utimeSince = validatorSet.Validators.UtimeSince
case "ValidatorsExt":
utimeSince = validatorSet.ValidatorsExt.UtimeSince
default:
return nil, toError(http.StatusInternalServerError, fmt.Errorf("unknown validator set type %v", validatorSet.SumType))
}
if list.ElectAt != int64(utimeSince) {
// this election is for the next validator set,
// let's take travel back in time to the current validator set
continue
}
validators := &oas.Validators{
ElectAt: list.ElectAt,
ElectClose: list.ElectClose,
MinStake: list.MinStake,
TotalStake: list.TotalStake,
Validators: make([]oas.Validator, 0, len(list.Validators)),
}
for _, v := range list.Validators {
validators.Validators = append(validators.Validators, oas.Validator{
Stake: int64(v.Stake),
MaxFactor: int64(v.MaxFactor),
Address: v.Address.ToRaw(),
AdnlAddress: v.AdnlAddr,
})
}
return validators, nil
}
err = result.Unmarshal(&partList)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
return &oas.Validators{}, nil
}
35 changes: 35 additions & 0 deletions pkg/api/blockchain_handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/tonkeeper/opentonapi/pkg/litestorage"
"github.com/tonkeeper/opentonapi/pkg/oas"
"github.com/tonkeeper/tongo/config"
"github.com/tonkeeper/tongo/ton"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -153,3 +154,37 @@ func TestHandler_GetBlockchainConfigFromBlock(t *testing.T) {
})
}
}

func TestHandler_GetBlockchainValidators(t *testing.T) {
var servers []config.LiteServer
if env, ok := os.LookupEnv("LITE_SERVERS"); ok {
var err error
servers, err = config.ParseLiteServersEnvVar(env)
require.Nil(t, err)
}
logger := zap.L()
liteStorage, err := litestorage.NewLiteStorage(logger, litestorage.WithLiteServers(servers))
require.Nil(t, err)
h, err := NewHandler(logger, WithStorage(liteStorage), WithExecutor(liteStorage))
require.Nil(t, err)
validators, err := h.GetBlockchainValidators(context.Background())
require.Nil(t, err)

rawConfig, err := h.storage.GetLastConfig(context.Background())
require.Nil(t, err)
config, err := ton.ConvertBlockchainConfig(rawConfig)
require.Nil(t, err)

require.NotNil(t, config.ConfigParam34)
curValidators := config.ConfigParam34.CurValidators.ValidatorsExt
require.Equal(t, len(validators.Validators), len(curValidators.List.Items()))
inCurrentSet := make(map[string]struct{})
for _, item := range curValidators.List.Items() {
inCurrentSet[item.Value.ValidatorAddr.AdnlAddr.Hex()] = struct{}{}
}
for _, v := range validators.Validators {
_, ok := inCurrentSet[v.AdnlAddress]
require.True(t, ok)
}
require.Equal(t, validators.ElectAt, int64(curValidators.UtimeSince))
}

0 comments on commit e0ae116

Please sign in to comment.