Giter Site home page Giter Site logo

Comments (241)

dellams avatar dellams commented on June 12, 2024 1

Ok great thanks, I will have a look at the https://github.com/holochain/lair/blob/main/crates/lair_keystore_api/src/ipc_keystore/raw_ipc.rs code you mentioned soon...

But first want to get it working the same way yours does... The next thing I need to implement is the Admin API so it can grant the capabilities to the zome calls etc as you do here:

https://github.com/holochain/holochain-client-js/blob/f9ba322b5cef2c9bd34d47f56a8c2b73a6d4ebe5/src/api/admin/websocket.ts#L20

Wish me luck! ;-) thanks!

from holochain.

dellams avatar dellams commented on June 12, 2024 1

ok great thanks, will attempt to implement this in my c# client now... wish me luck! ;-)

from holochain.

dellams avatar dellams commented on June 12, 2024 1

OK great thanks, I will begin testing it soon so will let you know how it goes! :)

Just finished upgrading the HoloNETDataAppInfoCall to work with v0.1.5 as you described... :)

from holochain.

dellams avatar dellams commented on June 12, 2024 1

Here it is actually a bundled AppManifest, which then points to the current version V1 as the only possible variant. Following that struct in turn leads to AppManifestCurrent.

Yes I saw that thanks but I wasn't sure if it also had resources, root_dir, PathBuf & DnaBundle? Why I said it wasn't clear like the rest of the documentation has been...

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

So hopefully this is the same stuct used for AppInfo?

I got it working by changing origin_time to a long so it must be! ;-)

=)

I also created a RustDuration struct to hold the secs (int) and nanos (float) it returns so hopefully that looks right to you? :)

In Rust the nanos are not a float, I'd make it an int too.

I am trying to convert the origin_time to a c# dateime object by multiplying the microseconds the conductor returns by 10 to get the long ticks which can then be used to create a dateime object but the date is well off so it can't be that! ;-)

So from the origin_time of 1688181237410745 I am getting {01/07/0054 03:13:57}. Any ideas? :)

In C# DateTime objects are based on the year 0001 of the Gregorian calendar https://learn.microsoft.com/en-us/dotnet/api/system.datetime?view=net-7.0#quick-links-to-remarks-topics

The Holochain Timestamp struct is based on the UNIX epoch which is 1970 https://docs.rs/holochain_types/0.2.1/holochain_types/prelude/struct.Timestamp.html#structfield.0

That is why you're getting a different date. You can do something like this

long microseconds = 1688181237410745;
DateTime date = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(microseconds * 10);
Console.WriteLine("DateTime from microseconds since UNIX epoch: " + date);

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

Ok great so I can add a path and bundle property to my InstallAppRequest object then I guess? Then just set whichever one is passed in? One or the other but not both I guess?

Correct, either path or bundle.

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

Ok thanks so like how I have handled it in CellType?

Yes, that is what I have seen on stackoverflow as a preferred way of imitating Rust enums in C#.

So when its a provisioned cell only provisioned property is populated and the others have child null properties etc? So in this case the stem and cloned properties will have instances of that class/struct but the child properties inside them will be null.
Or maybe the root property is null? So in this case cloned and stem would be null?

I'd set the root property to null. In JS an unused property can be deleted or set to undefined, but I think that's not possible in strongly typed C#.

from holochain.

dellams avatar dellams commented on June 12, 2024 1

Ok great thanks for the info about UNIX time, yeah that makes sense, I should have thought of that! Been rushing to get it upgraded and coding half asleep sometimes! ;-)

I'd set the root property to null. In JS an unused property can be deleted or set to undefined, but I think that's not possible in strongly typed C#.

Yes this is what I was thinking too thanks. :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

I am just looking at DeleteCloneCellRequest which uses DisableCloneCellRequest, which itself looks like this:
It takes a clone_cell_id property which looks it can be a RoleName (string) or a CellId (a byte[][])? I wanted to double check this was right? If so then I will need to use a special type called dynamic in c# allowing it to be any type you wish.

That is right, either a string or a tuple of byte arrays with two values [[dna_bytes], [agent_pub_key_bytes]].

How is RoleName used? Is it the name of a role that contains the cell or something?

Role names are human readable strings for cell ids.
From your example:

image

Role name is "oasis". The role name is specified in a happ.yaml and created for clone cells. For clone cells they're called clone ids. A clone id consists of the original DNA's role name (oasis) and an index (0, 1, ...). You can delete or disable a clone cell by its clone id, e. g. oasis.3.

Caution: Clone ids are local to the conductor. Other nodes' conductors won't know about those clone ids and will have their own. It's safest to use the cell id.

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

What does storage_info do? I presume it gets the storage info for the running hApp? Or is it the storage info for all the hApps the conductor is running or has installed/configured? Thanks :)

It returns an array with storage info for all installed DNAs. https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/storage_info/struct.StorageInfo.html with these fields https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/storage_info/struct.DnaStorageInfo.html.

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

I just wasn't sure how to handle KitsuneSpace, KitsuneAgent & KitsuneSignature? I wasn't sure if I should just use a normal byte array for them or create a separate class/struct as they have in rust?

I guess in this case you can do as you please. Generally speaking it's easier to define types even though they're just a wrapper around a primitive data type, because you can then change the inner structure of the type instead of changing every occurrence where the data type is used.

Do these look right to you? I wasn't sure how they worked in rust? Because there was no property name for the binary data so wasn't sure how to map that onto c#?

It looks fine to me, yes. It's just a byte array as you say.

Also there is no link/documentation for DhtArc so wasn't sure what type this is?

Yes, it's not made public. It's here https://github.com/holochain/holochain/blob/develop/crates/kitsune_p2p/dht_arc/src/dht_arc.rs#L75

But this is new territory and I invite @neonphog to comment how a client should implement that. It's just unknown in the JS client so no indication.

I also presumed url_list is a simple string array?

It boils down to an array of Url2, which can parse from a string so I think that works.

I also presumed encoded_bytes is a simple byte array?

It is. Again I ask @neonphog what that's for?

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

So I flattened it again to this:

image

This look right to you? :)

Yes

ResourceMap looks like it's a dictionary with key string and value is a byte array right?

Right

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

properties looks like it can just be a generic object? Because I looked up DnaProperties and it was just another unknown:

Yes, properties is indeed an object with arbitrary keys and values.

ZomeManifest looks like this:
Which contains another serde ZomeLocation so I flattened this also:

This look correct? :)

Yes!

Finally, ZomeDependency looks like this:

export interface ZomeDependency {
  name: ZomeName;
}

So I simplified to this:

image

Hope this is ok?

LGTM. As usual making an example call to the Conductor will tell the truth =)

Sorry for all the questions, really appreciate all the help as always! :)

Hope you had a good weekend?

That's fine. Yes, I did, thanks!

from holochain.

neonphog avatar neonphog commented on June 12, 2024 1

@dellams @jost-s - The internal agent_info structure including the dhtarc were intended to be opaque. You may need to transfer the data from node to node depending on your app design if you are not using our bootstrap protocol, but other than knowing which dna hash and agent the agent_info belongs to, there should be no need to decipher the internals.

Please let me know if you foresee a use case for decoding it! Otherwise maybe the js / dotnet clients should call it a BLOB or something?

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

Sorry one final question... ;-)

What is dHTOpsCursor? :) Thanks! :)

dht_ops_cursor in AdminRequest::DumpFullState is what you're referring to I suppose. You can use that cursor to limit the full DHT dump to start at a specific row in the DHT table instead of returning the entire table.

from holochain.

neonphog avatar neonphog commented on June 12, 2024 1

In terms of design philosophy, the first goal should be encapsulation so it doesn't matter what you are using to store it internally in your object's private data. In pseudocode:

class PubKey {
  /* don't know type of private members */
  PubKey fromBase64(String s) { .. }
  PubKey fromBytes(Byte[] b) { .. }
  String toBase64() { .. }
  Byte[] toBytes32() { .. }
  Byte[] toBytes39() { .. }
  bool validateSignature(Byte[] message, Byte[] signature) { .. }
}

Then only expose this PubKey type when returning data from holochain apis, and only accept PubKey types into holochain apis. These functions should work no matter your internal storage mechanism, and eliminate foot-guns for folks using your api.

Then the question becomes what is the most efficient data storage for that internal member data? If holochain is most often accepting byte arrays, and is most often returning byte arrays, and you need the byte array in order to do the signature validation, it seems like keeping the byte array in memory is the best option.

from holochain.

dellams avatar dellams commented on June 12, 2024 1

Oh ok I now see what you are saying... yes there are also overloads that takes agentKey as a byte array as well as ones that take a string so they have the choice of which they want to use.

Those ConvertHoloHashToBytes and ConvertHoloHashToString functions are also public so people can convert between strings and byte arrays too... :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024 1

Ah, I'm glad to know you are unblocked with app installing again! Curious these conundrums in different environments.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Hi David, you may as well post this in the JS client repo.

The thing with the additional keys is this: All zome calls have to be signed by the sender. Signing with your private agent key can only be done with access to the keystore. The only implemented protocol is over IPC, and from a browser you don't have access to IPC sockets. Therefore the JS client generates a separate pair of private and public key, which is henceforth used for signing zome calls.

The key structure is documented in the hash docs. The first three bytes are the hash type, which corresponds to an agent key. What follows is the 32 byte cryptographic key, generated with the ed25519 algorithm. The final 4 bytes are the location on the DHT. In the case of the JavaScript client, these 4 bytes don't matter, because the additional key pair is only used when you're developing an app and running it in dev mode.

from holochain.

dellams avatar dellams commented on June 12, 2024

Hi Jost,

Thanks for getting back to me. How do I get access to the keystore then? Remember this is c# so it will be running on the client along with the conductor so it should have access to it somehow?

I reverse engineered most of your code for signing zome calls:

var seed = RandomNumberGenerator.GetBytes(32);
                byte[] privateKey;
                byte[] publicKey;

                Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);
              
                HoloNETDataZomeCall payload = new HoloNETDataZomeCall()
                {
                    cap_secret = RandomNumberGenerator.GetBytes(64),
                    cell_id = new byte[2][] { ConvertHoloHashToBytes(Config.DnaHash), ConvertHoloHashToBytes(Config.AgentPubKey) },
                    fn_name = function,
                    zome_name = zome,
                    payload = MessagePackSerializer.Serialize(paramsObject),
                    //provenance = ConvertHoloHashToBytes(Config.AgentPubKey),
                    provenance = publicKey, //TODO: May need to adjust it as the js client does. https://github.com/holochain/holochain-client-js/blob/main/src/api/zome-call-signing.ts
                    nonce = RandomNumberGenerator.GetBytes(32),
                    expires_at = DateTime.Now.AddMinutes(5).ToBinary(), //TODO: Not sure what format the conductor is expecting this in?
                };

                byte[] hash = Sha512.Hash(DataHelper.ObjectToByteArray(payload));
                var sig = Ed25519.Sign(hash, privateKey);

                HoloNETDataZomeCallSigned signedPayload = new HoloNETDataZomeCallSigned()
                {
                    cap_secret = payload.cap_secret,
                    cell_id = payload.cell_id,
                    fn_name = payload.fn_name,
                    zome_name = payload.zome_name,
                    payload = payload.payload,
                    provenance = payload.provenance,
                    nonce = payload.nonce,
                    expires_at = payload.expires_at,
                    signature = sig
                };

                HoloNETData holoNETData = new HoloNETData()
                {
                    type = "zome_call",
                    data = signedPayload
                };

                await SendHoloNETRequestAsync(id, holoNETData);

So hopefully this makes sense and loos right to you?

So does my c# client need to generate a separate pair of private and public keys like you are doing? Or can it just use the keystore directly somehow?

It looks like it uses a Blake2b hashing algorithm? I was using Sha512 so I will need to try and find a Blake2b implementation on c#...

Are the DHT location bytes needed for the c# client? The additional key pair you refer to, is this the DHT location bytes?

Many thanks,
David.

from holochain.

dellams avatar dellams commented on June 12, 2024

Does it mean I can just continue to let the hc sandbox/conductor pass the agentpubkey to my client as it use to?

But then I wouldn't have the private key to sign the hash with like I am doing here:

var sig = Ed25519.Sign(hash, privateKey);

from holochain.

dellams avatar dellams commented on June 12, 2024

I am also using this code to convert the payload to a binary array so it can be hashed:

// Convert an object to a byte array
        public static byte[] ObjectToByteArray(object obj)
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (var ms = new MemoryStream())
            {
                bf.Serialize(ms, obj);
                return ms.ToArray();
            }
        }
HoloNETDataZomeCall payload = new HoloNETDataZomeCall()
                {
                    cap_secret = RandomNumberGenerator.GetBytes(64),
                    cell_id = new byte[2][] { ConvertHoloHashToBytes(Config.DnaHash), ConvertHoloHashToBytes(Config.AgentPubKey) },
                    fn_name = function,
                    zome_name = zome,
                    payload = MessagePackSerializer.Serialize(paramsObject),
                    //provenance = ConvertHoloHashToBytes(Config.AgentPubKey),
                    provenance = publicKey, //TODO: May need to adjust it as the js client does. https://github.com/holochain/holochain-client-js/blob/main/src/api/zome-call-signing.ts
                    nonce = RandomNumberGenerator.GetBytes(32),
                    expires_at = DateTime.Now.AddMinutes(5).ToBinary(), //TODO: Not sure what format the conductor is expecting this in?
                };

