Browse Source

PIP-0032 Atomic Swaps and JSON-RPC fixed, updated README

PascalCoin 6 years ago
parent
commit
6703df91dc
4 changed files with 88 additions and 57 deletions
  1. 12 2
      README.md
  2. 19 18
      src/core/UOpTransaction.pas
  3. 49 33
      src/core/URPC.pas
  4. 8 4
      src/gui-classic/UFRMOperation.pas

+ 12 - 2
README.md

@@ -36,10 +36,20 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 ### Current Build (Pending release date)
 - Upgrade to Protocol 5 (Hard fork)
-- Implementation of PIP-0030 -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0030.md
-- Implementation of PIP-0029 -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0029.md
+- Implementation of PIP-0032 (Atomic Swaps) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0032.md
+- Implementation of PIP-0030 (Safebox root) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0030.md
+- Implementation of PIP-0029 (Account Seals) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0029.md
 - New digest hash value for OP_DATA ( PIP-0016 ) on Protocol 5
 - Hardcoded RandomHash digest/hash values for quick speed safebox check on fresh installation
+- JSON-RPC changes:
+  - Updated "listaccountforsale" call to allow ATOMIC SWAPS (PIP-0032)
+    - Added "type" to discrimine between kind of listing. Available values are:
+      - "public_sale"
+      - "private_sale": Need to provide a valid "new_enc_pubkey" or "new_b58_pubkey"
+      - "atomic_account_swap": Need to provide a valid "new_enc_pubkey" or "new_b58_pubkey" and the unlocking param "enc_hash_lock"
+      - "atomic_coin_swap": Need to provide a valid "enc_hash_lock"
+      - If no "type" is defined, will automatically select between "public_sale" or "private_sale"
+    - Added "enc_hash_lock" (HEXASTRING) that must be exactly a 32 bytes value (stored as 64 bytes because is HexaString)
 TODO  
 - TODO: RPC calls for PIP-0029
 - TODO Implement Seal calculation

+ 19 - 18
src/core/UOpTransaction.pas

@@ -210,7 +210,8 @@ Type
 
 
 Const
-  CT_TOpListAccountData_NUL : TOpListAccountData = (account_signer:0;account_target:0;operation_type:lat_Unknown;n_operation:0;account_price:0;account_to_pay:0;fee:0;payload:Nil;public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;sign:(r:Nil;s:Nil));
+  CT_TOpListAccountData_NUL : TOpListAccountData = (account_signer:0;account_target:0;operation_type:lat_Unknown;n_operation:0;account_state:as_Unknown;account_price:0;account_to_pay:0;fee:0;
+    hash_lock:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);payload:Nil;public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;sign:(r:Nil;s:Nil));
   CT_TOpChangeAccountInfoData_NUL : TOpChangeAccountInfoData = (account_signer:0;account_target:0;n_operation:0;fee:0;payload:Nil;public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);changes_type:[];
     new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_name:Nil;new_type:0;sign:(r:Nil;s:Nil));
 
@@ -249,7 +250,7 @@ Type
     function GetOpSubType : Integer;
   public
     class function OpType : Byte; override;
-    Constructor CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; AListOpSubType : Integer; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey; const AHashLock : T32Bytes; const APayload: TRawBytes);
+    Constructor CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; ANewAccountState : TAccountState; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey; const AHashLock : T32Bytes; const APayload: TRawBytes);
     property OpSubType : Integer read GetOpSubType;
   End;
 
@@ -2258,26 +2259,29 @@ end;
 
 { TOpListAccountForSaleOrSwap }
 
-constructor TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; AListOpSubType : Integer; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey;  const AHashLock : T32Bytes; const APayload: TRawBytes);
+constructor TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; ANewAccountState : TAccountState; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey;  const AHashLock : T32Bytes; const APayload: TRawBytes);
 begin
   inherited Create(ACurrentProtocol);
-  if NOT (AListOpSubType IN [CT_OpSubtype_ListAccountForPublicSale, CT_OpSubtype_ListAccountForPrivateSale, CT_OpSubtype_ListAccountForAccountSwap, CT_OpSubtype_ListAccountForCoinSwap]) then
-    raise EArgumentOutOfRangeException.Create('Invalid list operation sub type');
-
-  case AListOpSubType of
-     CT_OpSubtype_ListAccountForPublicSale: begin
-       if (FData.new_public_key.EC_OpenSSL_NID<>0) then
-         raise EArgumentOutOfRangeException.Create('Public sale must be to a null key');
-       FData.account_state := as_ForSale;
-     end;
-     CT_OpSubtype_ListAccountForPrivateSale: FData.account_state := as_ForSale;
-     CT_OpSubtype_ListAccountForAccountSwap: FData.account_state := as_ForAtomicAccountSwap;
-     CT_OpSubtype_ListAccountForCoinSwap: FData.account_state := as_ForAtomicCoinSwap;
+  case ANewAccountState of
+    as_Normal: raise EArgumentOutOfRangeException.Create('Listing to normal state is not a listing');
+    as_ForSale: ;
+    as_ForAtomicAccountSwap: ;
+    as_ForAtomicCoinSwap: ;
+  else
+    raise EArgumentOutOfRangeException.Create('Invalid new list account state');
   end;
