-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
2,959 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Registry Client in Go | ||
|
||
Based on the [Rust Registry Client](https://github.com/dfinity/ic/tree/master/rs/registry) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package registry | ||
|
||
import ( | ||
"fmt" | ||
"sort" | ||
"sync" | ||
"time" | ||
|
||
"github.com/aviate-labs/agent-go" | ||
"github.com/aviate-labs/agent-go/ic" | ||
"github.com/aviate-labs/agent-go/identity" | ||
"github.com/aviate-labs/agent-go/principal" | ||
"github.com/aviate-labs/agent-go/registry/proto/v1" | ||
"github.com/fxamacker/cbor/v2" | ||
"google.golang.org/protobuf/proto" | ||
"google.golang.org/protobuf/types/known/wrapperspb" | ||
) | ||
|
||
type DataProvider struct { | ||
sync.RWMutex | ||
Records []v1.ProtoRegistryRecord | ||
} | ||
|
||
func (d *DataProvider) Add( | ||
key string, | ||
version uint64, | ||
value []byte, | ||
) error { | ||
if version < 1 { | ||
return fmt.Errorf("version must be greater than 0") | ||
} | ||
|
||
d.Lock() | ||
defer d.Unlock() | ||
|
||
var ok bool | ||
idx := sort.Search(len(d.Records), func(i int) bool { | ||
if d.Records[i].Key == key { | ||
if d.Records[i].Version == version { | ||
ok = true // Record already exists. | ||
} | ||
return d.Records[i].Version >= version | ||
} | ||
return d.Records[i].Key >= key | ||
}) | ||
if ok { | ||
// Key and version already exist. | ||
return fmt.Errorf("record already exists: %s@%d", key, version) | ||
} | ||
d.Records = append(d.Records, v1.ProtoRegistryRecord{}) | ||
copy(d.Records[idx+1:], d.Records[idx:]) // Shift right. | ||
d.Records[idx] = v1.ProtoRegistryRecord{ | ||
Key: key, | ||
Version: version, | ||
Value: wrapperspb.Bytes(value), | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DataProvider) GetChangesSince(version uint64) ([]*v1.RegistryDelta, uint64, error) { | ||
a, err := agent.New(agent.DefaultConfig) | ||
if err != nil { | ||
return nil, 0, err | ||
} | ||
payload, err := proto.Marshal(&v1.RegistryGetChangesSinceRequest{ | ||
Version: version, | ||
}) | ||
if err != nil { | ||
return nil, 0, err | ||
} | ||
request := agent.Request{ | ||
Type: agent.RequestTypeQuery, | ||
Sender: principal.AnonymousID, | ||
IngressExpiry: uint64(time.Now().Add(5 * time.Minute).UnixNano()), | ||
CanisterID: ic.REGISTRY_PRINCIPAL, | ||
MethodName: "get_changes_since", | ||
Arguments: payload, | ||
} | ||
requestID := agent.NewRequestID(request) | ||
id := new(identity.AnonymousIdentity) | ||
data, err := cbor.Marshal(agent.Envelope{ | ||
Content: request, | ||
SenderPubKey: id.PublicKey(), | ||
SenderSig: requestID.Sign(id), | ||
}) | ||
if err != nil { | ||
return nil, 0, err | ||
} | ||
resp, err := a.Client().Query(ic.REGISTRY_PRINCIPAL, data) | ||
if err != nil { | ||
return nil, 0, err | ||
} | ||
var response agent.Response | ||
if err := cbor.Unmarshal(resp, &response); err != nil { | ||
return nil, 0, err | ||
} | ||
if response.Status != "replied" { | ||
return nil, 0, fmt.Errorf("status: %s", response.Status) | ||
} | ||
|
||
changesResponse := new(v1.RegistryGetChangesSinceResponse) | ||
if err := proto.Unmarshal(response.Reply["arg"], changesResponse); err != nil { | ||
return nil, 0, err | ||
} | ||
if changesResponse.Error != nil { | ||
return nil, 0, fmt.Errorf("error: %s", changesResponse.Error.String()) | ||
} | ||
return changesResponse.Deltas, changesResponse.Version, nil | ||
} | ||
|
||
func (d *DataProvider) IsEmpty() bool { | ||
d.RLock() | ||
defer d.RUnlock() | ||
|
||
return len(d.Records) == 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package registry | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestDataProvider_GetChangesSince(t *testing.T) { | ||
if _, _, err := new(DataProvider).GetChangesSince(0); err != nil { | ||
t.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package registry | ||
|
||
import "fmt" | ||
|
||
type ClientDataProviderQueryFailedError struct { | ||
Source DataProviderError | ||
} | ||
|
||
func (e ClientDataProviderQueryFailedError) Error() string { | ||
return fmt.Sprintf("failed to query data provider: %s", e.Source) | ||
} | ||
|
||
func (ClientDataProviderQueryFailedError) registryClientError() {} | ||
|
||
type ClientDecodeError struct { | ||
Err string | ||
} | ||
|
||
func (e ClientDecodeError) Error() string { | ||
return fmt.Sprintf("failed to decode registry contents: %s", e.Err) | ||
} | ||
|
||
func (ClientDecodeError) registryClientError() {} | ||
|
||
type ClientError interface { | ||
registryClientError() | ||
error | ||
} | ||
|
||
type ClientPollLockFailedError struct { | ||
Err string | ||
} | ||
|
||
func (e ClientPollLockFailedError) Error() string { | ||
return fmt.Sprintf("failed to acquire poll lock: %s", e.Err) | ||
} | ||
|
||
func (ClientPollLockFailedError) registryClientError() {} | ||
|
||
type ClientPollingLatestVersionFailedError struct { | ||
Retries uint | ||
} | ||
|
||
func (e ClientPollingLatestVersionFailedError) Error() string { | ||
return fmt.Sprintf("failed to report the same version twice after %d times", e.Retries) | ||
} | ||
|
||
func (ClientPollingLatestVersionFailedError) registryClientError() {} | ||
|
||
type ClientVersionNotAvailableError struct { | ||
Version Version | ||
} | ||
|
||
func (e ClientVersionNotAvailableError) Error() string { | ||
return fmt.Sprintf("the requested version is not available locally: %d", e.Version) | ||
} | ||
|
||
func (ClientVersionNotAvailableError) registryClientError() {} | ||
|
||
type DataProviderError interface { | ||
dataProviderError() | ||
error | ||
} | ||
|
||
// DataProviderTimeoutError occurs when the registry transport client times out. | ||
type DataProviderTimeoutError struct{} | ||
|
||
func (DataProviderTimeoutError) Error() string { | ||
return "registry transport client timed out" | ||
} | ||
|
||
func (DataProviderTimeoutError) dataProviderError() {} | ||
|
||
// DataProviderTransferError occurs when using registry transfer. | ||
type DataProviderTransferError struct { | ||
Source string | ||
} | ||
|
||
func (e DataProviderTransferError) Error() string { | ||
return fmt.Sprintf("registry transport client failed to fetch registry update from registry canister: %s", e.Source) | ||
} | ||
|
||
func (DataProviderTransferError) dataProviderError() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
github.com/aviate-labs/agent-go v0.4.3 h1:XvUv23dw/77I5Rt6S89fHOuI9ci9ARiyLw8Lh4C2FTU= | ||
github.com/aviate-labs/agent-go v0.4.3/go.mod h1:uodw1LlLrP5A5XxXaNOE39iHYb6IGDqnVXEvwiG+YiM= | ||
github.com/aviate-labs/agent-go v0.4.4-0.20240515111542-ef8ce13f44f2 h1:4WA9qGa4z22TSJ6LApbRXtb/ZpeLkxgaKBOvu72srno= | ||
github.com/aviate-labs/agent-go v0.4.4-0.20240515111542-ef8ce13f44f2/go.mod h1:uodw1LlLrP5A5XxXaNOE39iHYb6IGDqnVXEvwiG+YiM= | ||
github.com/aviate-labs/leb128 v0.3.0 h1:s9htRv3OYk8nuHqJu9PiVFJxv1jIUTIcpEeiURa91uQ= | ||
github.com/aviate-labs/leb128 v0.3.0/go.mod h1:GclhBOjhIKmcDlgHKhj0AEZollzERfZUbcRUKiQVqgY= | ||
github.com/aviate-labs/secp256k1 v0.0.0-5e6736a h1:aQkG/D+l8Y7tr809l8pN+KebH2jzacWReSFQmeEKFgM= | ||
github.com/aviate-labs/secp256k1 v0.0.0-5e6736a/go.mod h1:C/lr3F9TimrVkdZckG5mz+VU0TrmpeyVKUjzv2YyGwA= | ||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= | ||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= | ||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/herumi/bls-go-binary v1.33.0 h1:OJwWkXTsxF7SLHT8cBLJfb6i97KHfrG4DkgejLcDm78= | ||
github.com/herumi/bls-go-binary v1.33.0/go.mod h1:O4Vp1AfR4raRGwFeQpr9X/PQtncEicMoOe6BQt1oX0Y= | ||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= | ||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= | ||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package registry | ||
|
||
import "time" | ||
|
||
func GetValue(c RegistryClient, key string, version Version) (*[]byte, error) { | ||
vv, err := c.GetVersionedValue(key, version) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return vv.Value, nil | ||
} | ||
|
||
type RegistryClient interface { | ||
GetVersionedValue(key string, version Version) (VersionedRecord[[]byte], error) | ||
GetKeyFamily(keyPrefix string, version Version) ([]string, error) | ||
GetLatestVersion() (Version, error) | ||
GetVersionTimestamp(version Version) (*int64, error) | ||
} | ||
|
||
type RegistryDataProvider interface { | ||
GetUpdatesSince(version Version) ([]TransportRecord, error) | ||
} | ||
|
||
type TransportRecord = VersionedRecord[[]byte] | ||
|
||
func EmptyZeroRecord(key string) TransportRecord { | ||
return TransportRecord{ | ||
Key: key, | ||
Version: ZeroRegistryVersion, | ||
Value: nil, | ||
} | ||
} | ||
|
||
type Version uint64 | ||
|
||
// Reference: https://github.com/dfinity/ic/blob/master/rs/interfaces/registry/src/lib.rs | ||
|
||
const ( | ||
// ZeroRegistryVersion is the version number of the empty registry. | ||
ZeroRegistryVersion Version = 0 | ||
// PollingPeriod is the period at which the local store is polled for updates. | ||
PollingPeriod = 5 * time.Second | ||
) | ||
|
||
// VersionedRecord is a key-value pair with a version. | ||
type VersionedRecord[T any] struct { | ||
// Key of the record. | ||
Key string | ||
// Version at which this record was created. | ||
Version Version | ||
// Value of the record. If the record was deleted in this version, this field is nil. | ||
Value *T | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package registry | ||
|
||
//go:generate go install google.golang.org/protobuf/cmd/protoc-gen-go@latest | ||
//go:generate protoc -I=testdata --go_out=. testdata/registry.proto | ||
//go:generate protoc -I=testdata --go_out=. testdata/local.proto | ||
//go:generate protoc -I=testdata --go_out=. testdata/transport.proto |
Oops, something went wrong.