byte[] hash = Blake2b.ComputeHash(DataHelper.ObjectToByteArray(payload));

But I noticed in your code you are using some sort of WASM function to do this? So wasn't sure if it was compatible?

from holochain.

jost-s avatar jost-s commented on June 12, 2024

How do I get access to the keystore then? Remember this is c# so it will be running on the client along with the conductor so it should have access to it somehow?

It's a lot more effort to get access to the keystore, but yes, you can do that from a C# client over inter process communication (IPC) socket, e. g. https://johnkoerner.com/csharp/IPC-in-net-core-using-protobuf/. You need to re-implement the IPC client protocol that Holochain uses to communicate to the keystore in that case. Not knowing more about this, I can only point you to the source code https://github.com/holochain/lair/blob/main/crates/lair_keystore_api/src/ipc_keystore/raw_ipc.rs and the surrounding folders' files. If you want to go down that road, I'll ask the author to chime in.

Without access to the keystore you need to compute a pair of keys that is managed by the client then.

expires_at = DateTime.Now.AddMinutes(5).ToBinary(), //TODO: Not sure what format the conductor is expecting this in?

The conductor expects microseconds as integer.

It looks like it uses a Blake2b hashing algorithm? I was using Sha512 so I will need to try and find a Blake2b implementation on c#...

Correct, a Blake2b hash is needed to be signed. The flow is this:

  • build a zome call object with the usual zome name, fn name, provenance etc, plus a nonce and its expiration, plus a capability secret in case the caller is authorized by virtue of an assigned or transferable cap grant. If it's an unrestricted cap grant, you don't need a secret. see here https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/struct.ZomeCall.html#fields
  • serialize this zome call object using serde in the message pack format
  • hash the serialized bytes call object with the blake2b algorithm
  • sign the hash with the caller's private key

But I noticed in your code you are using some sort of WASM function to do this? So wasn't sure if it was compatible?
To prevent errors and also keep it DRY there's a simple wasm wrapper that uses the exact same crates to hash and sign:
https://github.com/holochain/holochain-serialization-js/blob/main/src/lib.rs

You can serialize and hash with a native C# sharp library.

Are the DHT location bytes needed for the c# client?

Yes, they're needed. You can actually copy them from the initial agent pub key

The additional key pair you refer to, is this the DHT location bytes?

No, it's the last 4 bytes of an agent pub key.

Does it mean I can just continue to let the hc sandbox/conductor pass the agentpubkey to my client as it use to?
But then I wouldn't have the private key to sign the hash with like I am doing here:

Exactly. What I said about this above:

The thing with the additional keys is this: All zome calls have to be signed by the sender. Signing with your private agent key can only be done with access to the keystore. The only implemented protocol is over IPC, and from a browser you don't have access to IPC sockets. Therefore the JS client generates a separate pair of private and public key, which is henceforth used for signing zome calls.

The newly generated keys are used in place of the initial keys. Please point out what you don't understand about it.

I am also using this code to convert the payload to a binary array so it can be hashed:

No, that won't work. It must be in MessagePack format.

from holochain.

dellams avatar dellams commented on June 12, 2024

Thanks for getting back to me, appreciated. :)

Thanks for the IPC code you shared, I will look further into this... yes would be great to be connected with the author as you suggested thanks.

For the time being though, I guess my existing private public key code will work fine? Same as it does for yours...

expires_at is in microseconds using the .ToBinary() method so I guessed that part right then. :)

How do I serialize the zome call object using serde in the message pack format?

Looks like in your code you are using:

let unsigned_zome_call: ZomeCallUnsigned = serde_wasm_bindgen::from_value(value)?;
let serialized_zome_call = holochain_zome_types::encode(&unsigned_zome_call).map_err(wasm_bindgen::JsError::from)?;

But how do I do this in c#? You mentioned it needs to be in MessagePack format?

Many thanks,
David.

from holochain.

dellams avatar dellams commented on June 12, 2024

Actually, I think I figured out most my questions above, my code now looks like this:

                var seed = RandomNumberGenerator.GetBytes(32);
                byte[] privateKey;
                byte[] publicKey;

                Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);

                byte[] DHTLocation = ConvertHoloHashToBytes(Config.AgentPubKey).TakeLast(4).ToArray();
                byte[] signingKey = new byte[] { 132, 32, 36 }.Concat(publicKey).Concat(DHTLocation).ToArray();

                HoloNETDataZomeCall payload = new HoloNETDataZomeCall()
                {
                    cap_secret = RandomNumberGenerator.GetBytes(64),
                    cell_id = new byte[2][] { ConvertHoloHashToBytes(Config.DnaHash), ConvertHoloHashToBytes(Config.AgentPubKey) },
                    fn_name = function,
                    zome_name = zome,
                    payload = MessagePackSerializer.Serialize(paramsObject),
                    //provenance = ConvertHoloHashToBytes(Config.AgentPubKey),
                    provenance = signingKey,
                    nonce = RandomNumberGenerator.GetBytes(32),
                    expires_at = DateTime.Now.AddMinutes(5).Ticks / 10 //DateTime.Now.AddMinutes(5).ToBinary(), //Conductor expects it in microseconds.
                };

                byte[] hash = Blake2b.ComputeHash(MessagePackSerializer.Serialize(payload));
                var sig = Ed25519.Sign(hash, privateKey);

                HoloNETDataZomeCallSigned signedPayload = new HoloNETDataZomeCallSigned()
                {
                    cap_secret = payload.cap_secret,
                    cell_id = payload.cell_id,
                    fn_name = payload.fn_name,
                    zome_name = payload.zome_name,
                    payload = payload.payload,
                    provenance = payload.provenance,
                    nonce = payload.nonce,
                    expires_at = payload.expires_at,
                    signature = sig
                };

                HoloNETData holoNETData = new HoloNETData()
                {
                    type = "zome_call",
                    data = signedPayload
                };

                await SendHoloNETRequestAsync(id, holoNETData);

Does this look good to you? :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Thanks for the IPC code you shared, I will look further into this... yes would be great to be connected with the author as you suggested thanks.

For the time being though, I guess my existing private public key code will work fine? Same as it does for yours...

Yes. For the IPC calls as well as the message pack encoding you could directly call into the Rust crates from C# using P/Invoke.

Does this look good to you? :)

It does! That should get you through serialization and signature validation.

from holochain.

dellams avatar dellams commented on June 12, 2024

Looking at your code I found that this function looks like the main function in the admin api that grants capabilities to zome calls:

/**
   * Grant a capability for signing zome calls.
   *
   * @param cellId - The cell to grant the capability for.
   * @param functions - The zome functions to grant the capability for.
   * @param signingKey - The assignee of the capability.
   * @returns The cap secret of the created capability.
   */
  grantSigningKey = async (
    cellId: CellId,
    functions: GrantedFunctions,
    signingKey: AgentPubKey
  ): Promise<CapSecret> => {
    const capSecret = randomCapSecret();
    await this.grantZomeCallCapability({
      cell_id: cellId,
      cap_grant: {
        tag: "zome-call-signing-key",
        functions,
        access: {
          Assigned: {
            secret: capSecret,
            assignees: [signingKey],
          },
        },
      },
    });
    return capSecret;
  };

And the actual data you send to the conductor is:

{
      cell_id: cellId,
      cap_grant: {
        tag: "zome-call-signing-key",
        functions,
        access: {
          Assigned: {
            secret: capSecret,
            assignees: [signingKey],
          },
        },
      },
    });

Which then stores the capSecret, keyPair & signingKey for the given cellid in a in-memory lookup table/dictionary which the client then checks later on to make sure that zome call has been authorized for that cell?

Correcto? :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Yes

from holochain.

dellams avatar dellams commented on June 12, 2024

I am just trying to work out exactly what I need to send to the admin api, currently for the app api it sends the code I showed previously:

                var seed = RandomNumberGenerator.GetBytes(32);
                byte[] privateKey;
                byte[] publicKey;

                Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);

                byte[] DHTLocation = ConvertHoloHashToBytes(Config.AgentPubKey).TakeLast(4).ToArray();
                byte[] signingKey = new byte[] { 132, 32, 36 }.Concat(publicKey).Concat(DHTLocation).ToArray();

                HoloNETDataZomeCall payload = new HoloNETDataZomeCall()
                {
                    cap_secret = RandomNumberGenerator.GetBytes(64),
                    cell_id = new byte[2][] { ConvertHoloHashToBytes(Config.DnaHash), ConvertHoloHashToBytes(Config.AgentPubKey) },
                    fn_name = function,
                    zome_name = zome,
                    payload = MessagePackSerializer.Serialize(paramsObject),
                    //provenance = ConvertHoloHashToBytes(Config.AgentPubKey),
                    provenance = signingKey,
                    nonce = RandomNumberGenerator.GetBytes(32),
                    expires_at = DateTime.Now.AddMinutes(5).Ticks / 10 //DateTime.Now.AddMinutes(5).ToBinary(), //Conductor expects it in microseconds.
                };

                byte[] hash = Blake2b.ComputeHash(MessagePackSerializer.Serialize(payload));
                var sig = Ed25519.Sign(hash, privateKey);

                HoloNETDataZomeCallSigned signedPayload = new HoloNETDataZomeCallSigned()
                {
                    cap_secret = payload.cap_secret,
                    cell_id = payload.cell_id,
                    fn_name = payload.fn_name,
                    zome_name = payload.zome_name,
                    payload = payload.payload,
                    provenance = payload.provenance,
                    nonce = payload.nonce,
                    expires_at = payload.expires_at,
                    signature = sig
                };

                HoloNETData holoNETData = new HoloNETData()
                {
                    type = "zome_call",
                    data = signedPayload
                };

                await SendHoloNETRequestAsync(id, holoNETData);
 public async Task SendHoloNETRequestAsync(string id, HoloNETData holoNETData)
        {
            try
            {
                HoloNETRequest request = new HoloNETRequest()
                {
                    id = Convert.ToUInt64(id),
                    type = "Request",
                    data = MessagePackSerializer.Serialize(holoNETData)
                };

                if (WebSocket.State == WebSocketState.Open)
                {
                    Logger.Log("Sending HoloNET Request to Holochain Conductor...", LogType.Info, true);
                    await WebSocket.SendRawDataAsync(MessagePackSerializer.Serialize(request)); //This is the fastest and most popular .NET MessagePack Serializer.
                    Logger.Log("HoloNET Request Successfully Sent To Holochain Conductor.", LogType.Info, false);
                }
            }
            catch (Exception ex)
            {
                HandleError("Error occurred in HoloNETClient.SendHoloNETRequest method.", ex);
            }
        }

From what I can see the admin api also will use the same SendHoloNETRequestAsync function above so it is wrapped in the:

HoloNETRequest request = new HoloNETRequest()
                {
                    id = Convert.ToUInt64(id),
                    type = "Request",
                    data = MessagePackSerializer.Serialize(holoNETData)
                };

But what I am trying to work out is if the

{

      cell_id: cellId,
      cap_grant: {
        tag: "zome-call-signing-key",
        functions,
        access: {
          Assigned: {
            secret: capSecret,
            assignees: [signingKey],
          },
        },
      },
    });

is passed in directly as another HoloNETData object or if it itself is another level down embedded inside the HoloNETData like the app api is?

In other words is it set in the data property on HoloNETData or on HoloNETRequest? If it is set in the data property on HoloNETData then what would the type property be? For the app api it is "zome_call".

I couldn't tell by looking at your code.

Many thanks,
David.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

It's passed in as a HoloNETData object and sent over the admin websocket.

The type is grant_zome_call_capability.

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok great thanks. :)

In your code everything is set with a property name and value but for functions it has no property name? So far I have this code:

 public async Task GrantCapabilityAsync(string id, string cellId, GrantedFunctions functions)
        {
            var seed = RandomNumberGenerator.GetBytes(32);
            byte[] privateKey;
            byte[] publicKey;

            Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);

            byte[] DHTLocation = ConvertHoloHashToBytes(Config.AgentPubKey).TakeLast(4).ToArray();
            byte[] signingKey = new byte[] { 132, 32, 36 }.Concat(publicKey).Concat(DHTLocation).ToArray();

            if (functions == null)
            {
                functions = new GrantedFunctions() { Functions = new Dictionary<GrantedFunctionsType, object>() };
                functions.Functions.Add(GrantedFunctionsType.All, null);
            }

            HoloNETData holoNETData = new HoloNETData()
            {
                type = "grant_zome_call_capability",
                data = new HoloNETAdminDataCap()
                {
                    cell_id = cellId,
                    cap_grant = new CapGrant()
                    {
                        tag = "zome-call-signing-key",
                        functions = functions,
                        access = new CapGrantAccess()
                        {
                            Assigned = new CapGrantAccessAssigned() 
                            { 
                                secret = RandomNumberGenerator.GetBytes(64),
                                assignees = signingKey
                            }
                        }

                    }
                }
            };

            _signingCredentials[cellId] = new SigningCredentials()
            {
                CapSecret = holoNETData.data.cap_grant.access.Assigned.secret,
                KeyPair = new KeyPair() { PrivateKey = privateKey, PublicKey = publicKey },
                SigningKey = signingKey
            };

            await SendHoloNETRequestAsync(id, holoNETData);

            //{
            //cell_id: cellId,
            //      cap_grant:
            //                {
            //                tag: "zome-call-signing-key",
            //        functions,
            //        access:
            //                    {
            //                    Assigned:
            //                        {
            //                        secret: capSecret,
            //            assignees: [signingKey],
            //          },
            //        },
            //      },
            //    });
        }

