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

Demo telemetry #98

Draft
wants to merge 38 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ba0af4c
Upgrading sdk version i go.mod and go.sum
pablodeymo Oct 8, 2024
1c1041f
upgrade go-ethereum in go.sum
pablodeymo Oct 8, 2024
63e3f2c
fixing some compilation errors
pablodeymo Oct 8, 2024
dca8411
fixing challenger.go
pablodeymo Oct 8, 2024
cc9a42e
config.go upgraded
pablodeymo Oct 8, 2024
c2b5236
BuildAvsSubscriber fixed
pablodeymo Oct 8, 2024
18fb982
adding missing parameter to Send
pablodeymo Oct 8, 2024
4f47565
changing to logger := testutils.GetTestLogger()
pablodeymo Oct 8, 2024
e65fbde
challenger fixed
pablodeymo Oct 8, 2024
e1d4e35
hashFunction in aggregator.go
pablodeymo Oct 8, 2024
ab045cf
MockBlsAggregationService in aggregator_test.go
pablodeymo Oct 8, 2024
bb10b73
fixing config.go
pablodeymo Oct 9, 2024
588a6a5
fixing avs_reader avs_writer y bindings
pablodeymo Oct 9, 2024
8a16a93
fixing avs writer and reader
pablodeymo Oct 9, 2024
74e13e9
AvsWriterer and AvsReaderer fixed
pablodeymo Oct 9, 2024
253ea2b
operator.go fixed
pablodeymo Oct 9, 2024
5fdb7d7
registration.go fixed
pablodeymo Oct 9, 2024
da50d14
aggregator.go fixed
pablodeymo Oct 9, 2024
4880aaf
RegisterOperatorInQuorumWithAVSRegistryCoordinator in main.go fixed
pablodeymo Oct 9, 2024
db0e8c7
main.go fixed
pablodeymo Oct 9, 2024
1210835
integration_test.go fixed
pablodeymo Oct 9, 2024
fef94ea
challenger fixed
pablodeymo Oct 10, 2024
4e03208
Adding interface EthClientInterface
pablodeymo Oct 14, 2024
d862586
Merge branch 'master' into upgrade_to_eigen_sdk_0.1.12
pablodeymo Oct 14, 2024
e607aba
fix errors with pointers
pablodeymo Oct 14, 2024
cb9225b
aggregator hash function fixed to match the one used by the operator
pablodeymo Oct 15, 2024
fdb9942
removing extra if err != nil
pablodeymo Oct 15, 2024
aae8ccd
try 3 times to get responses from task manager
pablodeymo Oct 15, 2024
0bcc22b
pin foundry version for tests
TomasArrachea Oct 16, 2024
a7824da
fix aggregator task response processing
TomasArrachea Oct 18, 2024
fae8ffd
fix aggregator test
TomasArrachea Oct 18, 2024
8a36b73
Merge branch 'master' into upgrade_to_eigen_sdk_0.1.12
TomasArrachea Oct 18, 2024
ac30630
cli/actions fixed (ReadYamlConfig moved here)
pablodeymo Nov 11, 2024
86fdfff
Read config fixed
pablodeymo Nov 11, 2024
5005257
errors about context fixed
pablodeymo Nov 11, 2024
ba4761c
createMockAggregator: unused parameter
pablodeymo Nov 12, 2024
b6e4bc6
Use go sdk updated
pablodeymo Nov 12, 2024
21afd24
upgrade sdk commit
pablodeymo Nov 13, 2024
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
45 changes: 43 additions & 2 deletions aggregator/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import (
"time"

"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/ethereum/go-ethereum/accounts/abi"
"golang.org/x/crypto/sha3"

"github.com/Layr-Labs/eigensdk-go/chainio/clients"
sdkclients "github.com/Layr-Labs/eigensdk-go/chainio/clients"
"github.com/Layr-Labs/eigensdk-go/services/avsregistry"
blsagg "github.com/Layr-Labs/eigensdk-go/services/bls_aggregation"
"github.com/Layr-Labs/eigensdk-go/services/operatorsinfo"
oprsinfoserv "github.com/Layr-Labs/eigensdk-go/services/operatorsinfo"
sdktypes "github.com/Layr-Labs/eigensdk-go/types"
"github.com/Layr-Labs/incredible-squaring-avs/aggregator/types"
Expand Down Expand Up @@ -105,9 +108,47 @@ func NewAggregator(c *config.Config) (*Aggregator, error) {
return nil, err
}

operatorPubkeysService := oprsinfoserv.NewOperatorsInfoServiceInMemory(context.Background(), clients.AvsRegistryChainSubscriber, clients.AvsRegistryChainReader, c.Logger)
operatorPubkeysService := oprsinfoserv.NewOperatorsInfoServiceInMemory(context.Background(), clients.AvsRegistryChainSubscriber, clients.AvsRegistryChainReader, nil, operatorsinfo.Opts{}, c.Logger)

// This is the same hash function used by the operator to hash the task response before signing it.
hashFunction := func(taskResponse sdktypes.TaskResponse) (sdktypes.TaskResponseDigest, error) {
// The order here has to match the field ordering of cstaskmanager.IIncredibleSquaringTaskManagerTaskResponse
taskResponseType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{
{
Name: "referenceTaskIndex",
Type: "uint32",
},
{
Name: "numberSquared",
Type: "uint256",
},
})
if err != nil {
c.Logger.Error("Error creating taskResponseType")
return sdktypes.TaskResponseDigest{}, err
}
arguments := abi.Arguments{
{
Type: taskResponseType,
},
}

encodeTaskResponseByte, err := arguments.Pack(taskResponse)
if err != nil {
c.Logger.Error("Error Packing taskResponse", err)
return sdktypes.TaskResponseDigest{}, err
}

var taskResponseDigest [32]byte
hasher := sha3.NewLegacyKeccak256()
hasher.Write(encodeTaskResponseByte)
copy(taskResponseDigest[:], hasher.Sum(nil)[:32])

return taskResponseDigest, nil
}

