Charter Rode: A Sailing Resume Blockchain

Introduction

Sailors and/or mariners (the terms are used interchangeably in this paper) have, for a thousand years, written down, mostly on paper, a log of some sorts relating to distances traveled, places visited, conditions encountered, sites seen, and time spent at sea. These documents have been very important for a variety of reasons including navigation, historical documentation, understanding cultures, and education which have all served to the betterment of individual sailors and humanity as a whole.

Although industrialized nations are well into the digital revolutionary age, the vast majority of sailor's records are kept either on paper or digitally in disparate systems that can vary from vessel to vessel and sailor to sailor. These systems are difficult to verify and therefore trust. Institutions such as the United States Coast Guard issue credentials such as a 25 Gross Tonnage Master License, Near Coastal, based upon a written exam and a self-reported log of up to 720 qualifying days at sea by the applicant. It is impractical to verify all of an individuals self-reported experience, as much of it would be arguing of what happened five years ago or longer. So institutions, and the industry as a whole, is forced to accept self-reported (paper-based or digital forms of logbooks) experience with limited to no actual verification, relying on trusting that individual sailors are truthful with their reported experience.

A Sailing Resume or Boating Resume is a summary of relevant boating experience. A Sailing Resume is similar to a professional resume that one may use to find a job. A Sailing Resume will list not only on-the-water experience, typically logged in days, but also any certifications, licenses, and other formal education one may complete in their lifetime. Sailing Resumes, also known as a sailing logbook, are used by credentialing authorities (US Coast Guard), charter companies (companies who rent/lease boats to individuals/groups), insurance companies and many other individuals and organizations to determine the overall experience of an individual sailor. Most, if not all of these "users" of a Sailing Resume, look for the same information but in their own invented format.

What is needed is a secure public electronic database that is easy for sailors to document relevant sailing experience; easy for any independent third party to validate the stated experience of any individual sailor; and preserves the privacy of individual sailors. In order for the database to be trusted, it must be immutable (unchangeable by anyone), and each experience documented must carry with it enough information for any party to make a determination to the validity of the experience for their specific needs.

In this paper we propose, and via Charter Rode (www.charterrode.com) we have implemented, such a system by establishing a hybrid blockchain (immutable distributed ledger) with a set of rules that is easy for any individual sailor to use, and any interested party to verify and trust individual transactions with simple cryptographic proofs. This is all accomplished without compromising an individuals identity and privacy, unless the individual sailor publicly claims to own a specific Charter Rode Wallet, just as it would be with any public blockchain such as Bitcoin or Ethereum.

This paper is intended to be a technical document to help anyone with the requisite knowledge to create their own independent verification systems to validate any transaction on the Sailing Resume Blockchain. This paper is not intended for non-technical audiences, and we kindly suggest that any end-user looking for more user-friendly information and/or explanation of the Sailing Resume Blockchain first start with the available FAQs and Articles.

Key Terms and Prerequisites

The reader is assumed to have a good understanding of blockchain technologies and concepts from mainstream chains such as Bitcoin and Ethereum. It is out of the scope of this paper to provide a detailed introduction to concepts such as distributed ledger, immutable storage, merkle proofs, cryptography, digital signatures, hashes, etc. Luckily, there exists a vast knowledge base for the reader to explore and self-educate on these topics as needed. Rather than defining each term, technology, and/or concept, this paper will assume the reader already understands the term, technology, and/or concept and instead this paper will simply discuss how the technology and/or concept has been incorporated or not incorporated into the Sailing Resume Blockchain.

Some of the following sections will provide actual code excerpts from the Charter Rode Protocol, typically in NodeJS. Some of the pitfalls of validating data on the Sailing Resume Blockchain comes from incorrectly implementing the digital signing, cryptographic proofs, and hashes. Therefore, we have provided the actual source code for the reader to use as a reference when building their own validation routines.

If you have any questions, or are struggling with the implementation of a specific component, feel free to reach out to us at support@charterrode.com.

Blockchain Design

In order to design any system, one has to first have a fundamental understanding of the participants, the goals, and motivations. In the case of the Sailing Resume Blockchain, the primary purpose is for any individual sailor to log relevant sailing (boating) experience and to have the experience be trusted by others.

The full list of participants, and their primary goals and motivations are as follows:

  • Sailor - log my relevant experience and ensure that it will be accepted by others when I need them to trust my experience for some specific purpose
  • Credential Authority - provide a service and path for individuals to gain credentials while ensuring there is no fraud and that my credential(s) mean something
  • Charter Company - safely rent/lease a boat to an individual to make money
  • Charter Rode - provide a platform that brings sailors, credential authorities, and charter companies together to allow everyone in the world to trust and have faith in the stated experience of any individual sailor
  • Anonymous Users - any other user or group that is interested in the accurate experience of an individual sailor for some specific purpose

The reader will notice that we look at Charter Rode, more accurately, the Charter Rode Protocol, as a primary participant. This is because the Sailing Resume Blockchain is a hybrid blockchain (more on that below) and as such, requires all participants to trust that Charter Rode will follow the rules set out as the Central Network Authority and process approved transactions by other participants in the network.

This is purposeful in that designing with a Central Network Authority allows the entire network, system, and the Sailing Resume Blockchain to be much more efficient. Remember that the primary goal is to provide a trusted public database of validated sailor experience. We chose not to simply fork Bitcoin or Ethereum, or any other established public blockchain, as it would introduce unnecessary complications that would likely lead us to fail on the primary purpose.

To those that will undoubtedly argue that Charter Rode is not a blockchain because it is centrally controlled, we would agree that Charter Rode is not a pure public blockchain (such as Bitcoin or Ethereum), as it has not been designed to be one. Instead, we have consciously decided to take advantage of the concepts of a public distributed ledger as the permanent immutable storage for all approved transactions by the Charter Rode Network. This ledger provides a Web3 public data source that can be trusted, as long as Charter Rode can be trusted as an independent Central Network Authority.

We believe this can easily be achieved, through a set of rules that the Central Network Authority is expected to, does follow, and is held accountable for. See the sections that follow for more details.

For those readers that are still skeptical of a Central Network Authority, the top three Bitcoin mining pools, control over 59% of the bitcoin blocks mined, and with Ethereum's recent move to proof-of-stake, four entities control around 60% of validation operations. So even the purest in decentralized networks can see that consolidation is and will continue to happen, centralizing more and more control with fewer and fewer companies. The primary reason why these groups follow the rules is the same reason why Charter Rode, acting as the Central Network Authority, follows the rules, it is in our best interest to.

To put it more bluntly, Charter Rode is only successful if the community can trust us, and the community will only trust us if we show that we are a trusted Central Network Authority. It is in our self-interest to follow the rules and serve the community.

With that out of the way, let's continue to dive into the finer details of the overall blockchain design.

Public vs. Private vs. Hybrid

Again, we assume the reader understands the differences between public, private, and hybrid blockchains. Obviously a private blockchain does not meet the stated objective of providing a secure public electronic database.

The principle challenge with a "pure" public blockchain is that anyone should be able to participate as a node on the network without any restrictions (other than those codified into the source code of the chain itself).

Anonymity

One of the fundamental strategies that brings trust to the Sailing Resume Blockchain is the Identity Verification requirements. Every user that wishes to create or verify transactions on the Sailing Resume Blockchain, must first complete an identity verification process with the Central Network Authority. This ensures that only real people are granted a Charter Rode Wallet, which allows verified individuals to create and/or verify transactions on the Charter Rode Network.

One of the fundamental flaws with existing sailing logbooks, is that any user with an email address may sign up. This allows a single individual to sign up for multiple accounts. In many systems this would be okay, even in Bitcoin a single individual can, and likely will have multiple wallets. But as we will see in later sections, one of the possible verification methods for a transaction is a Crewmate. If an individual is allowed to have multiple accounts, it is very easy for this individual to cheat the system and use a Charter Rode Crewmate Verification, which carries more weight than a Charter Rode Self Verification, and the community would be none the wiser. For this reason, every new account requires a robust identity verification process by the Central Network Authority, and it is the Central Network Authority's responsibility to ensure that fraudulent accounts (wallets) are never created.

Because of this one identity management and verification requirement, the use of a "pure" public blockchain protocol is out of the question. There must be some trusted authority to verify identities, and rather than overcomplicating the solution, we choose to provide this service to the network as the Central Network Authority.

There are other design considerations that also make the decision to choose a hybrid blockchain approach a more reasonable one when building a Sailing Resume Blockchain.

Simplicity

By relying upon a Central Network Authority for identity management/verification and actual mining/minting of valid transactions into blocks, the entire Charter Rode Network may be simplified to focus on the interactions between an individual sailor, their crewmate and/or skipper, a charter company, and/or a credential authority. As all of these participants can trust the Central Network Authority to verify identity and process valid transactions onto the Sailing Resume Blockchain, transaction states (as described below) become straight forward and easy to manage and audit.

If the Central Network Authority was instead replaced with validator or mining nodes, the network would first have to recruit these nodes and incentivize them, typically through block rewards and/or transaction fees, which would then overcomplicate the blockchain design and forcing a monetary token that could be exchanged for other currencies. By completely removing the need for reward-based block mining/validation, the transaction fees (discussed below) may be kept low to better service the end-users and community.

Privacy

As the Central Network Authority manages all identities and creating new wallets, all users of the Sailing Resume Blockchain can take for granted and trust that if a transaction appears within a block on the Sailing Resume Blockchain, it was generated from a real human being and the fact that any interested party is able to validate the origin of any transaction with a cryptographic hash, the personally identifiable information (PII) does not need to appear in the Sailing Resume Blockchain.

Similarly, photographic evidence of a specific credential, used by the Central Network Authority and/or Credential Verification Authorities to verify the authenticity of an individual holding a specific credential, does not need to appear within the transaction on the Sailing Resume Blockchain. The Central Network Authority is able to publish just the verified proofs (cryptographic signatures and hashes) that verification occurred off-chain by a valid authority, instead of providing all the details and personally identifiable information (PII) used to verify the transaction off-chain.

This allows for individuals to remain anonymous if they choose to and only to be known by their unique Sailor ID or public key (collectively known as their Charter Rode Wallet Keys). If an individual wishes to publish their identity, they can simply claim ownership of a specific Sailor ID and/or public key. This can be verified through the Central Network Authority at the discretion of the individual sailor. This is similar to if a bitcoin wallet user publicly identified themselves on YouTube as owning a specific wallet address.

