Notarizing a file by its hash without metadata

  1. Start the Python shell and configure its session.

    The following variables should be available now:

    >>> base_url  # the base URL of the API
    'https://eu2-cloud.acronis.com/api/notary/v2'
    >>> auth  # the 'Authorization' header value with the access token
    {'Authorization': 'Bearer 8770b34b74f9e4d9424eff50c38182bb4ae7f5596582ae61900b1b6a23e3ec58'}
    
  2. Define a variable named file_path, and then assign the path to your file to this variable:

    >>> file_path = '<path to file>'
    

    Important

    If the path contains backslashes, remember to escape them with an additional backslash.

  3. Open the file for reading in binary mode:

    >>> file = open(file_path, 'rb')
    
  4. Calculate the hash value of the file contents by using the SHA-256 algorithm:

    >>> sha256 = hashlib.sha256()
    >>> chunk = file.read(128 * sha256.block_size)
    >>> while chunk:
    ...     sha256.update(chunk)
    ...     chunk = file.read(128 * sha256.block_size)
    >>> file_hash = sha256.hexdigest()
    >>> file_hash
    '2c7c3d5f244f1a40069a32224215e0cf9b42485c99d80f357d76f006359c7a18'
    
  5. Close the file:

    >>> file.close()
    
  6. Define a variable named hashes_data, and then assign an object with the hashes key containing an array of hashes to this variable:

    >>> hashes_data = {
    ...     'hashes': [
    ...         file_hash
    ...     ]
    ... }
    

    You may specify multiple hashes to send for notarization. Refer to the API reference for more information.

    Important

    Each hash will consume one point of the Notarizations quota.

  7. Convert the hashes_data object to a JSON text:

    >>> hashes_data = json.dumps(hashes_data, indent=4)
    
  8. Send a POST request with the JSON text to the /hashes endpoint:

    >>> response = requests.post(
    ...     f'{base_url}/hashes',
    ...     headers={'Content-Type': 'application/json', **auth},
    ...     data=hashes_data,
    ... )
    
  9. Check the status code of the response:

    >>> response.status_code
    200
    

    Status code 200 means that the notary service has received the hash and started notarizing it.

    A different status code means that an error has occurred. For the details, refer to “Status and error codes”.

  10. Convert the JSON text that the response body contains to an object, and then fetch the ID of the created notarization certificate:

    >>> certificate_id = response.json()[-1]['certificate_id']
    >>> certificate_id
    '7605f73deaee7b071a570b3ac20cc9fe7a3abf337be7c86c55c28af9d3d8435c'
    

    Important

    The certificate_id will be required in the requests to the verification endpoints.

  11. Check the notarization status by sending a GET request to the /certificates/{certificate_id} endpoint. We recommend waiting about an hour before checking because the notarization process may take a long time:

    >>> response = requests.get(f'{base_url}/certificates/{certificate_id}', headers=auth)
    
  12. Check the status code of the response:

    >>> response.status_code
    200
    

    Status code 200 means that the request was successful.

    A different status code means that an error has occurred. For the details, refer to “Status and error codes”.

    Also, the response body contains the notarization certificate object formatted as a JSON text. When converted to an object, it will look as follows:

    >>> pprint.pprint(response.json())
    {'contract': '0xd10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF',
     'eventtime': '2019-11-11T13:07:07.004366Z',
     'id': '7605f73deaee7b071a570b3ac20cc9fe7a3abf337be7c86c55c28af9d3d8435c',
     'merkle_proof': '[{"left":"88c20ca21dd6fa9e0a64c7e981a012812bbca152010195cd4296d959cfa35f1e"}]',
     'merkle_root': '6d05fb9f0c2cff4942987661a44e71f0f554d435ce494dd3e7a21df6c6ba963c',
     'notarized_location': 'beta-baas',
     'object': {'eTag': '2c7c3d5f244f1a40069a32224215e0cf9b42485c99d80f357d76f006359c7a18',
                'key': '2c7c3d5f244f1a40069a32224215e0cf9b42485c99d80f357d76f006359c7a18',
                'sequencer': 'DEF04BD0C5114542F8',
                'size': 0},
     'blockchain': 'eth',
     'qr_code': 'data:image/png;base64,iVBORw0KGgoAAAANSUh...',
     'sender': '0x201354729f8d0f8b64e9a0c353c672c6a66b3857',
     'signee_details': {'tenant_name': 'John Doe'},
     'timestamp': 1573572432,
     'txid': '0x6494a098f6487ebbcfa85b7cbe64c1f9f077f03866477b67be64320ea109fa73',
     'version': '3.1'}
    

    If the notarization is complete:

    • The txid key contains the hash of the blockchain transaction that can be viewed on https://etherscan.io/tx/{txid}.

    • The contract, sender, merkle_root, and merkle_proof keys contain the blockchain transaction details.

    • The timestamp key contains the Unix time when the hash value of the file contents was written to the blockchain (notarization completion time).

    • The eTag key of the object object contains the actual hash value that was written to the blockchain.

    • The web version of the notarization certificate is available at https://eu2-cloud.acronis.com/notary/certificate/{certificate_id}.

    Empty txid, contract, merkle_proof, merkle_root, and sender keys mean that the notarization is still in progress and the web version of the certificate is not created yet.

