rocklabs-io / ic-py

Python Agent Library for the DFINITY Internet Computer
MIT License
125 stars 25 forks source link

Does this Python agent support multiple variants in the same request? #60

Closed bodily11 closed 2 years ago

bodily11 commented 2 years ago

I'm getting some type errors when using multiple variants in the same request. For example, I have

key_type : variant { platform; seed_phrase; cross_platform; unknown };
purpose : variant { authentication; recovery };

But in the error, it returns the type table, and I only have one type definition that is variant:

type table1 = variant { 1_744_417_459; 3_053_362_247; 3_484_244_626; 3_782_943_626 }

Notice how in the first case, I have two different types of variants (length 4 and length 2 with different values), but in the error, it groups them into a common type of table1, with length 4.

I would have expected two different types for the two different variants, but this is not the case.

Is this user error on my part in defining types incorrectly? Or is there something weird going on with parsing the variant types? Thank you!

Myse1f commented 2 years ago

Can you provide a simple test that can produce the error here?

bodily11 commented 2 years ago

This isn't necessarily a simple example, but it is the one I'm working on, so relevant and easy to come by.

canister_did = '''
type verification_code = text;
type maxTimeToLive = opt nat64;
type UserNumber = nat64;
type PublicKey = blob;
type CredentialId = blob;
type DeviceKey = PublicKey;
type UserKey = PublicKey;
type SessionKey = PublicKey;
type FrontendHostname = text;
type Timestamp = nat64;

type HeaderField = record { text; text; };
type request = HttpRequest;
type HttpRequest = record {
  method: text;
  url: text;
  headers: vec HeaderField;
  body: blob;
};

type HttpResponse = record {
  status_code: nat16;
  headers: vec HeaderField;
  body: blob;
  streaming_strategy: opt StreamingStrategy;
};

type StreamingCallbackHttpResponse = record {
  body: blob;
  token: opt Token;
};

type Token = record {};

type StreamingStrategy = variant {
  Callback: record {
    callback: func (Token) -> (StreamingCallbackHttpResponse) query;
    token: Token;
  };
};

type Purpose = variant {
    recovery;
    authentication;
};

type KeyType = variant {
    unknown;
    platform;
    cross_platform;
    seed_phrase;
};

type Challenge = record {
    png_base64: text;
    challenge_key: ChallengeKey;
};

type DeviceData = record {
  pubkey : DeviceKey;
  alias : text;
  credential_id : opt CredentialId;
  purpose: Purpose;
  key_type: KeyType;
};

type RegisterResponse = variant {
  // A new user was successfully registered.
  registered: record { user_number: UserNumber; };
  // No more registrations are possible in this instance of the II service canister.
  canister_full;
  // The challenge was not successful.
  bad_challenge;
};

type AddTentativeDeviceResponse = variant {
  // The device was tentatively added.
  added_tentatively: record { verification_code: text; device_registration_timeout: Timestamp;};
  // Device registration mode is off, either due to timeout or because it was never enabled.
  device_registration_mode_off;
  // There is another device already added tentatively
  another_device_tentatively_added;
};

type VerifyTentativeDeviceResponse = variant {
  // The device was successfully verified.
  verified;
  // Wrong verification code entered. Retry with correct code.
  wrong_code: record { retries_left: nat8};
  // Device registration mode is off, either due to timeout or because it was never enabled.
  device_registration_mode_off;
  // There is no tentative device to be verified.
  no_device_to_verify;
};

type Delegation = record {
  pubkey: PublicKey;
  expiration: Timestamp;
  targets: opt vec principal;
};

type SignedDelegation = record {
  delegation: Delegation;
  signature: blob;
};

type GetDelegationResponse = variant {
  // The signed delegation was successfully retrieved.
  signed_delegation: SignedDelegation;

  // The signature is not ready. Maybe retry by calling `prepare_delegation`
  no_such_delegation
};

type InternetIdentityStats = record {
  users_registered: nat64;
  assigned_user_number_range: record { nat64; nat64; };
};

type InternetIdentityInit = record {
  assigned_user_number_range : record { nat64; nat64; };
};

type ChallengeKey = text;

type ChallengeResult = record {
    key : ChallengeKey;
    chars : text;
};

type DeviceRegistrationInfo = record {
    tentative_device : opt DeviceData;
    expiration: Timestamp;
};

type IdentityAnchorInfo = record {
    devices : vec DeviceData;
    device_registration: opt DeviceRegistrationInfo;
};

service : {
  init_salt: () -> ();
  create_challenge : () -> (Challenge);
  register : (DeviceData, ChallengeResult) -> (RegisterResponse);
  add : (UserNumber, DeviceData) -> ();
  remove : (UserNumber, DeviceKey) -> ();
  lookup : (UserNumber) -> (vec DeviceData) query;
  get_anchor_info : (UserNumber) -> (IdentityAnchorInfo);
  get_principal : (UserNumber, FrontendHostname) -> (principal) query;
  stats : () -> (InternetIdentityStats) query;
  enter_device_registration_mode : (UserNumber) -> (Timestamp);
  exit_device_registration_mode : (UserNumber) -> ();
  add_tentative_device : (UserNumber, DeviceData) -> (AddTentativeDeviceResponse);
  verify_tentative_device : (UserNumber, verification_code) -> (VerifyTentativeDeviceResponse);
  prepare_delegation : (UserNumber, FrontendHostname, SessionKey, maxTimeToLive) -> (UserKey, Timestamp);
  get_delegation: (UserNumber, FrontendHostname, SessionKey, Timestamp) -> (GetDelegationResponse) query;
  http_request: (request) -> (HttpResponse) query;
}
'''

import pandas as pd
import numpy as np
import requests as r
import datetime
import time
import json
import os

from ic.canister import Canister
from ic.client import Client
from ic.agent import Agent
from ic import Principal
from ic.candid import encode, decode, Types
from ic.identity import Identity

i1 = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(i1, client)
canister_id = 'rdmx6-jaaaa-aaaaa-aaadq-cai' #identity canister

my_canister = Canister(agent=agent, canister_id=canister_id, candid=canister_did)

my_canister.add_tentative_device(102634,
    {
      'pubkey':new_pub_key,
      'alias':'example_alias',
      'credential_id':[[]],
      'purpose':{'authentication':None},
      'key_type':{'unknown':None}
  }
)

Technically this request won't work normally (unless your II is in device registration mode), but I'm getting type errors right now, so I don't even get an error about running the code.

Myse1f commented 2 years ago

There is a bug in building type tables. I think it should be fixed in #61 and I have merged it to delegation branch.

bodily11 commented 2 years ago

Awesome. Let me try it out.

bodily11 commented 2 years ago

Yep, that did the trick. Thank you! Building type tables appears to be resolved in #61, and the delegation branch works great. Was able to import a delegated identity (using II info from local storage in the browser) and everything worked