Purpose Built

The Sailing Resume Blockchain, Charter Rode Protocol, and schemas have all been purpose built singularly for allowing sailors to document their relevant experience to establish their Trusted Sailing Resume. There does not exist, before Charter Rode, a Sailing Resume Blockchain for this purpose. Yes, one could create smart contracts on Ethereum, and use the very generic framework to store data there, but the current gas prices for Ethereum are so high that it is cost prohibitive as a storage solution. As of June 2022, a single KB (1 KB) of storage would cost around $79.00 USD. A single Charter Rode Block gzip compressed is around 300 - 400 KB. So using the lower end, 300 KB * $79 USD = $23,700 per block. This is why Ethereum is not used to store data.

Similarly, other blockchains could be bent and be used to solve this specific use case, but we believe a better approach is to have a clean, purpose-built schema and blockchain design that solves just this one problem, immutable storage of Sailing Resume transactions, to form the Sailing Resume Blockchain.

Designing and building a hybrid blockchain that is purpose-built for Sailing Resumes allows us to narrowly focus on the unique challenges of the problem space, and keep things really simple. All things being equal, usually the simpler solution is the better one.

With these primary considerations in mind, we have chosen to design Charter Rode as a hybrid blockchain. More specifically, the Sailing Resume Blockchain is a public immutable ledger containing blocks of transactions that have been authorized by the principle participants within the Charter Rode Network.

Central Network Authority

We have already established the need for a Central Network Authority to handle identity management, new wallet creation, and mining/minting unconfirmed transactions into blocks on the Sailing Resume Blockchain. In order for the participants to have trust in the Central Network Authority (Charter Rode), there must be a well-established set of rules that the Central Network Authority abides by and is held accountable to.

Identity Management

The rules for identity management are simple, don't allow fraudulent accounts to be created, don't allow an individual to secure more than one account, and be 100% certain that the account holder is who they say they are.

The financial industry has similar standards. Banks, for example, have regulatory requirements to "know their customer" which means among other things, they must use government issued IDs to verify the identity of account holders. Coinbase actually does a good job of this during their new user account process. Before you are allowed to make trades on their platform, you must upload an acceptable government ID to allow Coinbase to verify you are who you say you are.

(R1) The first, and most important rule of the Central Network Authority, (no this is not Fight Club), is that new accounts cannot be created without identity verification by the Central Network Authority.

R1 is implemented directly in the new account workflow where a new user is not allowed to proceed without uploading a valid, acceptable, government issued ID. Once the photo ID is uploaded, a human being (employee of the Central Network Authority) reviews the information provided during account creation and the photo ID. Additional checks are performed to ensure that (1) this individual does not already have a Charter Rode Wallet issued and (2) is a real human being and not a fake. This may include a live video call.

Wallet Creation

(R2) The rule for wallet creation is also straight-forward, only issue a Charter Rode Wallet to a validated identity.

R2 is implemented as an extension of the new account creation workflow. The Central Network Authority ensures that no transactions can be created by users who have not yet completed the identity verification step (R1 above). Once the identity verification step is complete, the flag is removed from the account and the individual now has full access to the Charter Rode Protocol to create and verify transactions.

Block Creation

The principal concerns with block creation is that no invalid, fraudulent, or otherwise suspect transactions make their way into a block, which remember is immutable once created and attached to the Sailing Resume Blockchain.

There are a few layers of rules during various points of a transaction's lifecycle that the Central Network Authority must implement.

(R3) When a transaction is submitted by a sailor, even when Charter Rode Self Verification is requested, basic verification of the requested transaction must be completed by the Central Network Authority.

These basic sanity checks ensure that a 28' sailboat under sail is not traveling 1,000 nautical miles in two hours. R3 is implemented by Charter Rode through a variety of validation rules on each transaction log entry type:

Identity Verification - user uploaded two pictures that may be used to verify their identity; user provided biographical information including name, phone number, address, and email address.

Credentials - the credential is listed in the set of credentials supported by the Central Network Authority; depending upon the validation rules of each credential, all valid fields, including photographic evidence, is provided for verification of credential request; simple verification of dates such as expiration date not before issue date.

Operational Experience - the reported operational experience count does not exceed a maximum of 12 entries per day.

Trips - each transaction has fewer than 500 entries, adding together reported day and night; each entry under normal conditions does not allow the vessel to exceed 175% of hull speed (formula below) with up to 6 kts added for current in appropriate conditions where current may be a factor (Near Coastal and Offshore).

Dave Gerr published a modified hull speed calculation formula in his book The Propeller Handbook. Using Gerr's method, the Central Network Authority verifies vessel speed as follows:

if ( anEntry.vessel.propulsion === CR_TX_ENUMS.PROPULSION.SAIL ) {
    // D/L = DLT ÷ (0.01 x LWL)³
    let vesselWaterLengthFeet = parseFloat(anEntry.vessel.waterLineLengthMeter) * 3.2808
    let DLT = (anEntry.vessel.displacementTons * 2000) / 2240
    let dlRatio = DLT / Math.pow(((0.01) * vesselWaterLengthFeet), 3)
    // Max S/L ratio = 8.26 ÷ ((D/L)↑.311)
    let maxSLRatio = 8.26 / Math.pow(dlRatio, 0.311)
    let hullSpeedGerr = maxSLRatio * Math.sqrt(vesselWaterLengthFeet)
    // so all this is to check that the reported speed is within reasonable limits for the hull...
    let reportedDuration = anEntry.durationHours
    if (numEntries > 1) {
        // multi-day trip....
        // if we allow for up to 12 hours to cover the reported daily average (which if the user
        // indicated they also did overnight/nights then they would have two entries per day...)
        reportedDuration = 12
    }
    let reportedSpeedKts = anEntry.nauticalMiles / reportedDuration
    let maxCurrentAllowance = 0
    if (anEntry.locationType === CR_TX_ENUMS.LOCATION_DESCRIPTION.NEAR_COASTAL ||
        anEntry.locationType === CR_TX_ENUMS.LOCATION_DESCRIPTION.OFFSHORE) {
        maxCurrentAllowance += 6 // this is VERY GENEROUS!
    }
    // *reasonable* baseline for single hull
    let maxSpeedCheck = (hullSpeedGerr * 1.75) + maxCurrentAllowance
    if ( vesselWaterLengthFeet < 25 ) {
        // these are not cruising boats, these are typically day sailors that are lightweight and fast ...
        if ( anEntry.vessel.hulls === CR_TX_ENUMS.HULLS.ONE ) {
            maxSpeedCheck = 18.6 // current record for a laser
        } else {
            // let them go up to 30 kts...
            maxSpeedCheck = 30
        }
    } else {
        // are we racing??
        if (anEntry.purpose === CR_TX_ENUMS.PURPOSE.RACING) {
            // override the max based upon a function of the overall length
            if (vesselWaterLengthFeet < 40) {
                // if less than 40 LWL, then just use max of 35 kts
                // this should get the crazy performance racing cats happy....
                maxSpeedCheck = 35
            } else {
                // for larger boats gradually increase until we get to crazy numbers... 60 kts???
                maxSpeedCheck = vesselWaterLengthFeet * .8
                if (maxSpeedCheck > 60) {
                    maxSpeedCheck = 60
                }
            }
        }
    }
    if (reportedSpeedKts > maxSpeedCheck) {
        // invalid as this is GREATER than the adjusted gross hull speed plus 75% and a potential current allowance. that is one hell of a current....
        results.validTx = false
    }
} else if ( anEntry.vessel.propulsionType === CR_TX_ENUMS.PROPULSION.POWER ) {
    // let's just make sure they are not going more than 30 kts....for now....
    let reportedDuration = anEntry.durationHours
    if (numEntries > 1) {
        // multi-day trip....
        // if we allow for up to 12 hours to cover the reported daily average (which if the user
        // indicated they also did overnight/nights then they would have two entries per day...)
        reportedDuration = 12
    }
    let reportedSpeedKts = anEntry.nauticalMiles / reportedDuration
    if ( reportedSpeedKts > 30 ) {
        results.validTx = false
    }
}

(R4) Central Network Authority must ensure that only valid transactions are included within a newly created block.

R4 is implemented through a series of cryptographic proofs that ensures the transaction (1) has not been modified since submitted by sailor and passed R3 validation, (2) has not been modified since the verification authority verified and signed transaction, and (3) has not already been added to another block on the Sailing Resume Blockchain. For the actual verification and cryptographic proofs, see the Appendix II: Verifying Data.

Charter Rode Protocol

The Charter Rode Protocol is the set of APIs that the Central Network Authority exposes to participants to allow the creation of transactions, verification/rejection of transactions, Charter Rode Wallet management, and management of other off-chain metadata and objects within the private Charter Rode Network. This includes things like payment processing for funding of Charter Rode Wallets, management of favorite vessels, crewmates, and available charter companies. All of these off-chain metadata and objects are out of the scope of this paper and provide ease of use to sailors and verification authorities off-chain within the Charter Rode Network.

The Central Network Authority is a critical participant in every transaction by providing the overall protocol, identity management and verification, wallet creation, and block creation services to the Charter Rode Network. This frees the other participants to submit transactions via the Charter Rode Protocol and verify/reject transactions upon request via the Charter Rode Protocol.

Public Ledger

As discussed previously in the public vs. private vs. hybrid blockchain section, the key problem we are attempting to solve is to provide a secure public electronic database. We have also established that we have consciously decided to take advantage of the concepts of a public distributed ledger as the permanent immutable storage for all approved transactions by the Charter Rode Network. This public ledger provides a Web3 public data source that can be trusted, as long as Charter Rode can be trusted as an independent Central Network Authority.

The Sailing Resume Blockchain can be found at https://blocks.charterrode.com. Anyone is free to replicate, copy, use, or otherwise store a full copy of the entire blockchain. The Central Network Authority (Charter Rode) ensures that the copy located at https://blocks.charterrode.com is stored (1) using immutable storage, and (2) replicated in near-realtime to multiple locations throughout the world using multiple storage providers.

Charter Rode has provided a basic block explorer app at https://bex.charterrode.com to make reading the blockchain data more friendly via a traditional web browser.

Any Anonymous User that wishes to validate the authenticity of a block, should request a copy of the block via https://blocks.charterrode.com by simply adding the block's hash to the end of the url. For example, the Charter Rode Genesis block is located here. Please see Appendix I: Schemas for a detailed schema of a block and Appendix II: Verifying Data for detailed instructions of how to verify an individual block.