Does this look right to you?

It is just the functions I am not sure about because in your code you have no property name.

I also used AI (https://codepal.ai/language-translator) to convert this:

export type GrantedFunctions =
  | { [GrantedFunctionsType.All]: null }
  | { [GrantedFunctionsType.Listed]: [ZomeName, FunctionName][] };

to:

using System;
using System.Collections.Generic;

public enum GrantedFunctionsType
{
    All,
    Listed
}

public class GrantedFunctions
{
    public Dictionary<GrantedFunctionsType, object> Functions { get; set; }

    public GrantedFunctions()
    {
        Functions = new Dictionary<GrantedFunctionsType, object>();
    }
}

public class ZomeName
{
    public string Name { get; set; }
}

public class FunctionName
{
    public string Name { get; set; }
}

public class Program
{
    public static void Main()
    {
        GrantedFunctions grantedFunctions = new GrantedFunctions();

        // Example 1: All functions granted
        grantedFunctions.Functions.Add(GrantedFunctionsType.All, null);

        // Example 2: Listed functions granted
        List<Tuple<ZomeName, FunctionName>> listedFunctions = new List<Tuple<ZomeName, FunctionName>>();
        listedFunctions.Add(new Tuple<ZomeName, FunctionName>(new ZomeName { Name = "Zome1" }, new FunctionName { Name = "Function1" }));
        listedFunctions.Add(new Tuple<ZomeName, FunctionName>(new ZomeName { Name = "Zome2" }, new FunctionName { Name = "Function2" }));
        grantedFunctions.Functions.Add(GrantedFunctionsType.Listed, listedFunctions);

        // Accessing the granted functions
        if (grantedFunctions.Functions.ContainsKey(GrantedFunctionsType.All))
        {
            Console.WriteLine("All functions granted");
        }
        else if (grantedFunctions.Functions.ContainsKey(GrantedFunctionsType.Listed))
        {
            List<Tuple<ZomeName, FunctionName>> listed = (List<Tuple<ZomeName, FunctionName>>)grantedFunctions.Functions[GrantedFunctionsType.Listed];
            Console.WriteLine("Listed functions granted:");
            foreach (var tuple in listed)
            {
                Console.WriteLine($"Zome: {tuple.Item1.Name}, Function: {tuple.Item2.Name}");
            }
        }
    }
}

Is this how the functions are set using tuples of zome name and function name or something similar? Be good to see an example of how to use your js client code somewhere setting the functions and then calling the admin api and then the app api...

Many thanks,
David.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

I don't have an example, but it's an array of tuples containing ZomeName and FunctionName which are aliases for string. So in the end it's

const functions = [["zome_name1", "function_name1"], ["zome_name1", "function_name2"], ["zome_name2", "function_name1"]];

Calling the admin function is then

const capSecret = randomCapSecret();
const [keyPair, signingKey] = await generateSigningKeyPair();
const capSecret = await this.grantSigningKey(
  cellId,
  functions || { [GrantedFunctionsType.All]: null },
  signingKey
);
const functions = {
  [GrantedFunctionsType.Listed]: [
    ["zome_name1", "function_name1"],
    ["zome_name1", "function_name2"],
    ["zome_name2", "function_name1"]
  ]
};
await AdminWebsocket.grantZomeCallCapability({
  cell_id: cellId,
  cap_grant: {
    tag: "zome-call-signing-key",
    functions,
    access: {
      Assigned: {
        secret: capSecret,
        assignees: [signingKey],
      },
    },
  },
});

see https://github.com/holochain/holochain-client-js/blob/main/src/api/admin/websocket.ts#L310-L322

And finally a zome call example.

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok thanks, so what is this in ts?

export type GrantedFunctions =
  | { [GrantedFunctionsType.All]: null }
  | { [GrantedFunctionsType.Listed]: [ZomeName, FunctionName][] };

A Dictionary containing a key of GrantedFunctionsType emum and value is an array of tuples?

Also in this code:

await AdminWebsocket.grantZomeCallCapability({
  cell_id: cellId,
  cap_grant: {
    tag: "zome-call-signing-key",
    functions,
    access: {
      Assigned: {
        secret: capSecret,
        assignees: [signingKey],
      },
    },
  },
});

I am still unclear how the functions should be passed in because there is no name for the property or field? All the others have a name/value pair.

Where can I find the conductor admin api documentation showing what each admin api call expects? I need to know the exact data format it is expecting to make sure my code is sending it correctly, thanks.

I also noticed in your admin api code above there is no MessagePack encoding? In the app api there are multiple levels of MessagePack encoding so I wanted to double check? Thanks

Cheers
D.

from holochain.

dellams avatar dellams commented on June 12, 2024

Does this look right to you?

 public async Task GrantCapabilityAsync(byte[][] cellId, GrantedFunctions functions, string id = "")
        {
            var seed = RandomNumberGenerator.GetBytes(32);
            byte[] privateKey;
            byte[] publicKey;

            if (string.IsNullOrEmpty(id))
            {
                _currentId++;
                id = _currentId.ToString();
            }

            Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed);

            byte[] DHTLocation = ConvertHoloHashToBytes(Config.AgentPubKey).TakeLast(4).ToArray();
            byte[] signingKey = new byte[] { 132, 32, 36 }.Concat(publicKey).Concat(DHTLocation).ToArray();

            //temp to test GrantedFunctions.
            functions = new GrantedFunctions() { Functions = new Dictionary<GrantedFunctionsType, List<(string, string)>>() };
            functions.Functions.Add(GrantedFunctionsType.Listed, new List<(string, string)>() 
            { 
                ("zome1", "func1"),
                ("zome2", "funct2")
            });

            if (functions == null)
            {
                functions = new GrantedFunctions() { Functions = new Dictionary<GrantedFunctionsType, List<(string, string)>>() };
                functions.Functions.Add(GrantedFunctionsType.All, null);
            }

            HoloNETData holoNETData = new HoloNETData()
            {
                type = "grant_zome_call_capability",
                data = new HoloNETAdminDataCap()
                {
                    cell_id = cellId,
                    cap_grant = new CapGrant()
                    {
                        tag = "zome-call-signing-key",
                        functions = functions,
                        access = new CapGrantAccess()
                        {
                            Assigned = new CapGrantAccessAssigned() 
                            { 
                                secret = RandomNumberGenerator.GetBytes(64),
                                assignees = signingKey
                            }
                        }

                    }
                }
            };

            _signingCredentialsForCell[cellId] = new SigningCredentials()
            {
                CapSecret = holoNETData.data.cap_grant.access.Assigned.secret,
                KeyPair = new KeyPair() { PrivateKey = privateKey, PublicKey = publicKey },
                SigningKey = signingKey
            };

            await SendHoloNETRequestAsync(id, holoNETData);

from holochain.

dellams avatar dellams commented on June 12, 2024

I also may have found a potential bug in your client because it only checks if a cell has been authorized and not what zomes or functions have been. It will work fine if they don't set the zome names or function names because it will then automatically authorize all of them but if they want to authorize for only given zome or function names, I can't see any check done in the app api call to enforce this?

export const signZomeCall = async (request: CallZomeRequest) => {
  const signingCredentialsForCell = getSigningCredentials(request.cell_id);
  if (!signingCredentialsForCell) {
    throw new Error(
      `cannot sign zome call: no signing credentials have been authorized for cell [${encodeHashToBase64(
        request.cell_id[0]
      )}, ${encodeHashToBase64(request.cell_id[1])}]`
    );
  }

Unless I am missing something?! ;-)

from holochain.

dellams avatar dellams commented on June 12, 2024

Good news is that I made more progress, and got my hApp up and running and the conductor connecting to my client again, but when I send this:

HoloNETData holoNETData = new HoloNETData()
                {
                    type = "app_info",
                    data = new HoloNETDataAppInfoCall()
                    {
                        installed_app_id = "test-app"
                    }
                };

As I use to, to get the cellid, it now returns null instead.

private void DecodeAppInfoDataReceived(HoloNETResponse response, WebSocket.DataReceivedEventArgs dataReceivedEventArgs, string rawBinaryDataAsString, string rawBinaryDataDecoded, string rawBinaryDataAfterMessagePackDecodeAsString, string rawBinaryDataAfterMessagePackDecodeDecoded)
        {
            AppInfoCallBackEventArgs args = new AppInfoCallBackEventArgs();

            try
            {
                Logger.Log("APP INFO RESPONSE DATA DETECTED\n", LogType.Info);
                HolonNETAppInfoResponse appInfoResponse = MessagePackSerializer.Deserialize<HolonNETAppInfoResponse>(response.data, messagePackSerializerOptions);

                string dnaHash = ConvertHoloHashToString(appInfoResponse.data.cell_data[0].cell_id[0]);
                string agentPubKey = ConvertHoloHashToString(appInfoResponse.data.cell_data[0].cell_id[1]);

                if (_updateDnaHashAndAgentPubKey)
                {
                    Config.AgentPubKey = agentPubKey;
                    Config.DnaHash = dnaHash;
                }

                Logger.Log($"AGENT PUB KEY RETURNED FROM CONDUCTOR: {Config.AgentPubKey}", LogType.Info);
                Logger.Log($"DNA HASH RETURNED FROM CONDUCTOR:: {Config.DnaHash}", LogType.Info);

ButappInfoResponse.data.cell_datais now null and before I upgraded to BETA it use to have the cell info.

This is how I use to get the AgentPubKey so is there another way of doing this now?

I think it may have something to do with needing to make the necessary admin api calls first to install the app etc?

But I should still be able to retrieve the AgentPubKey without this?

from holochain.

jost-s avatar jost-s commented on June 12, 2024

I can't see any check done in the app api call to enforce this?

No, if you want to authorize only certain functions, it's up to the happ dev to handle that. Authorize the functions for the cell id and then store the credentials in-memory under the cell id.

But appInfoResponse.data.cell_datais now null and before I upgraded to BETA it use to have the cell info.

Yes, the app info has changed significantly. See here for the current structure https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/struct.AppInfo.html

agent_pub_key is on the top-level of the app info struct.

Check here for an example of app info in JavaScript https://github.com/holochain/holochain-client-js/blob/main/test/e2e/index.ts#L325-L326

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok great thanks, I will look into what you shared... makes sense... :)

Well all makes sense apart from the functions, because I can't see why the admin api takes a list of functions if it is later ignored or not enforced?

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok thanks, so what is this in ts?

export type GrantedFunctions =
  | { [GrantedFunctionsType.All]: null }
  | { [GrantedFunctionsType.Listed]: [ZomeName, FunctionName][] };

A Dictionary containing a key of GrantedFunctionsType emum and value is an array of tuples?

Also in this code:

await AdminWebsocket.grantZomeCallCapability({
  cell_id: cellId,
  cap_grant: {
    tag: "zome-call-signing-key",
    functions,
    access: {
      Assigned: {
        secret: capSecret,
        assignees: [signingKey],
      },
    },
  },
});

I am still unclear how the functions should be passed in because there is no name for the property or field? All the others have a name/value pair.

Where can I find the conductor admin api documentation showing what each admin api call expects? I need to know the exact data format it is expecting to make sure my code is sending it correctly, thanks.

I also noticed in your admin api code above there is no MessagePack encoding? In the app api there are multiple levels of MessagePack encoding so I wanted to double check? Thanks

Cheers D.

Still unclear about this also?

I found this:
https://docs.rs/holochain_zome_types/0.2.1/holochain_zome_types/prelude/enum.GrantedFunctions.html

But I am still trying to work out how to convert this or your ts to c#... :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Well all makes sense apart from the functions, because I can't see why the admin api takes a list of functions if it is later ignored or not enforced?

It's not enforced client side. If you call a function that you're not authorized for, you'll be returned an error.

from holochain.

dellams avatar dellams commented on June 12, 2024

Oh right, yeah good point, yeah that makes more sense now thanks! :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024
export type GrantedFunctions =
  | { [GrantedFunctionsType.All]: null }
  | { [GrantedFunctionsType.Listed]: [ZomeName, FunctionName][] };

A Dictionary containing a key of GrantedFunctionsType emum and value is an array of tuples?

Yes

await AdminWebsocket.grantZomeCallCapability({
  cell_id: cellId,
  cap_grant: {
    tag: "zome-call-signing-key",
    functions,
    access: {
      Assigned: {
        secret: capSecret,
        assignees: [signingKey],
      },
    },
  },
});

I am still unclear how the functions should be passed in because there is no name for the property or field? All the others have a name/value pair.

When there's only a property name, it's a shorthand for the property and a variable as value of equal name.
These two are the same:

const text = "hello";
const a = { text: text };
const b = { text };

I had posted an example above #2577 (comment)

Where can I find the conductor admin api documentation showing what each admin api call expects? I need to know the exact data format it is expecting to make sure my code is sending it correctly, thanks.

Here https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/enum.AdminRequest.html

I also noticed in your admin api code above there is no MessagePack encoding? In the app api there are multiple levels of MessagePack encoding so I wanted to double check? Thanks

Both app and admin api use the same websocket client which encodes a request and its data payload in MessagePack format. https://github.com/holochain/holochain-client-js/blob/main/src/api/client.ts#L192-L196

Still unclear about this also?

I found this: https://docs.rs/holochain_zome_types/0.2.1/holochain_zome_types/prelude/enum.GrantedFunctions.html

But I am still trying to work out how to convert this or your ts to c#... :)

Well I can only give my uninformed opinion, my knowledge of C# is tiny. What you wrote earlier seems close enough

functions = new GrantedFunctions() { Functions = new Dictionary<GrantedFunctionsType, List<(string, string)>>() };
functions.Functions.Add(GrantedFunctionsType.Listed, new List<(string, string)>() 
{ 
    ("zome1", "func1"),
    ("zome2", "funct2")
});

if (functions == null)
{
    functions = new GrantedFunctions() { Functions = new Dictionary<GrantedFunctionsType, List<(string, string)>>() };
    functions.Functions.Add(GrantedFunctionsType.All, null);
}

You could use a static class with string for the GrantedFunctions type, but that's more a TS way of reaching a Rust enum-like type. In the end what you need is the serialized version of

{
  Listed: [
    ["zome_name1", "function_name1"],
    ["zome_name1", "function_name2"],
    ["zome_name2", "function_name1"]
  ]
}

Listed being a string property and the tuples being arrays of length 2. Can you print what is the MessagePacked version in your client of the ZomeCallCapGrant so far?

from holochain.

dellams avatar dellams commented on June 12, 2024

Hey, hope you good @jost-s ?

I just finished updating the HoloNETAppInfo with the latest version you pointed me to, took a while to work out how to convert some things to c# but got there in the end! :)

But had a few questions...

The celldata is still null even after updating to the newest format? The agent_pub_key is fine though so I can use this so was just wondering why celldata was null? Or is it always null? And what about DnaHash? Previously I was able to derive this from the cell data.

Also, I wanted to double check that the values that the provisioning strategy can be are "Create" and "CloneOnly" as shown here:

https://docs.rs/holochain_types/0.2.1/holochain_types/app/enum.CellProvisioning.html

I noticed the documentation is a little out of date too? Because the data I get back from the conductor shows that strategy is a enum/string inside Provisioning along with a boolean flag called deferred. But the documentation above doesn't mention any property called strategy for example.

image

Also the rust Option operator appears to allow an enum to have multiple values with the Some option and also a None option. Not sure how this would translate to c# because it doesn't support this kind of enum functionality. How does ts handle this I was wondering?

Any help be greatly appreciated thanks. :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Hey, hope you good @jost-s ?

Yes, thanks =)

The celldata is still null even after updating to the newest format? The agent_pub_key is fine though so I can use this so was just wondering why celldata was null? Or is it always null? And what about DnaHash? Previously I was able to derive this from the cell data.

The field is called cell_info now.

Also, I wanted to double check that the values that the provisioning strategy can be are "Create" and "CloneOnly" as shown here:

https://docs.rs/holochain_types/0.2.1/holochain_types/app/enum.CellProvisioning.html

Yes, those are the values.

I noticed the documentation is a little out of date too? Because the data I get back from the conductor shows that strategy is a enum/string inside Provisioning along with a boolean flag called deferred. But the documentation above doesn't mention any property called strategy for example.

That's what you put in the hApp manifest and is being returned under manifest. That can be found here https://docs.rs/holochain_types/0.2.1/holochain_types/app/enum.CellProvisioning.html#variant.Create.field.deferred

But I don't know where strategy comes from. I suppose it comes from deserialization of the enum to a C# type. It's not in the Holochain code.

Also the rust Option operator appears to allow an enum to have multiple values with the Some option and also a None option. Not sure how this would translate to c# because it doesn't support this kind of enum functionality. How does ts handle this I was wondering?

Option is just an enum itself with two variants, Some and None. None doesn't have a variant value and in JS is null. Some<T> has the value of the variant, for example Option<AgentPubKey> would be Some(my_agent_pubkey) and in JS simply my_agent_pubkey, but it cannot have multiple values of AgentPubKey, if that's what you mean.

from holochain.

dellams avatar dellams commented on June 12, 2024

Yeah I was thinking Option Some would allow multiple values of an enum?

Thanks for getting back to me and for spotting cell_info, can't believe I missed that rename! lol ;-)

strategy appears to come from the happ.yaml file so maybe its dynamic rust code so it allows custom properties to be added under the provisioning section in the yaml file?

image

I am now getting back cell data after I changed the property name to cell_info but I can only get it to work with a generic object, I want to make it strongly typed like I have the rest but I am having trouble trying to convert the rust enums to c# because the rust enums allows types to be included as member values of the enum and c# doesn't allow this, does ts?

So I am having trouble with this enum:

https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/enum.CellInfo.html

The closest I got to converting this to c# is to use a struct instead of a enum which doesn't give any error but unfortunately the provisionedCell etc have null properties inside.

image

Any ideas? :)

