PIP: PIP-0032B (Based on PIP-0032A) Title: Atomic Swaps via Hash-Locked Accounts Type: Protocol Impact: Hard-Fork Authors: Herman Schoenfeld Albert Molina Copyright: Herman Schoenfeld, Albert Molina, 2019 (All Rights Reserved) License: GNU Public License Comments-URI: https://discord.gg/sJqcgtD (channel #pip-0032) Status: Proposed Created: 2019-05-30
A minor protocol change is proposed to enable Atomic Swap capability within the SafeBox
This PIP is a small modification specified in PIP-0032A without use independent fields Account.Data
or Account.Type
As defined in PIP-0032A
Based on current PascalCoin source core and implementation of In-protocol PASA Exchange also called "public or private Account sell", this PIP-0032B extension will use a similar approach than PIP-0032A but reusing current source code implemented fields and without use independent fields Account.Data
or Account.Type
The current core defined data types are:
TAccountState = (as_Unknown, as_Normal, as_ForSale);
TAccountInfo = Record
state : TAccountState;
accountKey: TAccountKey;
// Trade info, only when state=as_ForSale
locked_until_block : Cardinal; // 0 = Not locked
price : UInt64; // 0 = invalid price
account_to_pay : Cardinal; // <> itself
new_publicKey : TAccountKey;
end;
TAccount = Record
account: Cardinal;
accountInfo : TAccountInfo;
... other ...
account_type : Word; // Protocol 2. Layer 2 use case
account_data : TRawBytes; // Protocol 5. PIP-0024 RAW data information
End;
PIP-0032B will not use fields TAccount.account_type
(TYPE) nor TAccount.account_data
(DATA) for a SimpleSwap (Used in PIP-0032A proposal)
Instead of use those fields, this PIP implements same workaround but adding needed fields inside TAccountInfo
data type, allowing the external TAccount
data type use other fields for free without limitation
So, proposal is introduce just one new field and improve current fields workaround, starting for upgrade the TAccountState
definition type adding as_AtomicSwap
:
TAccountState = (as_Unknown, as_Normal, as_ForSale, as_AtomicSwap);
TAccountInfo = Record
state : TAccountState;
accountKey: TAccountKey;
// Trade info, only when state in [as_ForSale or as_AtomicSwap]
locked_until_block : Cardinal; // 0 = Not locked
price : UInt64; // Fixed 0 when as_AtomicSwap, >0 when as_ForSale
account_to_pay : Cardinal; // Not used for as_AtomicSwap, <> itself when as_ForSale
new_publicKey : TAccountKey;
hashed_secret : TRawBytes; // Not used for as_ForSale, 32 bytes length when as_AtomicSwap
end;
Note
We can set optional value for new_publicKey
field, that means that we can have 2 different workarounds for AtomicSwap: Private AtomicSwap and Public AtomicSwap
TAccountInfo.hashed_secret
Suppose Alice has 100 PASC and Bob has 1 BTC, and they wish to perform an Atomic Swap between themselves. The below workflow can be employed:
Alice picks a very large random number known as SECRET
Alice calculates CODE = HASH(SECRET)
Bob gives Alice his public key B (Not this field is Optional, Private AtomicSwap if provided)
Alice owns account X and deposits 100 PASC into X and sets it for AtomicSwap to Bob as follows:
_NOTE At this point Bob has the ability to acquire account X containing 100 PASC at no cost. However, there is now an additional requirement now that he must supply SECRET in the payload of the purchasing operation. This new consensus rule is only applied for accounts typed "AtomicSwap". When purchasing Hash Locked accounts, the purchasing operations Payload must hash to the target accounts hashedsecret field. In other words, Bob needs to include SECRET in the Payload since it hashes to CODE. If the Payload does not Hash to the account data, the purchase operation is invalid.
This rule forces Bob to know SECRET before buying this account for nothing. If he does not know SECRET, he cannot buy X.
At this point, no one has exchanged anything yet. However, Bob knows he is guaranteed 100 PASC if and only if he learns SECRET within 3 weeks. If he does not learn secret in 3 weeks, Alice can take her 100 PASC back.
Alice gives Bob her BTC public key A (Note, PascalCoin public A does not need to match BTC address A, but the owner is Alice in both cases)
Bob creates a BTC transaction TXN1 with output:
Pay 1 BTC to A if
(x for H(x)=CODE and signed by A) OR
(Signed by B after two weeks from now)
NOTE At this point, Bob has published a BTC transaction that says:
Alice can spend this 1 BTC so long as she publishes SECRET and signs with her key A.
If after 2 weeks Alice hasn't done that, Bob reserves the right to take back this 1 BTC.
The swap has still not occured yet but is setup bi-directionally.
NOTE In the process of spending TXN1, Alice reveals the SECRET inside of TXN2, which was a necessary condition of the smart-contract defined in TXN1, in particular the x for H(x) = CODE
portion.
Bob detects the transaction TXN2 and extracts SECRET from TXN2.
Bob createas a Purchase Account operation for X and includes SECRET inside the Payload, thus taking possession of X containing the 100 PASC.
Atomic Swap Completed
IMPORTANT
Notice that Alice's offer to Bob was locked for 3 weeks, yet Bob's offer to Alice was locked for only 2 weeks.
The following changes are required to implement this type of HLTC in PascalCoin.
The consensus rule when purchasing an account listed for private sale requires a minor change. This rule applied for both OP_BuyAccount and OP_Transaction.
let S = target PASA
let T = purchasing operation (can be either OP_BuyAccount or OP_Transaction)
... implement existing consensus rules ...
... Allow (S.accountInfo.state = as_AtomicSwap) cases with S.accountInfo.price = 0
// PIP-0032B: Atomic Swap rule
if (S.accountInfo.state = as_AtomicSwap) then begin
// It's an atomic swap private sale, sender must provide secret
if SHA2_256(T.Payload) <> S.accountInfo.hashed_secret then
Error "Hash-locked accounts require correct hash pre-image when purchasing. Purchaser did not provide correct hash pre-image.";
end;