Identity and Privacy

Identity management and privacy have been discussed in a few different sections of this paper already. To summarize, every participant who wishes to create and/or verify transactions on the Sailing Resume Blockchain, must create an account and have their identity verified by the Central Network Authority. As discussed, this in itself does not publicly identify any user as the identity verification performed by the Central Network Authority is done off-chain and the documents and information used during the verification process are never included in the Sailing Resume Blockchain.

In addition, certain transactions, such as adding a credential, require additional personally identifiable information (PII) in order to verify the sailor does indeed hold the credential. This personally identifiable information (PII) is never included in the final transaction that is added onto the Sailing Resume Blockchain.

Anonymous users may trust that any transaction that is included in a block on the Sailing Resume Blockchain, (1) was created by a real human being, and (2) is a valid transaction that has been through the verification process. They don't need to know all the personally identifiable information (PII) in order to verify and trust the transaction is valid.

When anonymous users need to validate that the actual owner of a Charter Rode Wallet (the sailor ID and sailor public key) is who they claim to be, the anonymous user may request an identity verification from the Central Network Authority which will be routed to the Charter Wallet Owner for approval.

Any Charter Rode Wallet holder may choose to make their identity publicly known by simply sharing their personally identifiable information (PII) along with the Charter Rode Wallet keys that they are claiming to own. When sailors do this, they have lost all of their privacy when it comes to the Sailing Resume Blockchain as every sailor may only ever have a single Charter Rode Wallet.

Transaction Verification Types

All Charter Rode transactions carry with them a verification authority who fulfills the consensus requirements so that the Central Network Authority knows a transaction is valid and approved for inclusion in a block on the Sailing Resume Blockchain. We will discuss in-depth the various consensus algorithms used, in the Sailing Resume Blockchain and the Charter Rode Network, in the sections below.

There are currently three different types of Verification Authorities for transactions, Self, Crewmate, and Charter Rode. In the future, there may be additional participants who act as validators for transactions, such as education providers validating credentials earned or charter companies directly validating their charters. Each of the three current verification types are described from a participant perspective, leaving the details of the consensus algorithm(s) used in each to the section entitled Consensus Algorithms below.

Charter Rode

Charter Rode Verification through Charter Rode is the highest and most trusted verification. All credentials are verified by Charter Rode and therefore carry the Charter Rode verification authority. Similarly, if a sailor chartered a boat as skipper, they would use this verification option. Charter Rode will contact the charter company and verify the charter and details. Upon confirmation, Charter Rode will use our public and private keys to sign the transaction for the trip. The sailor's Trusted Sailing Resume will have the highest possible verification for the trip.

Crewmate

If a sailor did not charter a boat, or they were not the skipper of the chartered boat (the individual that signs the charter agreement), they would use Crewmate Verification if the sailor had someone else on the boat with them. Crewmate digital verification is considered to be very reliable by charter companies, insurance companies, and other agencies. A sailor's Trusted Sailing Resume will show a crewmate verification for the trip. When selecting Crewmate Verification, an email is sent to the crewmate selected for the verification asking them to log into their Charter Rode account to verify the trip using their public and private keys. The crewmate has the ability when approving the trip to add the trip to their own logbook which will immediately verify the trip in their logbook using the requesting sailor's public and private keys as the crewmate verification. This is known as a bidirectional crewmate verification.

Self

If a sailor is out by themselves on their own or a friend’s boat, they would use Self Verification. When a sailor selects Self Verification for a trip, the trip is immediately verified and signed using the sailor's public and private keys as the verification authority. The sailor's Trusted Sailing Resume will indicate that the experience for the trip is self-reported and not independently verified. Using Self Verification may lead to insurance companies, charter companies, or other agencies requiring other proof of experience. For example, an on-the-water checkout with a licensed captain in order to bareboat charter.

All sailors should strive for either Charter Rode or Crewmate verification on every log entry they make. As experience is built up on a Trusted Sailing Resume, showing that the majority of the experience has been verified by either a crewmate or by Charter Rode directly, will have more weight than if all of the experience has no verification and is self-reported. That said, as outlined in the Introduction, almost all logbooks today are self-reported without any verification. Even a signature from a skipper in a paper logbook could easily be forged.

Transaction State Diagram

The lifecycle of a transaction starts with a sailor creating a transaction and submitting it to the Central Network Authority. The initial state of a transaction is always PENDING VERIFICATION. Depending upon the verification type requested, and if the verification authority verifies (approves) or rejects a transaction, the state will transition from PENDING VERIFICATION to either REJECTED or PENDING PAYMENT. REJECTED transactions may never be resubmitted. A new transaction must be created with any changes and the process starts all over again. If a transaction is approved by the verification authority, and there is enough Charter Rode Tokens in the sailor's Charter Rode Wallet, payment processing will occur and the state of the transaction will transition to UNCONFIRMED. Once the Central Network Authority creates the next block, an included transaction's state will change from UNCONFIRMED to CONFIRMED, which is a terminal and permanent state.

Before a transaction is CONFIRMED, it may be voided by the sailor who created it and the transaction will transition to VOIDED which is a terminal and permanent state and will not be included in a block on the Sailing Resume Blockchain. Figure 1 below depicts the overall state transition diagram for transactions within the Charter Rode Network.

Charter Rode Transaction State Diagram
Figure 1: Charter Rode Transaction State Diagram

Consensus Algorithms

In most blockchain designs, such as Bitcoin and Ethereum, consensus is a problem that is solved at the block-level only. Typically, proof that the node that created the new block is a valid node and that the block should be trusted is what is needed. In the case of Bitcoin, the consensus algorithm used is Proof of Work. Ethereum has recently moved to a Proof of Stake consensus algorithm. Once again, it is left to the reader to research and understand these concepts on their own.

Within the Charter Rode Network, two primary consensus algorithms are used, Proof of Trust and Proof of Authority. However, instead of just using a consensus method for block creation, we have also implemented consensus at the transaction level so that the Central Network Authority may identify valid transactions that should be included in the next block on the Sailing Resume Blockchain.

First we will define the Proof of Trust and Proof of Authority consensus methods, and then provide the examples in which each are used at the transaction level and then again when the Central Network Authority creates a new block.

Proof of Trust

Proof of Trust (PoT) is a consensus method that relies upon the trust of the node within the network to validate transactions and/or create blocks on a blockchain. In the case of Sailing Resume Blockchain, Proof of Trust is used when a sailor selects either Self or Crewmate verification types. Proof of Trust may also be considered when the Central Network Authority creates new blocks.

When a sailor chooses Self verification, the verification authority is the sailor creating the transaction. Therefore, we must use the Proof of Trust method as every participant will trust that the sailor is truthful in the experience they are adding to their resume.

Similarly, when a sailor uses the Crewmate verification type, all participants are trusting that the sailor and the crewmate are truthful and honest in the experience that is being added and verified to their resumes.

The Central Network Authority uses Proof of Trust to ensure that all transactions are valid when including them into the next block. This consists of validating both the sailor signature and the verification authority signature of a transaction. If they are both valid, then the Central Network Authority trusts that both the sailor and the verification authority are being honest. The reader will note that this applies to any verification type, including Charter Rode.

Proof of Authority

Proof of Authority (PoA) is a consensus method that in which a designated set of nodes within a network have the ability to validate transactions and/or create blocks on a blockchain. In the case of Sailing Resume Blockchain, Proof of Authority is implemented through off-chain authority that has already been established. Specifically, credential authorities are created within the Charter Rode Network as authorities that are allowed to validate the granting of their managed credentials. Similarly, charter companies are created within the Charter Rode Network as authorities that are allowed to validate charters on vessels they manage/lease.

American Sailing Association (ASA), is an education provider and credential granting authority. If you look up the ASA, they have been around since 1983. They provide a number of certifications that are supported by the Central Network Authority. When one of these certifications is added to a transaction, Charter Rode uses Proof of Authority to validate that the individual requesting the certification actually has been granted the certification by the ASA. An employee of the ASA could also log into their Charter Rode account and by virtue of becoming a verification authority on behalf of the ASA credential provider, verify (approve) the transaction using their Proof of Authority.

Another example of Proof of Authority within the Charter Rode Network is when verifying a trip with a charter company. Charter Rode will verify the trip directly with the charter company, and through their authority by having a charter agreement and managing the vessel, they verify the details of the charter and Charter Rode, or the charter company directly, verifies (approves) the transaction.

As the reader can see, individual transactions may be approved using the Proof of Trust or Proof of Authority methods, depending upon the verification type requested within the transaction.

Block creation can be considered both Proof of Trust and Proof of Authority. Proof of Authority because only the Central Network Authority, Charter Rode, has been granted the ability to create blocks. But also Proof of Trust because all participants must inherently trust the Central Network Authority to be honest and follow the rules established. In this case, the Cenral Network Authority must both (1) only include valid transactions that have passed all verification rules and consensus methods, and (2) include all valid transactions presented by participants and not engage in any censorship of transactions on the Sailing Resume Blockchain.

Immutable Storage

We assume that the reader understands the concepts of immutable storage. There are many immutable storage solutions available today, ranging from Web3 specific (IPFS), to commercial solutions such as Amazon Web Services (AWS), and backup solution providers such as BackBlaze.

The important concept is that once a file has been uploaded/placed onto an immutable storage device is that no one, including the owner of the file or the account used to upload/create the file, may change the file. In the case of Charter Rode, this configuration is set for perpetuity, meaning forever. This is important as a typical transaction lives from a few seconds to a few days before it is included into the next block on the Sailing Resume Blockchain.

This short time window, along with the cryptographic hashes and signatures, and the permanent immutable storage of the blocks within the Sailing Resume Blockchain, provide the trust necessary that any specific transaction within a block has not been modified. In addition, since every block is immutable, there is no way an attacker would be successful in providing an alternate chain attack by providing a complete chain including the genesis block and attempting to overwrite the blocks stored.

Furthermore, as every block is replicated in near-realtime to multiple locations throughout the world using multiple storage providers, all of which are immutable, there is no way that an existing Sailing Resume Blockchain block may be modified ever. Again for clarity, once a block has been included on the Sailing Resume Blockchain, it has been added, stored, and becomes a permanent block on the Sailing Resume Blockchain forever.

Fees