avsRegistryService := avsregistry.NewAvsRegistryServiceChainCaller(avsReader, operatorPubkeysService, c.Logger)
blsAggregationService := blsagg.NewBlsAggregatorService(avsRegistryService, c.Logger)
blsAggregationService := blsagg.NewBlsAggregatorService(avsRegistryService, hashFunction, c.Logger)

return &Aggregator{
logger: c.Logger,
Expand Down
79 changes: 73 additions & 6 deletions aggregator/aggregator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package aggregator
import (
"context"
"math/big"
"reflect"
"testing"
"time"

Expand All @@ -13,8 +14,9 @@ import (
"go.uber.org/mock/gomock"

"github.com/Layr-Labs/eigensdk-go/crypto/bls"
sdklogging "github.com/Layr-Labs/eigensdk-go/logging"
blsaggservmock "github.com/Layr-Labs/eigensdk-go/services/mocks/blsagg"
blsagg "github.com/Layr-Labs/eigensdk-go/services/bls_aggregation"

"github.com/Layr-Labs/eigensdk-go/testutils"
sdktypes "github.com/Layr-Labs/eigensdk-go/types"

"github.com/Layr-Labs/incredible-squaring-avs/aggregator/mocks"
Expand All @@ -27,6 +29,71 @@ var MOCK_OPERATOR_ID = [32]byte{207, 73, 226, 221, 104, 100, 123, 41, 192, 3, 9,
var MOCK_OPERATOR_STAKE = big.NewInt(100)
var MOCK_OPERATOR_BLS_PRIVATE_KEY_STRING = "50"

// MockBlsAggregationService is a mock of BlsAggregationService interface.
type MockBlsAggregationService struct {
ctrl *gomock.Controller
recorder *MockBlsAggregationServiceMockRecorder
}

// MockBlsAggregationServiceMockRecorder is the mock recorder for MockBlsAggregationService.
type MockBlsAggregationServiceMockRecorder struct {
mock *MockBlsAggregationService
}

// NewMockBlsAggregationService creates a new mock instance.
func NewMockBlsAggregationService(ctrl *gomock.Controller) *MockBlsAggregationService {
mock := &MockBlsAggregationService{ctrl: ctrl}
mock.recorder = &MockBlsAggregationServiceMockRecorder{mock}
return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockBlsAggregationService) EXPECT() *MockBlsAggregationServiceMockRecorder {
return m.recorder
}

// GetResponseChannel mocks base method.
func (m *MockBlsAggregationService) GetResponseChannel() <-chan blsagg.BlsAggregationServiceResponse {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetResponseChannel")
ret0, _ := ret[0].(<-chan blsagg.BlsAggregationServiceResponse)
return ret0
}

// GetResponseChannel indicates an expected call of GetResponseChannel.
func (mr *MockBlsAggregationServiceMockRecorder) GetResponseChannel() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResponseChannel", reflect.TypeOf((*MockBlsAggregationService)(nil).GetResponseChannel))
}

// InitializeNewTask mocks base method.
func (m *MockBlsAggregationService) InitializeNewTask(arg0, arg1 uint32, arg2 sdktypes.QuorumNums, arg3 sdktypes.QuorumThresholdPercentages, arg4 time.Duration) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InitializeNewTask", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(error)
return ret0
}

// InitializeNewTask indicates an expected call of InitializeNewTask.
func (mr *MockBlsAggregationServiceMockRecorder) InitializeNewTask(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitializeNewTask", reflect.TypeOf((*MockBlsAggregationService)(nil).InitializeNewTask), arg0, arg1, arg2, arg3, arg4)
}

// ProcessNewSignature mocks base method.
func (m *MockBlsAggregationService) ProcessNewSignature(arg0 context.Context, arg1 uint32, arg2 interface{}, arg3 *bls.Signature, arg4 sdktypes.Bytes32) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ProcessNewSignature", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(error)
return ret0
}

// ProcessNewSignature indicates an expected call of ProcessNewSignature.
func (mr *MockBlsAggregationServiceMockRecorder) ProcessNewSignature(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProcessNewSignature", reflect.TypeOf((*MockBlsAggregationService)(nil).ProcessNewSignature), arg0, arg1, arg2, arg3, arg4)
}

type MockTask struct {
TaskNum uint32
BlockNumber uint32
Expand Down Expand Up @@ -77,11 +144,11 @@ func TestSendNewTask(t *testing.T) {
}

func createMockAggregator(
mockCtrl *gomock.Controller, operatorPubkeyDict map[sdktypes.OperatorId]types.OperatorInfo,
) (*Aggregator, *chainiomocks.MockAvsWriterer, *blsaggservmock.MockBlsAggregationService, error) {
logger := sdklogging.NewNoopLogger()
mockCtrl *gomock.Controller, _ map[sdktypes.OperatorId]types.OperatorInfo,
) (*Aggregator, *chainiomocks.MockAvsWriterer, *MockBlsAggregationService, error) {
logger := testutils.GetTestLogger()
mockAvsWriter := chainiomocks.NewMockAvsWriterer(mockCtrl)
mockBlsAggregationService := blsaggservmock.NewMockBlsAggregationService(mockCtrl)
mockBlsAggregationService := NewMockBlsAggregationService(mockCtrl)

aggregator := &Aggregator{
logger: logger,
Expand Down
3 changes: 3 additions & 0 deletions aggregator/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/urfave/cli"

"github.com/Layr-Labs/eigensdk-go/telemetry"
"github.com/Layr-Labs/incredible-squaring-avs/aggregator"
"github.com/Layr-Labs/incredible-squaring-avs/core/config"
)
Expand All @@ -22,6 +23,8 @@ var (

func main() {

_ = telemetry.InitTelemetry("phc_AGsxzgr4ETSVddRFfZXdHUnWLIW8ExxCBSqPLmc6qbl", "e77c390f-223e-4dda-aa3d-3e554c3f7270")

app := cli.NewApp()
app.Flags = config.Flags
app.Version = fmt.Sprintf("%s-%s-%s", Version, GitCommit, GitDate)
Expand Down
2 changes: 1 addition & 1 deletion aggregator/rpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (agg *Aggregator) ProcessSignedTaskResponse(signedTaskResponse *SignedTaskR
agg.taskResponsesMu.Unlock()

err = agg.blsAggregationService.ProcessNewSignature(
context.Background(), taskIndex, taskResponseDigest,
context.Background(), taskIndex, signedTaskResponse.TaskResponse,
&signedTaskResponse.BlsSignature, signedTaskResponse.OperatorId,
)
return err
Expand Down
4 changes: 1 addition & 3 deletions aggregator/rpc_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,11 @@ func TestProcessSignedTaskResponse(t *testing.T) {
NumberToSquare: NUMBER_TO_SQUARE,
}, *MOCK_OPERATOR_KEYPAIR)
assert.Nil(t, err)
signedTaskResponseDigest, err := core.GetTaskResponseDigest(&signedTaskResponse.TaskResponse)
assert.Nil(t, err)

// TODO(samlaf): is this the right way to test writing to external service?
// or is there some wisdom to "don't mock 3rd party code"?
// see https://hynek.me/articles/what-to-mock-in-5-mins/
mockBlsAggServ.EXPECT().ProcessNewSignature(context.Background(), TASK_INDEX, signedTaskResponseDigest,
mockBlsAggServ.EXPECT().ProcessNewSignature(context.Background(), TASK_INDEX, signedTaskResponse.TaskResponse,
&signedTaskResponse.BlsSignature, signedTaskResponse.OperatorId)
err = aggregator.ProcessSignedTaskResponse(signedTaskResponse, nil)
assert.Nil(t, err)
Expand Down
13 changes: 9 additions & 4 deletions challenger/challenger.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@ import (
"context"
"math/big"

ethclient "github.com/Layr-Labs/eigensdk-go/chainio/clients/eth"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/Layr-Labs/incredible-squaring-avs/common"
"github.com/Layr-Labs/incredible-squaring-avs/core/config"
"github.com/ethereum/go-ethereum/accounts/abi"
commoneth "github.com/ethereum/go-ethereum/common"
typeseth "github.com/ethereum/go-ethereum/core/types"

"github.com/Layr-Labs/incredible-squaring-avs/challenger/types"
cstaskmanager "github.com/Layr-Labs/incredible-squaring-avs/contracts/bindings/IncredibleSquaringTaskManager"
"github.com/Layr-Labs/incredible-squaring-avs/core/chainio"
)

type ChallengerClient interface {
TransactionByHash(ctx context.Context, hash commoneth.Hash) (tx *typeseth.Transaction, isPending bool, err error)
}

type Challenger struct {
logger logging.Logger
ethClient ethclient.Client
ethClient ChallengerClient
avsReader chainio.AvsReaderer
avsWriter chainio.AvsWriterer
avsSubscriber chainio.AvsSubscriberer
Expand Down Expand Up @@ -47,10 +52,10 @@ func NewChallenger(c *config.Config) (*Challenger, error) {
}

challenger := &Challenger{
ethClient: c.EthHttpClient,
logger: c.Logger,
avsWriter: avsWriter,
ethClient: &c.EthHttpClient,
avsReader: avsReader,
avsWriter: avsWriter,
avsSubscriber: avsSubscriber,
tasks: make(map[uint32]cstaskmanager.IIncredibleSquaringTaskManagerTask),
taskResponses: make(map[uint32]types.TaskResponseData),
Expand Down
72 changes: 67 additions & 5 deletions challenger/challenger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ package challenger
import (
"context"
"math/big"
"reflect"
"testing"

mockethclient "github.com/Layr-Labs/eigensdk-go/chainio/mocks"
sdklogging "github.com/Layr-Labs/eigensdk-go/logging"
"github.com/Layr-Labs/eigensdk-go/testutils"
aggtypes "github.com/Layr-Labs/incredible-squaring-avs/aggregator/types"
"github.com/Layr-Labs/incredible-squaring-avs/challenger/mocks"
chtypes "github.com/Layr-Labs/incredible-squaring-avs/challenger/types"
cstaskmanager "github.com/Layr-Labs/incredible-squaring-avs/contracts/bindings/IncredibleSquaringTaskManager"
chainiomocks "github.com/Layr-Labs/incredible-squaring-avs/core/chainio/mocks"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
Expand All @@ -22,6 +23,66 @@ var MOCK_OPERATOR_ID = [32]byte{207, 73, 226, 221, 104, 100, 123, 41, 192, 3, 9,
var MOCK_OPERATOR_STAKE = big.NewInt(100)
var MOCK_OPERATOR_BLS_PRIVATE_KEY_STRING = "50"

const (
TransactionHash = "0x0000000000000000000000000000000000000000000000000000000000001234"
TransactionNashNotInFake = "0xabcd"
BlockNumber = 1234
)

// MockEthClient is a mock of Client interface.
type MockEthClient struct {
ctrl *gomock.Controller
recorder *MockEthClientMockRecorder
}

// MockEthClientMockRecorder is the mock recorder for MockEthClient.
type MockEthClientMockRecorder struct {
mock *MockEthClient
}

// NewMockEthClient creates a new mock instance.
func NewMockEthClient(ctrl *gomock.Controller) *MockEthClient {
mock := &MockEthClient{ctrl: ctrl}
mock.recorder = &MockEthClientMockRecorder{mock}
return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockEthClient) EXPECT() *MockEthClientMockRecorder {
return m.recorder
}

// BalanceAt mocks base method.
func (m *MockEthClient) BalanceAt(arg0 context.Context, arg1 common.Address, arg2 *big.Int) (*big.Int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BalanceAt", arg0, arg1, arg2)
ret0, _ := ret[0].(*big.Int)
ret1, _ := ret[1].(error)
return ret0, ret1
}

// BalanceAt indicates an expected call of BalanceAt.
func (mr *MockEthClientMockRecorder) BalanceAt(arg0, arg1, arg2 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BalanceAt", reflect.TypeOf((*MockEthClient)(nil).BalanceAt), arg0, arg1, arg2)
}

// TransactionByHash indicates an expected call of TransactionByHash.
func (mr *MockEthClientMockRecorder) TransactionByHash(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionByHash", reflect.TypeOf((*MockEthClient)(nil).TransactionByHash), arg0, arg1)
}

// TransactionByHash mocks base method.
func (m *MockEthClient) TransactionByHash(arg0 context.Context, arg1 common.Hash) (*types.Transaction, bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TransactionByHash", arg0, arg1)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(bool)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}

// @samlaf I tried pulling the MockTask struct froma ggregator_test but getting error: "undefined: aggregator.MockTask"
type MockTask struct {
TaskNum uint32
Expand Down Expand Up @@ -170,12 +231,13 @@ func TestProcessTaskResponseLog(t *testing.T) {

}

func createMockChallenger(mockCtrl *gomock.Controller) (*Challenger, *chainiomocks.MockAvsWriterer, *chainiomocks.MockAvsReaderer, *chainiomocks.MockAvsSubscriberer, *mockethclient.MockEthClient, error) {
logger := sdklogging.NewNoopLogger()
func createMockChallenger(mockCtrl *gomock.Controller) (*Challenger, *chainiomocks.MockAvsWriterer, *chainiomocks.MockAvsReaderer, *chainiomocks.MockAvsSubscriberer, *MockEthClient, error) {
logger := testutils.GetTestLogger()
mockAvsWriter := chainiomocks.NewMockAvsWriterer(mockCtrl)
mockAvsReader := chainiomocks.NewMockAvsReaderer(mockCtrl)
mockAvsSubscriber := chainiomocks.NewMockAvsSubscriberer(mockCtrl)
mockEthClient := mockethclient.NewMockEthClient(mockCtrl)
mockEthClient := NewMockEthClient(mockCtrl)

challenger := &Challenger{
logger: logger,
avsWriter: mockAvsWriter,
Expand Down
4 changes: 2 additions & 2 deletions cli/actions/deposit_into_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"log"
"math/big"

sdkutils "github.com/Layr-Labs/eigensdk-go/utils"
commonincredible "github.com/Layr-Labs/incredible-squaring-avs/common"
"github.com/Layr-Labs/incredible-squaring-avs/core/config"
"github.com/Layr-Labs/incredible-squaring-avs/operator"
"github.com/Layr-Labs/incredible-squaring-avs/types"
Expand All @@ -18,7 +18,7 @@ func DepositIntoStrategy(ctx *cli.Context) error {

configPath := ctx.GlobalString(config.ConfigFileFlag.Name)
nodeConfig := types.NodeConfig{}
err := sdkutils.ReadYamlConfig(configPath, &nodeConfig)
err := commonincredible.ReadYamlConfig(configPath, &nodeConfig)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions cli/actions/print_operator_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"log"

sdkutils "github.com/Layr-Labs/eigensdk-go/utils"
commonincredible "github.com/Layr-Labs/incredible-squaring-avs/common"
"github.com/Layr-Labs/incredible-squaring-avs/core/config"
"github.com/Layr-Labs/incredible-squaring-avs/operator"
"github.com/Layr-Labs/incredible-squaring-avs/types"
Expand All @@ -15,7 +15,7 @@ func PrintOperatorStatus(ctx *cli.Context) error {

configPath := ctx.GlobalString(config.ConfigFileFlag.Name)
nodeConfig := types.NodeConfig{}
err := sdkutils.ReadYamlConfig(configPath, &nodeConfig)
err := commonincredible.ReadYamlConfig(configPath, &nodeConfig)
if err != nil {
return err
}
Expand Down
Loading
Loading