Skip to main content

Self Register Contracts

DEVELOPMENT

This functionality is in development

The self-registration convention describes a process in which a smart contract stores (=registers) a claim issued by itself that contains the required information for someone to reconstruct the virtual file system (VFS) that is required by the smart contract in order to be invoked within Coinweb's VM.

Self Register Kit

In order to simplify the process of contract registration we utilise a SDK that provides some preset methods for the self-registration process. As mentioned in the general smart contract registration this is not a restriction nor is it mandatory. Everybody can implement their own way of loading smart contracts. In fact @coinweb/self-register can entirely be reverse engineered utilising @coinweb/contract-kit.

The self-registration system is a mechanism implemented partly by the smart contract itself, and partly by the @coinweb/cweb-tool when deploying smart contracts to Coinweb.

In the self-registration system, the smart contract is expected to have a method handler for "SELF-REGISTER" that creates a transaction, which in turn creates a claim, that can be used to create the VFS for itself.

Registration StoreOp

The following code reassembles the sequence to register a claim with a StoreOp. In the following description will be an explanation of how the virtual file system is reconstructed by linking file contents via the @coinweb/data-hasher smart contract.

export type RegisterEntry = [FileDescription, SingleClaimRead][];

export type SingleClaimRead = {
Claim: { issuer: ClaimIssuer; key: GenericClaimKey };
};

export function dataHasherContractId(): HashId {
return '0xa59bfe9f094338753e18fe1ddf35ea3359a8a97f75c24418b1822c69770337ea';
}

export function dataHasherContract(): ClaimIssuer {
return contractIssuer(dataHasherContractId());
}

export function genericClaim(
key: GenericClaimKey,
body: OrdJson | null,
fees: string
): GenericClaim {
return { key, body, fees_stored: fees };
}

export function claimKey(
firstPart: OrdJson | null,
secondPart: OrdJson | null
): GenericClaimKey {
return { first_part, second_part };
}

export function selfRegisterKey(): GenericClaimKey {
return claimKey('RegistrationV0', null);
}

export function getRegisterEntry(
hasherIssuer: ClaimIssuer,
hashes: (readonly [FileDescription, HashId])[]
): [FileDescription, SingleClaimRead][] {
return hashes.map(([path, hash]) => {
return [path, readClaim(hasherIssuer, claimKey(hash, null))];
});
}

export function registerClaimTx(
issuer: ClaimIssuer,
registerEntry: RegisterEntry
): NewTx {
return continueTx([
store(genericClaim(selfRegisterKey(), registerEntry, toHex(0))),
passCwebFrom(issuer, 200),
]);
}

export function callDataHasher(
issuer: ClaimIssuer,
files: Base64String[]
): NewTx {
return continueTx([
call(3, dataHasherContractId()),
dataUnverified([DEFAULT_HANDLER_NAME, files]),
passCwebFrom(issuer, 100 * files.length + 500),
dataUnverified(false),
]);
}

export function selfRegisterHandler(
contextTx: TxContext,
_callInfo: CallContext
): NewTx[] {
const options = selfRegisterOptions(contextTx);
const contractId = getContractId(contextTx);
const self = contractIssuer(contractId);
const fileContents = getExplicitFiles(options).map(([_, content]) =>
uint8ToBase64(content)
);
const hashes = getExplicitFiles('ExplicitAll').map(
([path, content]) => [path, hashV0(content)] as const
);
return [
callDataHasher(self, fileContents),
registerClaimTx(self, getRegisterEntry(dataHasherContract(), hashes)),
];
}

function logic(contextTx: TxContext): NewTx[] {
// this function includes the actual smart contract logic
}

export function cwebMain() {
// handler for the actual smart contract logic
addDefaultMethodHandler(logic);
// handler for the self-registration of the smart contract
addMethodHandler(SELF_REGISTER_HANDLER_NAME, selfRegisterHandler);
executeHandler();
}

When the SELF-REGISTER convention is applied to a contract (namely by adding a method handler for the SELF_REGISTER_HANDLER_NAME), the selfRegisterHandler will create a transaction and write a claim to the database. This claim will ultimately be used to construct the virtual file system for the given smart contract.

Files and the DataHasher

From the described StoreOp in the code extract it is evident, that the type RegisterEntry contains a SingleClaimRead instead of the actual file contents. In this context, the files aren't physically written to the database. Instead, another claim reference is supplied, which consists of an array containing both a FileDescription and a SingleClaimRead.

In the scenario of self-registration, an additional smart contract is introduced, called @coinweb/data-hasher. The data-hasher's utility is to create a hash for each file, store the file in the database and link the file description to the created hash. You can read about the data-hasher contract here.

Both callDataHasher and registerClaimTx called within the selfRegisterHandler return a continuation transaction (typed under NewTx). These transactions, and therefore their generated operations, will be executed in parallel. Both transactions must finish before the smart contract can be called, otherwise for example the file contents may not have been written to the database yet.

In the scope of the selfRegisterHandler the function callDataHasher is supplied with the contract's file contents. This helper creates the CallOp addressing the dataHasherContractId, provided with DataOp slices, which in turn carry the actual file contents, that will be saved and referenced in the data base.

The second continuation transaction in the selfRegisterHandler is the claim registration. In the self-registration convention this will be the step in which the instructions to reconstruct the virtual file system of the smart contract is provided to the claim's StoreOp. Precisely the getRegisterEntry will build the needed array of tuples including ReadOps for the file contents via the data-hasher contract by providing the file hashes. Calling the registered smart contract will result in a reconstruction of the virtual file system by file hashIds and the related ReadOps via @coinweb/data-hasher from the database in order to execute the contract.

Examples

VFS from Read Operation

Universally when a layer 2 transaction appears AND the transaction includes a CallOp the Coinweb computer tries to find and invoke the provided smart contract identity. In the example below the (arbitrary) CallOp also embeds a ReadOp that reads a stored claim CLA (in this case the self-registration claim).

ditaa

This claim CLA has first_key = RegistrationV0 and is a 2-level list of claims that must be read to construct the VFS. If there was another claim that could reconstruct the virtual files system for this smart contract it would be fine to instead use that claim, or even inline the required data.

info

There is some resemblance here to operating systems using dynamic loaders. For example in Linux, when executing a program, the real program executed is the dynamic linker which can be /lib/ld-linux.so, /lib/ld-musl-aarch64.so.1, or /lib/ld-linux-aarch64.so.1. It is this program that is responsible for loading required dynamic libraries into memory, not the kernel.

If you wanted to, you could write your own dynamic linker that did this in a different manner and still be compatible with the Linux kernel.

The self_registration claim is similar to the dynamic loader information in an executable, instructing the dynamic loader what to load, and where to find, libraries that the executable needs.

VFS support within the Call operation

The Call operation in the example below has an embedded Read operation. There is dedicated support in the Coinweb VM for efficient (re)construction of a VFS by doing two-level reads. In the figure below, the Call operation indicates that the VFS should be read from claim X which in turn reads claims A, B, and C, which in turn read claims A1..C3 by addressing the data-hasher smart contract. These ultimately are pointing to the file contents needed to construct the VFS within the Coinweb VM for the provided smart contract identity.

ditaa