For the average sailor, less than $25.00 USD per year will secure all of their sailing experience and build their Trusted Sailing Resume on the Sailing Resume Blockchain. As discussed in the Blockchain Design section, the goal was to keep fees as low as possible. By designing with the Central Network Authority, and choosing to make Charter Rode a for-profit entity, Charter Rode, as the Central Network Authority, is able to control all aspects of fees including the costs associated with providing the entire Sailing Resume Blockchain solution.

It is in the best interest of Charter Rode to keep fees as low as possible, otherwise users will not create transactions to be included in the Sailing Resume Blockchain.

We have chosen a consumption-based fee model much like Twilio does for their services. This allows participants to verify other participants transactions without paying a fee. All fees are paid by the creator of the transaction, the sailor.

Logistically, having a credit card process as little as a $1 USD at a time is not efficient. Therefore, the minimum credit card transaction amount is $25 USD. We have also chosen to use a credit-based system by introducing the Charter Rode Token (CRT).

Charter Rode Tokens (CRTs)

The Charter Rode Token (CRT) is the payment mechanism that allows a sailor to finalize a transaction onto the Sailing Resume Blockchain. Refer back to the Transaction State Diagram subsection above for timing around payment processing of a transaction.

A sailor must have enough CRTs in their wallet at the time of payment processing in order for a transaction to move onto the next step, the unconfirmed status. If a sailor does not have enough CRT in their wallet, transactions will be held in the payment processing status until enough CRT is available to process.

CRTs are non-transferable, meaning that they hold no actual value outside the Charter Rode Network. This again is purposeful. CRTs will never appear on exchanges such as Coinbase, and there will never be support for accepting Bitcoins or other cryptocurrencies as payment. The goal of the Sailing Resume Blockchain is to provide a secure public database, not another cryptocurrency that may be traded or collected.

Using CRTs as the method of payment vs. keeping track of a wallet balance in another standard currency such as USD, also has a few other design benefits. First, it keeps everything simple within the Charter Rode Network. Every transaction that carries a fee is in a standardized credit, the CRT. If down the road the Central Network Authority is required to increase transaction fees, the exchange rate between USD and CRT may be adjusted for future Charter Rode Wallet funding transactions. This also has the benefit of protecting individual Charter Rode Wallet holders as when they fund their Charter Rode Wallet, they will always know how many transactions they have remaining before an additional funding transaction is required.

Second, as the Sailing Resume Blockchain is available to any sailor world-wide, handling of exchange rates and currency fluctuations is simplified and narrowed to only the Charter Rode Wallet funding transactions. More specifically, all Charter Rode Wallet funding transactions are processed in USD via a credit card. Anyone who is from a non-US country, their credit card will handle the current currency exchange rates to fund in USD at checkout. In the future, the payment processing system may be extended to accept additional payment types beyond VISA, Mastercard, Discover, and AMEX to support locale specific payment options, but not cryptocurrencies.

The following is a summary of the current fees and a few real-world examples for reference.

  • All transaction processing, verification, and blockchain confirmation fees are paid in Charter Rode Token (CRT)
  • Current exchange rate is 1 CRT = $1.00 USD
  • Minimum exchange to fund wallet is 25 CRT
  • Wallet funding is one-way. You cannot exchange CRT for any currency or use it outside of Charter Rode Network
  • Each logbook entry processed (no verification) = 1 CRT per day logged
  • Verify logbook entry with Charter Company - 7 CRT per charter trip, plus the processing fee of 1 CRT per day logged
  • Crewmate verification of logbook entry - no charge for verification just pay the processing fee of 1 CRT per day logged
  • One-time identity verification - 49 CRT, waived for new accounts invited by existing verified accounts with use of qualifying ID Voucher Code
  • One-time identity verification processing - 1 CRT to process and commit your identity on Sailing Resume Blockchain
  • Qualifying certifications and licenses are always complimentary, no verification or processing fees

Example 1: You go out and sail for the afternoon and log 5 hours and 15 nm. You take along a buddy as crew who happens to already have a verified Charter Rode account. You get back to the dock, add a new logbook entry for your afternoon trip. You ask your buddy to verify the entry using the crewmate verification process. The total fee for your logbook entry would be 1 CRT.

Example 2: You and your family charter a beautiful 45′ catamaran in the Virgin Islands for 10 days. You have an amazing trip and enjoy those painkillers at anchor. One of the nights is a full moon and you go out for a night-time sail for four hours. All other sailing is done during daylight hours. When you get home, you log your trip as 10 days and one night. You select the Charter Rode verification option to verify your trip with the charter company. Your total fee for this entry would be 18 CRT. 11 CRT for the trip (10 days + 1 night) and 7 CRT for charter company verification.

Example 3: You decide to take a class from a certified American Sailing Association (ASA) instructor to get your ASA 104, Bareboat Cruising certification. You complete the class and your instructor signs your ASA logbook. In a few weeks you get your official ASA sticker for your ASA logbook. You log your new certification on Charter Rode. Charter Rode verifies your new certification with ASA directly and then your new certification is confirmed on Charter Rode. The total fee for your logbook entry would be 0 CRT as all qualifying certifications and licenses are always complimentary.

All CRT Exchanges are via secure one-time credit card payments. An additional 4% fee applies to all CRT Exchanges to offset fees charged by our payment gateway provider. Each Charter Rode Wallet may hold a maximum of 75 CRT at any time

Incentives

Although discussed throughout this paper, this section summarizes the various incentives that each participant has for being honest and a good participant in the Charter Rode Network.

Sailor

The Sailing Resume Blockchain has been purpose-built for sailors. Sailors benefit the most from the Sailing Resume Blockchain and the Charter Rode Network. As outlined in the Introduction, almost all logbooks today are self-reported without any verification. It would be much easier, and free, for a sailor to just present a paper or digital logbook that is 100% self-reported than to go through the trouble of using and trying to abuse or take advantage of the Sailing Resume Blockchain. Even if all of a sailors experience is using the Self verification type, this is inherently more reliable than if the same sailor presented a paper or digital logbook on their own. At a minimum, the sailor recognized the importance of verifying their identity and using a standardized format for their experience.

Credential Authority

There is no reason why any credential authority would object to the Sailing Resume Blockchain. Each credentialing authority has 100% control over authorizing individual sailors and granting them their proprietary credentials. The Sailing Resume Blockchain brings a digital verification to their traditional paper certificates and licenses. Every credential request is verified with the credential authority to prevent any fraud. As the primary business motivation for these credential authorities is to ensure that the credential itself has meaning and to prevent fraud, it is very unlikely that any credential authority that is supported by the Central Network Authority would ever attempt to circumvent the Sailing Resume Blockchain or act in any dishonest way. It is in their own best interest to act honestly.

Charter Company

Charter companies have no reason to be dishonest in verifying charters for sailors. There is no business reason for a charter company not to verify the details of a specific charter and there is no business reason to verify a charter that never happened. Every charter has a charter agreement, which makes verification very easy. Furthermore, the concept of validating sailors experiences amongst charter companies actually benefits charter companies. For example, if a sailor bareboat chartered a vessel let's say in Greece and ended up sinking the boat in the Mediterranean, that would be noted on the charter agreement. Likely, this sailor would never be able to charter another vessel from this charter company again. Wouldn't every other charter company want to know that? These types of details would be discovered and verified by Charter Rode during the verification process. As the reader can see, it is actually in the best interest of every charter company to share their records with Charter Rode. As the sailor who chartered the vessel is requesting the information sharing, there is no reason why the charter company would not comply.

Central Network Authority (Charter Rode)

The Central Network Authority is a critical participant that everyone must trust and rely upon to be honest and follow the rules that have been created (R1 - R4). Providing identity management, new wallet creation, and mining/minting services, the Central Network Authority must be trusted as it is in the best interest of the Central Network Authority to abide and implement these rules, being an honest and trustworthy Central Network Authority. To put it more bluntly, Charter Rode is only successful if the community can trust us, and the community will only trust us if we show that we are a trusted Central Network Authority. It is in our self-interest to follow the rules and serve the community.

Anonymous Users

Anonymous users are only reading the Sailing Resume Blockchain. An anonymous user has trust in the Sailing Resume Blockchain to help their own business or personal interests to make a determination as to the relevant experience of an individual sailor for their specific needs. Even as anonymous users, they have a vested interest in the success of the Sailing Resume Blockchain. There exists no other secure public database and the Sailing Resume Blockchain is this secure public database. That said, assume for a moment that an anonymous user did fraudulently publish transactions to their user base that were either modified or not real transactions on the Sailing Resume Blockchain. As the entire Sailing Resume Blockchain is public, any user or competing group would easily be able to verify transactions independent of this nefarious anonymous user. The nefarious anonymous user's base would likely seek an alternative source for viewing the Sailing Resume Blockchain.

Conclusion

We have proposed, and implemented via Charter Rode (www.charterrode.com), a system for a secure public electronic database that is easy for sailors to document relevant sailing experience; easy for any independent third party to validate the stated experience of any individual sailor; and preserves the privacy of individual sailors. As this system is designed as a hybrid blockchain, one of the key components of the Sailing Resume Blockchain is the trusted Central Network Authority that must follow the defined rules (R1 - R4) in order to provide identity management, new wallet creation, and mining/minting unconfirmed transactions into blocks on the Sailing Resume Blockchain. We believe the reader will agree that it is in the best interest of this Central Network Authority (Charter Rode) to abide and implement the rules, and be an honest and trustworthy Central Network Authority.

The Sailing Resume Blockchain is a trusted public immutable ledger containing blocks of transactions that have been authorized and verified by the principle participants within the Charter Rode Network. Any interested party, anonymously, may independently view, verify, and determine, based upon their own set of rules, if a sailor has the necessary experience and competence for a specific situation.

We thank the long history of the paper-based sailing logbook, and to all the sailors that were trusted stewards of their self-reported experience, but we are happy to say goodbye and welcome all sailors into the 21st century. We don't yet have reliable flying cars, but we now have a reliable, and trusted, Sailing Resume Blockchain.

Happy Sailing!

Charter Rode

Appendix I: Schemas

In this section, the Sailing Resume Blockchain schemas are provided. Blocks on the Sailing Resume Blockchain are stored in gzip format. When a block is decompressed (using gzip), the result is a minified JSON text string of the block. Below is Version 1, the current version, of schema objects starting with a Block.

Block

