|
@@ -36,7 +36,7 @@ This opens the door to the following use-cases:
|
|
|
|
|
|
Since a PASA history of state-transitions, and the set of operations which created that history, can be independently verified by an individual node, the history can be re-purposed by users as a decentralized-consensus ledger.
|
|
Since a PASA history of state-transitions, and the set of operations which created that history, can be independently verified by an individual node, the history can be re-purposed by users as a decentralized-consensus ledger.
|
|
|
|
|
|
-Under this interpretation, a PASA history becomes a ledger of chronologically ordered statements made by parties of a contract which cannot be forged, altered or tampered with by those parties. Should a dispute arise between the parties, an arbitrating party can independently retreieve the ledger and cryptographically verify it. No trusted 3rd parties or notaries are required whatsoever.
|
|
|
|
|
|
+Under this interpretation, a PASA history becomes a ledger of chronologically ordered statements made by parties of a contract which cannot be forged, altered or tampered with by those parties. Should a dispute arise between the parties, an arbitrating party can independently retrieve the ledger and cryptographically verify it. No trusted 3rd parties or notaries are required whatsoever.
|
|
|
|
|
|
Such a ledger lends itself to many use-cases including:
|
|
Such a ledger lends itself to many use-cases including:
|
|
- Proof-of-Audit solutions between adversarial parties (i.e. consortiums of competitors, or enforcing treaties between adversarial governments)
|
|
- Proof-of-Audit solutions between adversarial parties (i.e. consortiums of competitors, or enforcing treaties between adversarial governments)
|
|
@@ -73,7 +73,7 @@ Since almost anything and everything can be modeled as an FSM, in combination wi
|
|
### Light-wallet Full Account History
|
|
### Light-wallet Full Account History
|
|
|
|
|
|
In addition to the above benefits, more mundane benefit includes ability for mobile-wallets and light-clients to:
|
|
In addition to the above benefits, more mundane benefit includes ability for mobile-wallets and light-clients to:
|
|
-- Retrieve account history from any untrusted peer and independently verify correctness.
|
|
|
|
|
|
+- Retrieve account history from any non-trusted peer and independently verify correctness.
|
|
- Not require any blocks whatsoever, only the SafeBox and PASA history for accounts they care about.
|
|
- Not require any blocks whatsoever, only the SafeBox and PASA history for accounts they care about.
|
|
- Dramatical UX improvement in a global-adoption scenario.
|
|
- Dramatical UX improvement in a global-adoption scenario.
|
|
|
|
|
|
@@ -106,14 +106,16 @@ end;
|
|
When an Account A is mutated by an operation O, the following rule must be applied **before** the mutation:
|
|
When an Account A is mutated by an operation O, the following rule must be applied **before** the mutation:
|
|
|
|
|
|
```
|
|
```
|
|
-A.Seal = HASH( AccountToRawBytes( A ) ++ OPID(O) )
|
|
|
|
|
|
+A.Seal = HASH( SerializeAccount( A ) ++ OPID(O) ++ SafeBox.Root )
|
|
where
|
|
where
|
|
HASH( X ) = RIPEMD160( SHA2_256 ( X ) )
|
|
HASH( X ) = RIPEMD160( SHA2_256 ( X ) )
|
|
- AccountToRawBytes ( X ) = account X serialized as an LE byte array as per network protocol
|
|
|
|
- OPID(X) = RIPEMD160( operation X serialized as an LE byte array as per network protocol )
|
|
|
|
|
|
+ OPID(X) = RIPEMD160( SerializeOperation ( O ) )
|
|
|
|
+ SafeBox.Root = the current committed SafeBoxHash resulting from the block tip , NOT the mempool SafeBoxHash
|
|
|
|
+ SerializeAccount ( X ) = account X serialized as an LE byte array as per network protocol
|
|
|
|
+ SerializeOperation ( X ) = operatin X seraialized as an LE byte array as per ntwork protocol
|
|
```
|
|
```
|
|
|
|
|
|
-This rule ensures that whenever an account A is updated by an operation O, the resulting state of A contains a hash commitment to it's previous state and the operation O used to mutate that previous state. Since each Seal transitively commits to prior Seal, each Seal commits to it's entire history.
|
|
|
|
|
|
+This rule ensures that whenever an account A is updated by an operation O, the resulting state of A contains a hash commitment to it's previous state and the operation O used to mutate that previous state, as well as the state of the SafeBox at the time. Since each Seal transitively commits to the prior Seal, each Seal commits to it's entire history.
|
|
|
|
|
|
**NOTE:** The OPID is the equivalent of TXID in Bitcoin, and can be easily extracted from the OPHASH as per below:
|
|
**NOTE:** The OPID is the equivalent of TXID in Bitcoin, and can be easily extracted from the OPHASH as per below:
|
|
|
|
|
|
@@ -128,44 +130,63 @@ OPHASH = [BLOCK NUMBER] ++ [SIGNER ACCOUNT] ++ [N_OPERATION] ++ [OPID]
|
|
|
|
|
|
### Seal Proofs
|
|
### Seal Proofs
|
|
|
|
|
|
-In order to prove an prior account state is valid, the user needs a copy of that account state, and a list of hash-pairs from that state to the current state.
|
|
|
|
-
|
|
|
|
|
|
+An account history is composed of a chronological order of account events. An account event denotes the state of an account at some point in time, the mutating operation which transitioned that state to the next state, the current block number and safebox state at the time the transition occured.
|
|
```pascal
|
|
```pascal
|
|
- TStateProof = record
|
|
|
|
- AccountState : TAccount;
|
|
|
|
- OPID : TBytes[20]
|
|
|
|
|
|
+ TAccountEvent = record
|
|
|
|
+ AccountState : TAccount; // the account before it was mutated operation with OPID
|
|
|
|
+ OPID : TBytes; // 20 bytes OPID portion of the mutating operation
|
|
|
|
+ LastBlockNum : UInt32; // the block count BEFORE the mutating operation was committed to blockchain
|
|
|
|
+ SafeBoxState : TBytes; // the SafeBoxHash (or Root) at block "LastBlockNum"
|
|
end;
|
|
end;
|
|
|
|
|
|
- TAccountProof = array of StateProof;
|
|
|
|
|
|
+ TAccountHistory = array of TAccountEvent;
|
|
|
|
|
|
- function IsValidAccountProof(Proof : TAccountProof) : Boolean;
|
|
|
|
|
|
+ function bool FetchSafeBoxState(ABlockNum : UInt32, out ASafeBoxRoot : TBytes);
|
|
begin
|
|
begin
|
|
- if (proof[Length(proof) - 1].AccountState = SafeBox.GetAccount(account.Number)) AND IsValidProofChain(Proof) then
|
|
|
|
- return true;
|
|
|
|
- return false;
|
|
|
|
- end
|
|
|
|
|
|
+ // This fetches the SafeBoxHash (or SafeBoxRoot) when block "ABlockNum" was applied. It can use live
|
|
|
|
+ // SafeBox or a trusted database maintained by user.
|
|
|
|
+ end;
|
|
|
|
|
|
- function IsValidProofChain( Proof : TAccountProof ) : Boolean;
|
|
|
|
|
|
+ function IsValidAccountHistory(const AHistory : TAccountHistory) : Boolean;
|
|
|
|
+ var
|
|
|
|
+ LPrevSeg : TAccountSegment; // note: this is confusingly called TBlockAccount in implementation (should be changed, IMO)
|
|
|
|
+ LActualPriorSafeBoxState : TBytes;
|
|
begin
|
|
begin
|
|
- // validate in reverse
|
|
|
|
- for i := Length(proof) - 1 downto 1 do begin
|
|
|
|
- if proof[i].AccountState.Seal <> CalculateSeal( proof[i - 1] ) then
|
|
|
|
- return false; // proof invalid at i - 1
|
|
|
|
- end
|
|
|
|
- return true;
|
|
|
|
- end;
|
|
|
|
|
|
+
|
|
|
|
+ // validate in reverse
|
|
|
|
+ for i := Length(History) - 1 downto 1 do begin
|
|
|
|
+ LAccount := History[i].AccountState;
|
|
|
|
+ LPriorEvent := History[i-1];
|
|
|
|
+ if NOT FetchSafeBoxState(LPriorEvent.LastBlockNum, out LActualPriorSafeBoxState)
|
|
|
|
+ exit(false); // prior event referred to non-existent "last block num"
|
|
|
|
+
|
|
|
|
+ if LPriorEvent.SafeBoxRoot <> LActualPriorRoot then
|
|
|
|
+ exit(false); // prior event referred to invalid "safebox state"
|
|
|
|
+
|
|
|
|
+ if LAccount.Seal <> CalculateSeal(LPriorEvent.AccountState, LPriorEvent.OPID, LPriorEvent.SafeBoxRoot) then
|
|
|
|
+ exit(false); // prior event did not result in current account state
|
|
|
|
+ end;
|
|
|
|
+ // account history is valid
|
|
|
|
+ exit(true);
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
|
|
- function CalculateSeal( proofLink : TStateProof ) : TBytes[32]
|
|
|
|
|
|
+ function CalculateSeal( AccountSate : TAccount; PriorSafeBoxState: TBytes; MutatingOPID : TBytes ) : TBytes;
|
|
begin
|
|
begin
|
|
- return RIPEMD160 ( SHA2_256 ( Array.Join( SerializeAccount ( proofLink.AccountState ) , proofLink.OPID ) ) );
|
|
|
|
|
|
+ return RIPEMD160 ( SHA2_256 ( Array.Join( PriorSafeBoxState, SerializeAccount ( AccountSate ), MutatingOPID ) ) );
|
|
end
|
|
end
|
|
|
|
|
|
function SerializeAccount(account : TAccount) : TBytes;
|
|
function SerializeAccount(account : TAccount) : TBytes;
|
|
begin
|
|
begin
|
|
- return ...; // as per network protocol
|
|
|
|
|
|
+ // as per network protocol
|
|
end
|
|
end
|
|
```
|
|
```
|
|
|
|
|
|
|
|
+**NOTES**
|
|
|
|
+* The above implementation does not validate the "final event" in the history, since by definition it is impossible as it requires knowledge of the "next event".
|
|
|
|
+* If an auditor needs to validate a subset of an account history between events N..M, the account owner is required to provide the events N..(M+1).
|
|
|
|
+* If an auditor is validating the full history, or a partial history up and until the live account state, the final element of the history should simply be the live account state copied from the SafeBox with all other fields set to nil or 0 (since they are not used or needed).
|
|
|
|
+
|
|
### Embedded-Chain Specification
|
|
### Embedded-Chain Specification
|
|
|
|
|
|
There are many methods to embed a blockchain inside a PASA chain, the general idea is to store the block-header and it's content-root inside the operation (or account state) and to depend on the Account Seal as the "chain mechanism".
|
|
There are many methods to embed a blockchain inside a PASA chain, the general idea is to store the block-header and it's content-root inside the operation (or account state) and to depend on the Account Seal as the "chain mechanism".
|
|
@@ -198,7 +219,9 @@ Both (1) and (2) basically solves for unintentional/accidental spam scenario. Fo
|
|
|
|
|
|
## Rationale
|
|
## Rationale
|
|
|
|
|
|
-Simple and powerful extension that yields tremendous value, for virtually no cost. No brainer update.
|
|
|
|
|
|
+Due to PascalCoin's deletable blockchain architecture, account histories are essentially lost during this block deletion. Providing a cryptographically secure mechanism for users to maintain their account histories is a fundamentally missing feature which is solved by this PIP. Many possible Seal calculations were considered, and discussed in the discussion form for this PIP. The selected algorithm is the easiest and most robust solution. It allows users to validate partial account histories **without** needing the full history which transitively implies partial histories can be validated without needing the SafeBox (so long as Auditor trusts and inputs the final expected SafeBoxState in the audit).
|
|
|
|
+
|
|
|
|
+Simple and powerful extension that yields tremendous value, for little cost.
|
|
|
|
|
|
## Backwards Compatibility
|
|
## Backwards Compatibility
|
|
|
|
|