The transactions on the Gunero network are a complex, multi-step process. The following lays out in clear terms the technical underpinnings of how they operate in order to maintain the privacy and security guarentees that GunClear's membership demands.
"Account Address" or account refers to the 160-bit identifier used to uniquely identify an Ethereum given a EDCSA public-private key pair. It is the first 20-bytes of the keccak256 hash of the public key.
Represented as: A_account
(account public address) and s_account
(account private key),
where account
is one of sender (S
), receiver (R
), or previous sender (PS
)
The "Authorization Root Hash" is the root hash of the sparse merkle tree data structure used to track
the authorization status of accounts in the system. The key used for obtaining this root hash is the
Ethereum account address A_account
, and the status is at the leaf.
Represented as: W
(W_P
used for "previous auth root hash") and N_account
(status of account)
The auth root hash W
is only updated once per Plasma Cycle.
The root hash of a merkle tree can be used to generate proofs of inclusion. This is done by providing
a list of sibling nodes that traverse up the tree. The proof starts at the leaf, which is first hashed
into the starting node, and then hashes are recursively computed from that hash and the sibling at level
i
of the tree until it reaches the root (at level 0). The order that the data is hashed in is determined
by the path, which is a list of boolean orderings obtained from the key of the data structure (every i
-th
bit in the integer). If the final hash matches a given root hash W
, we know the data is consistent.
Represented as: M_account[i]
([i]
denotes an array of length i
)
In order to tie the authorization proof (which is generated once per Plasma cycle), to the specific transaction in which a token is spent without incurring prohibitive overhead on the transaction process, we need to define a public parameter that is shared between the proofs verifying that the Account used to generate the authorization proof is the same account specified in the given transaction. The "view hash" is this data structure, protected from pre-imaging attacks from the use of a randomized secret called the "randomizer" that is never publicly divulged. The view hash is re-computed every cycle, but can be safely used multiple times within a given cycle to participate in different trades.
Represented as: V_account
(view hash) and r_account
(randomizer)
The view hash is computed as follows:
V_account = hash(A_account + W + r_account)
In order to obfuscate the serial numbers being traded on GunClear's network from surveilance, we hide the serial number with a unique and uncorrelated identifier called a "Token UID". This identifier is generated by the first account that originally "tokenized" the firearm, and is constructed from the serial number of the firearm, and a "Firearm View Randomizer" which is a randomly generated number. Token UID (bytes32 hash)This number is shared between all owners of the firearm and can be used to verify, prior to trading, that an owner actually "owns" that firearm, similar to 2FA (something you have and something you know).
Represented as: T
(Token UID) and j
(Firearm View Randomizer)
The Token UID is computed as follows:
T = hash(F + j)
where F
is the firearm's serial number, in a standardized numerical format (which may be pre-processed)
The transaction Lock is what the Gunero operators use to track the current state of ownership of a given firearm token. It is used as a commitment/nullifier in a similar fashion to Zcash; however, since we do not have a need to perform join-split transactions, we were able to reduce it to a single spending commitment based on the owner's private key.
Represented as: L
(L_P
is used for previous)
This hash is computed as follows:
L = hash(A_S + s_R + T + W)
Due to the structure of the transaction hash, there is a limit to the token movements possible within a given
Plasma synchronization cycle. The reason why this occurs is because of the time-sensitive parameter W
,
which is updated only once per cycle, which timebounds a transaction to within a given cycle to ensure the
authorization proof does not have to be recomputed more than necessary. It should be noted that it is possible
to send a token to yourself as an additional obfuscation measure, since the transactions are completely secret
this would disassociate the transaction from the previous one (between you and another party).
This is a recommended practice, but not required.
For two users a
and b
, the following transactions (=>) are possible (for a given token T
):
order of transfers | use case |
---|---|
a => b |
normal transaction |
a => a |
obfuscation |
a => b => a |
returns, re-purchases, other mistakes, etc. |
a => a => b |
obfuscation then transfer |
a => b => b |
transfer then obfuscation |
a => a => b => b |
obfuscation then transfer then obfuscation |
a => b => b => a |
transfer then obfuscation then return |
a => a => b => b => a |
technically possible, a subsequent a => a would not be however |
The following are disallowed:
order of transfers | reasoning |
---|---|
a => b => a => b |
hot potato, this would generate the same lock as the 1st transfer in the 3rd. It would therefore be possible to re-do the a => b transfer since a has all information to re-commit the transaction. |
a => b then a => c |
this is a double spend! |
All clients will reject transitions of the following nature, which may require storing the n-1
transaction hash for a given token that changed ownership 1 or more times within a given Plasma cycle.
With this proof, we are trying to show that the owner of the a private key is on the publicly available authorization list, without revealing their identity by leaking their public key. The "account view hash" validates that this proof is consistent with the others generated. This proof is expensive as there are 160+ hash operations, but it only has to be re-computed once per Plasma Cycle.
- Authorization Root Hash (
W
) - Account Status (
N_account
) - Account View Hash (
V_account
)
- Account Secret Key (
s_account
) - Authorization Merkle Path (
M_account[160]
) - Account View Randomizer (
r_account
)
- Obtain
A_account
froms_account
through EDCSA operations - Validate
W == calc_root(A_account, N_account, M_account[160])
(User is authorized) - Validate
V_account == hash(A_account + W + r_account)
(View Hash is consistent)
With this proof, we are validating that the receiver of the token has received all pertitent information about the firearm, and also that this transaction was not forged on their behalf. The "account view hash" validates that this proof is consistent with the others generated.
- Authorization Root Hash (
W
) - Token UID (
T
) - Sender Account View Hash (
V_S
) - Receiver Account View Hash (
V_R
) - Current Transaction Hash (
L
)
- Receiver Private Key (
s_R
) - Receiver Account View Randomizer (
r_R
) - Sender Account Address (
A_S
) - Sender Account View Randomizer (
r_S
) - Firearm Serial Number (
F
) - Firearm View Randomizer (
j
)
- Obtain
A_R
froms_R
through EDCSA operations - Validate
V_S == hash(A_S + W + r_S)
(View Hash is consistent for Sender) - Validate
V_R == hash(A_R + W + r_R)
(View Hash is consistent for Receiver) - Validate
T == hash(F + j)
(Both parties know the serial number) - Validate
L == hash(A_S + s_R + T + W)
(The send proof is consistent, not forged)
With this proof, we are validating that the sender of the token is accepting that this token's ownership should be transferred to the new transaction hash given. The "account view hash" validates that this proof is consistent with the others generated, and also serves as an additional precaution for others using this proof to validate an unauthorized release of the token to a party not covered in the transaction.
- Current Authorization Root Hash (
W
) - Token UID (
T
) - Sender Account View Hash (
V_S
) - Receiver Account View Hash (
V_R
) - Previous Transaction Hash (
L_P
)
- Sender Private Key (
s_S
) - Sender Account View Randomizer (
r_S
) - Receiver Account View Randomizer (
r_R
) - Previous Sender Account Address (
A_PS
) - Previous Authorization Root Hash (
W_P
)
- Obtain
A_S
froms_S
through EDCSA operations - Validate
V_S == hash(A_S + W + r_S)
(View Hash is consistent for Sender) - Validate
V_R == hash(A_R + W + r_R)
(View Hash is consistent for Receiver) - Validate
L_P == hash(A_PS + s_S + T + W_P)
(The send proof is valid, sender owns token)
In setting up a transaction, the sender must share the following with the receiver via secret communication:
- Sender Account Address (
A_S
) - Sender Account View Hash (
V_S
) - Sender Account View Randomizer (
r_S
) - Sender Account Status (
N_S
) - Sender Authorization Proof
- Firearm Serial Number (
F
) - Firearm View Randomizer (
j
)
The receiver must share the following with the sender:
- Receiver Account Address (
A_R
) - Receiver Account View Hash (
V_R
) - Receiver Account View Randomizer (
r_R
) - Receiver Account Status (
N_R
) - Receiver Authorization Proof
- Transaction Receive Proof
This is the structure of the transaction communicated over the network:
- Token UID (
T
) - Current Transaction Hash (
L
) - Sender Account View Hash (
V_S
) - Receiver Account View Hash (
V_R
) - Sender Account Status (
N_S
) - Receiver Account Status (
N_R
) - Sender Authorization Proof
- Receiver Authorization Proof
- Transaction Receive Proof
- Transaction Send Proof
The network operators and all other parties will know the following information, given the information provided along with a transaction:
- Previous Transaction Hash (
L_P
) - Current Authorization Root Hash (
W
)
This is an overview of how the transaction would work in practice between the two parties.
The steps are:
- Sender provides setup data to Receiver over secret channel
- Receiver verifies firearm details (serial number) are consistent with shared data
- Receiver validates Sender's authorization proof
- Receiver generates receivership proof (wait...)
- Receiver provides setup data and receivership proof to Sender over secret channel
- Sender validates Receiver's authorization proof
- Sender validates Reciever's receivership proof
- Sender generates ownership transfer proof (wait...)
- Sender broadcasts transaction data over network
- Sender and Receiver wait for lock (leaf of state tree) to be updated for token id
The transaction is considered "accepted" at this point in that it cannot be reverted assuming that the network operators do their jobs and properly synchronize the transaction to the root chain by uploading the state root hash during the Plasma synchronization. Clients should cache all transaction data and validate that synchronization updates when the operator publishes, and be prepared to challenge them if the data does not match expectations.
After several Plasma synchronization cycles corresponding to a review checkpoint, the transaction is considered "finalized" as the state root cannot be reverted. This process may take a month or more, so it is important to validate the network state at least once per checkpoint after a transaction has occured, to ensure that the operators are acting reliably and consistently with the state of the network.