{
   "blockId": "5d60659b-4e4e-4bfd-9a61-cd61081d610a",
   "version": 1,
   "hash": "3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4",
   "height": 0,
   "previousHash": "0000000000000000000InMemoriamHerMajestyQueenElizabethII1926-2022",
   "merkleRoot": "a5091ea1dc02f70d96d62aeb249d1fe00a3b61a2d299944623f710811cb9231f",
   "time": 1662658200000,
   "numTxs": 46,
   "blockPublicKeyBase64": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQW83azlqK0RYblpSK3NxcGh4amJjaGtBYUdYYWRTVnZlcThRK0pudisrcWdhMWJ4TXZiUWgKVW8vNVF0QjFTR1FJc3JqNmZRbE1SbTUyZGF3QjQxeGYzNW1ldGUxeEM5aWd1c2ZqMFFtTWUxY0FzemtLa3ErVQpvTWFSRVVWNGcrcXpTQ0JlUUZtcVB0MGFENWNBZzZXTGxWMG5CS2RrV1RMWjdHRXA0OEJtbEc0azA5VERuUXJXCllFM3NMTG5VYzRUSWF5SmJac2JXMjl6VGl6a1hndERoNjNNYnQ2OW1WZ0V0NTJiMEt5d1JQZFlFTEpMcTJpaUoKRDNDUnl0YjhqKzdjMGZScXNlOWFZQ1N3YW1XRUR0bzNRRHZKMmIzVEJSblFWaHhnaURhbUlGOG15SmFKOCs1NQpFQ3hOYzNIYUNsOHpOV1BwclhibHprMkwyb2M2bERHS1p3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K",
   "blockSignature": "7b05813242d88ea9e582702eace085ff706de8910df436bad0d33d1c1c68336b551cd3b61ea99807f53f9c10e7c80cd71c8482fac7f3ff34de53499fda2a8cb5ae13071048bf9f33c328676fa56613daa3fbcef3184e873432a2c1e83cc34276b9d6cbf309b7e78a2a21e7420267855b0b24840e7fd643e7d2a96dde3141de48d0bff460188a7dc66fd85b492a08566b8f236438b85284eff19cadedaf772dd89676ab8a2a54d5274d48dca7f2bb04b2a2249dfd4323fbb1df7ec345fec81410533ada0f2a0f900a71102b1eba69e8a61b5fff9353b4ff4419b4f3c7de71c8d1af98959adbc9be16b28a208c6b32360ef8916f0b876f29c461cfc949b595eeee",
   "txs": [ ... ]
}

Transaction

{
   "txId": "3e39c4b4-53de-4493-a0d3-570c0b73411c",
   "version": 1,
   "sequence": 1,
   "time": 1662658199900,
   "input": {
      "sailorId": "58ddd59e-8ec9-4682-8d49-1324ce5c62f1",
      "sailorPublicKeyBase64": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTUyUzZBN0V5UmQ1RG12L1k0VWpYYjljbnJodzVOV2F3RHB2WEhETzZTdWlPYTZuN05XODAKdU45MyszbzZrZksxT2tnVHg4dHFGenR1cUs1NFV2dHhvT2xlRDZvV0taUStDdko0OEF3ZzgzZlpQMnJQSWFFUQpwMWlwZmM2Ymp0dCtCOWpDMlBSTTFxamhkSnlZaWhlcWxJVTdjUnZKOSt2eDJFQURSUE81dzMwL21MZVd6S0MzCkVEL2hNUGxCMDBCZ3JoKy81cFB3QnRPNUFEMGdvU3BubkFyMlBmcGlROWFPZzZ3TnJQUUpQSWtwRDdKOUIxK0IKejJoMlhTeTJjalE5QkdzMjJvWkxlWjlmb1NYM2JoNk9Zb1dRQkJDa29SUGpTSERzeGw4OTV3RCtUUVRRZzJ2ZwpTc2laQVZRR09pMTloVFd2QkhwcnB3UkUya29BZEFKVjB3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K",
      "sailorSignature": "86793bca5d0bd75fd9f9213bbef4921fb269379fb14950c56979800264765da112fdf09563aae76eaf92e357f7f99e3db870dd72ac6a7f5b7e4552c8a87720ad04d5a7e41c53cbf134c0564e70e4db368d263f02e0113244e785139923dc0952df78253f66e2ba2200298ba55e29afdc1172e858c13c308d6dcf4de0db9c4a8bc4108fbb6bf9354b546ad9c3ef04a0fd0830b5b8f8f34ed44c1f0ef5a79576111c6ad7cf508a4e449b182dbc7cf85fa0dc9597d1b82de4dc846bc281363c3bf75922ac173eefbcf2562b8c4c526d4a77e982ab877d3d95d52868f36ea320f2ad5f1c3f6f6b58dcc2c9e82008fba5b778bc4be37dd1e15721af1726d0c2bdca06",
      "verificationAuthority": {
         "verificationAuthorityId": "58ddd59e-8ec9-4682-8d49-1324ce5c62f1",
         "type": "self",
         "verificationAuthorityPublicKeyBase64": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTUyUzZBN0V5UmQ1RG12L1k0VWpYYjljbnJodzVOV2F3RHB2WEhETzZTdWlPYTZuN05XODAKdU45MyszbzZrZksxT2tnVHg4dHFGenR1cUs1NFV2dHhvT2xlRDZvV0taUStDdko0OEF3ZzgzZlpQMnJQSWFFUQpwMWlwZmM2Ymp0dCtCOWpDMlBSTTFxamhkSnlZaWhlcWxJVTdjUnZKOSt2eDJFQURSUE81dzMwL21MZVd6S0MzCkVEL2hNUGxCMDBCZ3JoKy81cFB3QnRPNUFEMGdvU3BubkFyMlBmcGlROWFPZzZ3TnJQUUpQSWtwRDdKOUIxK0IKejJoMlhTeTJjalE5QkdzMjJvWkxlWjlmb1NYM2JoNk9Zb1dRQkJDa29SUGpTSERzeGw4OTV3RCtUUVRRZzJ2ZwpTc2laQVZRR09pMTloVFd2QkhwcnB3UkUya29BZEFKVjB3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K",
         "verificationAuthoritySignature": "a698d6ec977ff5aef646f52769d96a5dfdc3effe531b2bfc1508a315ce683431f0a16196bb05710dc500cb8559efd08c533f2e17853883373daee9231a50d5800d2658923b8591044d963f4d589ca8b5445592a192315d32135275dafe10e29bcafba468d2476380259305c3d1c825c057a8ba2390a0f18e08f7dfd1375293bd606f01a6aa352b9cd1de28eaca3d54aca7541d4095f704a308575a75c8e63734ce18393a7b715018e16b076f704b3e6c7c754e903960c6dc52c25cd480f2dca10467112cc9953e1b65beb0568c01da573ffe02841997b0f8ccb2a5f8ed0d0cccd39d963fedb84caa878ee9384b7ad37e77160af1efe92b2639bf439892733350"
      },
      "log": { .. }
   }
}

Logbook Entries

There are four different logbook entry types: Identity Validation, Credential, Operational Experience, and Trip.

Identity Verification

{
   ...
   "log":{
      "identityVerification":{
         "verifiedDate":"2021-09-29"
      }
   }
}

Credential

{
   ...
   "log":{
      "credential":{
         "credentialTitle":"ASA 101, Basic Keelboat Sailing",
         "issuedDate":"2019-12-01",
         "issuingAuthorityId":"38c533de-38c1-4891-9d51-0ae36a32ebae",
         "issuingAuthorityPublicKeyBase64":"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTMxNFVSak5KamlFcmppVUpLdjNNcWU5RkN3TDlkRElnQ1czYko1WHA0WWRqR0d5TEhHblIKYzdGOVd1Zmphdm1yMmRyQVlVZnd4c3B2UlJ4TFc2Wm91c3BCenZrQ1B5dDluN1lVN1E1aklEakhJWko4SmVhMwpUVUR3d2lZeXlwNlMzRTg3QXFBdlNsUTJwMDd6WHFlWG1ZMDVndXhsRUlKMFNUd2lCem0vQXNpa1hncVI2OFNCCklRWWJjT09FWnVhU0RwWDZqaHBkc0FWTUt1U0tSdWdtZzNNSDlSNjZNbmI3eE01cmZQaWI3ZnNkNWlFbEpOZEoKbGF3Wm4rWWdTY1BFaTZLUlZ6NzZuME45anpNZmYzQ2xmQlBDU3cwSGlLY2ZqSnRwK040YTNiWUpYQ3JsSW5RSQpzK1NKVEJ6WVZGRmM0YVJXeHdaclZPWTk2cWszUFVnek53SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K"
      }
   }
}

Operational Experience

{
   ...
   "log":{
      "operationalExperience":{
         "date":"2021-11-10",
         "role":"skipper",
         "dockingMarina":1,
         "vessel":{
            "vesselId":"d5042446-5f51-4403-9752-2c7c13512090",
            "ownedBySailor":false,
            "year":"1991",
            "ownerType":"charter_base",
            "manufacturer":"Hunter",
            "model":"28",
            "registrationInfo":"TX-ABC123X",
            "vesselName":"Texas Tack",
            "grossTons":9.9,
            "displacementTons":3.7,
            "overallLengthMeter":8.54,
            "waterLineLengthMeter":7.37,
            "beamMeter":3.2,
            "draftMeter":1.14,
            "hulls":1,
            "propulsion":"sail"
         }
      }
   }
}

Trip

{
   ...
   "log": {
      "trip": {
         "tripId": "2c8adc89-c761-4799-bf54-2617c7ed4a76",
            "entries": [
               {
                  "entryId": "932f6a0b-0c84-47aa-9b5b-a761aee9817f",
                  "sequence": 1,
                  "date": "1895-04-24",
                  "role": "skipper",
                  "durationHours": 12,
                  "nauticalMiles": 31.2,
                  "timeOfDay": "day",
                  "windSpeedBeaufort": 5,
                  "weatherConditions": "variable_conditions",
                  "location": "East Boston, MA, USA - Gloucester, MA, USA",
                  "locationType": "near_coastal",
                  "purpose": "cruising",
                  "notes": "I made for the cove, a lovely branch of Gloucester's fine harbor, again to look the Spray over and again to weigh the voyage, and my feelings, and all that.",
                  "vessel": {
                     "vesselId": "a51c847c-aa13-4027-9fb1-7717bdbdd2e6",
                     "ownedBySailor": true,
                     "year": "1894",
                     "ownerType": "private",
                     "manufacturer": "Joshua Slocum",
                     "model": "Sloop",
                     "vesselName": "Spray",
                     "grossTons": 12.7,
                     "displacementTons": 9,
                     "overallLengthMeter": 10.97,
                     "waterLineLengthMeter": 9.75,
                     "beamMeter": 4.33,
                     "draftMeter": 1.28,
                     "hulls": 1,
                     "propulsion": "sail"
                  }
               }
            ]
         }
      }
   }
}

