Initial Asset Registration¶
Generating an Integrity Proof¶
One of the key advantages of using the Capture SDK to register an asset is the full control it offers over the metadata associated with the asset.
The Capture API supports the registration of assets in any format, including images, videos, audio files, text files, or other file types. To prepare an asset for registration, you need to generate the following metadata:
- proof_hash: The SHA-256 hash of the asset.
- asset_mime_type: The MIME type of the asset (e.g.,
image/jpeg
). - created_at: The UNIX timestamp representing the asset's creation time.
Using this information, you can create a minimal Integrity Proof JSON file, as shown below:
{
"proof_hash": "5c1234ef67g8h901i2j3k4l56m7890nop1q2r3s4t5u6v7w8x9y0zab1c2d3e4f5",
"asset_mime_type": "image/png",
"created_at": 1693309348713
}
For adding more information in the Integrity Proof, please refer to the Integrity Cid guide for details and examples.
Creating an Asset Signature¶
Adding signatures¶
To safeguard asset integrity in the Capture system, Capture employs client-side digital signatures based on Ethereum's EIP-191 standard. This approach guarantees that asset metadata remains in a verifiable and secure state. When you're registering an asset, enhancing its authenticity can be achieved by including two optional JSON string fields: signed_metadata
and signature
. Hereβs how they work:
The Structure of signature
The signature
field should contain the actual digital signature. An example format is as follows:
[
{
"proofHash": "78b67e15077577a9b25393f13bfccde3f94f990cf2a7bb0584a51f09196abbbf",
"provider": "ExampleCaptureSignatureProvider",
"signature": "0xf64d0ea52564dd442bbea7f212dbf2fe464fd5674ffd9b68593b3e757c14873c5136afb4394aa5b4ef529ecb693aae84f827af1ce8ddb7538d46432022f8d1771c",
"publicKey": "0x114e5E0dce98180c98ADC62E9Ac6Ea7184417ecd",
"integritySha": "78e11c61f90ba0c52e9f10e4d829c09e1a0aeffbcf77ae940836c494cdb5ac8b"
}
]
Field Name | Description |
---|---|
proofHash | The SHA-256 hash of the asset. |
provider | Signature provider's name. |
signature | The signature generated by EIP-191 standard. |
publicKey | The Ethereum wallet address used for signing, equivalent to the Signature Wallet address. |
integritySha | The SHA-256 hash of signed_metadata. |
You can generate a signature using the example JavaScript code provided below. Before running the code, ensure you have installed the required packages by executing the following command:
npm install @numbersprotocol/nit ethers mime dotenv
Before running the script to create an Asset Signature from your asset file, ensure you have a .env
file set up with your Signature Wallet private key. Below is an example .env
file configuration:
SIGNATURE_WALLET_PRIVATE_KEY=your_private_key_here
Important Security Notes:¶
- Protect Your Private Key: Safeguarding your private key is critical. Never share it with others or store it in unsecured locations.
- Review Code Carefully: It is strongly advised to thoroughly review any code that interacts with your private key before executing it, to avoid potential security risks.
Alternative Method:¶
You can also create an Asset Signature using MetaMask without needing a .env
file. A guide for this method will be available in the future.
Example: Creating an Asset Signature Using Node.js¶
Below is an example of how to use Node.js to generate an Asset Signature with your Signature Wallet private key stored in a .env
file:
import crypto from "crypto";
import { promises as fs } from "fs";
import * as nit from "@numbersprotocol/nit";
import { ethers } from "ethers";
import dotenv from "dotenv";
import mime from "mime";
dotenv.config();
async function calculateSHA256(file) {
const data = await fs.readFile(file);
const hash = crypto.createHash("sha256");
hash.update(data);
return hash.digest("hex");
}
async function generateIntegritySha(proofMetadata) {
// Create a JSON string of the proofMetadata
const data = JSON.stringify(proofMetadata, null, 2);
// Calculate its SHA-256 hash using getIntegrityHash
const dataBytes = ethers.toUtf8Bytes(data);
const integritySha = await nit.getIntegrityHash(dataBytes);
return integritySha;
}
async function main() {
const filename = "example.png"; // Replace with the path to the file you want to sign
const privateKey = process.env.SIGNATURE_WALLET_PRIVATE_KEY;
if (!privateKey) {
console.error(
"SIGNATURE_WALLET_PRIVATE_KEY environment variable is not set."
);
process.exit(1);
}
const proofHash = await calculateSHA256(filename);
const integrityProof = {
proof_hash: proofHash,
asset_mime_type: mime.getType(filename) || "application/octet-stream",
created_at: Math.floor(Date.now() / 1000),
};
const integrityProofJson = JSON.stringify(integrityProof, null, 2);
console.log("integrityProof JSON string", integrityProofJson);
// Generate the integritySha from the Integrity Proof
const integritySha = await generateIntegritySha(integrityProof);
// Create the Asset Signature from Integrity Proof
const signer = new ethers.Wallet(privateKey);
const publicKey = await signer.getAddress();
const signature = await nit.signIntegrityHash(integritySha, signer);
const signatureJSON = JSON.stringify({
proofHash,
provider: "ExampleCaptureSignatureProvider",
signature,
publicKey,
integritySha,
});
console.log("signatureJSON", signatureJSON);
// Verify the signature
const recoveredAddress = await nit.verifyIntegrityHash(
integritySha,
signature
);
if (recoveredAddress === signer.address) {
console.log("Signature is valid!");
} else {
console.log("Signature is invalid.");
}
}
main();
For implementation details of nit.getIntegrityHash
and nit.signIntegrityHash
used in the signature generation process, visit GitHub Repository of the open-sourced "Git for Web3 assets" tool Nit for details. The implementation of the above functions can be found at the page.
For verification of signatures, see asset-signature-verification.mdfor details.
Registering an asset¶
POST
https://api.numbersprotocol.io/api/v3/assets/
Cost: 0.1 NUM per API call + Gas (\~0.016 NUM per transaction)
Headers¶
Name | Type | Description |
---|---|---|
Authorization* | String | Example
|
Content-Type | String | multipart/form-data |
X-Api-Key | String | An optional API key used for authentication and identification. If you do not have an API key, this field can be left blank. |
Request Body¶
Name | Type | Description |
---|---|---|
asset_file* | Object | The content file to be registered. It can be any file format, and its file size must not exceed the upload limit in the pricing plan. |
meta | String | JSON string containing proof and information to be registered. |
caption | String | The brief description of the content file. The value will be set to Asset Tree's abstract. |
headling | String | The title of the content file. The length limit is 25 characters. |
image_file | Object | It's preferable to provide an image file when the asset file is not an image. It is necessary to have an image (PNG, JPG, or GIF, etc.) for previewing your item in such cases. |
nit_commit_custom | Object | Set Asset Tree values. For a field defined in Asset Tree schema, its value will override the existing value; for a field not defined in Asset Tree schema, it will be added in the Asset Tree as a custom field. |
public_access | String | When registering asset, add and pin the file on the Numbers IPFS gateway. Default: true |
signature | String | JSON string containing a list of signatures which were used to sign the data or metadata. |
signed_metadata | String | The Integrity Proof JSON string which could be verified with signature. |
{% tabs %}
{% endtab %}
{% tab title="401: Unauthorized " %}
{% endtab %}
{% tab title="500: Internal Server Error " %}
{% endtab %}
{% tabs %} {% tab title="Example" %}
curl -s -X POST 'https://api.numbersprotocol.io/api/v3/assets/' \
-H "Authorization: token YOUR_CAPTURE_TOKEN" \
-F 'asset_file=@/tmp/demo.jpg' \
-F 'signed_metadata={
"proof_hash": "5c1234ef67g8h901i2j3k4l56m7890nop1q2r3s4t5u6v7w8x9y0zab1c2d3e4f5",
"asset_mime_type": "image/png",
"created_at": 1693309348713
}' \
-F 'signature=[
{
"proofHash": "78b67e15077577a9b25393f13bfccde3f94f990cf2a7bb0584a51f09196abbbf",
"provider": "ExampleCaptureSignatureProvider",
"signature": "0xf64d0ea52564dd442bbea7f212dbf2fe464fd5674ffd9b68593b3e757c14873c5136afb4394aa5b4ef529ecb693aae84f827af1ce8ddb7538d46432022f8d1771c",
"publicKey": "0x114e5E0dce98180c98ADC62E9Ac6Ea7184417ecd",
"integritySha": "78e11c61f90ba0c52e9f10e4d829c09e1a0aeffbcf77ae940836c494cdb5ac8b"
}
]' \
-F 'caption=This is an example caption.' \
-F 'headline=This is an example headline.'
{% tab title="Response" %}
{
"id": "bafybeie23hdjuurtc5n77vbhjx6mmrmjo3lyzmtks4eki5ibwpqjoeb3eq",
"asset_file_name": "demo.jpg",
"asset_file_mime_type": "image/jpeg",
"caption": "This is an example caption.",
"headline": "This is an example headline.",
}
Integrity Proof (represented as signed_metadata
in the API) and Asset Signature (represented as signature
in the API) are optional fields. However, including more data enhances the asset's trustworthiness and credibility.
If the asset file is not an image, it is recommended to provide a corresponding image file whenever possible. This helps improve the visual representation and identification of the asset.\ \ For more information on how this registration API works, please refer to the API documentation.
Query user's assets¶
GET
https://api.numbersprotocol.io/api/v3/assets/
The following API endpoint can be used to query asset information.
Cost: Free
Query Parameters¶
Name | Type | Description |
---|---|---|
limit | Number | Pagination size. Default: 200 |
offset | Number | Starting index of the assets. Default: 0 |
Headers¶
Name | Type | Description |
---|---|---|
Authorization* | String | Example
|
{% tabs %}
{% endtab %}
{% tab title="401: Unauthorized " %}
{% endtab %}
{% tab title="500: Internal Server Error " %}
{% endtab %}
{% tabs %} {% tab title="Example: query all assets" %}
curl -s -X GET 'https://api.numbersprotocol.io/api/v3/assets/' \
-H "Authorization: token YOUR_CAPTURE_TOKEN"
{% tab title="Response" %}
[
{
"id": "bafybeie23hdjuurtc5n77vbhjx6mmrmjo3lyzmtks4eki5ibwpqjoeb3eq",
"asset_file_name": "demo.jpg",
"asset_file_mime_type": "image/jpeg",
"caption": "This is an example."
}
]
{% endtab %}
{% tabs %} {% tab title="Example: query one asset" %}
curl -s -X GET 'https://api.numbersprotocol.io/api/v3/assets/YOUR_ASSET_NID/' \
-H "Authorization: token YOUR_CAPTURE_TOKEN"
{% tab title="Response" %}
{
"id": "bafybeie23hdjuurtc5n77vbhjx6mmrmjo3lyzmtks4eki5ibwpqjoeb3eq",
"asset_file_name": "demo.jpg",
"asset_file_mime_type": "image/jpeg",
"caption": "This is an example."
}
Import assets from their NFTs¶
POST
https://api.numbersprotocol.io/api/v3/assets/import/
The importing API endpoint is used for importing NFTs.
Cost: 0.1 NUM per API call + Gas (\~0.004 NUM per transaction)
Headers¶
Name | Type | Description |
---|---|---|
Authorization* | String | Example
|
Content-Type | String | multipart/form-data |
X-Api-Key | String | YOUR_API_KEY |
Request Body¶
Name | Type | Description |
---|---|---|
nft* | Object | The imported NFT. nft_chain_id nft_contract_address nft_token_id |
signed_metadata | String | The signed metadata which could be verified with signature. |
meta | String | JSON string containing the metadata of the asset_file provided by user. |
signature | String | JSON string containing a list of signatures which were used to sign the data or metadata. |
image_file | Object | The image file. Its size should not exceed 100 MB. |
{% tabs %}
{% endtab %}
{% tab title="401: Unauthorized " %}
{% endtab %}
{% tab title="500: Internal Server Error " %}
{% endtab %}
{% tabs %} {% tab title="Example: import an NFT where the owner is the asset wallet" %}
curl -s -X POST 'https://api.numbersprotocol.io/api/v3/assets/import/' \
-H "Authorization: token YOUR_CAPTURE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nft": {
"nft_chain_id": 1,
"nft_contract_address": "0xfDD0642479Bb1E219945E7a44B882AfaB8BaF68B",
"nft_token_id": "1"
},
"meta": "{\"proof\": {\"hash\": \"\",\"mimeType\":\"\",\"timestamp\": \"\"},\"information\": [{\"provider\": \"Capture API\",\"name\": \"version\",\"value\": \"v3\"}]}"
}'
{% tab title="Example: import an NFT where the owner is not the asset wallet " %}
curl -s -X POST 'https://api.numbersprotocol.io/api/v3/assets/import/' \
-H "Authorization: token YOUR_CAPTURE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nft": {
"nft_chain_id": 1,
"nft_contract_address": "0xfDD0642479Bb1E219945E7a44B882AfaB8BaF68B",
"nft_token_id": "1"
},
"meta": "{\"proof\": {\"hash\": \"\",\"mimeType\":\"\",\"timestamp\": \"\"},\"information\": [{\"provider\": \"Capture API\",\"name\": \"version\",\"value\": \"v3\"}]}",
"signed_metadata": "{\"api_version\": \"v3\"}",
"signature": "[{\"proofHash\": \"78b67e15077577a9b25393f13bfccde3f94f990cf2a7bb0584a51f09196abbbf\",\"provider\": \"Web3\",\"signature\": \"0x75f92194df339799748ad8bd12ad4b45ad7b6590431b67900c3b8df21764892b58cbcafb743253e5f70a7965e3706f3d556a056a0089cb164fa2b0fa5cf37ad91c\",\"publicKey\": \"0xbAbaB9E7790202A1a4d171E44767A531C25e200C\"}]"
}'
{% tab title="Response" %}
{
"id": "bafybeie23hdjuurtc5n77vbhjx6mmrmjo3lyzmtks4eki5ibwpqjoeb3eq",
"asset_file_name": "imported.jpg",
"asset_file_mime_type": "image/jpeg",
"caption": "This is an example.",
"nft_token_id": "1",
"nft_chain_id": 1,
"nft_contract_address": "0xfDD0642479Bb1E219945E7a44B882AfaB8BaF68B"
}
{% tabs %}
curl -s -X POST 'https://api.numbersprotocol.io/api/v3/assets/import/' \
-H "Authorization: token YOUR_CAPTURE_TOKEN" \
-F 'nft.nft_chain_id=1' \
-F 'nft.nft_contract_address=0xfDD0642479Bb1E219945E7a44B882AfaB8BaF68B' \
-F 'nft.nft_token_id=2' \
-F 'image_file=@/tmp/demo.jpg' \
-F 'signed_metadata={"api_version": "v3"}' \
-F 'signature=[{
"proofHash": "78b67e15077577a9b25393f13bfccde3f94f990cf2a7bb0584a51f09196abbbf",
"provider": "Web3",
"signature": "0x75f92194df339799748ad8bd12ad4b45ad7b6590431b67900c3b8df21764892b58cbcafb743253e5f70a7965e3706f3d556a056a0089cb164fa2b0fa5cf37ad91c",
"publicKey": "0xbAbaB9E7790202A1a4d171E44767A531C25e200C"
}]' \
-F 'meta={
"proof": {
"hash": "",
"mimeType": "",
"timestamp": ""
},
"information": [
{
"provider": "Capture API",
"name": "version",
"value": "v3"
}
]
}'
{% endtab %}
{% tab title="Response" %}
{
"id": "bafybeiagc3fjrdrrookzkbrtdnfuulpfmeqej3bqc6wpmuttsclykuidt4",
"asset_file_name": "imported.mp4",
"asset_file_mime_type": "video/mp4",
"image_file_mime_type": "image/jpeg",
"caption": "This is an example.",
"nft_token_id": "2",
"nft_chain_id": 1,
"nft_contract_address": "0xfDD0642479Bb1E219945E7a44B882AfaB8BaF68B"
}
The nft
is required to import an NFT from Numbers Search Engine.
If the NFT owner is not the user's Capture Vault, the signed_metadata
and signature
are also necessary. These will be used to validate the address that signed the signed_metadata
as the NFT owner.
It is recommended to provide an image_file
when the NFT's image is not in image file format.
If the owner of the imported NFT is the user's Capture Vault, the user can perform any NFT operation, including forcing a re-mint of an asset, exporting an asset, creating a transaction, creating an asset-origin product, creating an asset-clone product, or creating pack products from a series. However, if the owner is not the user's Capture Vault, the user can only mint new NFTs (create child assets), which includes creating an asset-clone product, and creating pack products from a series.