from holochain.

dellams avatar dellams commented on June 12, 2024

The HoloNETDataAppInfoCall looks like this now:

image

from holochain.

dellams avatar dellams commented on June 12, 2024

Hi again, hope you are having a good weekend? :)

I am just implementing the installApp function but I noticed in your example code on the root page you have:

const appInfo = await adminWs.installApp({
  agent_key,
  path: "path/to/happ/file",
  installed_app_id,
  membrane_proofs: {},
});

But I can't see the path property/param in your adminws code:

https://github.com/holochain/holochain-client-js/blob/main/src/api/admin/websocket.ts
https://github.com/holochain/holochain-client-js/blob/main/src/api/admin/types.ts

All I could find was this:

export type InstallAppRequest = {
  // The agent to use when creating Cells for this App.
  agent_key: AgentPubKey;

  // The unique identifier for an installed app in this conductor.
  // If not specified, it will be derived from the app name in the bundle manifest.
  installed_app_id?: InstalledAppId;

  // Include proof-of-membrane-membership data for cells that require it,
  // keyed by the CellNick specified in the app bundle manifest.
  membrane_proofs: { [key: string]: MembraneProof };

  // Optional global network seed override.  If set will override the network seed value for all DNAs in the bundle.
  network_seed?: NetworkSeed;
} & AppBundleSource; // The unique identifier for an installed app in this conductor.

It also looks like membrane_proofs cannot be null? But passing in an empty dictionary is ok as you do in your example?

from holochain.

dellams avatar dellams commented on June 12, 2024

I just found this:
https://docs.rs/holochain_types/0.2.1/holochain_types/app/struct.InstallAppPayload.html

So it looks like there is a source property which is where the path is set but only if the AppBundleSource enum is set to Path. I saw there is also Bundle and needs a AppBundle passed in:

https://docs.rs/holochain_types/0.2.1/holochain_types/app/struct.AppBundle.html

This looks like a struct but it wasn't clear what the properties are on this?

The AppBundleSource again is a rust enum so it has types as enum values, which as I said above c# unfortunately does not support so I need to try and find a work around for this. This is more of an issue for requests such as this than for responses since for responses as a workaround I can just manually parse the generic object to pull all the data out of it but there isn't much I can do for requests. It would be great to see what the actual serialized data looks like that your js client is sending over the wire to the conductor for installing apps? Thanks as always, really appreciated! :)

Also wondering why I couldn't see this source property in your code anywhere? I see you have & AppBundleSource; at the end of your type declaration but no source? Also not sure what this means in ts? Sorry, still trying to get up to speed with ts! ;-)

from holochain.

dellams avatar dellams commented on June 12, 2024

FYI: So far I have this:

image

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Yeah I was thinking Option Some would allow multiple values of an enum?

It allows whatever T is in Option<T>.

strategy appears to come from the happ.yaml file so maybe its dynamic rust code so it allows custom properties to be added under the provisioning section in the yaml file?

Right, that's where it must come from.

So I am having trouble with this enum:

https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/enum.CellInfo.html

The closest I got to converting this to c# is to use a struct instead of a enum which doesn't give any error but unfortunately the provisionedCell etc have null properties inside.

Any ideas? :)

Yes, make the Keys lowercased. All variable names coming from the Conductor API are lowercase.

from holochain.

jost-s avatar jost-s commented on June 12, 2024
export type InstallAppRequest = {
  // The agent to use when creating Cells for this App.
  agent_key: AgentPubKey;

  // The unique identifier for an installed app in this conductor.
  // If not specified, it will be derived from the app name in the bundle manifest.
  installed_app_id?: InstalledAppId;

  // Include proof-of-membrane-membership data for cells that require it,
  // keyed by the CellNick specified in the app bundle manifest.
  membrane_proofs: { [key: string]: MembraneProof };

  // Optional global network seed override.  If set will override the network seed value for all DNAs in the bundle.
  network_seed?: NetworkSeed;
} & AppBundleSource; // The unique identifier for an installed app in this conductor.

AppBundleSource is flattened by "serde" into this same dictionary InstallAppRequest, so it becomes either path or bundle. There's no additional property source or the like.

It also looks like membrane_proofs cannot be null? But passing in an empty dictionary is ok as you do in your example?

That's correct, it cannot be null. Yes, passing in an empty dictionary is the way to go, meaning no membrane proof for every role/DNA.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

https://docs.rs/holochain_types/0.2.1/holochain_types/app/struct.AppBundle.html

This looks like a struct but it wasn't clear what the properties are on this?

In a case like this you can click on source to see the implementation details.
image

Here it is actually a bundled AppManifest, which then points to the current version V1 as the only possible variant. Following that struct in turn leads to AppManifestCurrent.

The AppBundleSource again is a rust enum so it has types as enum values, which as I said above c# unfortunately does not support so I need to try and find a work around for this. This is more of an issue for requests such as this than for responses since for responses as a workaround I can just manually parse the generic object to pull all the data out of it but there isn't much I can do for requests. It would be great to see what the actual serialized data looks like that your js client is sending over the wire to the conductor for installing apps? Thanks as always, really appreciated! :)

In JS that generally translates to the enum's variant being a property identifier of an object (say path or provisioned) and the property value being null for no-data variants (e. g. { some_property: null }) and the corresponding data for other variants (e. g. { some_property: { prop1: ..., prop2: ...} }). The actual serialized data are byte arrays, something like [192, 43, 30, 12, ...] etc. Or what are you referring you?

Also wondering why I couldn't see this source property in your code anywhere? I see you have & AppBundleSource; at the end of your type declaration but no source? Also not sure what this means in ts? Sorry, still trying to get up to speed with ts! ;-)

Sure, you have to know this about serde in order to figure it out, it's a field attribute https://serde.rs/field-attrs.html#flatten.

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok great thanks for getting back to me, appreciated, will reply properly later but for now I wanted to quickly know where I can find DnaModifiers?

https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/struct.ProvisionedCell.html

ProvisionedCell has a modifiers property but for some reason the documentation has no link to DnaModifiers like it does for other types, is there a reason for this?

I got further by changing the keys to lower case as you suggested but now I got a new error:

"Unexpected msgpack code 207 (uint 64) encountered."

So looks like something is a unit64 but it expects something else, or it could be something else...

My ProvisionedCell looks like this:

image

And DnaModifiers looks like this:

image

I found it somewhere else in the documentation but can't remember where and can't find it again now! ;-) lol

Do these both look correct to you? The error is being caused somewhere in these 2 objects...

Thanks.

from holochain.

dellams avatar dellams commented on June 12, 2024

I found it again! :)

https://docs.rs/holochain_types/0.2.1/holochain_types/prelude/struct.DnaModifiers.html

So hopefully this is the same stuct used for AppInfo?

I got it working by changing origin_time to a long so it must be! ;-)

image

I also created a RustDuration struct to hold the secs (int) and nanos (float) it returns so hopefully that looks right to you? :)

image

I am trying to convert the origin_time to a c# dateime object by multiplying the microseconds the conductor returns by 10 to get the long ticks which can then be used to create a dateime object but the date is well off so it can't be that! ;-)

So from the origin_time of 1688181237410745 I am getting {01/07/0054 03:13:57}. Any ideas? :)

image

You will see I also check that the cell data and agentpubkey match up and give an error if they do not match as a extra safety check.... ;-)

from holochain.

dellams avatar dellams commented on June 12, 2024

AppBundleSource is flattened by "serde" into this same dictionary InstallAppRequest, so it becomes either path or bundle. There's no additional property source or the like.

Ok great so I can add a path and bundle property to my InstallAppRequest object then I guess? Then just set whichever one is passed in? One or the other but not both I guess?

from holochain.

dellams avatar dellams commented on June 12, 2024

