WebHooks and Laravel Horizon Setup

When VASPs post attestations to the Shyft Blockchain in Step 1 these attestations arrive to all connected VASPs in Step 2 as events.

Alt text

Attestations in Step 2.1 are sent over to the Exchange via a custom webhook that is configured below.

As you can imagine there will be millions of attestations in the Shyft Blockchain, one for every crypto withdrawal from each Exchange. Therefore Veriscope uses a Horizon/Redis queuing framework to post these attestations as they arrive directly to the exchange.

When the exchange receives the attestation, it can confirm if the crypto address (in the attestation) is a deposit address on their exchange. If so, it can proceed by preparing the KYC Template (Step 3).

Below describes the setup and configuration for Horizon.

Install Horizon (Step 15 in the install-script, if not done already)

sudo scripts/setup-vasp.sh
+ Located in /opt/veriscope/
+ Service user will be forge

1) Refresh dependencies
2) Install/update nethermind
3) Set up new postgres user
4) Obtain/renew SSL certificate
5) Install/update NGINX
6) Install/update node.js web service
7) Install/update PHP web service
8) Update static node list for nethermind
9) Create admin user
10) Regenerate webhook secret
11) Regenerate oauth secret (passport)
12) Regenerate encrypt secret (EloquentEncryption)
13) Install Redis server
14) Install Passport Client Environment Variables
15) Install Horizon
16) Install Address Proofs
i) Install Everything
p) show daemon status
w) restart all services
r) reboot
q) quit
Choose what to do:

You can either step through each step — see note below — or choose i (Install Everything) and run steps 9 & 16 once that has completed.

For step 16 (Install Address Proofs) you will be prompted to enter a personal access token (type classic). Please follow the checklist below. If you have any questions, please contact your Veriscope representative.

  • Sign the Veriscope Trust Anchor agreement or an addendum to your existing agreement

  • Request access to the ShyftNetwork/addressproofs repo. The GitHub username that you provide will be added to the repo as a collaborator.

  • Generate a GitHub classic personal access token with scopes equal to repo (full access).

  • Install Veriscope

  • Once the installation is complete run step 16 in the set-up script. You will be prompted to enter the GitHub token created above. Enter and hit return. This downloads the proofs onto your node.

  • Verify that the addressproofs folder exists at /opt/veriscope/veriscope_addressproofs

  • Next, install the proofs by running the following commands:

pip3 install -r requirements.txt

Also install protobuf compiler from your system’s package manager:

sudo apt-get install protobuf-compiler

For some proof types, NodeJS is required. The Python library will call out to Javascript to run these verifications. Install NodeJS and then run:

npm install

Run Step 15

Webhook configuration for Horizon

Log into the backoffice and choose Constants.

Alt text

When in constants (Portal Settings) add your custom Webhook URL and Webhook Secret.

Choose Update to save the Webhook URL and Secret.

Webhook URL should be the Exchange host API service to receive attestations (Step 2.1 and Step 2.2 above).
Alt text

Webhook Jobs

When Veriscope receives an attestation event (Step 2.1) it will add the payload to Horizon and post it to the Webhook URL.

To view Completed Jobs, navigate to the Horizon Dashboard.

Alt text

Choose Completed Jobs.

Alt text

Successful Webhook jobs are called "CallWebhookJob". To view the job details choose "CallWebhookJob".

Alt text

Displayed are the details of the Webhook Post.

Alt text

Below is an example of the payload sent to the Webhook URL.

{
  "ta_account": "0x1bD8d3A9AF399Bfdcd17e45DA27c13C05ef64892",
  "jurisdiction": "1",
  "effective_time": "1615913522",
  "expiry_time": "1679158322",
  "is_managed": "1",
  "attestation_hash": "0xdfd0a3b7766bc7f9ae5af62ba814a6e9e860e4da6bca90fc31865b50c8f79e5f",
  "transaction_hash": "0x3a6890fae7782203c0b2e66385e1bc304da0c698251ebda913b4ec5230adce6b",
  "user_account": "0xDAe1743aE4F79DdEa582C3BF2CdF582F1514e365",
  "public_data": "0x313233",
  "public_data_decoded": "123",
  "documents_matrix_encrypted": "0x3738396338356366346230656333323030633435643162346464343865343331303362306639393964353634316635356637356563346364393833306530303933646539363031666337663639636662666137383366663439663837666564616437616666333262353134363063393264366164656233363139313632346362613833353438393164313733393033613566326434383933653132353438613737336261313432393133343834323439343636346132313039353638343432373963636639393434656635313530313434353531313434353531313434353531313463353530306363356565386435303063633535303063633564613561643761366532373330393962386366376239666537346463376637663665623733353432",
  "documents_matrix_encrypted_decoded": "789c85cf4b0ec3200c45d1b4dd48e43103b0f999d5641f55f75ec4cd9830e0093de9601fc7f69cfbfa783ff49f87fedad7aff32b51460c92d6adeb36191624cba8354891d173903a5f2d4893e12548a773ba1429134842494664a210956844279ccf9944ef515014455114455114455114c5500cc5ee8d500cc5500cc5da5ad7a6e273099b8cf7b9fe74dc7f7f6eb73542",
  "availability_address_encrypted": "0x2020202020202020202020202020202020202020202020202020202020202020",
  "availability_address_encrypted_decoded": null,
  "version_code": "3",
  "coin_blockchain": "ETH",
  "coin_token": "USDC",
  "coin_address": "0x6ec88a2cb932eb46dfda0280c0eadb93b6eca13b",
  "coin_memo": "123"
}