Appendix II: Verifying Data

In the following sections, each relevant code snippet is listed only once for clarity. For example, the CryptoService helpers are all listed together and only once, same with the Block class and it's relevant methods. Check this entire section as code snippets referenced in the Block subsection may reference code snippets provided in other sections.

Blocks

There are two ways to verify a block, first to re-calculate the block hash from the header, and the second to verify the blockSignature provided in a block. The primary purpose of a blockSignature is to ensure that the last block added is also secure from tampering. This is required as there are not yet blocks that reference it via the previousHash field until the future.

Step 1: Download a block, and gzip decompress it. In our example we will use the genesis block.

% curl https://blocks.charterrode.com/3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4 > 3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4.gz
% file 3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4.gz
3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4.gz: gzip compressed data, from Unix, original size modulo 2^32 1030220
% gunzip 3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4.gz
% ls -al
total 2248
drwxr-xr-x   4 user  group      128 Sep 26 10:59 .
drwx------@ 31 user  group      992 Sep 26 10:56 ..
-rw-r--r--   1 user  group  1030220 Sep 26 10:59 3c1fe0746741ff48d67ce07f87c7bc51125c9d8abc78f782bf220399dd99b1c4

Step 2: Verify the hash of the header - now you know you have an unaltered block, but in order to verify that the block is a real block minted by the Central Network Authority, proceed to Step 3.

// Block.js
class Block {
   ...
   getStringVersionOfHeaderForHashing() {
      // 1. create a copy of the object to sign
      let copyToSign = JSON.parse(JSON.stringify(this))

      copyToSign.hash = undefined

      return blockSerializer.stringifyBlockHeader(copyToSign)
   }

   getStringVersionForSigning() {
      // for this we exclude out chicken and egg issues, which really just boils down
      // to the signature.....

      // 1. create a copy of the object to sign
      let copyToSign = JSON.parse(JSON.stringify(this))

      copyToSign.blockSignature = undefined
      copyToSign.hash = undefined

      return blockSerializer.stringifyBlock(copyToSign)
   }
}

// BlockSerializer.js
'use strict'

const transactionSerializer = require('../transaction/CharterRodeTransactionSerializer')

/*
 * Let's discuss why this is even necessary. Well JSON does not guarantee the ordering of the structure, so it becomes impossible
 * to consistently 'sign' cryptographically the JSON.stringify() version of a transaction. Thus, we have to manually create
 * the JSON string so that we may control the exact sequence.
 */

const Handlebars = require("handlebars")

const fullBlocktemplateStr = "{" +
    "         \"blockId\":\"{{block.id}}\"," +
    "         \"version\":{{block.version}}," +
    "         {{#if block.hash}}" +
    "         \"hash\":\"{{block.hash}}\"," +
    "          {{/if}}" +
    "         \"height\":{{block.blockHeight}}," +
    "         \"previousHash\":\"{{block.previousHash}}\"," +
    "         \"merkleRoot\":\"{{block.merkleRoot}}\"," +
    "         \"time\":{{block.time}}," +
    "         \"numTxs\":{{block.numTxs}}," +
    "         \"blockPublicKeyBase64\":\"{{block.blockPublicKeyBase64}}\"," +
    "         {{#if block.blockSignature}}" +
    "         \"blockSignature\":\"{{block.blockSignature}}\"," +
    "          {{/if}}" +
    "         \"txs\":{{{txAsStr}}}" +
    "      }"
const blockHeaderTemplateStr = "{" +
    "         \"blockId\":\"{{block.id}}\"," +
    "         \"version\":{{block.version}}," +
    "         \"height\":{{block.blockHeight}}," +
    "         \"previousHash\":\"{{block.previousHash}}\"," +
    "         \"merkleRoot\":\"{{block.merkleRoot}}\"," +
    "         \"time\":{{block.time}}," +
    "         \"numTxs\":{{block.numTxs}}," +
    "         \"blockPublicKeyBase64\":\"{{block.blockPublicKeyBase64}}\"," +
    "         {{#if block.blockSignature}}" +
    "         \"blockSignature\":\"{{block.blockSignature}}\"" +
    "          {{/if}}" +
    "      }"

const compiledFullBlockTemplate = Handlebars.compile(fullBlocktemplateStr)
const compiledBlockHeaderTemplate = Handlebars.compile(blockHeaderTemplateStr)

module.exports.stringifyBlockHeader = (aBlock) => {
    let blockAsString = compiledBlockHeaderTemplate({block: aBlock})
    // now replace all white space to make compact!
    let returnJSON = undefined
    try {
        returnJSON = JSON.stringify(JSON.parse(blockAsString))
    } catch (err) {
        console.log(blockAsString)
        throw err
    }
    // console.log(returnJSON)
    return returnJSON
}

module.exports.stringifyBlock = (aBlock) => {
    // first thing we have to do is serialize the transactions.....
    let txAsStr = '['
    if ( aBlock.txs ) {
        let first = true
        aBlock.txs.forEach(function(aTx) {
            if ( !first ) {
                txAsStr += ','
            } else {
                first = false
            }
            txAsStr += transactionSerializer.stringifyTX(aTx)
        })
    }
    txAsStr += ']'

    let blockAsString = compiledFullBlockTemplate({block: aBlock, txAsStr:txAsStr})
    // now replace all white space to make compact!
    let returnJSON = undefined
    try {
        returnJSON = JSON.stringify(JSON.parse(blockAsString))
    } catch (err) {
        console.log(blockAsString)
        throw err
    }
    return returnJSON
}

// verify hash of a block
function async verifyHashForBlock(aBlock) {
   // create the string of the header to hash
   let blockHeaderAsStr = aBlock.getStringVersionOfHeaderForHashing()
   // create the double-hash
   let hash = await cryptoService.doubleSHA256AsHex(blockHeaderAsStr)
   if ( hash != aBlock.hash ) {
       return false
   }
   return true
}

Step 3: Verify the blockSignature of the block.

// verify entire block via block-level signature verification
function async verifySignatureForBlock(aBlock) {
   // create the string to check the signature against
   let blockStrToSign = aBlock.getStringVersionForSigning()
   // check the signature
   let blockSignatureValid = await cryptoService.verifySignatureForAsync(aBlock.blockPublicKeyBase64, aBock.blockSignature, blockStrToSign)
   return blockSignatureValid
}

Merkle Trees

We assume that the reader understands why merkle trees are used and why a merkle root is included in each block on the Sailing Resume Blockchain. As creating these calculations can be cumbersome and easy to get wrong without very specific instructions, we have provided the actual code that is used by the Central Network Authority when creating a merkle tree for a block and calculating the merkle root for a block.

//MerkleTree.js
'use strict'

const cryptoService = require("../crypto/CryptoService");

module.exports.findMerkleRootFor = async (aBlock) => {
    try {
        if ( aBlock == undefined || aBlock.txs == undefined || aBlock.txs.length == 0 ) {
            throw "Invalid Block - no transactions"
        }
        let nodes = []
        for (let i = 0; i < aBlock.txs.length; ++i) {
            let txStrToHash = aBlock.txs[i].getStringVersionForHashing()
            let hash = await cryptoService.doubleSHA256AsBuffer(txStrToHash)
            nodes.push(hash)
        }
        // now we have hashes of all the leaf nodes,
        // we need to start hashing pairs until we no longer have any....
        do {
            let parentHashes = []
            for (let i = 0; i < nodes.length; i += 2) {
                // are we at the end of the nodes for this layer?
                if (i + 1 === nodes.length) {
                    // just push the last orphan node up a level...
                    parentHashes.push(nodes[nodes.length - 1])
                } else {
                    let leftNode = nodes[i]
                    let rightNode = nodes[i + 1]
                    parentHashes.push(await cryptoService.doubleSHA256AsBuffer(Buffer.concat([leftNode, rightNode])))
                }
            }
            nodes = parentHashes.map((x) => x)
        } while (nodes.length > 1)
        // so nodes should have a single entry now which is the merkle root
        if ( nodes.length === 1 ) {
            return cryptoService.bufferToHex(nodes[0])
        } else {
            throw nodes.length +" # nodes left in merkle tree!"
        }
    } catch (error) {
        console.log(error, error.stack)
        throw error
    }
}

//CryptoService.js
'use strict'

const crypto = require("crypto")
...
module.exports.verifySignatureForAsync = async (aPublicKeyBase64, aSignature, aStringToSign) => {
    try {
        if (aPublicKeyBase64 == undefined) {
            throw new Error("Invalid public key provided")
        }
        // we know that we store the keys base64 encoded
        const publicKey = new Buffer.from(aPublicKeyBase64, 'base64').toString('ascii')

        const verify = crypto.createVerify('SHA256')
        verify.update(aStringToSign)
        verify.end()
        const verifySignatureResult = verify.verify(publicKey, aSignature, 'hex')
        return verifySignatureResult
    } catch (error) {
        console.log(JSON.stringify(error))
        throw error
    }
}

module.exports.doubleSHA256AsHex  = async (data) => {
    let firstHash = crypto.createHash('SHA256').update(data).digest()
    let secondHashResult = crypto.createHash('SHA256').update(firstHash).digest('hex')
    return secondHashResult
}

module.exports.doubleSHA256AsBuffer  = async (data) => {
    let firstHash = crypto.createHash('SHA256').update(data).digest()
    let secondHashResult = crypto.createHash('SHA256').update(firstHash).digest()
    return secondHashResult
}

module.exports.bufferToHex = (aBufferValue) => {
    return `${(aBufferValue || Buffer.alloc(0)).toString('hex')}`
}

// verify block's merkle root
function async verifyMerkleRoot(aBlock) {
   let merkleRoot = await merkleTree.findMerkleRootFor(aBlock)
   if ( merkleRoot != aBlock.merkleRoot ) {
      return false
   }
   return true
}

Transactions