In JS that generally translates to the enum's variant being a property identifier of an object (say path or provisioned) and the property value being null for no-data variants (e. g. { some_property: null }) and the corresponding data for other variants (e. g. { some_property: { prop1: ..., prop2: ...} }). The actual serialized data are byte arrays, something like [192, 43, 30, 12, ...] etc. Or what are you referring you?

Ok thanks so like how I have handled it in CellType?

image

So when its a provisioned cell only provisioned property is populated and the others have child null properties etc? So in this case the stem and cloned properties will have instances of that class/struct but the child properties inside them will be null.

Or maybe the root property is null? So in this case cloned and stem would be null?

from holochain.

dellams avatar dellams commented on June 12, 2024

Got another question, I was implementing add_agent_info and agent_info and noticed that they use a type called AgentInfoSigned but when I looked it up it said it was unknown?

export type AddAgentInfoRequest = { agent_infos: Array<AgentInfoSigned> };

export type AgentInfoSigned = unknown;

What does unknown mean in ts?

Thanks :)

from holochain.

dellams avatar dellams commented on June 12, 2024

Hey @jost-s, how are you? Hope you are having a good weekend? :)

I am just looking at DeleteCloneCellRequest which uses DisableCloneCellRequest, which itself looks like this:

https://github.com/holochain/holochain-client-js/blob/5bb565cf834b2a09f70ef178a04dabea61163fe2/src/api/app/types.ts#L109

It takes a clone_cell_id property which looks it can be a RoleName (string) or a CellId (a byte[][])? I wanted to double check this was right? If so then I will need to use a special type called dynamic in c# allowing it to be any type you wish.

How is RoleName used? Is it the name of a role that contains the cell or something?

Thanks! :)

from holochain.

dellams avatar dellams commented on June 12, 2024

What does storage_info do? I presume it gets the storage info for the running hApp? Or is it the storage info for all the hApps the conductor is running or has installed/configured? Thanks :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Got another question, I was implementing add_agent_info and agent_info and noticed that they use a type called AgentInfoSigned but when I looked it up it said it was unknown?

What does unknown mean in ts?

It means that the type isn't known and you cannot access any properties before having checked for their existence.

It's an omission that these types are not specified in the client. In Rust they reside in kitsune https://docs.rs/kitsune_p2p/0.2.1/kitsune_p2p/agent_store/struct.AgentInfoSigned.html. That leads to https://docs.rs/kitsune_p2p/0.2.1/kitsune_p2p/agent_store/struct.AgentInfoInner.html.

from holochain.

dellams avatar dellams commented on June 12, 2024

It means that the type isn't known and you cannot access any properties before having checked for their existence.

It's an omission that these types are not specified in the client. In Rust they reside in kitsune https://docs.rs/kitsune_p2p/0.2.1/kitsune_p2p/agent_store/struct.AgentInfoSigned.html. That leads to https://docs.rs/kitsune_p2p/0.2.1/kitsune_p2p/agent_store/struct.AgentInfoInner.html.

Ok thanks so do I use AgentInfoInner for the type? I'm not sure how I am meant to implement these methods? add_agent_info need agent infos passed in.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Yes, it must be a call that takes an array of AgentInfoInner as only parameter.

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok great thanks, I have just finished converting AgentInfoInner to c# and came up with this:

image

I just wasn't sure how to handle KitsuneSpace, KitsuneAgent & KitsuneSignature? I wasn't sure if I should just use a normal byte array for them or create a separate class/struct as they have in rust?

I created these:

image

image

image

Do these look right to you? I wasn't sure how they worked in rust? Because there was no property name for the binary data so wasn't sure how to map that onto c#?

Also there is no link/documentation for DhtArc so wasn't sure what type this is?

I also presumed url_list is a simple string array?

I also presumed encoded_bytes is a simple byte array?

Thanks! :)

from holochain.

dellams avatar dellams commented on June 12, 2024

I was also just looking at RegisterDna again noticed it has the same serde DnaSource thing on it that InstalllApp has:

export type RegisterDnaRequest = {
  modifiers?: {
    network_seed?: string;
    properties?: DnaProperties;
  };
} & DnaSource;

DnaSource looks like this:

export type DnaSource =
  | {
      hash: HoloHash;
    }
  | {
      path: string;
    }
  | {
      bundle: DnaBundle;
    };

So I flattened it again to this:

image

This look right to you? :)

DnaBundle looks like this:

image

ResourceMap looks like it's a dictionary with key string and value is a byte array right?

export type ResourceMap = { [key: string]: ResourceBytes };

export type ResourceBytes = Uint8Array;

from holochain.

dellams avatar dellams commented on June 12, 2024

And in DnaManifest I found this:

export type DnaManifest = {
  /**
   * Currently one "1" is supported
   */
  manifest_version: string;

  /**
   * The friendly "name" of a Holochain DNA.
   */
  name: string;

  /**
   * A network seed for uniquifying this DNA.
   */
  network_seed?: NetworkSeed;

  /**
   * Any arbitrary application properties can be included in this object.
   */
  properties?: DnaProperties;

  /**
   * An array of zomes associated with your DNA.
   * The order is significant: it determines initialization order.
   */
  zomes: Array<ZomeManifest>;
};

Which I converted to:

image

properties looks like it can just be a generic object? Because I looked up DnaProperties and it was just another unknown:

export type DnaProperties = unknown;

ZomeManifest looks like this:

export type ZomeManifest = {
  name: string;
  hash?: string;
  dependencies?: ZomeDependency[];
} & ZomeLocation;

Which contains another serde ZomeLocation so I flattened this also:

image

This look correct? :)

export type ZomeLocation = Location;

export type Location =
  | {
      /**
       * Expect file to be part of this bundle
       */
      bundled: string;
    }
  | {
      /**
       * Get file from local filesystem (not bundled)
       */
      path: string;
    }
  | {
      /**
       * Get file from URL
       */
      url: string;
    };

Finally, ZomeDependency looks like this:

export interface ZomeDependency {
  name: ZomeName;
}

So I simplified to this:

image

Hope this is ok?

Sorry for all the questions, really appreciate all the help as always! :)

Hope you had a good weekend?

from holochain.

dellams avatar dellams commented on June 12, 2024

Sorry one final question... ;-)

What is dHTOpsCursor? :) Thanks! :)

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok great thank you @jost-s and @neonphog really appreciated! :)

So @neonphog are you saying that dhtarc should just be a byte[] array then? Or maybe a generic object? In C# this could be a object or dynamic type.

Not sure what you mean by agent_info being a blob because it contains many internal properties so is a complex type?

from holochain.

neonphog avatar neonphog commented on June 12, 2024

Ok great thank you @jost-s and @neonphog really appreciated! :)

So @neonphog are you saying that dhtarc should just be a byte[] array then? Or maybe a generic object? In C# this could be a object or dynamic type.

Not sure what you mean by agent_info being a blob because it contains many internal properties so is a complex type?

Ideally, rather than returning or accepting the AgentInfoSigned type in the holochain_conductor_api, we would instead be returning and accepting Vec<u8>s in those positions, which you could decode with message pack if you really needed access to the internals, but most of the time you could just treat it as opaque.

With the API as-is, the typescript library is doing the closest thing to that it can, which is marking that type as unknown (or it could also use any). So the code cannot interact with the internals, but it is still able to pass it back and forth.

I only very briefly worked with C#, so I can't suggest the best equivalent for that language, but hopefully this gives you a sense of what might be appropriate.

from holochain.

dellams avatar dellams commented on June 12, 2024

Ok great thanks, so yeah the equivalent in C# would be object or dynamic as I said above, this way the c# client will accept whatever data/object you send it from the conductor... This just means it will be a generic object and so will not be strongly typed.

from holochain.

dellams avatar dellams commented on June 12, 2024

The only issue we will have with using a generic object is sending requests to the conductor such as add_agent because this means the user of the client will not know what data format the conductor is expecting because it is no longer strongly typed so I am not sure how we can get around this issue? This is why I always prefer to use strongly typed objects if possible so it is clear how it is to be used and prevent any errors etc...

from holochain.

neonphog avatar neonphog commented on June 12, 2024

This is why I always prefer to use strongly typed objects if possible so it is clear how it is to be used and prevent any errors etc...

For sure. Is there any equivalent to rust's concept of a new type? Basically create a named structure for strong typing that then, in turn, has a member variable that is the object or dynamic type. Then return that strong type from the get request, and accept it in the add_agent calls?

Alternately, we can totally delve into the typing of that struct.

And yeah, the encoded_bytes field is the raw original encoded format that was signed, which we need to maintain for validating signatures or re-encoding, because message pack as a format is not strictly deterministic (that is, you can choose to encode an integer in multiple different ways, for example).

from holochain.

dellams avatar dellams commented on June 12, 2024

Thanks for getting back to me, appreciated. :)

For sure. Is there any equivalent to rust's concept of a new type? Basically create a named structure for strong typing that then, in turn, has a member variable that is the object or dynamic type. Then return that strong type from the get request, and accept it in the add_agent calls?

Yes this can be done so would be something like what I now have:

image

But this will still have the same issue that the user of the client will not know what goes into the agent_info objects...

I looked at DhtArcRange and DhtArc but I cannot see where the properties are defined? All I can see are functions?

UrlList looks like it's just an array of strings right?

from holochain.

dellams avatar dellams commented on June 12, 2024

I am getting this odd issue that every time I call the install_app admi function on the conductor admin api, I am getting this error:

←[2mSep 06 13:40:54.083←[0m ←[31mERROR←[0m holochain_websocket::websocket: websocket_error_from_network=Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })
←[2mSep 06 13:40:54.136←[0m ←[31mERROR←[0m holochain::conductor::interface::websocket: error=Failed to send response to request

What is odd that it was sometimes returning a successful response and then the client would randomly bomb out with no error message so thought it was an issue in .net itself somewhere but now I am also getting this error from the conductor so maybe something else is going on?

As always, any help would be really appreciated thanks! :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

@dellams That is actually the point. As @neonphog said (emphasis added by me)

The internal agent_info structure including the dhtarc were intended to be opaque. You may need to transfer the data from node to node depending on your app design if you are not using our bootstrap protocol, but other than knowing which dna hash and agent the agent_info belongs to, there should be no need to decipher the internals.

How you'd use them is by querying them from one conductor and inject them into another conductor. You're not supposed to construct new AgentInfo objects.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

I am getting this odd issue that every time I call the install_app admi function on the conductor admin api, I am getting this error:

←[2mSep 06 13:40:54.083←[0m ←[31mERROR←[0m holochain_websocket::websocket: websocket_error_from_network=Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })
←[2mSep 06 13:40:54.136←[0m ←[31mERROR←[0m holochain::conductor::interface::websocket: error=Failed to send response to request

What is odd that it was sometimes returning a successful response and then the client would randomly bomb out with no error message so thought it was an issue in .net itself somewhere but now I am also getting this error from the conductor so maybe something else is going on?

As always, any help would be really appreciated thanks! :)

Probably an error with the app installation. What's the full code that leads to this error?

from holochain.

dellams avatar dellams commented on June 12, 2024

Oh right! Now I see what you mean! ;-) So you get the agentinfo from the agent_info function and then pass this raw data to the add_agent_info function running on another conductor? :)

The only issue is that the agentinfo function appears to return a 2 dimensional byte array containing only the cellid, no agent info object as per the js client code I looked at?

export type AgentInfoRequest = { cell_id: CellId | null };

Unless I am missing something? :)

from holochain.

dellams avatar dellams commented on June 12, 2024

Probably an error with the app installation. What's the full code that leads to this error?

UTF8: ??id☻?type?request?data???type?install_app?data??agent_key?'? $??u?6?m???(3^c??B?&?n?}JS+??♂9??installed_app_id?oasis-app4?membrane_proofs??network_seed??path?IC:\Users\USER\holochain-holochain-0.1.5\happs\oasis\BUILD\happ\oasis.happ?bundle?

Bytes: 131, 162, 105, 100, 2, 164, 116, 121, 112, 101, 167, 114, 101, 113, 117, 101, 115, 116, 164, 100, 97, 116, 97, 196, 222, 130, 164, 116, 121, 112, 101, 171, 105, 110, 115, 116, 97, 108, 108, 95, 97, 112, 112, 164, 100, 97, 116, 97, 134, 169, 97, 103, 101, 110, 116, 95, 107, 101, 121, 196, 39, 132, 32, 36, 167, 155, 199, 154, 145, 54, 183, 109, 164, 241, 138, 228, 159, 130, 40, 51, 94, 99, 139, 172, 66, 249, 38, 226, 110, 170, 125, 74, 7, 83, 43, 229, 199, 11, 57, 139, 176, 105, 110, 115, 116, 97, 108, 108, 101, 100, 95, 97, 112, 112, 95, 105, 100, 170, 111, 97, 115, 105, 115, 45, 97, 112, 112, 52, 175, 109, 101, 109, 98, 114, 97, 110, 101, 95, 112, 114, 111, 111, 102, 115, 128, 172, 110, 101, 116, 119, 111, 114, 107, 95, 115, 101, 101, 100, 192, 164, 112, 97, 116, 104, 217, 73, 67, 58, 92, 85, 115, 101, 114, 115, 92, 85, 83, 69, 82, 92, 104, 111, 108, 111, 99, 104, 97, 105, 110, 45, 104, 111, 108, 111, 99, 104, 97, 105, 110, 45, 48, 46, 49, 46, 53, 92, 104, 97, 112, 112, 115, 92, 111, 97, 115, 105, 115, 92, 66, 85, 73, 76, 68, 92, 104, 97, 112, 112, 92, 111, 97, 115, 105, 115, 46, 104, 97, 112, 112, 166, 98, 117, 110, 100, 108, 101, 192

Which resolves to:

{
"id": 2,
"type": "request",
"data": {
"type": "Buffer",
"data": [
130,
164,
116,
121,
112,
101,
171,
105,
110,
115,
116,
97,
108,
108,
95,
97,
112,
112,
164,
100,
97,
116,
97,
134,
169,
97,
103,
101,
110,
116,
95,
107,
101,
121,
196,
39,
132,
32,
36,
167,
155,
199,
154,
145,
54,
183,
109,
164,
241,
138,
228,
159,
130,
40,
51,
94,
99,
139,
172,
66,
249,
38,
226,
110,
170,
125,
74,
7,
83,
43,
229,
199,
11,
57,
139,
176,
105,
110,
115,
116,
97,
108,
108,
101,
100,
95,
97,
112,
112,
95,
105,
100,
170,
111,
97,
115,
105,
115,
45,
97,
112,
112,
52,
175,
109,
101,
109,
98,
114,
97,
110,
101,
95,
112,
114,
111,
111,
102,
115,
128,
172,
110,
101,
116,
119,
111,
114,
107,
95,
115,
101,
101,
100,
192,
164,
112,
97,
116,
104,
217,
73,
67,
58,
92,
85,
115,
101,
114,
115,
92,
85,
83,
69,
82,
92,
104,
111,
108,
111,
99,
104,
97,
105,
110,
45,
104,
111,
108,
111,
99,
104,
97,
105,
110,
45,
48,
46,
49,
46,
53,
92,
104,
97,
112,
112,
115,
92,
111,
97,
115,
105,
115,
92,
66,
85,
73,
76,
68,
92,
104,
97,
112,
112,
92,
111,
97,
115,
105,
115,
46,
104,
97,
112,
112,
166,
98,
117,
110,
100,
108,
101,
192
]
}
}

I tried to resolve the internal data packet but I think because it is encoded with messagepack we can't see what it is sending?

I was using this tool to decode: https://kawanet.github.io/msgpack-lite/

Although this tool should decode for us because it managed to decode the top level fine! :) For the internal data it just shows 0.

But as I said it's odd that up to now it was installing fine or giving the app already installed error if I tried to install it more than once and then it randomly bombed out but now it won't even give me any response so I have no idea what is going on?! lol

Any help would be really appreciated! thanks! 👍

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Oh right! Now I see what you mean! ;-) So you get the agentinfo from the agent_info function and then pass this raw data to the add_agent_info function running on another conductor? :)

Yes.

The only issue is that the agentinfo function appears to return a 2 dimensional byte array containing only the cellid, no agent info object as per the js client code I looked at?

export type AgentInfoRequest = { cell_id: CellId | null };

That's the request object. You pass in the cell id whose agents you're requesting.

The response of the agent_info call is an Array of AgentInfoSigned. AgentInfoSigned is unknown. It could also be an Array<Uint8array> in TypeScript, arguably.

So the 2-dimensional array is an array of byte arrays. Each element = byte array is the msgpack encoded agent info. Does that clarify it?

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Probably an error with the app installation. What's the full code that leads to this error?

UTF8: ??id☻?type?request?data???type?install_app?data??agent_key?'? $??u?6?m???(3^c??B?&?n?}JS+??♂9??installed_app_id?oasis-app4?membrane_proofs??network_seed??path?IC:\Users\USER\holochain-holochain-0.1.5\happs\oasis\BUILD\happ\oasis.happ?bundle?
...
I tried to resolve the internal data packet but I think because it is encoded with messagepack we can't see what it is sending?

I was using this tool to decode: https://kawanet.github.io/msgpack-lite/

Although this tool should decode for us because it managed to decode the top level fine! :) For the internal data it just shows 0.

But as I said it's odd that up to now it was installing fine or giving the app already installed error if I tried to install it more than once and then it randomly bombed out but now it won't even give me any response so I have no idea what is going on?! lol

Any help would be really appreciated! thanks! 👍

Please post the C# client code that you're executing.

from holochain.

dellams avatar dellams commented on June 12, 2024

Please post the C# client code that you're executing.

private async Task<AdminAppInstalledCallBackEventArgs> AdminInstallAppAsync(string installedAppId, string hAppPath = null, AppBundle appBundle = null, string agentKey = null, Dictionary<string, byte[]> membraneProofs = null, string network_seed = null, ConductorResponseCallBackMode conductorResponseCallBackMode = ConductorResponseCallBackMode.WaitForHolochainConductorResponse, string id = null)
        {
            if (string.IsNullOrEmpty(agentKey))
            {
                if (string.IsNullOrEmpty(Config.AgentPubKey))
                    await AdminGenerateAgentPubKeyAsync();

                agentKey = Config.AgentPubKey;
            }

            if (membraneProofs == null)
                membraneProofs = new Dictionary<string, byte[]>();

            return await CallAdminFunctionAsync("install_app", new HoloNETAdminInstallAppRequest()
            {
                path = hAppPath,
                bundle = appBundle,
                agent_key = ConvertHoloHashToBytes(agentKey),
                installed_app_id = installedAppId,
                membrane_proofs = membraneProofs,
                network_seed = network_seed
            }, _taskCompletionAdminAppInstalledCallBack, "OnAdminAppInstalledCallBack", conductorResponseCallBackMode, id);
         }

 private async Task<T> CallAdminFunctionAsync<T>(string holochainConductorFunctionName, dynamic holoNETDataDetailed, Dictionary<string, TaskCompletionSource<T>> _taskCompletionCallBack, string eventCallBackName, ConductorResponseCallBackMode conductorResponseCallBackMode = ConductorResponseCallBackMode.WaitForHolochainConductorResponse, string id = null) where T : HoloNETDataReceivedBaseEventArgs, new()
        {
            HoloNETData holoNETData = new HoloNETData()
            {
                type = holochainConductorFunctionName,
                data = holoNETDataDetailed
            };

            if (string.IsNullOrEmpty(id))
                id = GetRequestId();

            _taskCompletionCallBack[id] = new TaskCompletionSource<T> { };
            await SendHoloNETRequestAsync(holoNETData, id);

            if (conductorResponseCallBackMode == ConductorResponseCallBackMode.WaitForHolochainConductorResponse)
            {
                Task<T> returnValue = _taskCompletionCallBack[id].Task;
                return await returnValue;
            }
            else
                return new T() { EndPoint = EndPoint, Id = id, Message = $"conductorResponseCallBackMode is set to UseCallBackEvents so please wait for {eventCallBackName} event for the result." };
        }

from holochain.

dellams avatar dellams commented on June 12, 2024

The response of the agent_info call is an Array of AgentInfoSigned. AgentInfoSigned is unknown. It could also be an Array<Uint8array> in TypeScript, arguably.

Oh yeah! Of course! ;-) lol Sorry im ill at the moment so really should be resting but been forcing myself to keep going! But I think I should take some time to get better first! ;-) Feel like a zombie at the moment!

from holochain.

jost-s avatar jost-s commented on June 12, 2024

I tried to resolve the internal data packet but I think because it is encoded with messagepack we can't see what it is sending?

I was using this tool to decode: https://kawanet.github.io/msgpack-lite/

Although this tool should decode for us because it managed to decode the top level fine! :) For the internal data it just shows 0.

Removing all the newlines yields this

{
  "type": "install_app",
  "data": {
    "agent_key": {
      "type": "Buffer",
      "data": [
        132,
        32,
        36,
        167,
        155,
        199,
        154,
        145,
        54,
        183,
        109,
        164,
        241,
        138,
        228,
        159,
        130,
        40,
        51,
        94,
        99,
        139,
        172,
        66,
        249,
        38,
        226,
        110,
        170,
        125,
        74,
        7,
        83,
        43,
        229,
        199,
        11,
        57,
        139
      ]
    },
    "installed_app_id": "oasis-app4",
    "membrane_proofs": {},
    "network_seed": null,
    "path": "C:\\Users\\USER\\holochain-holochain-0.1.5\\happs\\oasis\\BUILD\\happ\\oasis.happ",
    "bundle": null
  }
}

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Please post the C# client code that you're executing.

private async Task<AdminAppInstalledCallBackEventArgs> AdminInstallAppAsync(string installedAppId, string hAppPath = null, AppBundle appBundle = null, string agentKey = null, Dictionary<string, byte[]> membraneProofs = null, string network_seed = null, ConductorResponseCallBackMode conductorResponseCallBackMode = ConductorResponseCallBackMode.WaitForHolochainConductorResponse, string id = null)
        {
            if (string.IsNullOrEmpty(agentKey))
            {
                if (string.IsNullOrEmpty(Config.AgentPubKey))
                    await AdminGenerateAgentPubKeyAsync();

                agentKey = Config.AgentPubKey;
            }

            if (membraneProofs == null)
                membraneProofs = new Dictionary<string, byte[]>();

            return await CallAdminFunctionAsync("install_app", new HoloNETAdminInstallAppRequest()
            {
                path = hAppPath,
                bundle = appBundle,
                agent_key = ConvertHoloHashToBytes(agentKey),
                installed_app_id = installedAppId,
                membrane_proofs = membraneProofs,
                network_seed = network_seed
            }, _taskCompletionAdminAppInstalledCallBack, "OnAdminAppInstalledCallBack", conductorResponseCallBackMode, id);
         }

I can't see what's wrong, it looks fine to me. What I strongly recommend is to handle all hashes as byte arrays and not as base64 strings. Base64 string is only meant for display, but anywhere else byte arrays should be used.

from holochain.

dellams avatar dellams commented on June 12, 2024

Great, thanks for looking into this for me, appreciated! :)

Thanks for the tip, I convert the HoloHashes to a byte array using this function:

/// <summary>
        /// Utility method to convert a string to base64 encoded bytes (Holochain Conductor format). This is used to convert the AgentPubKey & DnaHash when making a zome call.
        /// </summary>
        /// <param name="hash"></param>
        /// <returns></returns>
        public byte[] ConvertHoloHashToBytes(string hash)
        {
            return Convert.FromBase64String(hash.Replace('-', '+').Replace('_', '/').Substring(1, hash.Length - 1)); //also remove the u prefix.

            //Span<byte> output = null;
            //int bytesWritten = 0;

            //if (Convert.TryFromBase64String(hash.Replace('-', '+').Replace('_', '/').Substring(1, hash.Length - 1), output, out bytesWritten)) //also remove the u prefix.
            //    return output.ToArray();
            //else
            //    return null;
        }

from holochain.

jost-s avatar jost-s commented on June 12, 2024

All hashes in responses from the Conductor API are byte arrays, and all requests take byte arrays. HoloHash is a byte array. On the client side these values should remain byte arrays as much as possible. Only in case you want to show something to the user it can come in handy to display a base64 string.

I suggest you follow this pattern to avoid conversion problems. So what I meant earlier is that await CallAdminFunctionAsync("install_app", new HoloNETAdminInstallAppRequest() should take a byte array for agent_key.

from holochain.

dellams avatar dellams commented on June 12, 2024

Yes I understand what you are saying but I need agentKey to be human readable because it is displayed in the logs/console etc and also it can be set in the Config and passed in other places so a string is more easy to work with and see. HoloNET then converts it to a byte array when it is sent to the conductor and then converts it back again.

Unless there are other benefits of forcing users to pass in byte arrays instead of strings? Which would mean they would need to do more work converting it to a byte array which may not be in the correct format and could cause more errors and so why HoloNET does all this work for you to make it as easy and safe to use as possible... :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Yes I understand what you are saying but I need agentKey to be human readable because it is displayed in the logs/console etc and also it can be set in the Config and passed in other places so a string is more easy to work with and see. HoloNET then converts it to a byte array when it is sent to the conductor and then converts it back again.

You can convert a byte array just for display to a string. As I said, the Conductor API never accepts nor returns strings, it's byte arrays all the way.

Unless there are other benefits of forcing users to pass in byte arrays instead of strings? Which would mean they would need to do more work converting it to a byte array which may not be in the correct format and could cause more errors and so why HoloNET does all this work for you to make it as easy and safe to use as possible... :)

It's the opposite way around. Byte arrays cannot be in the wrong format, they're just bytes. Whereas base64 has multiple encodings and you can make mistakes confusing them.

from holochain.

dellams avatar dellams commented on June 12, 2024

But people still need to convert their agentkey to a byte array and may make mistakes doing it, because they need to do it this way to be compatible with the conductor:

/// <summary>
        /// Utility method to convert a string to base64 encoded bytes (Holochain Conductor format). This is used to convert the AgentPubKey & DnaHash when making a zome call.
        /// </summary>
        /// <param name="hash"></param>
        /// <returns></returns>
        public byte[] ConvertHoloHashToBytes(string hash)
        {
            return Convert.FromBase64String(hash.Replace('-', '+').Replace('_', '/').Substring(1, hash.Length - 1)); //also remove the u prefix.

            //Span<byte> output = null;
            //int bytesWritten = 0;

            //if (Convert.TryFromBase64String(hash.Replace('-', '+').Replace('_', '/').Substring(1, hash.Length - 1), output, out bytesWritten)) //also remove the u prefix.
            //    return output.ToArray();
            //else
            //    return null;
        }