+
   FData.account_signer := AAccountSigner;
   FData.account_target := AAccountTarget;
   FData.operation_type := lat_ListAccount;
   FData.n_operation := ANOperation;
+
+  FData.account_state := ANewAccountState;
+  if ANewAccountState in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap] then begin
+    // Hash lock is stored only if AtomicSwap
+    FData.hash_lock := AHashLock;
+  end;
+  FData.locked_until_block := ALockedUntilBlock;
   FData.account_price := AAccountPrice;
   FData.account_to_pay := AAccountToPay;
   FData.fee := AFee;
@@ -2285,9 +2289,6 @@ begin
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // FData.public_key := key.PublicKey;
   FData.new_public_key := ANewPublicKey;
-  FData.locked_until_block := ALockedUntilBlock;
-  if AListOpSubType in [CT_OpSubtype_ListAccountForAccountSwap, CT_OpSubtype_ListAccountForCoinSwap] then
-    FData.hash_lock := AHashLock;
 
   if Assigned(AKey) then begin
     FData.sign := TCrypto.ECDSASign(AKey.PrivateKey, GetDigestToSign(ACurrentProtocol));

+ 49 - 33
src/core/URPC.pas

@@ -1145,7 +1145,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
   // This function creates a TOpListAccountForSale without looking for actual state (cold wallet)
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
-  Function CreateOperationListAccountForSale(current_protocol : Word; AListType : Word; account_signer, account_last_n_operation, account_listed : Cardinal; const account_signer_pubkey: TAccountKey;
+  Function CreateOperationListAccountForSale(current_protocol : Word; ANewAccountState : TAccountState; account_signer, account_last_n_operation, account_listed : Cardinal; const account_signer_pubkey: TAccountKey;
     account_price : UInt64; locked_until_block : Cardinal; account_to_pay : Cardinal; Const new_account_pubkey : TAccountKey;
     fee : UInt64; const AHashLock: T32Bytes; const RawPayload : TRawBytes; Const Payload_method, EncodePwd : String) : TOpListAccountForSaleOrSwap;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
@@ -1163,7 +1163,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     if Not CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,account_signer_pubkey,aux_target_pubkey,f_raw) then Exit(Nil);
     Result := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(
       current_protocol,
-      AListType,
+      ANewAccountState,
       account_signer,
       account_last_n_operation+1,
       account_listed,
@@ -1536,7 +1536,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
   function SignListAccountForSaleEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; current_protocol : Word; const actualAccounKey : TAccountKey; last_n_operation : Cardinal) : boolean;
     // params:
-    // "type" (optional) is the type of listing to perform public_sale, private_sale, atomic_account_swap, atomic_coin_swap
+    // "type" (optional) is the type of listing to perform "public_sale", "private_sale", "atomic_account_swap", "atomic_coin_swap"
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the account being listed
     // "locked_until_block" is until which block will be locked this account (Note: A locked account cannot change it's state until sold or finished lock)
@@ -1546,13 +1546,14 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     // "enc_hash_lock" (optional) hex-encoded hash-lock for an atomic swap
   var
     opSale: TOpListAccountForSaleOrSwap;
-    listType : Integer;
+    LListType : TAccountState;
     account_signer, account_target, seller_account : Cardinal;
     locked_until_block : Cardinal;
     price,fee : Int64;
-    new_pubkey : TAccountKey;
-    LHasHashLock : Boolean;
-    LHashLock : T32Bytes;
+    LNew_pubkey : TAccountKey;
+    LHasHashLock, LHasNewPubkey : Boolean;
+    LHashLock32 : T32Bytes;
+    LHashLockRaw : TRawBytes;
     LStrVal : String;
   begin
     Result := false;
@@ -1593,55 +1594,70 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       Exit;
     end;
     if (params.IndexOfName('new_b58_pubkey')>=0) or (params.IndexOfName('new_enc_pubkey')>=0) then begin
-      If Not CapturePubKey('new_',new_pubkey,ErrorDesc) then begin
+      If Not CapturePubKey('new_',LNew_pubkey,ErrorDesc) then begin
         ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
         Exit;
       end;
-    end else new_pubkey := CT_TECDSA_Public_Nul;
+      LHasNewPubkey := True;
+    end else begin
+      LHasNewPubkey := False;
+      LNew_pubkey := CT_TECDSA_Public_Nul;
+    end;
 
     LHasHashLock := False;