Verifying a transaction requires verifying both signatures on a transaction, the sailor signature and the verification authority signature. Keep in mind that the version of the transaction that the sailor signs is different from the version that the verification authority signs, which is also different from the merkle hash of the transaction that is created during the minting of a block. The sailor cannot include in the signature the verification authority signature, because it comes later, and cannot sign the sequence assigned to the transaction in the block as that assignment happens during the block creation process.

Below is the code snippets to use when verifying a transaction.

//CharterRodeTransaction.js
use 'strict'

class CharterRodeTransaction {
   ...
   getStringVersionForHashing() {
      // for this there is nothing to exlude as everything is in the transaction in the block

      // 1. create a copy of the object to sign
      let copyToSign = JSON.parse(JSON.stringify(this))

      return crTxSerializer.stringifyTX(copyToSign)
   }

   getStringVersionForSigningBySailor() {
      // for this we exclude out chicken and egg issues for the sailor, which includes their signature
      // and all of the verification stuff which happens after the transaction is initially created

      // 1. create a copy of the object to sign
      let copyToSign = JSON.parse(JSON.stringify(this))

      // 2. remove the things that a sailor would not sign
      copyToSign.sequence = undefined

      // this is obviously what we are looking to create
      copyToSign.input.sailorSignature = undefined

      // these come after the sailor signs the transaction
      if ( copyToSign.input ) {
         if ( copyToSign.input.verificationAuthority ) {
            if (copyToSign.input.verificationAuthority.verificationAuthoritySignature) {
               copyToSign.input.verificationAuthority.verificationAuthoritySignature = undefined
            }
            if (copyToSign.input.verificationAuthority.verificationAuthorityUserId) {
               copyToSign.input.verificationAuthority.verificationAuthorityUserId = undefined
            }
            if (copyToSign.input.verificationAuthority.crewmateRole) {
               copyToSign.input.verificationAuthority.crewmateRole = undefined
            }
         }
         if ( copyToSign.input.log ) {
            if ( copyToSign.input.log.identityVerification ) {
               if ( copyToSign.input.log.identityVerification.verifiedDate ) {
                  copyToSign.input.log.identityVerification.verifiedDate = undefined
               }
            }
            if (copyToSign.input.log.credential) {
               if (copyToSign.input.log.credential.issuingAuthoritySignature) {
                  copyToSign.input.log.credential.issuingAuthoritySignature = undefined
               }
               if (copyToSign.input.log.credential.issuingAuthorityUserId) {
                  copyToSign.input.log.credential.issuingAuthorityUserId = undefined
               }
            }
         }
      }
      return crTxSerializer.stringifyTX(copyToSign)
   }

   getStringVersionForSigningByVerificationAuthority() {
      // for this we exclude out chicken and egg issues for the verification authority, which is primarily the
      // verification authorities signature

      // 1. create a copy of the object to sign
      let copyToSign = JSON.parse(JSON.stringify(this))

      // 2. remove the things that a verification authority would not sign
      copyToSign.sequence = undefined

      // these come after the sailor signs the transaction
      if ( copyToSign.input ) {
         if ( copyToSign.input.verificationAuthority ) {
            if (copyToSign.input.verificationAuthority.verificationAuthoritySignature) {
               copyToSign.input.verificationAuthority.verificationAuthoritySignature = undefined
            }
            if (copyToSign.input.verificationAuthority.verificationAuthorityUserId) {
               copyToSign.input.verificationAuthority.verificationAuthorityUserId = undefined
            }
            if (copyToSign.input.verificationAuthority.crewmateRole) {
               copyToSign.input.verificationAuthority.crewmateRole = undefined
            }
         }
      }
      return crTxSerializer.stringifyTX(copyToSign)
   }

   async isValidTransactionForVerification() {
      // need to ensure that everything we require to verify transaction
      let txSailorStrToSign = this.getStringVersionForSigningBySailor()
      // sign the block
      let sailorSignatureValid = await cryptoService.verifySignatureForAsync(this.input.sailorPublicKeyBase64, this.input.sailorSignature, txSailorStrToSign)
      return sailorSignatureValid
   }
}

// CharterRodeTransactionSerializer.js
'use strict'

/*
 * Let's discuss why this is even necessary. Well JSON does not guarantee the ordering of the structure, so it becomes impossible
 * to consistently 'sign' cryptographically the JSON.stringify() version of a transaction. Thus, we have to manually create
 * the JSON string so that we may control the exact sequence.
 */

const Handlebars = require("handlebars")

