Skip to content

Commit

Permalink
Rewrite certificate package.
Browse files Browse the repository at this point in the history
  • Loading branch information
q-uint committed Mar 25, 2024
1 parent 1205f48 commit cb28d54
Show file tree
Hide file tree
Showing 34 changed files with 758 additions and 459 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Supported identities are `Ed25519` and `Secp256k1`. By default, the agent uses t
```go
id, _ := identity.NewEd25519Identity(publicKey, privateKey)
config := agent.Config{
Identity: id,
Identity: id,
}
```

Expand All @@ -65,8 +65,8 @@ If you are running a local replica, you can use the `FetchRootKey` option to fet
```go
u, _ := url.Parse("http://localhost:8000")
config := agent.Config{
ClientConfig: &agent.ClientConfig{Host: u},
FetchRootKey: true,
ClientConfig: &agent.ClientConfig{Host: u},
FetchRootKey: true,
}
```

Expand Down Expand Up @@ -103,3 +103,8 @@ installed then those tests will be ignored.
```shell
go test -v ./...
```

## Reference Implementations

- [Rust Agent](https://github.com/dfinity/agent-rs/)
- [JavaScript Agent](https://github.com/dfinity/agent-js/)
69 changes: 46 additions & 23 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"time"

"github.com/aviate-labs/agent-go/candid/idl"
"github.com/aviate-labs/agent-go/certificate"
"github.com/aviate-labs/agent-go/certification"
"github.com/aviate-labs/agent-go/certification/hashtree"
"github.com/aviate-labs/agent-go/identity"
"github.com/aviate-labs/agent-go/principal"

"github.com/fxamacker/cbor/v2"
)

Expand Down Expand Up @@ -63,7 +65,7 @@ func New(cfg Config) (*Agent, error) {
ccfg = *cfg.ClientConfig
}
client := NewClient(ccfg)
rootKey, _ := hex.DecodeString(certificate.RootKey)
rootKey, _ := hex.DecodeString(certification.RootKey)
if cfg.FetchRootKey {
status, err := client.Status()
if err != nil {
Expand Down Expand Up @@ -129,37 +131,45 @@ func (a Agent) GetCanisterControllers(canisterID principal.Principal) ([]princip

// GetCanisterInfo returns the raw certificate for the given canister based on the given sub-path.
func (a Agent) GetCanisterInfo(canisterID principal.Principal, subPath string) ([]byte, error) {
path := [][]byte{[]byte("canister"), canisterID.Raw, []byte(subPath)}
c, err := a.readStateCertificate(canisterID, [][][]byte{path})
path := []hashtree.Label{hashtree.Label("canister"), canisterID.Raw, hashtree.Label(subPath)}
c, err := a.readStateCertificate(canisterID, [][]hashtree.Label{path})
if err != nil {
return nil, err
}
var state map[string]any
if err := cbor.Unmarshal(c, &state); err != nil {
return nil, err
}
node, err := certificate.DeserializeNode(state["tree"].([]any))
node, err := hashtree.DeserializeNode(state["tree"].([]any))
if err != nil {
return nil, err
}
return certificate.Lookup(path, node), nil
result := hashtree.NewHashTree(node).Lookup(path...)
if err := result.Found(); err != nil {
return nil, err
}
return result.Value, nil
}

func (a Agent) GetCanisterMetadata(canisterID principal.Principal, subPath string) ([]byte, error) {
path := [][]byte{[]byte("canister"), canisterID.Raw, []byte("metadata"), []byte(subPath)}
c, err := a.readStateCertificate(canisterID, [][][]byte{path})
path := []hashtree.Label{hashtree.Label("canister"), canisterID.Raw, hashtree.Label("metadata"), hashtree.Label(subPath)}
c, err := a.readStateCertificate(canisterID, [][]hashtree.Label{path})
if err != nil {
return nil, err
}
var state map[string]any
if err := cbor.Unmarshal(c, &state); err != nil {
return nil, err
}
node, err := certificate.DeserializeNode(state["tree"].([]any))
node, err := hashtree.DeserializeNode(state["tree"].([]any))
if err != nil {
return nil, err
}
return certificate.Lookup(path, node), nil
result := hashtree.NewHashTree(node).Lookup(path...)
if err := result.Found(); err != nil {
return nil, err
}
return result.Value, nil
}

// GetCanisterModuleHash returns the module hash for the given canister.
Expand Down Expand Up @@ -208,28 +218,32 @@ func (a Agent) Query(canisterID principal.Principal, methodName string, args []a
}

// RequestStatus returns the status of the request with the given ID.
func (a Agent) RequestStatus(canisterID principal.Principal, requestID RequestID) ([]byte, certificate.Node, error) {
path := [][]byte{[]byte("request_status"), requestID[:]}
c, err := a.readStateCertificate(canisterID, [][][]byte{path})
func (a Agent) RequestStatus(canisterID principal.Principal, requestID RequestID) ([]byte, hashtree.Node, error) {
path := []hashtree.Label{hashtree.Label("request_status"), requestID[:]}
c, err := a.readStateCertificate(canisterID, [][]hashtree.Label{path})
if err != nil {
return nil, nil, err
}
var state map[string]any
if err := cbor.Unmarshal(c, &state); err != nil {
return nil, nil, err
}
cert, err := certificate.New(canisterID, a.rootKey[len(a.rootKey)-96:], c)
cert, err := certification.New(canisterID, a.rootKey[len(a.rootKey)-96:], c)
if err != nil {
return nil, nil, err
}
if err := cert.Verify(); err != nil {
return nil, nil, err
}
node, err := certificate.DeserializeNode(state["tree"].([]any))
node, err := hashtree.DeserializeNode(state["tree"].([]any))
if err != nil {
return nil, nil, err
}
return certificate.Lookup(append(path, []byte("status")), node), node, nil
result := hashtree.NewHashTree(node).Lookup(append(path, hashtree.Label("status"))...)
if err := result.Found(); err != nil {
return nil, nil, err
}
return result.Value, node, nil
}

// Sender returns the principal that is sending the requests.
Expand All @@ -256,15 +270,24 @@ func (a Agent) poll(canisterID principal.Principal, requestID RequestID, delay,
return nil, err
}
if len(data) != 0 {
path := [][]byte{[]byte("request_status"), requestID[:]}
path := []hashtree.Label{hashtree.Label("request_status"), requestID[:]}
switch string(data) {
case "rejected":
code := certificate.Lookup(append(path, []byte("reject_code")), node)
rejectMessage := certificate.Lookup(append(path, []byte("reject_message")), node)
return nil, fmt.Errorf("(%d) %s", uint64FromBytes(code), string(rejectMessage))
tree := hashtree.NewHashTree(node)
codeResult := tree.Lookup(append(path, hashtree.Label("reject_code"))...)
messageResult := tree.Lookup(append(path, hashtree.Label("reject_message"))...)
if codeResult.Found() != nil || messageResult.Found() != nil {
return nil, fmt.Errorf("no reject code or message found")
}
return nil, fmt.Errorf("(%d) %s", uint64FromBytes(codeResult.Value), string(messageResult.Value))
case "replied":
path := [][]byte{[]byte("request_status"), requestID[:]}
return certificate.Lookup(append(path, []byte("reply")), node), nil
fmt.Println(node)
repliedResult := hashtree.NewHashTree(node).Lookup(append(path, hashtree.Label("reply"))...)
fmt.Println(repliedResult)
if repliedResult.Found() != nil {
return nil, fmt.Errorf("no reply found")
}
return repliedResult.Value, nil
}
}
case <-timer.C:
Expand All @@ -291,7 +314,7 @@ func (a Agent) readState(canisterID principal.Principal, data []byte) (map[strin
return m, cbor.Unmarshal(resp, &m)
}

func (a Agent) readStateCertificate(canisterID principal.Principal, paths [][][]byte) ([]byte, error) {
func (a Agent) readStateCertificate(canisterID principal.Principal, paths [][]hashtree.Label) ([]byte, error) {
_, data, err := a.sign(Request{
Type: RequestTypeReadState,
Sender: a.Sender(),
Expand Down
8 changes: 4 additions & 4 deletions agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ func Example_json() {
// {"e8s":0}
}

func Example_query_prime256v1() {
id, _ := identity.NewRandomPrime256v1Identity()
func Example_query_ed25519() {
id, _ := identity.NewRandomEd25519Identity()
ledgerID, _ := principal.Decode("ryjl3-tyaaa-aaaaa-aaaba-cai")
a, _ := agent.New(agent.Config{Identity: id})
var balance struct {
Expand All @@ -64,8 +64,8 @@ func Example_query_prime256v1() {
// 0
}

func Example_query_ed25519() {
id, _ := identity.NewRandomEd25519Identity()
func Example_query_prime256v1() {
id, _ := identity.NewRandomPrime256v1Identity()
ledgerID, _ := principal.Decode("ryjl3-tyaaa-aaaaa-aaaba-cai")
a, _ := agent.New(agent.Config{Identity: id})
var balance struct {
Expand Down
70 changes: 0 additions & 70 deletions certificate/lookup.go

This file was deleted.

19 changes: 0 additions & 19 deletions certificate/lookup_test.go

This file was deleted.

Loading

0 comments on commit cb28d54

Please sign in to comment.