Full code example

 1#!/usr/bin/env python3
 2
 3import requests  # Will be used for sending requests to the API.
 4import hashlib   # Will be used for calculating hash values.
 5import os.path   # Will be used for path-related operations.
 6import pprint    # Will be used for formatting the output of JSON objects received in API responses.
 7import json      # Will be used for converting dictionaries into JSON text
 8
 9# Define variables named "LOGIN" and "PASSWORD" and then assign them with your account credentials
10LOGIN = '<your login>'        # Change login here
11PASSWORD = '<your password>'  # Change password here
12
13# Define a variable named "cloud_url" and then assign it with the URL of the cloud platform
14cloud_url = 'https://cloud.acronis.com'
15
16# Fetch the URL of the data center where your account is located by sending a GET request to the "/api/1/accounts" endpoint
17response = requests.get(
18    f'{cloud_url}/api/1/accounts',
19    params={'login': LOGIN}
20)
21response.raise_for_status()
22
23# Convert the JSON text that the response body contains to a dictionary and store the data center URL
24# in a variable that will be used in further requests
25server_url = response.json()['server_url']
26
27# Define a variable named "account_creds", and then assign the username and password to this variable
28account_creds = {
29    'username': LOGIN,
30    'password': PASSWORD
31}
32
33# Generate a token by sending a POST request to the "/api/2/idp/token" with your account credentials to the cloud platform
34response = requests.post(
35    f'{server_url}/api/2/idp/token',
36    headers={'Content-Type': 'application/x-www-form-urlencoded'},
37    data={'grant_type': 'password', **account_creds}
38)
39response.raise_for_status()
40
41# Convert the JSON text that the response body contains to a dictionary and then assign it to a variable named "token_info"
42token_info = response.json()
43
44# Define a variable named "auth" and then assign it with a dictionary with "Authorization" key containing
45# token string formatted as "Bearer <access_token>"
46auth = {
47    'Authorization': 'Bearer ' + token_info['access_token']
48}
49
50# Define a variable named "base_url", and then assign the API base URL using the data center URL
51# to this variable
52base_url = f'{server_url}/api/notary/v2'
53
54# Define a variable named "file_path" and then assign it with the path to your file
55file_path = '<path to file>'  # Change path to file here
56
57# Open the file for reading in binary mode
58file = open(file_path, 'rb')
59
60# Calculate the SHA-256 hash of the file
61sha256 = hashlib.sha256()
62chunk = file.read(128 * sha256.block_size)
63while chunk:
64    sha256.update(chunk)
65    chunk = file.read(128 * sha256.block_size)
66file_hash = sha256.hexdigest()
67
68# Close the file
69file.close()
70
71# Define a variable named "hashes_data" and then assign it with the dictionary with "hashes" key containing an array
72# of file hashes
73hashes_data = {
74    'hashes': [
75        file_hash
76    ]
77}
78
79# Convert the "hashes_data" dictionary to a JSON text
80hashes_data = json.dumps(hashes_data, indent=4)
81
82# Send the file hash for notarization by sending a POST request to the "/hashes" endpoint
83response = requests.post(
84    f'{base_url}/hashes',
85    headers={'Content-Type': 'application/json', **auth},
86    data=hashes_data,
87)
88response.raise_for_status()
89
90# Convert the JSON text that the response body contains to a dictionary and fetch the certificate ID from the array
91# This code fetches the notarization certificate ID only of the last provided hash
92certificate_id = response.json()[-1]['certificate_id']
93
94# Fetch the notarization certificate by sending a GET request to the "/certificates/{certificate_id}" endpoint
95response = requests.get(f'{base_url}/certificates/{certificate_id}', headers=auth)
96response.raise_for_status()