So it is better for HoloNET to automatically manage this for them... :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

But people still need to convert their agentkey to a byte array and may make mistakes doing it, because they need to do it this way to be compatible with the conductor

If your client only returns and accepts byte arrays, people won't need to convert agent keys to a byte array. You can provide a function to convert from byte array to base64 with the correct encoding (and the other way around for some special cases), so people can use it for displaying values. That's what the JS client does and how hApp devs are using it.

from holochain.

dellams avatar dellams commented on June 12, 2024

Apologies for the delay, I was working on the new NFT API and GeoNFT API on the OASIS API but I am back onto HoloNET again now and made a breakthrough in that I got it installing hApp's again, as I suspected it was an issue with the console (CLI) project, when I hosted HoloNET in another environment such as WPF (Windows Presentation Foundation/Windows Desktop UI) it worked just fine!

So it's very odd that the console app was just bombing out in random places with no error given, seems like maybe a bug with .net? I need to investigate further soon... but for now I will continue within the WPF testing environment as well as test in other environments such as Linux, Web, WebAssembly, Android, IOS, Mac, TVOS etc... .NET makes it really easy to build and deploy to multiple platforms and OS's and why I chose it originally and also because its my background and been working with it for around 20 years.

from holochain.

dellams avatar dellams commented on June 12, 2024

Yes thank you, was very happy! :)

Now I got a bit further and managed to install and enable the app but having a new error returned when calling the "grant_zome_call_capability" API call.

Here is my code (I worked out how to pass the granted functions now because I had an error with this too but then fixed it):

public async Task<AdminSigningCredentialsAuthorizedEventArgs> AdminAuthorizeSigningCredentialsAsync(byte[][] cellId, GrantedFunctionsType grantedFunctionsType, List<(string, string)> functions = null, ConductorResponseCallBackMode conductorResponseCallBackMode = ConductorResponseCallBackMode.WaitForHolochainConductorResponse, string id = "")
        {
            (AdminSigningCredentialsAuthorizedEventArgs args, Dictionary<GrantedFunctionsType, List<(string, string)>> grantedFunctions, byte[] signingKey) = AuthorizeSigningCredentials(cellId, grantedFunctionsType, functions, id);

            if (!args.IsError)
            {
                return await CallAdminFunctionAsync("grant_zome_call_capability", new GrantZomeCallCapabilityRequest()
                {
                    cell_id = cellId,
                    cap_grant = new ZomeCallCapGrant()
                    {
                        tag = "zome-call-signing-key",
                        functions = grantedFunctions,
                        access = new CapGrantAccess()
                        {
                            Assigned = new CapGrantAccessAssigned()
                            {
                                secret = _signingCredentialsForCell[cellId].CapSecret,
                                assignees = signingKey
                            }
                        }
                    }
                },
                _taskCompletionAdminSigningCredentialsAuthorizedCallBack, "OnAdminSigningCredentialsAuthorized", conductorResponseCallBackMode, id);
            }
            else
                return args;
        }

And this returns the following error from the conductor:

��type�response�id��data�q��type�error�data��type�deserialization�data�CBytes(Deserialize("invalid type: byte array, expected a sequence"))[�\���e�~$�߁��
@����!�L�/@���.�'� $��	]s�)���4Qi�2E�W�o�Y�Z���ϖ����_s�dna_modifiers��network_seed��properties����origin_time�

I am sending this to the conductor:

��id\u0004�type�request�data�\u0001~��type�grant_zome_call_capability�data��cell_id��'�-$4�H�V[�\\\u0003��e�~$\u0012߁\u0019�\n@\u0012���!\aL�/@\u0010��.�'� $��\t]s\f�)���4Qi�2E�W\u0006o�Y\u0003Z���ϖ��\u0019�_s�cap_grant��tag�zome-call-signing-key�access��Assigned��secret�@����'<*�\u001c�lT���\u001d��:��>6�g �\nU\u001f��l\r�~���'\"��RP��8�����R\u001a�/'�assignees�'� $:��dɯ�\u001b�\u001c�Cd�d\u0015_o\v�\u001e��>\u000eKuɸ�B@\u0019�_s�functions�\u0001���oasis�create_avatar��oasis�get_avatar��oasis�update_avatar

Or in bytes it is:

131, 162, 105, 100, 4, 164, 116, 121, 112, 101, 167, 114, 101, 113, 117, 101, 115, 116, 164, 100, 97, 116, 97, 197, 1, 126, 130, 164, 116, 121, 112, 101, 186, 103, 114, 97, 110, 116, 95, 122, 111, 109, 101, 95, 99, 97, 108, 108, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, 121, 164, 100, 97, 116, 97, 130, 167, 99, 101, 108, 108, 95, 105, 100, 146, 196, 39, 132, 45, 36, 52, 178, 72, 194, 86, 91, 147, 92, 3, 211, 194, 101, 246, 126, 36, 18, 223, 129, 25, 183, 10, 64, 18, 138, 185, 129, 33, 7, 76, 154, 47, 64, 16, 175, 249, 46, 196, 39, 132, 32, 36, 168, 250, 9, 93, 115, 12, 162, 41, 191, 148, 208, 52, 81, 105, 252, 50, 69, 213, 87, 6, 111, 186, 89, 3, 90, 253, 142, 172, 207, 150, 229, 225, 25, 234, 95, 115, 169, 99, 97, 112, 95, 103, 114, 97, 110, 116, 131, 163, 116, 97, 103, 181, 122, 111, 109, 101, 45, 99, 97, 108, 108, 45, 115, 105, 103, 110, 105, 110, 103, 45, 107, 101, 121, 166, 97, 99, 99, 101, 115, 115, 129, 168, 65, 115, 115, 105, 103, 110, 101, 100, 130, 166, 115, 101, 99, 114, 101, 116, 196, 64, 171, 150, 216, 231, 39, 60, 42, 206, 28, 214, 108, 84, 154, 187, 143, 29, 242, 197, 58, 130, 239, 129, 143, 236, 62, 54, 198, 103, 32, 208, 10, 85, 31, 167, 200, 108, 13, 147, 126, 226, 188, 231, 244, 130, 39, 34, 239, 130, 204, 82, 80, 222, 252, 56, 184, 185, 187, 182, 208, 82, 26, 182, 47, 39, 169, 97, 115, 115, 105, 103, 110, 101, 101, 115, 196, 39, 132, 32, 36, 58, 173, 222, 100, 201, 175, 162, 27, 153, 28, 177, 67, 100, 223, 100, 21, 95, 111, 11, 218, 30, 147, 134, 62, 14, 75, 117, 201, 184, 169, 66, 64, 25, 234, 95, 115, 169, 102, 117, 110, 99, 116, 105, 111, 110, 115, 129, 1, 147, 146, 165, 111, 97, 115, 105, 115, 173, 99, 114, 101, 97, 116, 101, 95, 97, 118, 97, 116, 97, 114, 146, 165, 111, 97, 115, 105, 115, 170, 103, 101, 116, 95, 97, 118, 97, 116, 97, 114, 146, 165, 111, 97, 115, 105, 115, 173, 117, 112, 100, 97, 116, 101, 95, 97, 118, 97, 116, 97, 114

If I put this into https://kawanet.github.io/msgpack-lite/ I get:

{
  "id": 4,
  "type": "request",
  "data": {
    "type": "Buffer",
    "data": [
      130,
      164,
      116,
      121,
      112,
      101,
      186,
      103,
      114,
      97,
      110,
      116,
      95,
      122,
      111,
      109,
      101,
      95,
      99,
      97,
      108,
      108,
      95,
      99,
      97,
      112,
      97,
      98,
      105,
      108,
      105,
      116,
      121,
      164,
      100,
      97,
      116,
      97,
      130,
      167,
      99,
      101,
      108,
      108,
      95,
      105,
      100,
      146,
      196,
      39,
      132,
      45,
      36,
      52,
      178,
      72,
      194,
      86,
      91,
      147,
      92,
      3,
      211,
      194,
      101,
      246,
      126,
      36,
      18,
      223,
      129,
      25,
      183,
      10,
      64,
      18,
      138,
      185,
      129,
      33,
      7,
      76,
      154,
      47,
      64,
      16,
      175,
      249,
      46,
      196,
      39,
      132,
      32,
      36,
      168,
      250,
      9,
      93,
      115,
      12,
      162,
      41,
      191,
      148,
      208,
      52,
      81,
      105,
      252,
      50,
      69,
      213,
      87,
      6,
      111,
      186,
      89,
      3,
      90,
      253,
      142,
      172,
      207,
      150,
      229,
      225,
      25,
      234,
      95,
      115,
      169,
      99,
      97,
      112,
      95,
      103,
      114,
      97,
      110,
      116,
      131,
      163,
      116,
      97,
      103,
      181,
      122,
      111,
      109,
      101,
      45,
      99,
      97,
      108,
      108,
      45,
      115,
      105,
      103,
      110,
      105,
      110,
      103,
      45,
      107,
      101,
      121,
      166,
      97,
      99,
      99,
      101,
      115,
      115,
      129,
      168,
      65,
      115,
      115,
      105,
      103,
      110,
      101,
      100,
      130,
      166,
      115,
      101,
      99,
      114,
      101,
      116,
      196,
      64,
      171,
      150,
      216,
      231,
      39,
      60,
      42,
      206,
      28,
      214,
      108,
      84,
      154,
      187,
      143,
      29,
      242,
      197,
      58,
      130,
      239,
      129,
      143,
      236,
      62,
      54,
      198,
      103,
      32,
      208,
      10,
      85,
      31,
      167,
      200,
      108,
      13,
      147,
      126,
      226,
      188,
      231,
      244,
      130,
      39,
      34,
      239,
      130,
      204,
      82,
      80,
      222,
      252,
      56,
      184,
      185,
      187,
      182,
      208,
      82,
      26,
      182,
      47,
      39,
      169,
      97,
      115,
      115,
      105,
      103,
      110,
      101,
      101,
      115,
      196,
      39,
      132,
      32,
      36,
      58,
      173,
      222,
      100,
      201,
      175,
      162,
      27,
      153,
      28,
      177,
      67,
      100,
      223,
      100,
      21,
      95,
      111,
      11,
      218,
      30,
      147,
      134,
      62,
      14,
      75,
      117,
      201,
      184,
      169,
      66,
      64,
      25,
      234,
      95,
      115,
      169,
      102,
      117,
      110,
      99,
      116,
      105,
      111,
      110,
      115,
      129,
      1,
      147,
      146,
      165,
      111,
      97,
      115,
      105,
      115,
      173,
      99,
      114,
      101,
      97,
      116,
      101,
      95,
      97,
      118,
      97,
      116,
      97,
      114,
      146,
      165,
      111,
      97,
      115,
      105,
      115,
      170,
      103,
      101,
      116,
      95,
      97,
      118,
      97,
      116,
      97,
      114,
      146,
      165,
      111,
      97,
      115,
      105,
      115,
      173,
      117,
      112,
      100,
      97,
      116,
      101,
      95,
      97,
      118,
      97,
      116,
      97,
      114
    ]
  }
}

But I wasn't able to to decode the internal data using the same tool? I wanted to double check exactly what I was sending... any ideas how?

Or any clues of what the error message means? I am not sure what it means by sequence? Or what property/field it is referring to?

Any help would be really appreciated as always, thanks! :)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

You need to remove the formatting, line breaks and spaces and like this

[130,164,116,121,112,101,186,103,114,97,110,116,95,122,111,109,101,95,99,97,108,108,95,99,97,112,97,98,105,108,105,116,121,164,100,97,116,97,130,167,99,101,108,108,95,105,100,146,196,39,132,45,36,52,178,72,194,86,91,147,92,3,211,194,101,246,126,36,18,223,129,25,183,10,64,18,138,185,129,33,7,76,154,47,64,16,175,249,46,196,39,132,32,36,168,250,9,93,115,12,162,41,191,148,208,52,81,105,252,50,69,213,87,6,111,186,89,3,90,253,142,172,207,150,229,225,25,234,95,115,169,99,97,112,95,103,114,97,110,116,131,163,116,97,103,181,122,111,109,101,45,99,97,108,108,45,115,105,103,110,105,110,103,45,107,101,121,166,97,99,99,101,115,115,129,168,65,115,115,105,103,110,101,100,130,166,115,101,99,114,101,116,196,64,171,150,216,231,39,60,42,206,28,214,108,84,154,187,143,29,242,197,58,130,239,129,143,236,62,54,198,103,32,208,10,85,31,167,200,108,13,147,126,226,188,231,244,130,39,34,239,130,204,82,80,222,252,56,184,185,187,182,208,82,26,182,47,39,169,97,115,115,105,103,110,101,101,115,196,39,132,32,36,58,173,222,100,201,175,162,27,153,28,177,67,100,223,100,21,95,111,11,218,30,147,134,62,14,75,117,201,184,169,66,64,25,234,95,115,169,102,117,110,99,116,105,111,110,115,129,1,147,146,165,111,97,115,105,115,173,99,114,101,97,116,101,95,97,118,97,116,97,114,146,165,111,97,115,105,115,170,103,101,116,95,97,118,97,116,97,114,146,165,111,97,115,105,115,173,117,112,100,97,116,101,95,97,118,97,116,97,114]