-    LHashLock := CT_HashLock_NUL;
+    LHashLock32 := CT_HashLock_NUL;
     if (params.IndexOfName('enc_hash_lock') >= 0) then begin
       LStrVal := params.AsString('enc_hash_lock', '');
-      if (NOT TCrypto.IsHexString( LStrVal )) OR (Length(LStrVal) <> 32*2) then begin
-         ErrorNum := CT_RPC_ErrNum_InvalidData;
-         ErrorDesc := 'Invalid "enc_hash_lock" value. Must be 32 byte hexadecimal string.';
-         Exit;
+      if TCrypto.HexaToRaw(LStrVal,LHashLockRaw) then begin
+        if Length(LHashLockRaw)=32 then begin
+          LHasHashLock := True;
+          LHashLock32 := TBaseType.To32Bytes( LHashLockRaw );
+        end;
+      end;
+      if Not LHasHashLock then begin
+        ErrorNum := CT_RPC_ErrNum_InvalidData;
+        ErrorDesc := 'Invalid "enc_hash_lock" value. Must be 32 byte hexadecimal string.';
+        Exit;
       end;
-      LHasHashLock := True;
-      LHashLock := TBaseType.To32Bytes( TCrypto.HexaToRaw( LStrVal ) );
     end;
 
     if params.IndexOfName('type') >= 0 then begin
       LStrVal := params.AsString('type', '');
-      if (LStrVal = 'public_sale') then
-        listType := CT_OpSubtype_ListAccountForPublicSale
-      else if (LStrVal = 'private_sale') then
-        listType := CT_OpSubtype_ListAccountForPrivateSale
+      if (LStrVal = 'public_sale') then begin
+        LListType := as_ForSale;
+        if LHasNewPubkey then begin
+          ErrorNum := CT_RPC_ErrNum_InvalidData;
+          ErrorDesc := 'public_sale type must not contain new public key param';
+          Exit;
+        end;
+      end else if (LStrVal = 'private_sale') then begin
+        LListType := as_ForSale;
+        if Not LHasNewPubkey then begin
+          ErrorNum := CT_RPC_ErrNum_InvalidData;
+          ErrorDesc := 'private_sale type must contain new public key param';
+          Exit;
+        end;
+      end else if (LStrVal = 'atomic_account_swap') then
+        LListType := as_ForAtomicAccountSwap
       else if (LStrVal = 'atomic_coin_swap') then
-        listType := CT_OpSubtype_ListAccountForAccountSwap
-      else if (LStrVal = 'public_sale') then
-        listType := CT_OpSubtype_ListAccountForCoinSwap
+        LListType := as_ForAtomicCoinSwap
       else begin
         ErrorNum := CT_RPC_ErrNum_InvalidData;
-        ErrorDesc := 'Invalid "type" value';
+        ErrorDesc := 'Invalid "type" value provided: "'+LStrVal+'"';
         Exit;
       end;
-      if (listType in [CT_OpSubtype_ListAccountForAccountSwap, CT_OpSubtype_ListAccountForCoinSwap]) and (NOT LHasHashLock) then begin
+      if (LListType in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) and (NOT LHasHashLock) then begin
         ErrorNum := CT_RPC_ErrNum_InvalidData;
-        ErrorDesc := 'Missing "enc_hash_lock" field. Required for atomic swaps';
+        ErrorDesc := 'Missing "enc_hash_lock" param. Required for atomic swaps';
         Exit;
       end;
     end else begin
-      // type not specified, implied private or public sale, figure out based on key
-      if new_pubkey.EC_OpenSSL_NID = CT_TECDSA_Public_Nul.EC_OpenSSL_NID then
-        listType := CT_OpSubtype_ListAccountForPublicSale
-      else
-        listType := CT_OpSubtype_ListAccountForPrivateSale;
+      // type not specified, implied private or public sale, based on provided new publick key or not
+      LListType := as_ForSale;
     end;
 
-    opSale := CreateOperationListAccountForSale(current_protocol, listType, account_signer,last_n_operation,account_target,actualAccounKey,price,locked_until_block,
-      seller_account, new_pubkey,fee, LHashLock,
+    opSale := CreateOperationListAccountForSale(current_protocol, LListType, account_signer,last_n_operation,account_target,actualAccounKey,price,locked_until_block,
+      seller_account, LNew_pubkey,fee, LHashLock32,
       TCrypto.HexaToRaw(params.AsString('payload','')),
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
     if opSale=nil then exit;

+ 8 - 4
src/gui-classic/UFRMOperation.pas

@@ -299,13 +299,17 @@ loop_start:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         if (rbListAccountForPublicSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForPublicSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
+            destAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
         end else if (rbListAccountForPrivateSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForPrivateSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
+            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
         end  else if (rbListAccountForAccountSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForAccountSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicAccountSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
+            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end  else if (rbListAccountForCoinSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForCoinSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicCoinSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
+            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end else raise Exception.Create('Select Sale type');
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin