hyperledger/indy-shared-rs

Python wrapper verification error when there are multiple revocation registry entries

conanoc opened this issue · 2 comments

This is the reason for the error openwallet-foundation/acapy#2036

The problem is here:
https://github.com/hyperledger/indy-shared-rs/blob/v0.3.1/wrappers/python/indy_credx/types.py#L457-L464
스크린샷 2023-01-12 오후 8 37 50

The RevocationRegistry created at L459 will be deallocated outside the for loop, but it's handle entry.handle at L462 should be retained by the reg_entries list.
This would work if the RevocationEntry at L461 is not Structure type. RevocationEntry subclasses Structure of ctypes and this makes entry.handle deallocated before bindings.verify_presentation() starts.
I could make it work by retaining the RevocationRegistry created at L459 in a dummy list to prevent deallocation, which also prevents the deallocation of its handle.

You may wonder why it works when there is only one revocation entry. I suppose it's dependent on the GC behavior of python.
I created a sample code to see this issue.

from ctypes import c_int64, Structure, c_char_p

class Age(c_int64):
    def __del__(self):
        print(f"Age: delete called for {self.value}")

class Applicant:
    def __init__(self, name, age = None):
        self.name = name
        if not age:
            age = Age(0)
        self.age = age

    def __del__(self):
        print(f"Applicant: delete called for {self.name}")

class Candidate(Structure):
    _fields_ = [("name", c_char_p), ("age", Age)]

    @classmethod
    def create(
        cls,
        name,
        age,
    ):
        return Candidate(name=c_char_p(name.encode("utf-8")), age=age)

def print_candidates(applicants):
    candidates = []
    for applicant in applicants:
        if not isinstance(applicant, Applicant):
            applicant = Applicant(applicant)
        print("== appending candidate: ", applicant.name)
        candidates.append(Candidate.create(applicant.name, applicant.age))  # <-- applicant.age is not retained by this because Candidate is a Struct.

    print("== print candidates ==")
    for candidate in candidates:
        print(candidate.name)

print("== print multiple candidates ==")
applicants = [
    "Alice",
    Applicant("Bob", Age(30)),
]

print_candidates(applicants)

print("== print a single candidate ==")
applicants = [
    "Alice",
]

print_candidates(applicants)

You can find that the Age instance, which corresponds to ObjectHandle, is deallocated before printing the candidates when there is more than one entry.

This is the output of the sample code above:

== print multiple candidates ==
== appending candidate:  Alice
Applicant: delete called for Alice
Age: delete called for 0
== appending candidate:  Bob
== print candidate ==
b'Alice'
b'Bob'
== print a single candidate ==
Applicant: delete called for Bob
Age: delete called for 30
== appending candidate:  Alice
== print candidate ==
b'Alice'
Applicant: delete called for Alice
Age: delete called for 0

Heads up on this issue @andrewwhitehead @TimoGlastra @blu3beri and @whalelephant. This will need to be looked at in the anoncreds-rs for sure, perhaps retrofitted here as well.

Thanks for diving into this @conanoc .