it can be converted to

{
  "type": "grant_zome_call_capability",
  "data": {
    "cell_id": [
      {
        "type": "Buffer",
        "data": [
          132,
          45,
          36,
          52,
          178,
          72,
          194,
          86,
          91,
          147,
          92,
          3,
          211,
          194,
          101,
          246,
          126,
          36,
          18,
          223,
          129,
          25,
          183,
          10,
          64,
          18,
          138,
          185,
          129,
          33,
          7,
          76,
          154,
          47,
          64,
          16,
          175,
          249,
          46
        ]
      },
      {
        "type": "Buffer",
        "data": [
          132,
          32,
          36,
          168,
          250,
          9,
          93,
          115,
          12,
          162,
          41,
          191,
          148,
          208,
          52,
          81,
          105,
          252,
          50,
          69,
          213,
          87,
          6,
          111,
          186,
          89,
          3,
          90,
          253,
          142,
          172,
          207,
          150,
          229,
          225,
          25,
          234,
          95,
          115
        ]
      }
    ],
    "cap_grant": {
      "tag": "zome-call-signing-key",
      "access": {
        "Assigned": {
          "secret": {
            "type": "Buffer",
            "data": [
              171,
              150,
              216,
              231,
              39,
              60,
              42,
              206,
              28,
              214,
              108,
              84,
              154,
              187,
              143,
              29,
              242,
              197,
              58,
              130,
              239,
              129,
              143,
              236,
              62,
              54,
              198,
              103,
              32,
              208,
              10,
              85,
              31,
              167,
              200,
              108,
              13,
              147,
              126,
              226,
              188,
              231,
              244,
              130,
              39,
              34,
              239,
              130,
              204,
              82,
              80,
              222,
              252,
              56,
              184,
              185,
              187,
              182,
              208,
              82,
              26,
              182,
              47,
              39
            ]
          },
          "assignees": {
            "type": "Buffer",
            "data": [
              132,
              32,
              36,
              58,
              173,
              222,
              100,
              201,
              175,
              162,
              27,
              153,
              28,
              177,
              67,
              100,
              223,
              100,
              21,
              95,
              111,
              11,
              218,
              30,
              147,
              134,
              62,
              14,
              75,
              117,
              201,
              184,
              169,
              66,
              64,
              25,
              234,
              95,
              115
            ]
          }
        }
      },
      "functions": {
        "1": [
          [
            "oasis",
            "create_avatar"
          ],
          [
            "oasis",
            "get_avatar"
          ],
          [
            "oasis",
            "update_avatar"
          ]
        ]
      }
    }
  }
}

sequence as opposed to byte array means that MessagePack expects an array of items. I suppose that assignees is the problem, you're passing in a single value - signingKey. Try converting that to an array [signingKey].

from holochain.

dellams avatar dellams commented on June 12, 2024

Great thank you, that fixed it! :)

However, I was curious why it is an array when we only ever pass in one assignee? Is this for future use or something then?

Also, I noticed in your code and in the conductor api docs that you can also pass in Unrestricted or Transferable? But when I look in your code and see these are never used and only Assigned is used?

When I tried to use Transferable I get the following error:

error: deserialization: Bytes(Deserialize("array had incorrect length, expected 3"))

And the code I am using:

image

When I try to use Unrestricted like this:

image

I get the same error:

error: deserialization: Bytes(Deserialize("array had incorrect length, expected 3"))

When I try to use it like this:

image

I get a different error:

error: deserialization: Bytes(Deserialize("wrong msgpack marker FixStr(12)"))

If I try and use it like this:

image

I get the same error again:

error: deserialization: Bytes(Deserialize("wrong msgpack marker FixStr(12)"))

Any ideas how it should be used? Or maybe its because its not supported yet and why you are also not using these yet?

Many thanks,
David.

from holochain.

dellams avatar dellams commented on June 12, 2024

Another issue I am facing is with the "attach_app_interface" api call, I notice in the rust conductor api docs it says this needs to be a u16 but you are passing in a number for the port?

When I tried passing in port 65001 I got this error:

error: internal_error: Conductor returned an error while using a ConductorApi: InterfaceError(WebsocketError(Io(Os { code: 10013, kind: PermissionDenied, message: "An attempt was made to access a socket in a way forbidden by its access permissions." }))).'

When I don't pass in a port to allow the conductor to assign one for me I didn't get back the format I was expecting as dictated in the rust conductor api docs:

��type�response�id��data�+��type�app_interface_attached�data��port��
p_id�oasis-app�cell_info��oasis���provisioned��cell_id��'�-$4�H�V[�\���e�~$�߁��
@����!�L�/@���.�'� $����^gQ/����h�D��M�5|��4�ͨɓ�G�o;.�dna_modifiers��network_seed��properties����origin_time� ��dR�빬quantum_time��secs��,�nanos �name�oasis�status��running��agent_pub_key�'� $����^gQ/����h�D��M�5|��4�ͨɓ�G�o;.�manifest��manifest_version�1�name�oasis�description��roles���name�oasis�provisioning��strategy�create�deferred£dna��bundled�../dna/oasis.dna�modifiers��network_seed��properties��origin_time��quantum_time��installed_hash�5uhC0kNLJIwlZbk1wD08Jl9n4kEt-BGbcKQBKKuYEhB0yaL0AQr_ku�_version��clone_limit �errors��clone_limit         

Whereas the docs and your js code says it should only be expecting the port? Is the documentation out of date? Currently I am unable to decode the port that the conductor assigned because I cannot deserialize the response when I do not know the format it is expecting.

from holochain.

dellams avatar dellams commented on June 12, 2024

Hope you guys are having a great weekend?

I got a bit further and now I'm trying to make a zome call with the appagent connection after I attached the app interface but I am getting this error back:

error: deserialization: Bytes(Deserialize("invalid value: byte array, expected 64 bytes, got 128 bytes")).'

My code is below:

image

This is the raw data I am sending:

��id��type�request�data��낤type�call_zome�data��cell_id��'�-$4�H�V[�\���e�~$�߁��
@����!�L�/@���.�'� $,�t���v� �}<���{�
�FDV���ɢ��З��/dȩzome_name�oasis�fn_name�create_avatar�payload����cap_secret�@׵JY!�b̜�H��nMI��V��G�G��b��eԳ���w�߬@t�o��L��+ �1�����cڻ�+�nonce� �	1Wޞ�A�P�9q�ի�*��c_���T��r
?�expires_at�����,~�provenance�'� $�%����R?�`eO���������$	��=D6샤G�/dȩsignatureĀ�����^8�v��_&���g�na��iL�������l4KP�����)7F�������8�8��2�=�.E	���Z�&�^�����_��T%w��d�D��ɇK;�Yo�ʭ<�^F%A��B̶�u��T������.

131,162,105,100,1,164,116,121,112,101,167,114,101,113,117,101,115,116,164,100,97,116,97,197,1,235,130,164,116,121,112,101,169,99,97,108,108,95,122,111,109,101,164,100,97,116,97,137,167,99,101,108,108,95,105,100,146,196,39,132,45,36,52,178,72,194,86,91,147,92,3,211,194,101,246,126,36,18,223,129,25,183,10,64,18,138,185,129,33,7,76,154,47,64,16,175,249,46,196,39,132,32,36,149,145,220,120,71,38,142,135,91,119,105,249,173,112,170,94,103,64,176,206,102,125,23,178,9,120,149,83,252,163,81,60,199,135,43,161,169,122,111,109,101,95,110,97,109,101,165,111,97,115,105,115,167,102,110,95,110,97,109,101,173,99,114,101,97,116,101,95,97,118,97,116,97,114,167,112,97,121,108,111,97,100,196,1,192,170,99,97,112,95,115,101,99,114,101,116,196,64,221,52,65,88,141,91,250,11,109,41,252,67,27,167,188,237,91,206,250,74,206,78,219,149,23,243,222,61,49,8,5,109,143,233,16,113,140,2,73,30,97,209,172,7,225,228,126,208,97,20,58,172,223,255,222,92,160,51,136,95,119,226,45,106,165,110,111,110,99,101,196,32,25,42,127,184,49,120,32,39,98,145,66,9,9,254,150,41,206,1,14,199,204,56,218,101,118,116,150,232,97,34,107,30,170,101,120,112,105,114,101,115,95,97,116,207,226,199,195,71,128,20,227,170,112,114,111,118,101,110,97,110,99,101,196,39,132,32,36,102,114,74,145,161,215,28,16,25,149,97,111,41,80,109,177,100,206,179,197,72,82,185,232,170,14,51,252,48,35,248,190,199,135,43,161,169,115,105,103,110,97,116,117,114,101,196,128,97,237,8,34,47,209,149,154,69,234,84,212,12,183,9,110,190,4,252,9,244,185,69,45,153,110,97,18,44,101,204,104,204,60,204,76,153,170,119,170,4,164,137,114,189,158,28,20,161,50,147,211,103,109,241,109,112,180,196,23,254,184,16,8,123,51,18,72,224,60,166,120,57,233,56,188,203,94,235,140,31,65,120,210,208,32,65,167,109,185,67,189,63,146,207,91,7,135,196,98,112,55,193,17,14,121,175,142,237,152,253,190,167,90,242,107,247,7,236,193,225,87,120,139,197,121,88,152

If I put this into the tool https://kawanet.github.io/msgpack-lite/ I get this error:

Error: BUFFER_SHORTAGE

Any ideas? Any help would be greatly appreciated as always thanks! :)

from holochain.

dellams avatar dellams commented on June 12, 2024

I also noticed in your js client example on the homepage you are setting provenance to the AgentPubKey instead of the generated signing key? My code use to do the same but somewhere else in your code I am sure you are using the signing key also? And this is why I changed mine to match....

from holochain.

dellams avatar dellams commented on June 12, 2024

Afternoon,

Hope you all had a good weekend? :)

How do you start the conductor on a given admin port for both the hc.exe tool and the holochain.exe?

I know with the hc.exe tool you can use:

hc sandbox generate workdir/happ --run=8888

To run the app on a given port but how to you set the admin port?

Also, how do you get lairkeystone to stop asking for a passphrase when it starts? HoloNET auto starts and shutsdown the conductor and this use to work fine until the lairkeystone started asking for a passphrase forcing the user to press enter to accept the default one. I need this step to be by-passed somehow? Thanks.

Many thanks,
David.

from holochain.

jost-s avatar jost-s commented on June 12, 2024

However, I was curious why it is an array when we only ever pass in one assignee? Is this for future use or something then?

Zome call capabilities are the permission feature in Holochain. It's not only to allow a key associated to an agent pub key for signing its zome calls on its behalf, but can also be used to allow other agents to call functions on the agent's cell. It can be allowed for one or multiple agents. Hence the array.

When I tried to use Transferable I get the following error:

Transferable works like Assigned without the assignees, as I've just confirmed. So please show me the implementations of CapGrantAccessTransferable and CapGrantAccessAssigned.

When I try to use Unrestricted like this:
I get a different error:
Any ideas how it should be used? Or maybe its because its not supported yet and why you are also not using these yet?>

Yes, I confirm there's a bug in the JS client and it's supposed to be:

...
  access: {
    Unrestricted: null
  }
...

from holochain.

jost-s avatar jost-s commented on June 12, 2024

Another issue I am facing is with the "attach_app_interface" api call, I notice in the rust conductor api docs it says this needs to be a u16 but you are passing in a number for the port?

number is the only number primitive there is in JS, for float and integer.

When I tried passing in port 65001 I got this error:

error: internal_error: Conductor returned an error while using a ConductorApi: InterfaceError(WebsocketError(Io(Os { code: 10013, kind: PermissionDenied, message: "An attempt was made to access a socket in a way forbidden by its access permissions." }))).'

Please show me the call. When I run adminWebsocket.attachAppInterface({ port: 65001 }) I get

{ port: 65001 }

as response.

When I don't pass in a port to allow the conductor to assign one for me I didn't get back the format I was expecting as dictated in the rust conductor api docs:

Without the port passed in, I also get

{ port: 52560 }

Whereas the docs and your js code says it should only be expecting the port? Is the documentation out of date?

It's returning a struct/object with port as the only field (https://docs.rs/holochain_conductor_api/latest/holochain_conductor_api/enum.AdminResponse.html#variant.AppInterfaceAttached)

from holochain.

jost-s avatar jost-s commented on June 12, 2024

I got a bit further and now I'm trying to make a zome call with the appagent connection after I attached the app interface but I am getting this error back:

error: deserialization: Bytes(Deserialize("invalid value: byte array, expected 64 bytes, got 128 bytes")).'

I suppose that the signature is made up of the signature and something else, the public key perhaps? I can't make out which library you're using for signing, it will tell you what the signature is composed of. Probably taking the first 64 bytes of the signature will solve the issue.

from holochain.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.