Open bodily11 opened 2 years ago
I'm guessing no as when the signing occurs here:
https://github.com/rocklabs-io/ic-py/blob/7754db0ada112a409ec50221e85c6cbed0ab44c5/ic/agent.py#L15
In the envelope there is no option for sender_delegation, as defined here: https://smartcontracts.org/docs/current/references/ic-interface-spec/#authentication
Any plans to add sender_delegation?
Try to support delegation in #59. It is just a simple implementation.
You can switch to that branch and test. Using the ic_delegation and ic_identity in the local storage can construct an II delegation identity.
from ic.Identity import DelegationIdentity
from ic.Agent import Agent
from ic.Client import Client
# delegation and identity get from browser local storage
ic_delegation = """
{
"delegations": [
{
"delegation": {
"expiration": "xxx",
"pubkey": "xxx"
},
"signature": "xxx"
}
],
"publicKey": "xxx"
}
"""
ic_identity = """
[
"xxx",
"xxx"
]
"""
client = Client()
iden = DelegateIdentity.from_json(ic_identity, ic_delegation)
print('principal:', Principal.self_authenticating(iden.der_pubkey))
ag = Agent(iden, client)
# now can use the agent to do query or update call
Remember that delegation identity from II has an expiry, thus it can't be used forever.
Oh nice! This is awesome! I'll test this out this week. And yeah, I know it has an expiry, but I'm going to use the II backend to actually add my script as a new device so then I have permanent authenticated access. I had to figure out authenticated/delegated II calls first though as you need those to register a new device.
All of my testing appears to be working on the delegation branch. Loading in a delegated identity using the information from II in the browser works great.
I just tried to pull some data from a different canister using the same feat_delegation branch I used for my other testing, and it gave me an error reading the data using the candid. When I switched back to main it worked again, so something is probably still off on the feat_delegation branch. Maybe you have tests you can run to spruce it up before merging those changes in to main. 😀 Just figured I would let you know.
Can you paste your error here? Some snippets that can reproduce it would be great!
Yeah, here's an error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_39454/2771234657.py in <module>
22 canister = Canister(agent=agent, canister_id=canister_id, candid=canister_did)
23
---> 24 result = canister.getCollectionDump()
/opt/anaconda3/lib/python3.9/site-packages/ic/canister.py in __call__(self, *args, **kwargs)
55 effective_cansiter_id = args[0]['canister_id'] if self.canister_id == 'aaaaa-aa' and len(args) > 0 and type(args[0]) == dict and 'canister_id' in args[0] else self.canister_id
56 if self.anno == 'query':
---> 57 res = self.agent.query_raw(
58 self.canister_id,
59 self.name,
/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in query_raw(self, canister_id, method_name, arg, return_type, effective_canister_id)
77 raise ValueError("Malformed result: " + str(result))
78 if result['status'] == 'replied':
---> 79 return decode(result['reply']['arg'], return_type)
80 elif result['status'] == 'rejected':
81 return result['reject_message']
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in decode(data, retTypes)
1293 for i, t in enumerate(types if retTypes == None else retTypes):
1294 outputs.append({
-> 1295 'type': t.name,
1296 'value': t.decodeValue(b, types[i])
1297 })
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in name(self)
599 @property
600 def name(self) -> str:
--> 601 return 'opt ({})'.format(str(self._type.name))
602
603 @property
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in name(self)
555 @property
556 def name(self) -> str:
--> 557 return 'vec ({})'.format(str(self._type.name))
558
559 @property
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in name(self)
677 @property
678 def name(self) -> str:
--> 679 fields = ";".join(map(lambda kv: kv[0] + ":" + kv[1].name, self._fields.items()))
680 return "record {{{}}}".format(fields)
681
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in <lambda>(kv)
677 @property
678 def name(self) -> str:
--> 679 fields = ";".join(map(lambda kv: kv[0] + ":" + kv[1].name, self._fields.items()))
680 return "record {{{}}}".format(fields)
681
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in name(self)
555 @property
556 def name(self) -> str:
--> 557 return 'vec ({})'.format(str(self._type.name))
558
559 @property
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in name(self)
677 @property
678 def name(self) -> str:
--> 679 fields = ";".join(map(lambda kv: kv[0] + ":" + kv[1].name, self._fields.items()))
680 return "record {{{}}}".format(fields)
681
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in <lambda>(kv)
677 @property
678 def name(self) -> str:
--> 679 fields = ";".join(map(lambda kv: kv[0] + ":" + kv[1].name, self._fields.items()))
680 return "record {{{}}}".format(fields)
681
TypeError: can only concatenate str (not "int") to str
And this happens when I call a few methods. Let me find you a public method to call to reproduce the error.
Here's a public reproducible error for you:
# governance_did can be downloaded from canlista here:
# https://k7gat-daaaa-aaaae-qaahq-cai.ic0.app/listing/nns-governance-10222/qoctq-giaaa-aaaaa-aaaea-cai
governance_canister_id = 'rrkah-fqaaa-aaaaa-aaaaq-cai'
i1 = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(i1, client)
testCanister = Canister(agent=agent, canister_id=governance_canister_id, candid=governance_did)
testCanister.get_monthly_node_provider_rewards()
Just as a follow-up, this code works fine on the main branch, but breaks with the error above on the feat-delegation branch. Maybe due to the type table parsing fix you pull into it to fix the other bug.
Should be fixed now. Thanks!
Yeah perfect. That solved the issue. Running into one more issue.
This is the governance canister (same .did as above)
governanceCanister.manage_neuron(
{
'id':[{
'id':all_icp_neurons[0]
}],
'command':[{'MergeMaturity':{
'percentage_to_merge':1
}
}],
'neuron_id_or_subaccount':[]
})
In this case, I get the following error:
TypeError Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_41783/2057413411.py in <module>
----> 1 governanceCanister.manage_neuron(
2 {
3 'id':[{
4 'id':all_icp_neurons[0]
5 }],
/opt/anaconda3/lib/python3.9/site-packages/ic/canister.py in __call__(self, *args, **kwargs)
66 self.canister_id,
67 self.name,
---> 68 encode(arguments),
69 self.rets,
70 effective_cansiter_id
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in encode(params)
1258 for i in range(len(args)):
1259 t = argTypes[i]
-> 1260 if not t.covariant(args[i]):
1261 raise TypeError("Invalid {} argument: {}".format(t.display(), str(args[i])))
1262 vals += t.encodeValue(args[i])
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in covariant(self, x)
835
836 def covariant(self, x):
--> 837 return self._type if self._type.covariant(x) else False
838
839 def encodeValue(self, val):
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in covariant(self, x)
630 if not k in x:
631 raise ValueError("Record is missing key {}".format(k))
--> 632 if v.covariant(x[k]):
633 continue
634 else:
/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in covariant(self, x)
571
572 def covariant(self, x):
--> 573 return type(x) == list and (len(x) == 0 | (len(x) == 1 and self._type.covariant(x[0])))
574
575 def encodeValue(self, val):
TypeError: unsupported operand type(s) for |: 'int' and 'RecordClass'
Usually if I have a formatting error between types/values in candid I get a record formatting error, so it seems my values match the candid. But for some reason it is failing to correctly encode the data using the candid. So I thought maybe an ic-py issue?
Fixed! Thank you very much for the detail.
I think we should test it more carefully and thoroughly.
Yeah, that fixes it. Thanks! Everything seems to be working now on feat-delegation branch. I'm able to authenticate with II across apps and automate process. So I'm in really good shape. Thanks for all the help.
When are you thinking about merging this branch into production? Would love to get it in sooner rather than later (assuming all bugs have been fixed) to prevent too many conflicts from the main repo.
Would love to be able to pass in a delegation as a parameter to Identity or Canister or query_raw (or somewhere else) to be able to call on behalf of delegated principal. Is this possible right now?