const templateStr = "{" +
    "         {{#if tx.txId}}" +
    "         \"txId\":\"{{tx.txId}}\"," +
    "         {{/if}}" +
    "         {{/if}}" +
    "         \"version\":{{tx.version}}," +
    "         {{#if tx.sequence}}" +
    "         \"sequence\":{{tx.sequence}}," +
    "         {{/if}}" +
    "         \"time\":{{tx.time}}," +
    "         \"input\":{" +
    "            \"sailorId\": \"{{tx.input.sailorId}}\"," +
    "            \"sailorPublicKeyBase64\":\"{{tx.input.sailorPublicKeyBase64}}\"" +
    "            {{#if tx.input.sailorSignature}}" +
    "            ,\"sailorSignature\":\"{{tx.input.sailorSignature}}\"" +
    "            {{/if}}" +
    "            ,\"verificationAuthority\":{" +
    "               \"verificationAuthorityId\": \"{{tx.input.verificationAuthority.verificationAuthorityId}}\"," +
    "               \"type\":\"{{tx.input.verificationAuthority.type}}\"," +
    "               \"verificationAuthorityPublicKeyBase64\":\"{{tx.input.verificationAuthority.verificationAuthorityPublicKeyBase64}}\"" +
    "               {{#if tx.input.verificationAuthority.verificationAuthoritySignature}}" +
    "               ,\"verificationAuthoritySignature\":\"{{tx.input.verificationAuthority.verificationAuthoritySignature}}\"" +
    "               {{/if}}" +
    "               {{#if tx.input.verificationAuthority.verificationAuthorityUserId}}" +
    "               ,\"verificationAuthorityUserId\":\"{{tx.input.verificationAuthority.verificationAuthorityUserId}}\"" +
    "               {{/if}}" +
    "            }," +
    "            \"log\":{" +
    "               {{#if tx.input.log.identityVerification}}" +
    "               \"identityVerification\": {" +
    "                  {{#if tx.input.log.identityVerification.verifiedDate}}" +
    "                  \"verifiedDate\":\"{{tx.input.log.identityVerification.verifiedDate}}\"" +
    "                  {{/if}}" +
    "               }" +
    "               {{/if}}" +
    "               {{#if tx.input.log.credential}}" +
    "               \"credential\":{" +
    "                  \"credentialTitle\":\"{{tx.input.log.credential.credentialTitle}}\"," +
    "                  {{#if tx.input.log.credential.credentialInfo}}" +
    "                  ,\"credentialInfo\":\"{{tx.input.log.credential.credentialInfo}}\"" +
    "                  {{/if}}" +
    "                  \"issuedDate\":\"{{tx.input.log.credential.issuedDate}}\"" +
    "                  {{#if tx.input.log.credential.expireDate}}" +
    "                  ,\"expireDate\":\"{{tx.input.log.credential.expireDate}}\"" +
    "                  {{/if}}" +
    "                  {{#if tx.input.log.credential.issuingAuthorityId}}" +
    "                  ,\"issuingAuthorityId\":\"{{tx.input.log.credential.issuingAuthorityId}}\"" +
    "                  {{/if}}" +
    "                  {{#if tx.input.log.credential.endorsements}}" +
    "                  ,\"endorsements\":[" +
    "                    {{#each tx.input.log.credential.endorsements}}" +
    "                    {{#if @first}}{{else}},{{/if}}" +
    "                    {" +
    "                     \"label\":\"{{label}}\"," +
    "                     \"sequence\":{{sequence}}" +
    "                    }" +
    "                    {{/each}}" +
    "                  ]" +
    "                  {{/if}}" +
    "                  {{#if tx.input.log.credential.issuingAuthorityPublicKeyBase64}}" +
    "                  ,\"issuingAuthorityPublicKeyBase64\":\"{{tx.input.log.credential.issuingAuthorityPublicKeyBase64}}\"" +
    "                  {{/if}}" +
    "                  {{#if tx.input.log.credential.issuingAuthoritySignature}}" +
    "                  ,\"issuingAuthoritySignature\":\"{{tx.input.log.credential.issuingAuthoritySignature}}\"" +
    "                  {{/if}}" +
    "               }" +
    "               {{/if}}" +
    "               {{#if tx.input.log.operationalExperience}}" +
    "               \"operationalExperience\": {" +
    "                     \"date\":\"{{tx.input.log.operationalExperience.date}}\"," +
    "                     \"role\":\"{{tx.input.log.operationalExperience.role}}\"" +
    "                     {{#if tx.input.log.operationalExperience.dayAnchoring}}" +
    "                     ,\"dayAnchoring\":{{tx.input.log.operationalExperience.dayAnchoring}}" +
    "                     {{/if}}" +
    "                     {{#if tx.input.log.operationalExperience.overnightAnchoring}}" +
    "                     ,\"overnightAnchoring\":{{tx.input.log.operationalExperience.overnightAnchoring}}" +
    "                     {{/if}}" +
    "                     {{#if tx.input.log.operationalExperience.mediterraneanMooring}}" +
    "                     ,\"mediterraneanMooring\":{{tx.input.log.operationalExperience.mediterraneanMooring}}" +
    "                     {{/if}}" +
    "                     {{#if tx.input.log.operationalExperience.dockingMarina}}" +
    "                     ,\"dockingMarina\":{{tx.input.log.operationalExperience.dockingMarina}}" +
    "                     {{/if}}" +
    "                     {{#if tx.input.log.operationalExperience.overnightSailing}}" +
    "                     ,\"overnightSailing\":{{tx.input.log.operationalExperience.overnightSailing}}" +
    "                     {{/if}}" +
    "                     {{#if tx.input.log.operationalExperience.twentyFourHourCharterPeriods}}" +
    "                     ,\"twentyFourHourCharterPeriods\":{{tx.input.log.operationalExperience.twentyFourHourCharterPeriods}}" +
    "                     {{/if}}" +
    "                     ,\"vessel\":{" +
    "                        \"vesselId\":\"{{tx.input.log.operationalExperience.vessel.vesselId}}\"," +
    "                        \"ownedBySailor\":{{tx.input.log.operationalExperience.vessel.ownedBySailor}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.year}}" +
    "                        ,\"year\":\"{{tx.input.log.operationalExperience.vessel.year}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.ownerType}}" +
    "                        ,\"ownerType\":\"{{tx.input.log.operationalExperience.vessel.ownerType}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.charterCompanyName}}" +
    "                        ,\"charterCompanyName\":\"{{tx.input.log.operationalExperience.vessel.charterCompanyName}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.charterBase}}" +
    "                        ,\"charterBase\":\"{{tx.input.log.operationalExperience.vessel.charterBase}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.commercialOwnerName}}" +
    "                        ,\"vesselOwnerName\":\"{{tx.input.log.operationalExperience.vessel.commercialOwnerName}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.commercialVesselFlag}}" +
    "                        ,\"vesselFlag\":\"{{tx.input.log.operationalExperience.vessel.commercialVesselFlag}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.manufacturer}}" +
    "                        ,\"manufacturer\":\"{{tx.input.log.operationalExperience.vessel.manufacturer}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.model}}" +
    "                        ,\"model\":\"{{tx.input.log.operationalExperience.vessel.model}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.registrationInfo}}" +
    "                        ,\"registrationInfo\":\"{{tx.input.log.operationalExperience.vessel.registrationInfo}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.vesselName}}" +
    "                        ,\"vesselName\":\"{{tx.input.log.operationalExperience.vessel.vesselName}}\"" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.grossTons}}" +
    "                        ,\"grossTons\":{{tx.input.log.operationalExperience.vessel.grossTons}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.displacementTons}}" +
    "                        ,\"displacementTons\":{{tx.input.log.operationalExperience.vessel.displacementTons}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.overallLengthMeter}}" +
    "                        ,\"overallLengthMeter\":{{tx.input.log.operationalExperience.vessel.overallLengthMeter}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.waterLineLengthMeter}}" +
    "                        ,\"waterLineLengthMeter\":{{tx.input.log.operationalExperience.vessel.waterLineLengthMeter}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.beamMeter}}" +
    "                        ,\"beamMeter\":{{tx.input.log.operationalExperience.vessel.beamMeter}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.draftMeter}}" +
    "                        ,\"draftMeter\":{{tx.input.log.operationalExperience.vessel.draftMeter}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.hulls}}" +
    "                        ,\"hulls\":{{tx.input.log.operationalExperience.vessel.hulls}}" +
    "                        {{/if}}" +
    "                        {{#if tx.input.log.operationalExperience.vessel.propulsion}}" +
    "                        ,\"propulsion\":\"{{tx.input.log.operationalExperience.vessel.propulsion}}\"" +
    "                        {{/if}}" +
    "                     }" +
    "               }" +
    "               {{/if}}" +
    "               {{#if tx.input.log.trip}}" +
    "               \"trip\":{" +
    "                  \"tripId\":\"{{tx.input.log.trip.tripId}}\"" +
    "                  {{#if tx.input.log.trip.charterCompany}}" +
    "                  ,\"charterCompany\":{" +
    "                     \"charterCompanyBaseId\":\"{{tx.input.log.trip.charterCompany.charterCompanyBaseId}}\"," +
    "                     \"charterCompanyName\":\"{{tx.input.log.trip.charterCompany.charterCompanyName}}\"," +
    "                     \"charterCompanyBaseName\":\"{{tx.input.log.trip.charterCompany.charterCompanyBaseName}}\"," +
    "                     \"charterCompanyBaseLocation\":\"{{tx.input.log.trip.charterCompany.charterCompanyBaseLocation}}\"" +
    "                  }," +
    "                  {{else}}" +
    "                  ," +
    "                  {{/if}}" +
    "               {{#if tx.input.log.trip.entries}}" +
    "               \"entries\":[" +
    "                  {{#each tx.input.log.trip.entries}}" +
    "                  {{#if @first}}{{else}},{{/if}}" +
    "                  {" +
    "                     \"entryId\":\"{{entryId}}\"," +
    "                     \"sequence\":{{sequence}}," +
    "                     \"date\":\"{{date}}\"," +
    "                     \"role\":\"{{role}}\"," +
    "                     \"durationHours\":{{durationHours}}," +
    "                     \"nauticalMiles\":{{nauticalMiles}}," +
    "                     \"timeOfDay\":\"{{timeOfDay}}\"" +
    "                     {{#if windSpeedBeaufort}}" +
    "                     ,\"windSpeedBeaufort\":{{windSpeedBeaufort}}" +
    "                     {{/if}}" +
    "                     {{#if weatherConditions}}" +
    "                     ,\"weatherConditions\":\"{{weatherConditions}}\"" +
    "                     {{/if}}" +
    "                     {{#if cloudConditions}}" +
    "                     ,\"cloudConditions\":\"{{cloudConditions}}\"" +
    "                     {{/if}}" +
    "                     {{#if location}}" +
    "                     ,\"location\":\"{{location}}\"" +
    "                     {{/if}}" +
    "                     {{#if locationType}}" +
    "                     ,\"locationType\":\"{{locationType}}\"" +
    "                     {{/if}}" +
    "                     {{#if purpose}}" +
    "                     ,\"purpose\":\"{{purpose}}\"" +
    "                     {{/if}}" +
    "                     {{#if notes}}" +
    "                     ,\"notes\":\"{{notes}}\"" +
    "                     {{/if}}" +
    "                     ,\"vessel\":{" +
    "                        \"vesselId\":\"{{vessel.vesselId}}\"," +
    "                        \"ownedBySailor\":{{vessel.ownedBySailor}}" +
    "                        {{#if vessel.year}}" +
    "                        ,\"year\":\"{{vessel.year}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.ownerType}}" +
    "                        ,\"ownerType\":\"{{vessel.ownerType}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.charterCompanyName}}" +
    "                        ,\"charterCompanyName\":\"{{vessel.charterCompanyName}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.charterBase}}" +
    "                        ,\"charterBase\":\"{{vessel.charterBase}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.commercialOwnerName}}" +
    "                        ,\"vesselOwnerName\":\"{{vessel.commercialOwnerName}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.commercialVesselFlag}}" +
    "                        ,\"vesselFlag\":\"{{vessel.commercialVesselFlag}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.manufacturer}}" +
    "                        ,\"manufacturer\":\"{{vessel.manufacturer}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.model}}" +
    "                        ,\"model\":\"{{vessel.model}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.registrationInfo}}" +
    "                        ,\"registrationInfo\":\"{{vessel.registrationInfo}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.vesselName}}" +
    "                        ,\"vesselName\":\"{{vessel.vesselName}}\"" +
    "                        {{/if}}" +
    "                        {{#if vessel.grossTons}}" +
    "                        ,\"grossTons\":{{vessel.grossTons}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.displacementTons}}" +
    "                        ,\"displacementTons\":{{vessel.displacementTons}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.overallLengthMeter}}" +
    "                        ,\"overallLengthMeter\":{{vessel.overallLengthMeter}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.waterLineLengthMeter}}" +
    "                        ,\"waterLineLengthMeter\":{{vessel.waterLineLengthMeter}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.beamMeter}}" +
    "                        ,\"beamMeter\":{{vessel.beamMeter}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.draftMeter}}" +
    "                        ,\"draftMeter\":{{vessel.draftMeter}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.hulls}}" +
    "                        ,\"hulls\":{{vessel.hulls}}" +
    "                        {{/if}}" +
    "                        {{#if vessel.propulsion}}" +
    "                        ,\"propulsion\":\"{{vessel.propulsion}}\"" +
    "                        {{/if}}" +
    "                     }" +
    "                  }" +
    "               {{/each}}" +
    "               ]" +
    "            {{/if}}" +
    "            }" +
    "            {{/if}}" +
    "            }" +
    "         }" +
    "      }"

const compiledTemplate = Handlebars.compile(templateStr)

module.exports.stringifyTX = (theTransaction) => {
    let txAsString = compiledTemplate({tx: theTransaction})
    // now replace all white space to make compact!
    let returnJSON = undefined
    try {
        returnJSON = JSON.stringify(JSON.parse(txAsString)).replace(/&/g,"&")
    } catch (err) {
        console.log(txAsString)
        throw err
    }
    return returnJSON
}


function async isValidTransaction(aTx) {
   // a valid transaction has a valid sailor signature
   let validSailorSignature = await aTx.isValidTransactionForVerification()
   if ( !validSailorSignature ) {
      return false
   }
   // and a valid verification signature
   let txVerificationStrToSign = aTx.getStringVersionForSigningByVerificationAuthority()
   // verify the signature
   let validVerificationSignature = await cryptoService.verifySignatureForAsync(aTx.input.verificationAuthority.verificationAuthorityPublicKeyBase64, aTx.input.verificationAuthority.verificationAuthoritySignature, txVerificationStrToSign)
   return validVerificationSignature
}

Verification Authorities

Once a transaction has been created by a sailor, passes transaction-level basic sanity checks, and is signed by the sailor's public/private keypair, it is ready for a verification authority to act upon it. If a verification authority approves a transaction for inclusion in a block on the Sailing Resume Blockchain, it will be signed using the public/private keypair of the verification authority. Along with the verification authorities public key, the actual globally unique user ID of the user logged into Charter Rode who approved the transaction is also included in the verification section. Below is the code snippet to use when verifying that a transaction was verified by a specific verification authority's public/private keypair.


function async isTransactionVerificationSignatureValid(aTx) {
   let txVerificationStrToSign = aTx.getStringVersionForSigningByVerificationAuthority()
   // verify the signature
   let validVerificationSignature = await cryptoService.verifySignatureForAsync(aTx.input.verificationAuthority.verificationAuthorityPublicKeyBase64, aTx.input.verificationAuthority.verificationAuthoritySignature, txVerificationStrToSign)
   return validVerificationSignature
}

Sailor Inputs

All transactions are created by a sailor. Once the transaction has passed basic sanity checks as described before, the transaction is immediately signed using the sailor's public/private keypair. Below is the code snippet to use when verifying that a transaction was created by a specific sailor's public/private keypair.


function async isTransactionSailorSignatureValid(aTx) {
   let validSailorSignature = await aTx.isValidTransactionForVerification()
   return validSailorSignature
}

Last updated September 26, 2022

@ Copyright 2022-, Charter Rode. Weigh anchor. Explore the world. Charter Rode is building the world's first blockchain for your sailing experience. | Terms of Use | Privacy Policy