diff --git a/contract/pkg/actcapture/activity_capture_contract.go b/contract/pkg/actcapture/activity_capture_contract.go index b42f816..20a748f 100644 --- a/contract/pkg/actcapture/activity_capture_contract.go +++ b/contract/pkg/actcapture/activity_capture_contract.go @@ -6,7 +6,8 @@ import ( "github.com/centrifuge/go-substrate-rpc-client/v4/signature" "github.com/centrifuge/go-substrate-rpc-client/v4/types" "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" - "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/utils" log "github.com/sirupsen/logrus" "math/big" ) @@ -26,7 +27,7 @@ type ( } activityCaptureContract struct { - client pkg.BlockchainClient + client sdktypes.BlockchainClient account types.AccountID keyringPair signature.KeyringPair contractAddress types.AccountID @@ -37,7 +38,7 @@ type ( } ) -func CreateActivityCaptureContract(client pkg.BlockchainClient, contractAddressSS58 string, secret string) ActivityCaptureContract { +func CreateActivityCaptureContract(client sdktypes.BlockchainClient, contractAddressSS58 string, secret string) ActivityCaptureContract { keyringPair, err := signature.KeyringPairFromSecret(secret, 42) if err != nil { log.WithError(err).Fatal("Can't initialize keyring pair for activity capture contract") @@ -63,7 +64,7 @@ func CreateActivityCaptureContract(client pkg.BlockchainClient, contractAddressS log.WithError(err).WithField("method", getEraSettings).Fatal("Can't decode method getEraSettingsMethod") } - contractAddress, err := pkg.DecodeAccountIDFromSS58(contractAddressSS58) + contractAddress, err := utils.DecodeAccountIDFromSS58(contractAddressSS58) if err != nil { log.WithError(err).WithField("contractAddressSS58", contractAddressSS58).Fatal("Can't decode contract address SS58") } @@ -101,7 +102,7 @@ func (a *activityCaptureContract) SetCommit(ctx context.Context, hash []byte, ga From := types.U64(from) To := types.U64(to) - call := pkg.ContractCall{ + call := sdktypes.ContractCall{ ContractAddress: a.contractAddress, ContractAddressSS58: a.contractAddressSS58, From: a.keyringPair, diff --git a/contract/pkg/bucket/ddc_bucket_contract.go b/contract/pkg/bucket/ddc_bucket_contract.go index eaf8ab8..2bb1cae 100644 --- a/contract/pkg/bucket/ddc_bucket_contract.go +++ b/contract/pkg/bucket/ddc_bucket_contract.go @@ -4,12 +4,12 @@ import ( _ "embed" "encoding/hex" "errors" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" "reflect" "time" "github.com/centrifuge/go-substrate-rpc-client/v4/signature" "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg" log "github.com/sirupsen/logrus" ) @@ -50,11 +50,11 @@ type ( CDNNodeGet(nodeId uint32) (*CDNNodeStatus, error) AccountGet(account types.AccountID) (*Account, error) AddContractEventHandler(event string, handler func(interface{})) error - GetEventDispatcher() map[types.Hash]pkg.ContractEventDispatchEntry + GetEventDispatcher() map[types.Hash]sdktypes.ContractEventDispatchEntry } ddcBucketContract struct { - contract pkg.BlockchainClient + contract sdktypes.BlockchainClient lastAccessTime time.Time contractAddressSS58 string keyringPair signature.KeyringPair @@ -64,7 +64,7 @@ type ( cdnClusterGetMethodId []byte cdnNodeGetMethodId []byte accountGetMethodId []byte - eventDispatcher map[types.Hash]pkg.ContractEventDispatchEntry + eventDispatcher map[types.Hash]sdktypes.ContractEventDispatchEntry } ) @@ -85,7 +85,7 @@ var eventDispatchTable = map[string]reflect.Type{ GrantPermissionEventId: reflect.TypeOf(GrantPermissionEvent{}), RevokePermissionEventId: reflect.TypeOf(RevokePermissionEvent{})} -func CreateDdcBucketContract(client pkg.BlockchainClient, contractAddressSS58 string) DdcBucketContract { +func CreateDdcBucketContract(client sdktypes.BlockchainClient, contractAddressSS58 string) DdcBucketContract { bucketGetMethodId, err := hex.DecodeString(bucketGetMethod) if err != nil { log.WithError(err).WithField("method", bucketGetMethod).Fatal("Can't decode method bucketGetMethod") @@ -116,12 +116,12 @@ func CreateDdcBucketContract(client pkg.BlockchainClient, contractAddressSS58 st log.WithError(err).WithField("method", accountGetMethod).Fatal("Can't decode method accountGetMethod") } - eventDispatcher := make(map[types.Hash]pkg.ContractEventDispatchEntry) + eventDispatcher := make(map[types.Hash]sdktypes.ContractEventDispatchEntry) for k, v := range eventDispatchTable { if key, err := types.NewHashFromHexString(k); err != nil { log.WithError(err).WithField("hash", k).Fatalf("Bad event hash for event %s", v.Name()) } else { - eventDispatcher[key] = pkg.ContractEventDispatchEntry{ArgumentType: v} + eventDispatcher[key] = sdktypes.ContractEventDispatchEntry{ArgumentType: v} } } @@ -224,6 +224,6 @@ func (d *ddcBucketContract) GetLastAccessTime() time.Time { return d.lastAccessTime } -func (d *ddcBucketContract) GetEventDispatcher() map[types.Hash]pkg.ContractEventDispatchEntry { +func (d *ddcBucketContract) GetEventDispatcher() map[types.Hash]sdktypes.ContractEventDispatchEntry { return d.eventDispatcher } diff --git a/contract/pkg/cache/ddc_bucket_contract_cache.go b/contract/pkg/cache/ddc_bucket_contract_cache.go index 987ddcd..5e6c8c0 100644 --- a/contract/pkg/cache/ddc_bucket_contract_cache.go +++ b/contract/pkg/cache/ddc_bucket_contract_cache.go @@ -2,11 +2,11 @@ package cache import ( "encoding/hex" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" "strconv" "time" "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg" "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/bucket" "github.com/golang/groupcache/singleflight" "github.com/patrickmn/go-cache" @@ -184,7 +184,7 @@ func (d *ddcBucketContractCached) AddContractEventHandler(event string, handler return d.ddcBucketContract.AddContractEventHandler(event, handler) } -func (d *ddcBucketContractCached) GetEventDispatcher() map[types.Hash]pkg.ContractEventDispatchEntry { +func (d *ddcBucketContractCached) GetEventDispatcher() map[types.Hash]sdktypes.ContractEventDispatchEntry { return d.ddcBucketContract.GetEventDispatcher() } diff --git a/contract/pkg/cache/ddc_bucket_contract_cache_test.go b/contract/pkg/cache/ddc_bucket_contract_cache_test.go index c385b8e..ee439b7 100644 --- a/contract/pkg/cache/ddc_bucket_contract_cache_test.go +++ b/contract/pkg/cache/ddc_bucket_contract_cache_test.go @@ -1,11 +1,11 @@ package cache import ( + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" "testing" "time" "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg" "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/bucket" "github.com/patrickmn/go-cache" "github.com/stretchr/testify/assert" @@ -60,7 +60,7 @@ func (d *mockedDdcBucketContract) AddContractEventHandler(event string, handler return nil } -func (d *mockedDdcBucketContract) GetEventDispatcher() map[types.Hash]pkg.ContractEventDispatchEntry { +func (d *mockedDdcBucketContract) GetEventDispatcher() map[types.Hash]sdktypes.ContractEventDispatchEntry { return nil } diff --git a/contract/pkg/client.go b/contract/pkg/client.go index 490155c..45aaa82 100644 --- a/contract/pkg/client.go +++ b/contract/pkg/client.go @@ -3,6 +3,8 @@ package pkg import ( "bytes" "context" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/utils" "os/signal" "reflect" "sync" @@ -22,69 +24,16 @@ const ( ) type ( - BlockchainClient interface { - CallToReadEncoded(contractAddressSS58 string, fromAddress string, method []byte, args ...interface{}) (string, error) - CallToExec(ctx context.Context, contractCall ContractCall) (types.Hash, error) - Deploy(ctx context.Context, deployCall DeployCall) (types.AccountID, error) - SetEventDispatcher(contractAddressSS58 string, dispatcher map[types.Hash]ContractEventDispatchEntry) error - } - blockchainClient struct { *gsrpc.SubstrateAPI eventContractAccount types.AccountID - eventDispatcher map[types.Hash]ContractEventDispatchEntry + eventDispatcher map[types.Hash]sdktypes.ContractEventDispatchEntry eventContextCancel context.CancelFunc connectMutex sync.Mutex } - - ContractCall struct { - ContractAddress types.AccountID - ContractAddressSS58 string - From signature.KeyringPair - Value float64 - GasLimit float64 - Method []byte - Args []interface{} - } - - DeployCall struct { - Code []byte - Salt []byte - From signature.KeyringPair - Value float64 - GasLimit float64 - Method []byte - Args []interface{} - } - - ContractEventDispatchEntry struct { - ArgumentType reflect.Type - Handler ContractEventHandler - } - - ContractEventHandler func(interface{}) - - Response struct { - DebugMessage string `json:"debugMessage"` - GasConsumed int `json:"gasConsumed"` - Result struct { - Ok struct { - Data string `json:"data"` - Flags int `json:"flags"` - } `json:"Ok"` - } `json:"result"` - } - - Request struct { - Origin string `json:"origin"` - Dest string `json:"dest"` - GasLimit uint `json:"gasLimit"` - InputData string `json:"inputData"` - Value int `json:"value"` - } ) -func CreateBlockchainClient(apiUrl string) BlockchainClient { +func CreateBlockchainClient(apiUrl string) sdktypes.BlockchainClient { substrateAPI, err := gsrpc.NewSubstrateAPI(apiUrl) if err != nil { log.WithError(err).WithField("apiUrl", apiUrl).Fatal("Can't connect to blockchainClient") @@ -95,8 +44,8 @@ func CreateBlockchainClient(apiUrl string) BlockchainClient { } } -func (b *blockchainClient) SetEventDispatcher(contractAddressSS58 string, dispatcher map[types.Hash]ContractEventDispatchEntry) error { - contract, err := DecodeAccountIDFromSS58(contractAddressSS58) +func (b *blockchainClient) SetEventDispatcher(contractAddressSS58 string, dispatcher map[types.Hash]sdktypes.ContractEventDispatchEntry) error { + contract, err := utils.DecodeAccountIDFromSS58(contractAddressSS58) if err != nil { return err } @@ -129,6 +78,7 @@ func (b *blockchainClient) listenContractEvents() error { b.eventContextCancel = cancel watchdog := time.NewTicker(time.Minute) eventArrived := true + var lastEventBlock types.BlockNumber go func() { defer sub.Unsubscribe() for { @@ -158,6 +108,13 @@ func (b *blockchainClient) listenContractEvents() error { break } eventArrived = true + block, err := b.RPC.Chain.GetBlock(evt.Block) + if err != nil { + log.WithError(err).Warn("Error fetching block") + break + } + lastEventBlock = block.Block.Header.Number + print(lastEventBlock) // parse all events for this block for _, chng := range evt.Changes { @@ -179,8 +136,8 @@ func (b *blockchainClient) listenContractEvents() error { } // Identify the event by matching one of its topics against known signatures. The topics are sorted so - // the the needed one may be in the arbitrary position. - var dispatchEntry ContractEventDispatchEntry + // the needed one may be in the arbitrary position. + var dispatchEntry sdktypes.ContractEventDispatchEntry found := false for _, topic := range e.Topics { dispatchEntry, found = b.eventDispatcher[topic] @@ -217,7 +174,7 @@ func (b *blockchainClient) listenContractEvents() error { } func (b *blockchainClient) CallToReadEncoded(contractAddressSS58 string, fromAddress string, method []byte, args ...interface{}) (string, error) { - data, err := GetContractData(method, args...) + data, err := utils.GetContractData(method, args...) if err != nil { return "", errors.Wrap(err, "getMessagesData") } @@ -230,27 +187,27 @@ func (b *blockchainClient) CallToReadEncoded(contractAddressSS58 string, fromAdd return res.Result.Ok.Data, nil } -func (b *blockchainClient) callToRead(contractAddressSS58 string, fromAddress string, data []byte) (Response, error) { - params := Request{ +func (b *blockchainClient) callToRead(contractAddressSS58 string, fromAddress string, data []byte) (sdktypes.Response, error) { + params := sdktypes.Request{ Origin: fromAddress, Dest: contractAddressSS58, GasLimit: 500_000_000_000, InputData: codec.HexEncodeToString(data), } - res, err := withRetryOnClosedNetwork(b, func() (Response, error) { - res := Response{} + res, err := withRetryOnClosedNetwork(b, func() (sdktypes.Response, error) { + res := sdktypes.Response{} return res, b.Client.Call(&res, "contracts_call", params) }) if err != nil { - return Response{}, errors.Wrap(err, "call") + return sdktypes.Response{}, errors.Wrap(err, "call") } return res, nil } -func (b *blockchainClient) CallToExec(ctx context.Context, contractCall ContractCall) (types.Hash, error) { - data, err := GetContractData(contractCall.Method, contractCall.Args...) +func (b *blockchainClient) CallToExec(ctx context.Context, contractCall sdktypes.ContractCall) (types.Hash, error) { + data, err := utils.GetContractData(contractCall.Method, contractCall.Args...) if err != nil { return types.Hash{}, err } @@ -285,13 +242,13 @@ func (b *blockchainClient) CallToExec(ctx context.Context, contractCall Contract return hash, err } -func (b *blockchainClient) Deploy(ctx context.Context, deployCall DeployCall) (types.AccountID, error) { +func (b *blockchainClient) Deploy(ctx context.Context, deployCall sdktypes.DeployCall) (types.AccountID, error) { deployer, err := types.NewAccountID(deployCall.From.PublicKey) if err != nil { return types.AccountID{}, err } - data, err := GetContractData(deployCall.Method, deployCall.Args...) + data, err := utils.GetContractData(deployCall.Method, deployCall.Args...) if err != nil { return types.AccountID{}, err } @@ -435,7 +392,7 @@ func (b *blockchainClient) submitAndWaitExtrinsic(ctx context.Context, extrinsic func withRetryOnClosedNetwork[T any](b *blockchainClient, f func() (T, error)) (T, error) { result, err := f() - if isClosedNetworkError(err) { + if utils.IsClosedNetworkError(err) { if b.reconnect() != nil { return result, err } @@ -449,7 +406,7 @@ func (b *blockchainClient) reconnect() error { b.connectMutex.Lock() defer b.connectMutex.Unlock() _, err := b.RPC.State.GetRuntimeVersionLatest() - if !isClosedNetworkError(err) { + if !utils.IsClosedNetworkError(err) { return nil } diff --git a/contract/pkg/mock/ddc_bucket_contract_mock.go b/contract/pkg/mock/ddc_bucket_contract_mock.go index e9f6f52..941d843 100644 --- a/contract/pkg/mock/ddc_bucket_contract_mock.go +++ b/contract/pkg/mock/ddc_bucket_contract_mock.go @@ -3,13 +3,14 @@ package mock import ( "encoding/json" "errors" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" "math" "math/big" "time" "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg" "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/bucket" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/utils" log "github.com/sirupsen/logrus" ) @@ -228,7 +229,7 @@ func CreateBucket(bucketId uint32, clusterId uint32, bucketParams string, writer func getAccountIDs(ss58Addresses []string) []types.AccountID { accountIDs := make([]types.AccountID, len(ss58Addresses)) for i, address := range ss58Addresses { - if accountID, err := pkg.DecodeAccountIDFromSS58(address); err != nil { + if accountID, err := utils.DecodeAccountIDFromSS58(address); err != nil { log.Fatal("Failed decode private key ed25519") } else { accountIDs[i] = accountID @@ -238,6 +239,6 @@ func getAccountIDs(ss58Addresses []string) []types.AccountID { return accountIDs } -func (d *ddcBucketContractMock) GetEventDispatcher() map[types.Hash]pkg.ContractEventDispatchEntry { +func (d *ddcBucketContractMock) GetEventDispatcher() map[types.Hash]sdktypes.ContractEventDispatchEntry { return nil } diff --git a/contract/pkg/sdktypes/types.go b/contract/pkg/sdktypes/types.go new file mode 100644 index 0000000..e7fb563 --- /dev/null +++ b/contract/pkg/sdktypes/types.go @@ -0,0 +1,61 @@ +package sdktypes + +import ( + "context" + "github.com/centrifuge/go-substrate-rpc-client/v4/signature" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "reflect" +) + +type ContractEventDispatchEntry struct { + ArgumentType reflect.Type + Handler ContractEventHandler +} + +type ContractEventHandler func(interface{}) + +type BlockchainClient interface { + CallToReadEncoded(contractAddressSS58 string, fromAddress string, method []byte, args ...interface{}) (string, error) + CallToExec(ctx context.Context, contractCall ContractCall) (types.Hash, error) + Deploy(ctx context.Context, deployCall DeployCall) (types.AccountID, error) + SetEventDispatcher(contractAddressSS58 string, dispatcher map[types.Hash]ContractEventDispatchEntry) error +} + +type Response struct { + DebugMessage string `json:"debugMessage"` + GasConsumed int `json:"gasConsumed"` + Result struct { + Ok struct { + Data string `json:"data"` + Flags int `json:"flags"` + } `json:"Ok"` + } `json:"result"` +} + +type Request struct { + Origin string `json:"origin"` + Dest string `json:"dest"` + GasLimit uint `json:"gasLimit"` + InputData string `json:"inputData"` + Value int `json:"value"` +} + +type DeployCall struct { + Code []byte + Salt []byte + From signature.KeyringPair + Value float64 + GasLimit float64 + Method []byte + Args []interface{} +} + +type ContractCall struct { + ContractAddress types.AccountID + ContractAddressSS58 string + From signature.KeyringPair + Value float64 + GasLimit float64 + Method []byte + Args []interface{} +} diff --git a/contract/pkg/utils.go b/contract/pkg/utils/utils.go similarity index 96% rename from contract/pkg/utils.go rename to contract/pkg/utils/utils.go index ff544a5..6dc6729 100644 --- a/contract/pkg/utils.go +++ b/contract/pkg/utils/utils.go @@ -1,4 +1,4 @@ -package pkg +package utils import ( "bytes" @@ -72,6 +72,6 @@ func GetContractData(method []byte, args ...interface{}) ([]byte, error) { return buf.Bytes(), nil } -func isClosedNetworkError(err error) bool { +func IsClosedNetworkError(err error) bool { return err != nil && strings.Contains(err.Error(), "use of closed network connection") } diff --git a/contract/pkg/utils_test.go b/contract/pkg/utils/utils_test.go similarity index 96% rename from contract/pkg/utils_test.go rename to contract/pkg/utils/utils_test.go index b46103f..fbbc35a 100644 --- a/contract/pkg/utils_test.go +++ b/contract/pkg/utils/utils_test.go @@ -1,4 +1,4 @@ -package pkg +package utils import ( "encoding/hex" diff --git a/test/setup_test.go b/test/setup_test.go index 8c32897..37ba707 100644 --- a/test/setup_test.go +++ b/test/setup_test.go @@ -5,8 +5,9 @@ import ( "encoding/hex" "github.com/centrifuge/go-substrate-rpc-client/v4/signature" "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg" "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/bucket" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/sdktypes" + "github.com/cerebellum-network/cere-ddc-sdk-go/contract/pkg/utils" "github.com/google/uuid" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -56,7 +57,7 @@ func TestApplicationSuite(t *testing.T) { suite.Run(t, new(ApplicationTestSuite)) } -func (a *ApplicationTestSuite) deployBucketContract(client pkg.BlockchainClient) (*types.AccountID, error) { +func (a *ApplicationTestSuite) deployBucketContract(client sdktypes.BlockchainClient) (*types.AccountID, error) { methodId, err := hex.DecodeString("9bae9d5e") // label: "new" if err != nil { logrus.WithError(err).Fatal("Can't decode method") @@ -66,7 +67,7 @@ func (a *ApplicationTestSuite) deployBucketContract(client pkg.BlockchainClient) return nil, err } - call := pkg.DeployCall{ + call := sdktypes.DeployCall{ Code: code, Salt: []byte(uuid.New().String()), From: signature.TestKeyringPairAlice, @@ -85,19 +86,19 @@ func (a *ApplicationTestSuite) deployBucketContract(client pkg.BlockchainClient) return &contract, nil } -func (a *ApplicationTestSuite) bucketSetAvailability(contractAddress string, client pkg.BlockchainClient, ctx context.Context, bucketId bucket.BucketId, avail bool) (string, error) { +func (a *ApplicationTestSuite) bucketSetAvailability(contractAddress string, client sdktypes.BlockchainClient, ctx context.Context, bucketId bucket.BucketId, avail bool) (string, error) { methodId, err := hex.DecodeString("053eb3ce") if err != nil { logrus.WithError(err).Fatal("Can't decode method") } - c, err := pkg.DecodeAccountIDFromSS58(contractAddress) + c, err := utils.DecodeAccountIDFromSS58(contractAddress) if err != nil { return "", err } arg1 := types.U32(bucketId) arg2 := types.NewBool(avail) - call := pkg.ContractCall{ + call := sdktypes.ContractCall{ ContractAddress: c, ContractAddressSS58: contractAddress, From: signature.TestKeyringPairAlice, @@ -117,12 +118,12 @@ func (a *ApplicationTestSuite) bucketSetAvailability(contractAddress string, cli return blockHash.Hex(), nil } -func (a *ApplicationTestSuite) bucketCreate(contractAddress string, client pkg.BlockchainClient, ctx context.Context) (string, error) { +func (a *ApplicationTestSuite) bucketCreate(contractAddress string, client sdktypes.BlockchainClient, ctx context.Context) (string, error) { methodId, err := hex.DecodeString("0aeb2379") if err != nil { logrus.WithError(err).Fatal("Can't decode method") } - c, err := pkg.DecodeAccountIDFromSS58(contractAddress) + c, err := utils.DecodeAccountIDFromSS58(contractAddress) if err != nil { return "", err } @@ -138,7 +139,7 @@ func (a *ApplicationTestSuite) bucketCreate(contractAddress string, client pkg.B alice := signature.TestKeyringPairAlice - call := pkg.ContractCall{ + call := sdktypes.ContractCall{ ContractAddress: c, ContractAddressSS58: contractAddress, From: alice,