Skip to content

Bindings source code conventions

eloylp edited this page Mar 22, 2023 · 11 revisions

When modifying bindings source code, please stick to the following conventions.

Use simple tuple structs:

This:

pub struct ZcashDiversifier(Diversifier);

NOT this:

pub struct ZcashDiversifier {
    inner: Diversifier,
}

Import and use name directly

When possible (no type names clashing):

use zcash_primitives::legacy::TransparentAddress;

// ...

pub struct ZcashTransparentAddress(TransparentAddress);

Instead of using longer paths like:

pub struct ZcashTransparentAddress(zcash_primitives::legacy::TransparentAddress);

Constructors

Default constructors

Constructor named new in librustzcash - simply use a constructor in .udl.

Named constructors

Constructor with a name different than new use Name={constructor name} attribute in .udl:

interface ZcashOrchardDiversifier {
  [Name=from_bytes, Throws=ZcashError]
  constructor(sequence<u8> bytes);
};

Unless it's the only constructor - then you may consider simply using new in bindings Rust code and constructor without Name attribute in .udl.

Constructors returning optional

Constructors returning Option - use Result in bindings and ZcashError in .udl (chose or create a new error that seems the most appropriate) - uniffi-rs does not support constructors returning optional types.

interface ZcashAccountPrivKey {

// ...

  [Name=from_bytes, Throws=ZcashError]
  constructor(sequence<u8> data);

Methods returning tuples

We cannot return tuples in UDL files. The workaround for representing this case is to return a struct (dictionary in the UDL file) that will hold the same elements as the original tuple. As a convention, the name of the struct will be ZcashElement1Element2. Lets see an example:

Original API

pub fn ovks_for_shielding(&self) -> (InternalOvk, ExternalOvk) {
//...
}

UDL file

interface ZcashAccountPubKey {
  // ...
  ZcashInternalOvkExternalOvk ovks_for_shielding();sssss
  // ...
};

dictionary ZcashInternalOvkExternalOvk {
    ZcashInternalOvk internal_ovk;
    ZcashExternalOvk external_ovk;
};

Our Rust wrapper API

pub fn ovks_for_shielding(&self) -> ZcashInternalOvkExternalOvk {
//...
}

pub struct ZcashInternalOvkExternalOvk {
    pub internal_ovk: Arc<ZcashInternalOvk>,
    pub external_ovk: Arc<ZcashExternalOvk>,
}

impl From<(InternalOvk, ExternalOvk)> for ZcashInternalOvkExternalOvk {
    fn from((internal_ovk, external_ovk): (InternalOvk, ExternalOvk)) -> Self {
        ZcashInternalOvkExternalOvk {
            internal_ovk: Arc::new(internal_ovk.into()),
            external_ovk: Arc::new(external_ovk.into()),
        }
    }
}

Conversions using From traits

Sometimes conversions among different objects are being done by implementing the From trait. But this is not available for users of other programming languages. Whenever we want to explicitly expose this behaviour to the user, we will extend the API creeating methods like to_*() which might make use of the From traits under the hood.