"coin_address": "0x6ec88a2cb932eb46dfda0280c0eadb93b6eca13b" is the crypto wallet address to store and query against (VASP owned) deposit addresses.

Signature is the header param you can use to verify the webhook payload originated from Veriscope.

Signed Signature Verification

We add a header called Signature that will contain a signature the receiving app can use to ensure the payload hasn’t been tampered with. This is how that signature is calculated:

// payload is the array passed to the `payload` method of the webhook
// secret is the string given to the `signUsingSecret` method on the webhook.
$payloadJson = $payload;

$signature = hash_hmac('sha256', $payloadJson, $secret);

Sample code

$payload = '{"ta_account":"0x1bD8d3A9AF399Bfdcd17e45DA27c13C05ef64892","jurisdiction":"1","effective_time":"1616519661","expiry_time":"1679764461","is_managed":"1","attestation_hash":"0x2b9094e7dd7ab75ca33c061491e3a67abd0442c1513e78ddbaeba9796ba53d07","transaction_hash":"0x40e6cb3187442af3f9c6b0b1715ebdd25a2e0fc1e111b8d9d9b4da5889de149d","user_account":"0xB7391F81113F7CaBca9FCbb90Bb7db4EE8Fe576a","public_data":"0x686f72697a6f6e","public_data_decoded":"horizon","documents_matrix_encrypted":"0x373839633835643134623732383333303130303435303963356363343335366232643938396632343734396164636333653536626535376365393731353339353164373834316162653865313231653436646262666333646166656265646562613666666265653937666165656263376566346237363539376231336664356365643733373535396465323436346635613334396361396164316134636231616436363463383361623263396334346463343831633061343832653831333039613430663234313937353436333039326431313938333331313930376466636561646438623931373261343663356138313831356133363235343863386135313731326134656335636634666132653235343963386135333731323835363039343630336662373734306638653430306134646162313030363535313062363064613331316135653135346532303030343631643536643638643561663439616335363163346138643931613031366235393066383164353839326130623336353430393538303764653938383063646562623430313537303137363430346262303565376461393762326461303636626566666666633133666465653235316231","documents_matrix_encrypted_decoded":"789c85d14b7283301004509c5cc4356b2d989f24749adcc3e56be57ce97153951d7841abe8e121e46dbbfc3dafebedeba6ffbee97faeebc7ef4b76597b13fd5ced737559de2464f5a349ca9ad1a4cb1ad664c83ab2c9c44dc481c0a482e81309a40f241975463092d11983311907dfceadd8b9172a46c5a81815a362548c8a51712a4ec5cf4fa2e2549c8a53712856094603fb7740f8e400a4dab10065510b60da311a5e154e2000461d56d68d5af49ac561c4a8d91a016b590f81d5892a0b3654095807de9880cdebb401570176404bb05e7da97b2da066beffffc13fdee251b1","availability_address_encrypted":"0x2020202020202020202020202020202020202020202020202020202020202020","availability_address_encrypted_decoded":null,"version_code":"3","coin_blockchain":"ETH","coin_token":"USDC","coin_address":"0x930474f6a0732b71f8a5fabc7db1ef054925cf37","coin_memo":"horizon"}';
$secret= "super secret";
$signature = hash_hmac("sha256", $payload, $secret);
echo $signature;
// 9c7a2858b452cdd10bbc36453e6c95d0c8841112606d1092131469596b363571
//headers

Content-Length: 2170
Content-Type: application/json
Signature: 9c7a2858b452cdd10bbc36453e6c95d0c8841112606d1092131469596b363571
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.xml.bind.DatatypeConverter;

class Main {
  public static void main(String[] args) {
  	try {
	    String key = "the shared secret key here";
	    String message = "[JSON PAYLOAD]";

	    Mac hasher = Mac.getInstance("HmacSHA256");
	    hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));

	    byte[] hash = hasher.doFinal(message.getBytes());

	    // to lowercase hexits
	    DatatypeConverter.printHexBinary(hash);
  	}
  	catch (NoSuchAlgorithmException e) {}
  	catch (InvalidKeyException e) {}
  }
}