Techworker 5 years ago
parent
commit
1e9ca3b1d4
100 changed files with 11257 additions and 2611 deletions
  1. 5 0
      .gitignore
  2. 68 19
      PIP/PIP-0035.md
  3. 714 0
      PIP/PIP-0036.md
  4. 2 0
      PIP/README.md
  5. 0 0
      PIP/resources/PIP-0009/Random Hash Diagram.draw.io.xml
  6. 368 116
      src/core/UAccounts.pas
  7. 5 2
      src/core/UBaseTypes.pas
  8. 169 59
      src/core/UBlockChain.pas
  9. 24 15
      src/core/UConst.pas
  10. 42 10
      src/core/UCrypto.pas
  11. 5 2
      src/core/UFileStorage.pas
  12. 28 1
      src/core/UNetProtection.pas
  13. 18 8
      src/core/UNetProtocol.pas
  14. 13 7
      src/core/UNode.pas
  15. 259 149
      src/core/UOpTransaction.pas
  16. 7 4
      src/core/UPCOperationsSignatureValidator.pas
  17. 274 0
      src/core/UPCRPCFindAccounts.pas
  18. 449 0
      src/core/UPCRPCOpData.pas
  19. 11 5
      src/core/UPCSafeBoxRootHash.pas
  20. 6 4
      src/core/UPCTNetDataExtraMessages.pas
  21. 105 26
      src/core/UPoolMinerThreads.pas
  22. 4 1
      src/core/UPoolMining.pas
  23. 359 204
      src/core/URPC.pas
  24. 41 18
      src/core/URandomHash.pas
  25. 1144 0
      src/core/URandomHash2.pas
  26. 2 2
      src/core/UServerApp.pas
  27. 2 2
      src/core/USettings.pas
  28. 53 24
      src/core/UTxMultiOperation.pas
  29. 2 2
      src/core/UWallet.pas
  30. 4 4
      src/core/upcdaemon.pas
  31. 2 0
      src/gui-classic/UFRMAbout.pas
  32. 10 7
      src/gui-classic/UFRMAccountSelect.dfm
  33. 23 24
      src/gui-classic/UFRMAccountSelect.lfm
  34. 31 10
      src/gui-classic/UFRMAccountSelect.pas
  35. 90 0
      src/gui-classic/UFRMDiagnosticTool.dfm
  36. 66 0
      src/gui-classic/UFRMDiagnosticTool.lfm
  37. 479 0
      src/gui-classic/UFRMDiagnosticTool.pas
  38. 3 0
      src/gui-classic/UFRMDiagnosticTool.vlb
  39. 113 0
      src/gui-classic/UFRMHashLock.lfm
  40. 52 31
      src/gui-classic/UFRMHashLock.pas
  41. 23 26
      src/gui-classic/UFRMOperation.dfm
  42. 412 294
      src/gui-classic/UFRMOperation.lfm
  43. 99 26
      src/gui-classic/UFRMOperation.pas
  44. 1 1
      src/gui-classic/UFRMOperationsExplorer.pas
  45. 3 3
      src/gui-classic/UFRMPascalCoinWalletConfig.pas
  46. 2 1
      src/gui-classic/UFRMPayloadDecoder.dfm
  47. 6 2
      src/gui-classic/UFRMPayloadDecoder.pas
  48. 2 8
      src/gui-classic/UFRMRPCCalls.dfm
  49. 2 0
      src/gui-classic/UFRMRPCCalls.pas
  50. 44 0
      src/gui-classic/UFRMRandomHashUtil.dfm
  51. 127 0
      src/gui-classic/UFRMRandomHashUtil.pas
  52. 9 2
      src/gui-classic/UFRMRandomOperations.lfm
  53. 270 12
      src/gui-classic/UFRMRandomOperations.pas
  54. 109 24
      src/gui-classic/UFRMWallet.pas
  55. 34 9
      src/gui-classic/UGridUtils.pas
  56. 273 0
      src/libraries/cryptolib4pascal/ClpAESPRNGRandom.pas
  57. 0 101
      src/libraries/cryptolib4pascal/ClpAbstractECMultiplier.pas
  58. 4 0
      src/libraries/cryptolib4pascal/ClpAesEngine.pas
  59. 4 0
      src/libraries/cryptolib4pascal/ClpAesLightEngine.pas
  60. 8 0
      src/libraries/cryptolib4pascal/ClpAgreementUtilities.pas
  61. 65 50
      src/libraries/cryptolib4pascal/ClpArgon2ParametersGenerator.pas
  62. 45 12
      src/libraries/cryptolib4pascal/ClpArrayUtils.pas
  63. 28 46
      src/libraries/cryptolib4pascal/ClpAsn1Objects.pas
  64. 1 1
      src/libraries/cryptolib4pascal/ClpBigInteger.pas
  65. 48 0
      src/libraries/cryptolib4pascal/ClpBits.pas
  66. 2 2
      src/libraries/cryptolib4pascal/ClpBlockCipherModes.pas
  67. 4 0
      src/libraries/cryptolib4pascal/ClpBlowfishEngine.pas
  68. 1 1
      src/libraries/cryptolib4pascal/ClpBsiObjectIdentifiers.pas
  69. 1 1
      src/libraries/cryptolib4pascal/ClpBufferedBlockCipher.pas
  70. 6 0
      src/libraries/cryptolib4pascal/ClpChaChaEngine.pas
  71. 90 0
      src/libraries/cryptolib4pascal/ClpCryptLibObjectIdentifiers.pas
  72. 3 36
      src/libraries/cryptolib4pascal/ClpCryptoApiRandomGenerator.pas
  73. 1 1
      src/libraries/cryptolib4pascal/ClpCryptoLibTypes.pas
  74. 55 25
      src/libraries/cryptolib4pascal/ClpCurve25519Custom.pas
  75. 48 42
      src/libraries/cryptolib4pascal/ClpCustomNamedCurves.pas
  76. 187 0
      src/libraries/cryptolib4pascal/ClpDHAgreement.pas
  77. 148 0
      src/libraries/cryptolib4pascal/ClpDHBasicAgreement.pas
  78. 104 0
      src/libraries/cryptolib4pascal/ClpDHBasicKeyPairGenerator.pas
  79. 242 0
      src/libraries/cryptolib4pascal/ClpDHDomainParameters.pas
  80. 78 0
      src/libraries/cryptolib4pascal/ClpDHKeyGenerationParameters.pas
  81. 140 0
      src/libraries/cryptolib4pascal/ClpDHKeyGeneratorHelper.pas
  82. 104 0
      src/libraries/cryptolib4pascal/ClpDHKeyPairGenerator.pas
  83. 115 0
      src/libraries/cryptolib4pascal/ClpDHKeyParameters.pas
  84. 266 0
      src/libraries/cryptolib4pascal/ClpDHParameters.pas
  85. 88 0
      src/libraries/cryptolib4pascal/ClpDHParametersGenerator.pas
  86. 270 0
      src/libraries/cryptolib4pascal/ClpDHParametersHelper.pas
  87. 117 0
      src/libraries/cryptolib4pascal/ClpDHPrivateKeyParameters.pas
  88. 135 0
      src/libraries/cryptolib4pascal/ClpDHPublicKeyParameters.pas
  89. 103 0
      src/libraries/cryptolib4pascal/ClpDHValidationParameters.pas
  90. 145 0
      src/libraries/cryptolib4pascal/ClpDHValidationParams.pas
  91. 6 26
      src/libraries/cryptolib4pascal/ClpDigestRandomGenerator.pas
  92. 1 1
      src/libraries/cryptolib4pascal/ClpDsaKeyPairGenerator.pas
  93. 7 6
      src/libraries/cryptolib4pascal/ClpDsaKeyParameters.pas
  94. 1 1
      src/libraries/cryptolib4pascal/ClpDsaParametersGenerator.pas
  95. 11 4
      src/libraries/cryptolib4pascal/ClpDsaPrivateKeyParameters.pas
  96. 6 6
      src/libraries/cryptolib4pascal/ClpDsaPublicKeyParameters.pas
  97. 243 974
      src/libraries/cryptolib4pascal/ClpECAlgorithms.pas
  98. 319 105
      src/libraries/cryptolib4pascal/ClpECC.pas
  99. 1534 0
      src/libraries/cryptolib4pascal/ClpECCompUtilities.pas
  100. 5 6
      src/libraries/cryptolib4pascal/ClpECDHBasicAgreement.pas

+ 5 - 0
.gitignore

@@ -48,3 +48,8 @@ __history/
 __recovery/
 __recovery/
 dunit.ini
 dunit.ini
 *.res
 *.res
+/*.DS_Store
+/PIP/*.DS_Store
+/PIP/resources/*.DS_Store
+/PIP/resources/PIP-0034A/*.DS_Store
+/src/*.tvsconfig

+ 68 - 19
PIP/PIP-0035.md

@@ -1,6 +1,6 @@
 <pre>
 <pre>
   PIP: PIP-0035
   PIP: PIP-0035
-  Title: Block Policy: Layer-1 Governance (Velocity Cash)
+  Title: Block Policy: Layer-1 Governance
   Type: User Interface
   Type: User Interface
   Impact: None
   Impact: None
   Author(s): Herman Schoenfeld <[email protected]>
   Author(s): Herman Schoenfeld <[email protected]>
@@ -13,18 +13,21 @@
 
 
 ## Summary
 ## Summary
 
 
-A simple and efficient layer-1 governance model is proposed called "Block Policy". In the opinion of the author, the introduction of Block Policy is a game-changing innovation that fundamentally changes the underlying "economic code" of PascalCoin, and any other cryptocurrency which adopts it. Whilst only a minor technical change, it solves many problems afflicting cryptocurrencies today. 
+A simple and efficient layer-1 governance model is proposed called "Block Policy". In the opinion of the author, the introduction of Block Policy is a significant innovation that fundamentally changes the underlying "economic code" of PascalCoin, and any other cryptocurrency which adopts it. Whilst only a minor technical change, it solves many problems afflicting cryptocurrencies today. 
 
 
-## Re-brand & License
+## Re-brand Option
 
 
-If adopted, this proposal will fundamentally change the original vision of PascalCoin. As a result, a re-brand is now warranted and fully justified. The proposed new brand for the project is "Velocity Cash" and domain names and web-assets have been secured for the re-branding. Also, since the author has [contributed significantly][1] to PascalCoin's architecture and code, the author imposes the following conditions on the adoption of this proposal:
-* Block Policy is adopted fully or not at all;
-* PascalCoin is re-branded to Velocity Cash;
-* The author shall be legally considered a "co-founder" of Velocity Cash.
+If adopted, this proposal will fundamentally change the original vision of PascalCoin. As a result, a re-brand is now warranted and fully justified. As a result, it is recommended that:
+* Block Policy be adopted fully;
+* PascalCoin is re-branded;
 
 
-No part of this proposal may be disregarded in the implementation without the consent of the author. Apart from this condition, GNU Public License applies.
+Alternatively, another option is:
+* Block Policy be adopted partially;
+* PascalCoin brand is retained;
 
 
-For the remainder of this document, PascalCoin will herein be referred to as "Velocity Cash".
+In the case of the former, the proposed new brand for the project is "Velocity Cash" and domain names and web-assets have been secured for this re-branding. However, the proposed re-branding is flexible and should require consensus from community.
+
+In the interim, PascalCoin will herein be referred to as "Velocity Cash" for the remainder of this document even though it may be not be re-branded or subject to a different re-brand.
 
 
 ## Layer-1 Governance
 ## Layer-1 Governance
 
 
@@ -36,7 +39,6 @@ In short, the proposal is to:
 * Allow these variables to change on a block-by-block basis, enabling real-time governance.
 * Allow these variables to change on a block-by-block basis, enabling real-time governance.
 * No longer award new accounts to miners, instead issue them without ownership and list them for public sale at a floating price. 
 * No longer award new accounts to miners, instead issue them without ownership and list them for public sale at a floating price. 
 
 
-
 #### Diagram
 #### Diagram
 
 
 ![Layer 1 Voting](resources/PIP-0035/Layer-1-Voting.png)
 ![Layer 1 Voting](resources/PIP-0035/Layer-1-Voting.png)
@@ -171,6 +173,32 @@ Post-implementation, the following methodologies are to be researched and develo
 
 
 Vote tallying methodologies are crucial for security, so as to prevent malicious actors from seizing control of the protocol.
 Vote tallying methodologies are crucial for security, so as to prevent malicious actors from seizing control of the protocol.
 
 
+## Governance Security
+
+A major concern with the implementation of Block Policy is preventing the hijacking, attack and seizure of the policy by malcious actors or economically disproportionate actors ("whales").
+
+This can be achieved by employing implementing this feature in the following manner:
+
+1. **Autocratic Model**: In the initial implementation, the account 0-10 (controlled by Albert Molina) defined the block policy. This will remain in effect until a suitable time that dust settles from this change. During this period, the community can socially organize on Discord/Social Media to determine minor policy adjustments and Albert implements them.  Minor development/infrastructure adjustments can be made to fund specific projects during this period.
+
+2. **Aristocratic Model**: After some time, this model is enabled allowing account holders to vote on policy on a 1 PASC = 1 VOTE basis. Conceptually this will work because it's the same a Delegated Proof-of-Stake used in EOS and other billion dollar coins. 
+
+3. During (2), we allow a 2-year period where the Aristocratic model can be changed back to Autocratic using account 0-10. This is to ensure the protocol can be recovered in case the Block Policy is attacked, hijacked or mismanaged.
+
+After a suitable period with significant R&D, the final and permanent governance model is rolled out.
+
+4. **Democratic Model**: this works on 1 USER = 1 VOTE governance system for determining Block Policy. The author has significant R&d here to achieve this. Users could always vote to rollback to Aristocratic in case something goes wrong.
+
+**NOTE** in between (3) and (4) there are also **Nobility Model** which can be rolled out. The Nobility Model encompasses a set of methodologies that represent a logical interpolation between Aristocratic to Democratic systems.
+
+## Policy Limits
+
+In order to prevent catastrophic failure such as issuing a billion new coins in a block, the byte lengths of policy variables are chosen so as to impose **natural limits** on the policy values.
+
+For example, the miner reward is a 1 byte variable which constrains from 0 to 255 PASC. At any time, a maximum of 255 PASC can be issued as the miner reward. Even if the policy were temporarily hi-jacked by a mining pool, it would not be possible to cause any significant harm to eco-system in the time intervention was applied.
+
+If byte lengths are insufficient to impose limits, hard-coded rule-based limits can be imposed on variables.
+
 ## Benefits
 ## Benefits
 
 
 The benefits of this change are profound in nature and includes the following benefits:
 The benefits of this change are profound in nature and includes the following benefits:
@@ -179,7 +207,7 @@ The benefits of this change are profound in nature and includes the following be
 
 
 This proposal introduces in-protocol real-time governance in layer-1 and establishes a cryptographically secure voting mechanism that can be audited by anyone, anytime, permission-free and instantly. By moving economic constants from the source-code and into the Block Policy, the layer-1 protocol is now fundamentally enhanced with "external-world feedback". This has far-reaching consequences at every level of the project including security, usability, adoption, governance and market-price.
 This proposal introduces in-protocol real-time governance in layer-1 and establishes a cryptographically secure voting mechanism that can be audited by anyone, anytime, permission-free and instantly. By moving economic constants from the source-code and into the Block Policy, the layer-1 protocol is now fundamentally enhanced with "external-world feedback". This has far-reaching consequences at every level of the project including security, usability, adoption, governance and market-price.
 
 
-Also, this proposal puts control of the protocol squarely in the hands of the users who can hire and fire developers and infrastructure providers via voting. It puts the economic power in the hands of the users since they can influence the price of the cryptocurrency via real-time adjustments to inflation. Over time, this role can be delegated to AI-trained bots who regulate the protocol for the benefit of the community using neural networks trained on past behaviour.
+Also, this proposal puts control of the protocol squarely in the hands of the users who can hire and fire developers and infrastructure providers via voting. It puts the economic power in the hands of the users since they can influence the price of the cryptocurrency via real-time adjustments to inflation. In the future, this role may optionally - depending on the community's approval - be delegated to AI-trained bots who regulate the protocol for the benefit of the community using neural networks trained on past behaviour.
 
 
 ### Crypto-Political Parties
 ### Crypto-Political Parties
 
 
@@ -199,7 +227,7 @@ By funding miners, developers and infrastructure directly from the protocol, a d
 
 
 ### Dynamic-Inflation 
 ### Dynamic-Inflation 
 
 
-By enabling onthe community to vote on what the miner, developer and infrastructure rewards should be for the next block, the issuance-model of Velocity Cash is fundamentally changed from a "gold-like" model to a "fiat-like" model. Whilst many in cryptocurrency may consider this sacrilege, the reader is encouraged to understand the difference between "negative fiat" and "positive fiat" currencies. In the modern era, national central banks (with the exception of Iran) issue fiat currency as a debt-based instrument into the economy. In this model, the newly issued fiat currency represents a debt which must be repaid later by future issuance of this debt-based fiat (but compounded with interest). Such debt-based currency represents a "negative value" which almost always results in a pyramid-scheme style collapse causing the familiar negative economic outcomes of bankruptcy, recession, capital hoarding and centralization. In the "positive fiat-currency" model, the currency is issued as credit-based instrument that attains a "positive value" determined purely by the market supply/demand (just as cryptocurrencies are today). This "positive fiat-currency" model has historically successful precedents such as the [Colonial Scrip][3] which effectively built the American Colonies and the [Tally-Stick][4] system which effectively built the British Empire (before it collapsed soon after the introduction of the debt-based "British Pound").
+By enabling on the community to vote on what the miner, developer and infrastructure rewards should be for the next block, the issuance-model of Velocity Cash is fundamentally changed from a "gold-like" model to a "fiat-like" model. Whilst many in cryptocurrency may consider this sacrilege, the reader is encouraged to understand the difference between "negative fiat" and "positive fiat" currencies. In the modern era, national central banks (with the exception of Iran) issue fiat currency as a debt-based instrument into the economy. In this model, the newly issued fiat currency represents a debt which must be repaid later by future issuance of this debt-based fiat (but compounded with interest). Such debt-based currency represents a "negative value" which almost always results in a pyramid-scheme style collapse causing the familiar negative economic outcomes of bankruptcy, recession, capital hoarding and centralization. In the "positive fiat-currency" model, the currency is issued as credit-based instrument that attains a "positive value" determined purely by the market supply/demand (just as cryptocurrencies are today). This "positive fiat-currency" model has historically successful precedents such as the [Colonial Scrip][3] which effectively built the American Colonies and the [Tally-Stick][4] system which effectively built the British Empire (before it collapsed soon after the introduction of the debt-based "British Pound").
 
 
 The point here is that the negative outcomes commonly associated with "fiat currencies" are more to do with the "debt-based" aspect than the "fiat" aspect. This is an important and nuanced point.
 The point here is that the negative outcomes commonly associated with "fiat currencies" are more to do with the "debt-based" aspect than the "fiat" aspect. This is an important and nuanced point.
 
 
@@ -207,23 +235,31 @@ The point here is that the negative outcomes commonly associated with "fiat curr
 
 
 One of the biggest flaws in cryptocurrency architecture today is the inflation distribution. Most cryptocurrencies simply distribute the new inflation to the miners who then act as first source of value utilization (usually for dumping on markets). This gives miners a disproportionately biased economic role within the eco-system. Miners only secure the network, they do not write the code, design the protocol, develop wallets or run exchanges. Yet they receive all the new inflation. By distributing the new inflation between miners, developers and infrastructure a more balanced distribution is achieved resulting in more efficient utilization of the newly created value.
 One of the biggest flaws in cryptocurrency architecture today is the inflation distribution. Most cryptocurrencies simply distribute the new inflation to the miners who then act as first source of value utilization (usually for dumping on markets). This gives miners a disproportionately biased economic role within the eco-system. Miners only secure the network, they do not write the code, design the protocol, develop wallets or run exchanges. Yet they receive all the new inflation. By distributing the new inflation between miners, developers and infrastructure a more balanced distribution is achieved resulting in more efficient utilization of the newly created value.
 
 
-### First True Stable-Coin
+### Long-Term Stable Value
+
+Much in the same way that central banks have proven they can stabilize the value of national currencies through monetary policy, it is expected that the community can similarly stabilize the cryptocurrency value using the tools of real-time dynamic inflation adjustments. It should be stressed that Velocity Cash is not a stablecoin in the traditional sense. It is not strictly pegged to any price. Rather, it follows a piecewise price trajectory depending on the social contract. 
+
+For example, if the Block Policy agrees that the monetary policy should attain $100 then the inflation would be at a bare minimum in order to support the miners, developers, and infrastructure providers. If the price at last attains $100, then inflation would increase in order to signal both an economic and a psychological deterrent for further price growth. However, if there is significantly more demand for Velocity Cash then the "soft" peg to $100 could then be allowed to adjust upwards subject to the Block Policy. An infinite ROI due to a forever increasing price is inherently unsustainable and harmful to the cryptocurrency; as a result, the Block Policy offers the opportunity and tool to serve as a soft stabilizer for Velocity Cash's price especially at a high market capitalization.
+
+To promote a stable value in the other direction, the Block Policy also involves deflation adjustments through controlled burning of already circulating coin supply. The following sources of burning can be achieved:
+
+1. **Coinrot**: coins on abandoned accounts due to user death/lost keys are considered rotten coins. Instead of awarding expired PASAs and their PASC to miners, the OP_RECOVER can be changed to simply burn these coins 
 
 
-Much in the same way that central banks have proven they can stabilize the value of national currencies through monetary policy, it is expected that the community can similarly stabilize the cryptocurrency value using the tools of real-time dynamic inflation adjustments. Over-time, this task can be delegated to an AI-driven bot that adjusts inflation variables in real-time based on neural networks trained with historical  market data in order to achieve super-stability with controlled value appreciation (i.e. 10% per/annum). Once this system is built, users can simply vote to activate it.
+2. **Transaction Fees**: since miners are guaranteed miner reward, the transaction fee is no longer needed to incentivize mining. Instead, these fees are burnt and serve as a drain to extinguish coin supply.
 
 
-A fundamental achievement here is the first true stablecoin that attains it's stability intrinsically **without** dependence on an external backing commodity.
+3. Other multiple sources, not disclosed in this pip but will be revealed if/when after PIP approval.
 
 
 ![Stable Coin](resources/PIP-0035/StableCoin.png)
 ![Stable Coin](resources/PIP-0035/StableCoin.png)
 
 
-### Solves the Pump-n-Dump Problem
+#### Deterrent to Pump-n-Dump Problem
 
 
 Much in the same way that the threat of force by law-enforcement prevents crimes from occurring, the threat of real-time inflation-intervention via community governance thwarts market manipulation. As a result, "whale" activity, hoarding and pump-n-dumping problems are expected to be greatly diminished.
 Much in the same way that the threat of force by law-enforcement prevents crimes from occurring, the threat of real-time inflation-intervention via community governance thwarts market manipulation. As a result, "whale" activity, hoarding and pump-n-dumping problems are expected to be greatly diminished.
 
 
 ![Why Real Time Voting Is Better](resources/PIP-0035/WhyRealTimeVotingBetter.png)
 ![Why Real Time Voting Is Better](resources/PIP-0035/WhyRealTimeVotingBetter.png)
 
 
-### Trading-Pair for DEX
+### Reserve-Currency for a DEX
 
 
-If Velocity Cash can achieves stablecoin status, it will be the ideal base currency for a decentralized-exchange (DEX). The author of this PIP has designed a high-frequency DEX, and with the recently added [atomic swap functionality][1], Velocity Cash is perfectly positioned for this role.
+If Velocity Cash can achieves "soft stablecoin" status, it can serve as an ideal base currency in a decentralized-exchange (DEX), much in the same way that USDT serves this purpose today. The fundamental difference is that it is **not** "stablecoin" in the sense of having a fixed-value based on external backing, instead it's value is sensibly stablized via social consensus of the eco-system of users of the currency. The author of this PIP has designed a high-frequency DEX, and with the recently added [atomic swap functionality][1], Velocity Cash is perfectly positioned for this role.
 
 
 ### Currency for Commerce
 ### Currency for Commerce
 
 
@@ -249,6 +285,15 @@ In the same way PASA issuance can be increased to support new adoption, it can a
 
 
 Since new PASA will no longer be issued to miners, they need to be purchased from day 1. Also, since PASA issuance is dynamic and controlled by the community, their prices will always be low. This disincentives PASA hoarding completely.
 Since new PASA will no longer be issued to miners, they need to be purchased from day 1. Also, since PASA issuance is dynamic and controlled by the community, their prices will always be low. This disincentives PASA hoarding completely.
 
 
+## Upcoming PIP Disclosures
+
+The following will be disclosed in full pending pip approval:
+
+1. Security against gaming the Block Policy via an Aristocratic voting methodology.
+2. Technical implementation of the Democratic voting methodology.
+3. Sources of deflation control.
+4. Others.
+
 ## Rationale
 ## Rationale
 
 
 Since Block Policy is a simple change that fits naturally into the SafeBox model, it is proposed for upcoming V5 release.
 Since Block Policy is a simple change that fits naturally into the SafeBox model, it is proposed for upcoming V5 release.
@@ -256,7 +301,11 @@ Since Block Policy is a simple change that fits naturally into the SafeBox model
 ## Backwards Compatibility
 ## Backwards Compatibility
 
 
 This change is not backwards compatible and requires a hard-fork activation. 
 This change is not backwards compatible and requires a hard-fork activation. 
- 
+
+## Acknowledgements
+
+* Tyler Swob for follow-up edits, in-depth discussion and helpful suggestions for altering the tone of this proposal.
+
 ## Links
 ## Links
 
 
 1. [PascalCoin Improvement Proposals][1]
 1. [PascalCoin Improvement Proposals][1]

+ 714 - 0
PIP/PIP-0036.md

@@ -0,0 +1,714 @@
+<pre>
+  PIP: PIP-0036
+  Title: RandomHash2: Enhanced GPU & ASIC Resistant Hash Algorithm
+  Type: Protocol
+  Impact: Hard-Fork
+  Author: Herman Schoenfeld <i>&lt;[email protected]&gt;</i>
+  Comments-URI: https://discord.gg/sJqcgtD  (channel #pip-0036)
+  Status: Active
+  Created: 2019-07-31
+</pre>
+
+## Summary
+
+A major revision of RandomHash, a GPU and ASIC resistant hashing algorithm, is proposed.
+
+## Motivation
+
+Since the fundamental motivations behind RandomHash2 follow that of [RandomHash][1], only the changes and motivations of moving from RandomHash to RandomHash2 are discussed in this document. The reader is advised to familiarize themselves with [PIP-0009][1] for full context. 
+
+In short, the purpose of RandomHash2 is to address the the need for faster block-header validation currently afflicting PascalCoin due to the predecessor RandomHash PoW algorithm.
+
+## Background 
+
+Whilst RandomHash appears to have empirically achieved it's GPU and ASIC resistivity goals, it's computationally-heavy nature has resulted in an unforeseen consequence of slow blockchain validation.
+
+First of all, RandomHash introduces the concept of nonce-dependencies between themselves. This means in order to evaluate the hash for a block-header with some nonce, the partial evaluation of the same header with random "neighbouring nonces" is required. This allows RandomHash to operate in two modes, validation mode and mining mode. 
+
+In validation mode, RandomHash is simply used to hash a block-header in order to verify it's position in a blockchain -- just the same as how SHA2-256D does in Bitcoin.
+
+In mining mode, RandomHash is used to mine the next block-header by enumerating many nonces to find an acceptable Proof-of-Work solution for the next block. In this mode, RandomHash exploits the partial calculations from previous rounds by resuming them in subsequent hashing rounds. In RandomHash 1, this mode operates at twice the hashrate as validation mode (200%), and is the basis for the "CPU Bias" and important to achieve GPU resistivity. It is biased towards CPU's since re-using the partially calculated nonces is a serial optimization that cannot be efficiently exploited in a parallelized, batch-computation context (such as GPU mining). In other words, the optimal nonce-set is enumerated on-the-fly and cannot be pre-determined into a range for parallel mining as GPU mining requires.
+
+However, on a typical 2019 desktop computer, the validation hashrate of RandomHash is approximately 20 hashes per second. At 288 blocks per day, that's 14 seconds to validate a days worth of blocks and over 1 hour to validate 1 year of blocks. Whilst multi-threading, performance tuning and other optimizations have significantly optimized this performance oversight, it remains a fundamental a long-term issue that needs to be resolved.
+
+RandomHash2 offers an order of magnitude improvement in both validation and mining hashrate. In RandomHash2, the same machine validates at ~400 hashes per second yet mines at ~2,000 hashes per second with far less memory-hardness. Whilst not empirically tested against GPU performance, these numbers suggest a 500% CPU bias.
+
+#### RandomHash vs RandomHash2 Measured Performance
+
+| Algorithm                  | Mean Hashrate (H/s)       | Mean Mem Per Hash (b) | Min Mem Per Hash (b) | Max Mem Per Hash (b) | Sample Std Dev. (b) |
+| :------------------------- | :------------------------ | :-------------------- | :------------------- | :------------------- | :------------------ |
+| RandomHash (validation)    | 23                        | 5,018,876             | 5,018,628            | 5,019,116            | 83.86               |
+| RandomHash (mining)        | 44                        | 7,528,288             | 7,527,872            | 7,528,732            | 136                 |
+| RandomHash2 (validation)   | 401                       | 16,361                | 560                  | 73,872               | 14,934              |
+| **RandomHash2 (mining)**   | 2,109                     | 74,642                | 1,048                | 157,944              | 15,460              |
+
+_**Machine**: AMD FX-8150 8 Core 3.60 Ghz utilizing 1 thread_
+
+**NOTE:**
+- RandomHash2 (validation) is the mode used to validate blocks during load and sync.
+- **RandomHash2 (mining)** is the mode the mining network will use on V5 activation.
+
+## Specification
+
+RandomHash2 is similarly structured to RandomHash, but has some key differences. The below overview outlines the full algorithm. 
+
+### Overview
+
+1. Hashing a nonce requires ```N``` iterations (called levels), which are evaluated recursively;
+2. ```N``` varies per nonce in a non-deterministic manner between ```MIN_N=2``` and ```MAX_N=4```, inclusive;
+3. Each level in (1) also depends on ```J``` neighboring nonces, determined randomly and non-deterministically;
+4. The value ```J``` is restricted to ```MIN_J=0``` and ```MAX_J=4```;
+5. Each level selects a random hash function from a set of 77 well-known cryptographic hash algorithms;
+6. The input digest hashed at a level is the compression of the transitive closure of the hash outputs of all it's prior-levels (1) and it's neighbouring nonce's prior-levels (3);
+7. The output of every level is expanded for (low) memory-hardness;
+8. Randomness is generated using ```Mersenne Twister``` algorithm;
+9. Randomness is always seeded using last DWORD of a cryptographic hash algorithm output;
+10. The first input is pre-hashed as using ```SHA2-256```;
+11. The last output is post-hashed using ```SHA2-256```;
+
+### Differences to RandomHash 1
+
+The primary differences to predecessor are:
+
+**Constants**
+```pascal
+    MIN_N = 2;
+    MAX_N = 4;
+    MIN_J = 1;
+    MAX_J = 8;
+    M = 256;
+```
+
+**N is now random per nonce**
+```pascal
+    Result := (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0));       
+```
+
+**Block Header is pre-hashed before used (last DWORD is seed)**
+```pascal
+    if ARound = 1 then begin
+        LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
+        LSeed := GetLastDWordLE( LRoundInput );
+        LGen.Initialize(LSeed);   
+        ...
+```
+
+**Random number of dependent neighbour nonces**
+```pascal
+    LNumNeighbours := (LGen.NextUInt32 MOD (MAX_J - MIN_J)) + MIN_J;
+    for i := 1 to LNumNeighbours do begin
+```
+ 
+**MurMur3 checksumming removed completely***
+```pascal
+   // MurMur3 checksumming has been removed completely and seeding now uses last dword of a cryptographic hash
+   LSeed := GetLastDWordLE( LRoundInput );
+  ...
+   // Compression & Expansion no longer use MurMur3 checksum for seeding mersenne twister, instead seed is passed as argument
+   function Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32) : TBytes;
+   function Compress(const AInputs: TArray<TBytes>; ASeed : UInt32): TBytes; inline;
+```
+
+**Neighbouring nonces are only cached when fully evaluated**
+```pascal
+LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
+LNeighbourWasLastRound := CalculateRoundOutputs(LNeighbourNonceHeader, ARound - 1, LNeighborOutputs);
+LRoundOutputs.AddRange(LNeighborOutputs);     
+
+if LNeighbourWasLastRound then begin
+    LCachedHash.Nonce := GetLastDWordLE(LNeighbourNonceHeader);
+    LCachedHash.Header := LNeighbourNonceHeader;
+    LCachedHash.Hash := ComputeVeneerRound(LNeighborOutputs);
+    // if header is different (other than nonce), clear cache
+    if NOT BytesEqual(FCachedHeaderTemplate, LCachedHash.Header, 0, 32 - 4) then begin
+        FCachedHashes.Clear;
+        FCachedHeaderTemplate := SetLastDWordLE(LCachedHash.Header, 0);
+    end;
+    FCachedHashes.Add(LCachedHash);
+end; 
+```
+
+**There are now 77 internally used hash algorithms, not 18**
+
+In order to strengthen ASIC and GPU resistance, RandomHash2 now employs 77 internal hash algorithms to transform the data. This moves the economic costs of an ASIC far beyond viability for any rational economic actor, and since the sequence in which these 77 hash algorithms are used is in-determinable and random on a nonce-by-nonce basis. This number is also such that it makes GPU-programs (kernels) similarly non-viable, although not to the same degree as an ASIC.
+
+The below are the algorithms in their indices as they appear within RandomHash2 algorithm.
+
+Blake2B_160, Blake2B_256, Blake2B_512, Blake2B_384, Blake2S_128, Blake2S_160, Blake2S_224, Blake2S_256, Gost, GOST3411_2012_256, GOST3411_2012_512, Grindahl256, Grindahl512, HAS160, Haval_3_128, Haval_3_160, Haval_3_192, Haval_3_224, Haval_3_256, Haval_4_128, Haval_4_160, Haval_4_192, Haval_4_224, Haval_4_256, Haval_5_128, Haval_5_160, Haval_5_192, Haval_5_224, Haval_5_256, Keccak_224, Keccak_256, Keccak_288, Keccak_384, Keccak_512, MD2, MD5, MD4, Panama, RadioGatun32, RIPEMD, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, SHA0, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512, SHA2_512_224, SHA2_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512, Snefru_8_128, Snefru_8_256, Tiger_3_128, Tiger_3_160, Tiger_3_192, Tiger_4_128, Tiger_4_160, Tiger_4_192, Tiger_5_128, Tiger_5_160, Tiger_5_192, Tiger2_3_128, Tiger2_3_160, Tiger2_3_192, Tiger2_4_128, Tiger2_4_160, Tiger2_4_192, Tiger2_5_128, Tiger2_5_160, Tiger2_5_192, WhirlPool 
+
+```pascal
+
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool(); 
+
+
+```
+
+
+### Analysis
+ 
+#### Cryptographic Strength
+
+Since hashing starts and ends with a ```SHA2-256``` the cryptographic strength of RandomHash2 is **at least** that of ```SHA2-256D``` as used in Bitcoin. Even if one were to assume the data transformations in between the start/end were cryptographically insecure, it wouldn't change this minimum security guarantee.
+
+However, the transformations in between are not cryptographically weak and involve the use of 77 other cryptographically strong hash algorithms. As a result, RandomHash2 is orders of magnitude stronger than any single cryptographic hash algorithm since it fundamentally is composed of a random combination of many cryptographic hash algorithms. It should be noted that this achievement is paid for by significant performance overhead, which is intentional since the purpose of this algorithm if Proof-of-Work more than cryprographic obfuscaion.
+
+One criticism levelled against RandomHash2 is that within the set of 77 hash algorithms used, some are considered "cryptographically weak", such as **MD5**. These criticisms are misguided since the use of some weak algorithms is **inconsequential** to the cryptographic security since their purpose is only to add computational complexity to prevent ASIC implementations, not to complement security. 
+
+In order to get a grasp of the minmum security provided by RandomHash2, consider the high-level algorithmic structure as essentially a recurrence of nested hashes as follows:
+```
+RandomHash2(Data) = SHA2_256( H1( H2( .... H_N( SHA2_256( DATA ) ) ...) )
+where
+   H_i = a randomly selected hash function based on the output of H_(i-1)   
+   N = a random number determined by the nonce and neighbouring nonces (indeterminable but bound)
+```
+
+It follows that the weakest possible RandomHash2 for some ```WeakestDigest``` would comprise of 2 levels of evaluation (```MIN_N=2```) with each of those 2 levels having 1 neighbouring nonce dependency (```MIN_J=1```). Also, we assume the hash algorithms used at all levels were ```MD5```, as it is considered weakest of the set of 18 possible algorithms. In this case, 
+```
+RandomHash2(WeakestDigest) = SHA2_256( MD5 ( MD5 ( MD5 ( MD5 ( SHA2_256( WeakestDigest ) ) ) ) ) )
+```
+
+Clearly the above shows the weakest possible RandomHash2 is still **far stronger than a SHA2-256D** as typically employed in Bitcoin-based cryptocurrencies.
+
+```pascal
+SHA2-256D(WeakestDigest) = SHA2-256 ( SHA2-256 ( WeakestDigest ) )
+```
+
+In addition to the above, RandomHash2 internally transforms data using expansions and compressions which are themselves cryptographically secure. As a result, it's clear that RandomHash2's cryptographic strength is at least as strong as Bitcoin's ```SHA2-256D``` with the likelihood of also being orders of magnitude stronger.
+
+### Nonce Scanning Attack
+
+In RandomHash2, the number of levels ```N``` required to mine a nonce is now random and varies per nonce in a non-deterministic manner. The randomization of ```N``` introduces new level of randomness and executive decision-making into the core algorithm in order to enhance GPU and ASIC resistivity. However, it introduces a new attack vector called "Nonce Scanning Attack". In this attack, a miner can implement a simplified miner that only tries to mine "simple nonces" that require few levels to evaluate whilst rejecting "complex nonces" that require more levels to evaluate. By reducing the number of computations required to evaluate a nonce and simplifying the algorithm implementation, a higher hashrate could be achieved and an ASIC implementation made viable.
+
+To thwart this attack, RandomHash2 restricts the range of values ```N``` can take to be between ```MIN_N = 2``` and ```MAX_N = 4```, inclusive.
+
+By forcing a nonce evaluation to have at least 2 levels of computation, the miner necessarily requires the full algorithm implementation which prevents simplified ASIC miners. Also, since each nonce requires at least 2 levels of evaluation, and each of those levels is likely to depend other nonces to 1 level each, the number of computations saved by nonce-scanning must be balanced by the number of pre-computed cached nonces a miner would get if they were honestly mining without nonce-scanning (due to higher number of dependent neighboring nonces).
+
+In order to determine if this balance is achieved, an empirical nonce-scanning attack was conducted. The below table shows empirical results from nonce-scanning ```N=MIN_N``` to ```N=MAX_N```.
+
+| N       | Mean Hashrate (H/s)       | Mean Mem Per Hash (b) | Min Mem Per Hash (b) | Max Mem Per Hash (b) | Sample Std Dev. (b) |
+| :------ | :------------------------ | :-------------------- | :------------------- | :------------------- | :------------------ |
+| 2 (min) | 964                       | 1,462                 | 760                  | 2,292                | 443                 |
+| 3       | 1,708                     | 1,978                 | 760                  | 14,844               | 6,293               |
+| 4 (max) | 2,109                     | 74,642                | 1,048                | 157,944              | 15,460              |
+
+
+_**Machine**: AMD FX-8150 8 Core 3.60 Ghz utilizing 1 thread_
+
+As the above table shows, this balance is achieved. Nonce-scanning (via CPU) yields no observable benefit and in fact incurs a hashrate penalty. Also, it is the opinion of the author that any future optimization would **not** change this balance since an optimization would benefit all nonce scanning levels proportionally. However, a worthy line of inquiry is whether or not the reduced memory-hardness of nonce-scanning at lower levels may yield some optimization benefit for GPU-based miner. In any event, the result of this attack is only to gain higher hashrate and **does not compromise the cryptographic security** of the blockchain whatsoever.
+ 
+#### CPU Bias
+
+The RandomHash2 algorithm, like it's predecessor, is inherently biased towards CPU mining due to it's highly serial nature, use of non-deterministic recursion and executive-decision making. In addition, RandomHash2 can now evaluate many nonces when evaluating one, allowing CPU miners to enumerate the optimal nonce-set on the fly. Testing shows a 300% - 400% advantage for serial mining over batch mining, which indicates a proportional CPU bias.
+
+#### Memory Complexity
+
+RandomHash is memory-light in order to support low-end hardware.  A CPU will only need 300KB of memory to verify a hash. Unlike RandomHash, mining does not consume additional memory since the cached nonces are fully evaluated.
+
+#### GPU Resistance 
+
+GPU performance is generally driven by parallel execution of identical non-branching code-blocks across private regions of memory. RandomHash2 is a highly serial and recursive algorithm requiring a lot of executive-decision making, and decisions driven by Mersenne Twister random number generator. These characteristics make GPU implementations quite tedious and inefficient. Since the predecessor algorithm was shown to be GPU resistant, and this algorithm only exarcerbates these characteristics (except for memory hardness), it is expected that GPU resistance is maintained, although not confirmed as of the writing of this PIP.
+
+#### ASIC Resistance 
+
+ASIC-resistance is fundamentally achieved on an economic basis. Due to the use of 18 sub-hash algorithms and the use of recursion in the core algorithm, it is expected that the R&D costs of a RandomHash ASIC will mirror that of building 18 independent ASICs rather than 1. This moves the economic viability goal-posts away by an order of magnitude. For as long as the costs of general ASIC development remain in relative parity to the costs of consumer grade CPUs as of today, a RandomHash ASIC will always remain "not worth it" for a "rational economic actor".
+
+Furthermore, RandomHash offers a wide ASIC-breaking attack surface. This is due to it's branch-heavy, serial, recursive nature and heavy dependence on sub-algorithms. By making minor tweaks to the high-level algorithm, or changing a sub-algorithm, an ASIC design can be mostly invalidated and sent back the drawing board with minimal updates to the CPU miner.
+
+This is true since ASIC designs tend to mirror the assembly structure of an algorithm rather than the high-level algorithm itself. Thus by making relatively minor tweaks at the high-level that necessarily result in significant low-level assembly restructuring, an ASIC design is made obsolete. So long as this "tweak-to-break-ASIC" policy is maintained by the PascalCoin Developers and Community, ASIC resistance is guaranteed.
+
+### Hard-Fork Activation
+
+The PIP requires a hard-fork activation involving various aspects discussed below.
+
+## Rationale
+
+Aside from a hash algorithm change, the only other known option to resolve slow validation time is to ship the client with precomputed lookup tables to speed up verification. This has already been done for RandomHash1 periods, but is not a viable option long-term.
+
+## Backwards Compatibility
+
+This PIP is not backwards compatible and requires a hard-fork activation. Previous hashing algorithm must be retained in order to validate blocks mined prior to the hard-fork.
+ 
+## Reference Implementation
+
+A reference implementation of RandomHash can be found [here][2]. A full implementation is provided below.
+
+```pascal
+
+TRandomHash2 = class sealed(TObject)
+const
+    MIN_N = 2; // Min-number of hashing rounds required to compute a nonce
+    MAX_N = 4; // Max-number of hashing rounds required to compute a nonce
+    MIN_J = 1; // Min-number of dependent neighbouring nonces required to evaluate a nonce round
+    MAX_J = 8; // Max-number of dependent neighbouring nonces required to evaluate a nonce round
+    M = 64;    // The memory expansion unit (in bytes)
+    NUM_HASH_ALGO = 77; // Number of internal cryptographic hash algorithms used
+    SHA2_256_IX = 47; // index of SHA2-256 within the 77 algorithms used
+
+private
+    FHashAlg : array[0..NUM_HASH_ALGO-1] of IHash;  // declared here to avoid race-condition during mining
+
+    function MemTransform1(const AChunk: TBytes): TBytes; inline;
+    function MemTransform2(const AChunk: TBytes): TBytes; inline;
+    function MemTransform3(const AChunk: TBytes): TBytes; inline;
+    function MemTransform4(const AChunk: TBytes): TBytes; inline;
+    function MemTransform5(const AChunk: TBytes): TBytes; inline;
+    function MemTransform6(const AChunk: TBytes): TBytes; inline;
+    function MemTransform7(const AChunk: TBytes): TBytes; inline;
+    function MemTransform8(const AChunk: TBytes): TBytes; inline;
+    function Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32) : TBytes;
+    function Compress(const AInputs: TArray<TBytes>; ASeed : UInt32): TBytes; inline;
+    function ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes; inline;
+    function CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean; overload;
+public
+    constructor Create;
+    destructor Destroy; override;
+    function TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+    function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
+    class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
+end; 
+
+{ ERandomHash2 }
+
+ERandomHash2 = class(Exception);
+
+{ TRandomHash2 implementation }
+
+constructor TRandomHash2.Create;
+begin
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool();
+end;
+
+destructor TRandomHash2.Destroy;
+var i : integer;
+begin
+  for i := Low(FHashAlg) to High(FHashAlg) do
+    FHashAlg[i] := nil;
+  inherited Destroy;
+end;
+
+class function TRandomHash2.Compute(const ABlockHeader: TBytes): TBytes;
+var
+  LHasher : TRandomHash2;
+  LDisposables : TDisposables;
+begin
+ LHasher := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+ Result := LHasher.Hash(ABlockHeader);
+end;
+
+function TRandomHash2.TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+var
+  LOutputs: TArray<TBytes>;
+  LSeed: UInt32;
+begin
+  if NOT CalculateRoundOutputs(ABlockHeader, AMaxRound, LOutputs) then
+    Exit(False);
+  AHash := ComputeVeneerRound(LOutputs);
+  Result := True;
+end;
+
+function TRandomHash2.Hash(const ABlockHeader: TBytes): TBytes;
+begin
+  if NOT TryHash(ABlockHeader, MAX_N, Result) then
+    raise ERandomHash2.Create('Internal Error: 984F52997131417E8D63C43BD686F5B2'); // Should have found final round!
+end;
+
+function TRandomHash2.ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes;
+var
+  LSeed : UInt32;
+begin
+  LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
+  // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
+  Result := FHashAlg[SHA2_256_IX].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+end;
+
+function TRandomHash2.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean;
+var
+  LRoundOutputs: TList<TBytes>;
+  LNeighbourWasLastRound : Boolean;
+  LSeed, LNumNeighbours: UInt32;
+  LGen: TMersenne32;
+  LRoundInput, LNeighbourNonceHeader, LOutput : TBytes;
+  LParentOutputs, LNeighborOutputs, LToArray, LBuffs2: TArray<TBytes>;
+  LHashFunc: IHash;
+  i: Int32;
+  LDisposables : TDisposables;
+  LBuff : TBytes;
+begin
+  if (ARound < 1) or (ARound > MAX_N) then
+    raise EArgumentOutOfRangeException.CreateRes(@SInvalidRound);
+
+  LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
+  LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
+  if ARound = 1 then begin
+    LRoundInput := FHashAlg[SHA2_256_IX].ComputeBytes(ABlockHeader).GetBytes;
+    LSeed := GetLastDWordLE( LRoundInput );
+    LGen.Initialize(LSeed);
+  end else begin
+    if CalculateRoundOutputs(ABlockHeader, ARound - 1, LParentOutputs) = True then begin
+      // Previous round was the final round, so just return it's value
+      ARoundOutputs := LParentOutputs;
+      Exit(True);
+    end;
+
+    // Add parent round outputs to this round outputs
+    LSeed := GetLastDWordLE( LParentOutputs[High(LParentOutputs)] );
+    LGen.Initialize(LSeed);
+    LRoundOutputs.AddRange( LParentOutputs );
+
+    // Add neighbouring nonce outputs to this round outputs
+    LNumNeighbours := (LGen.NextUInt32 MOD (MAX_J - MIN_J)) + MIN_J;
+    for i := 1 to LNumNeighbours do begin
+      LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
+      LNeighbourWasLastRound := CalculateRoundOutputs(LNeighbourNonceHeader, ARound - 1, LNeighborOutputs);
+      LRoundOutputs.AddRange(LNeighborOutputs);
+    end;
+    // Compress the parent/neighbouring outputs to form this rounds input
+    LRoundInput := Compress( LRoundOutputs.ToArray, LGen.NextUInt32 );
+  end;
+
+  // Select a random hash function and hash the input to find the output
+  LHashFunc := FHashAlg[LGen.NextUInt32 mod NUM_HASH_ALGO];
+  LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
+
+  // Memory-expand the output, add to output list and return output list
+  LOutput := Expand(LOutput, MAX_N - ARound, LGen.NextUInt32);
+  LRoundOutputs.Add(LOutput);
+  ARoundOutputs := LRoundOutputs.ToArray;
+
+  // Determine if final round
+  Result := (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0));
+end;
+
+function TRandomHash2.Compress(const AInputs : TArray<TBytes>; ASeed : UInt32): TBytes;
+var
+  i: Int32;
+  LSource: TBytes;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  SetLength(Result, 100);
+  LGen := LDisposables.AddObject( TMersenne32.Create( ASeed ) ) as TMersenne32;
+  for i := 0 to 99 do
+  begin
+    LSource := AInputs[LGen.NextUInt32 mod Length(AInputs)];
+    Result[i] := LSource[LGen.NextUInt32 mod Length(LSource)];
+  end;
+end;
+
+function TRandomHash2.Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32): TBytes;
+var
+  LSize, LBytesToAdd: Int32;
+  LOutput, LNextChunk: TBytes;
+  LRandom: UInt32;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  LGen := LDisposables.AddObject( TMersenne32.Create (ASeed) ) as TMersenne32;
+  LSize := Length(AInput) + (AExpansionFactor * M);
+  LOutput := Copy(AInput);
+  LBytesToAdd := LSize - Length(AInput);
+
+  while LBytesToAdd > 0 do
+  begin
+    LNextChunk := Copy(LOutput);
+    if Length(LNextChunk) > LBytesToAdd then
+      SetLength(LNextChunk, LBytesToAdd);
+
+    LRandom := LGen.NextUInt32;
+    case LRandom mod 8 of
+      0: LOutput := ContencateBytes(LOutput, MemTransform1(LNextChunk));
+      1: LOutput := ContencateBytes(LOutput, MemTransform2(LNextChunk));
+      2: LOutput := ContencateBytes(LOutput, MemTransform3(LNextChunk));
+      3: LOutput := ContencateBytes(LOutput, MemTransform4(LNextChunk));
+      4: LOutput := ContencateBytes(LOutput, MemTransform5(LNextChunk));
+      5: LOutput := ContencateBytes(LOutput, MemTransform6(LNextChunk));
+      6: LOutput := ContencateBytes(LOutput, MemTransform7(LNextChunk));
+      7: LOutput := ContencateBytes(LOutput, MemTransform8(LNextChunk));
+    end;
+    LBytesToAdd := LBytesToAdd - Length(LNextChunk);
+  end;
+  Result := LOutput;
+end;
+
+function TRandomHash2.MemTransform1(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength : UInt32;
+  LState : UInt32;
+begin
+  // Seed XorShift32 with last byte
+  LState := GetLastDWordLE(AChunk);
+  if LState = 0 then
+    LState := 1;
+
+  // Select random bytes from input using XorShift32 RNG
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := AChunk[TXorShift32.Next(LState) MOD LChunkLength];
+end;
+
+function TRandomHash2.MemTransform2(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  Move(AChunk[LPivot + LOdd], Result[0], LPivot);
+  Move(AChunk[0], Result[LPivot + LOdd], LPivot);
+  // Set middle-byte for odd-length arrays
+  if LOdd = 1 then
+    Result[LPivot] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform3(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := AChunk[LChunkLength - i - 1];
+end;
+
+function TRandomHash2.MemTransform4(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := 0 to Pred(LPivot) do
+  begin
+    Result[(i * 2)] := AChunk[i];
+    Result[(i * 2) + 1] := AChunk[i + LPivot + LOdd];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[High(Result)] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform5(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := Low(AChunk) to Pred(LPivot) do
+  begin
+    Result[(i * 2)] := AChunk[i + LPivot + LOdd];
+    Result[(i * 2) + 1] := AChunk[i];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[High(Result)] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform6(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := 0 to Pred(LPivot) do
+  begin
+    Result[i] := AChunk[(i * 2)] xor AChunk[(i * 2) + 1];
+    Result[i + LPivot + LOdd] := AChunk[i] xor AChunk[LChunkLength - i - 1];
+  end;
+  // Set middle-byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[LPivot] := AChunk[High(AChunk)];
+end;
+
+function TRandomHash2.MemTransform7(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := TBits.RotateLeft8(AChunk[i], LChunkLength - i);
+end;
+
+function TRandomHash2.MemTransform8(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
+end;
+   
+
+
+
+```
+
+
+
+## Links
+
+1. [PIP-0009 RandomHash: GPU & ASIC Resistant Hash Algorithm][1]
+2. [RandomHash2 Reference Implementation][2]
+
+[1]: https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0009.md
+[2]: https://github.com/PascalCoin/PascalCoin/blob/master/src/core/URandomHash2.pas

+ 2 - 0
PIP/README.md

@@ -44,4 +44,6 @@ If they wish to continue, copy [this template](PIP-template.md) and ensure your
 | [33](PIP-0033.md)     | DATA operation RPC implementation             | Benjamin Ansbach               | Backend    | Proposed |
 | [33](PIP-0033.md)     | DATA operation RPC implementation             | Benjamin Ansbach               | Backend    | Proposed |
 | [34A](PIP-0034A.md)   | Website UI/UX Redesigns                       | Appditto                       | Website    | Proposed |
 | [34A](PIP-0034A.md)   | Website UI/UX Redesigns                       | Appditto                       | Website    | Proposed |
 | [35](PIP-0035.md)     | Block Policy: Layer-1 Governance (Velocity Cash) | Herman Schoenfeld           | Protocol   | Draft    |
 | [35](PIP-0035.md)     | Block Policy: Layer-1 Governance (Velocity Cash) | Herman Schoenfeld           | Protocol   | Draft    |
+| [36](PIP-0036.md)     | RandomHash2: Enhanced GPU & ASIC Resistant Hash Algorithm | Herman Schoenfeld  | Protocol   | Accepted    |
+
 
 

File diff suppressed because it is too large
+ 0 - 0
PIP/resources/PIP-0009/Random Hash Diagram.draw.io.xml


File diff suppressed because it is too large
+ 368 - 116
src/core/UAccounts.pas


+ 5 - 2
src/core/UBaseTypes.pas

@@ -370,9 +370,12 @@ end;
 class function TBaseType.StartsWith(const subst, target: TRawBytes): Boolean;
 class function TBaseType.StartsWith(const subst, target: TRawBytes): Boolean;
 var i : Integer;
 var i : Integer;
 begin
 begin
+  if (Length(target)<Length(subst)) then begin
+    Exit(False);
+  end;
   i := Low(subst);
   i := Low(subst);
   while (i<=High(subst)) and (i<=High(target)) and (target[i]=subst[i]) do inc(i);
   while (i<=High(subst)) and (i<=High(target)) and (target[i]=subst[i]) do inc(i);
-  Result := (i>High(subst)) and (i>High(target));
+  Result := (i>High(subst));
 end;
 end;
 
 
 class procedure TBaseType.Concat(const addBytes: T32Bytes; var target: TDynRawBytes);
 class procedure TBaseType.Concat(const addBytes: T32Bytes; var target: TDynRawBytes);
@@ -497,7 +500,7 @@ begin
       auxSize := auxSize + FDefaultIncrement;
       auxSize := auxSize + FDefaultIncrement;
     until auxSize >= newSize;
     until auxSize >= newSize;
     System.SetLength(FBytes,auxSize);
     System.SetLength(FBytes,auxSize);
-    FillByte(FBytes[actSize],auxSize - actSize +1,0);
+    FillByte(FBytes[actSize],auxSize - actSize ,0);
   end;
   end;
 end;
 end;
 
 

+ 169 - 59
src/core/UBlockChain.pas

@@ -115,6 +115,19 @@ Type
   TOpChangeAccountInfoType = (public_key, account_name, account_type, list_for_public_sale, list_for_private_sale, delist, account_data, list_for_account_swap, list_for_coin_swap );
   TOpChangeAccountInfoType = (public_key, account_name, account_type, list_for_public_sale, list_for_private_sale, delist, account_data, list_for_account_swap, list_for_coin_swap );
   TOpChangeAccountInfoTypes = Set of TOpChangeAccountInfoType;
   TOpChangeAccountInfoTypes = Set of TOpChangeAccountInfoType;
 
 
+  TOperationPayload = record
+    { As described on PIP-0027 (introduced on Protocol V5)
+      the payload of an operation will contain an initial byte that will
+      provide information about the payload content.
+      The "payload_type" byte value will help in payload decoding if good used
+      but there is no core checking that payload_type has been used properly.
+      It's job of any third party app (Layer 2) working with payloads to
+      check/ensure they can read/decode properly Payload value if the
+      content is not saved using E-PASA standard (PIP-0027) }
+    payload_type : Byte;
+    payload_raw : TRawBytes;
+  end;
+
   // MultiOp... will allow a MultiOperation
   // MultiOp... will allow a MultiOperation
   TMultiOpData = record
   TMultiOpData = record
     ID : TGUID;
     ID : TGUID;
@@ -126,16 +139,15 @@ Type
     Account : Cardinal;
     Account : Cardinal;
     Amount : Int64;
     Amount : Int64;
     N_Operation : Cardinal;
     N_Operation : Cardinal;
-    Data : TMultiOpData;
-    Payload : TRawBytes;
+    OpData : TMultiOpData; // Filled only when Operation is TOpData type
+    Payload : TOperationPayload;
     Signature : TECDSA_SIG;
     Signature : TECDSA_SIG;
   end;
   end;
   TMultiOpSenders = Array of TMultiOpSender;
   TMultiOpSenders = Array of TMultiOpSender;
   TMultiOpReceiver = Record
   TMultiOpReceiver = Record
     Account : Cardinal;
     Account : Cardinal;
     Amount : Int64;
     Amount : Int64;
-    Data : TMultiOpData;
-    Payload : TRawBytes;
+    Payload : TOperationPayload;
   end;
   end;
   TMultiOpReceivers = Array of TMultiOpReceiver;
   TMultiOpReceivers = Array of TMultiOpReceiver;
   TMultiOpChangeInfo = Record
   TMultiOpChangeInfo = Record
@@ -149,7 +161,7 @@ Type
     Seller_Account : Int64;
     Seller_Account : Int64;
     Account_Price : Int64;
     Account_Price : Int64;
     Locked_Until_Block : Cardinal;
     Locked_Until_Block : Cardinal;
-    Data : TMultiOpData;
+    Hashed_secret : TRawBytes;
     Fee: Int64;
     Fee: Int64;
     Signature: TECDSA_SIG;
     Signature: TECDSA_SIG;
   end;
   end;
@@ -172,7 +184,7 @@ Type
     Amount : Int64;
     Amount : Int64;
     Fee : Int64;
     Fee : Int64;
     Balance : Int64;
     Balance : Int64;
-    OriginalPayload : TRawBytes;
+    OriginalPayload : TOperationPayload;
     PrintablePayload : String;
     PrintablePayload : String;
     OperationHash : TRawBytes;
     OperationHash : TRawBytes;
     OperationHash_OLD : TRawBytes; // Will include old oeration hash value
     OperationHash_OLD : TRawBytes; // Will include old oeration hash value
@@ -225,8 +237,10 @@ Type
     Property Previous_Signer_updated_block : Cardinal read FPrevious_Signer_updated_block; // deprecated
     Property Previous_Signer_updated_block : Cardinal read FPrevious_Signer_updated_block; // deprecated
     Property Previous_Destination_updated_block : Cardinal read FPrevious_Destination_updated_block; // deprecated
     Property Previous_Destination_updated_block : Cardinal read FPrevious_Destination_updated_block; // deprecated
     Property Previous_Seller_updated_block : Cardinal read FPrevious_Seller_updated_block; // deprecated
     Property Previous_Seller_updated_block : Cardinal read FPrevious_Seller_updated_block; // deprecated
-    function IsValidECDSASignature(const PubKey: TECDSA_Public; current_protocol : Word; const Signature: TECDSA_SIG): Boolean;
+    function IsValidECDSASignature(const PubKey: TECDSA_Public; const Signature: TECDSA_SIG): Boolean;
     procedure CopyUsedPubkeySignatureFrom(SourceOperation : TPCOperation); virtual;
     procedure CopyUsedPubkeySignatureFrom(SourceOperation : TPCOperation); virtual;
+    function SaveOperationPayloadToStream(const AStream : TStream; const APayload : TOperationPayload) : Boolean;
+    function LoadOperationPayloadFromStream(const AStream : TStream; out APayload : TOperationPayload) : Boolean;
   public
   public
     constructor Create(AProtocolVersion : Word); virtual;
     constructor Create(AProtocolVersion : Word); virtual;
     destructor Destroy; override;
     destructor Destroy; override;
@@ -236,11 +250,11 @@ Type
     procedure AffectedAccounts(list : TList<Cardinal>); virtual; abstract;
     procedure AffectedAccounts(list : TList<Cardinal>); virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; virtual; abstract;
+    Function GetDigestToSign : TRawBytes; virtual; abstract;
     function OperationAmount : Int64; virtual; abstract;
     function OperationAmount : Int64; virtual; abstract;
     function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationFee: Int64; virtual; abstract;
     function OperationFee: Int64; virtual; abstract;
-    function OperationPayload : TRawBytes; virtual; abstract;
+    function OperationPayload : TOperationPayload; virtual; abstract;
     function SignerAccount : Cardinal; virtual; abstract;
     function SignerAccount : Cardinal; virtual; abstract;
     procedure SignerAccounts(list : TList<Cardinal>); virtual;
     procedure SignerAccounts(list : TList<Cardinal>); virtual;
     function IsSignerAccount(account : Cardinal) : Boolean; virtual;
     function IsSignerAccount(account : Cardinal) : Boolean; virtual;
@@ -346,7 +360,7 @@ Type
     Property TotalAmount : Int64 read FTotalAmount;
     Property TotalAmount : Int64 read FTotalAmount;
     Property TotalFee : Int64 read FTotalFee;
     Property TotalFee : Int64 read FTotalFee;
     function SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage : Boolean): Boolean;
     function SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage : Boolean): Boolean;
-    function LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; LoadProtocolVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors : String): Boolean;
+    function LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors : String): Boolean;
     function IndexOfOperation(op : TPCOperation) : Integer;
     function IndexOfOperation(op : TPCOperation) : Integer;
     function CountOperationsBySameSignerWithoutFee(account_number : Cardinal) : Integer;
     function CountOperationsBySameSignerWithoutFee(account_number : Cardinal) : Integer;
     Procedure Delete(index : Integer);
     Procedure Delete(index : Integer);
@@ -557,10 +571,13 @@ Type
   End;
   End;
 
 
 Const
 Const
-  CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:Nil;PrintablePayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
-  CT_TMultiOpSender_NUL : TMultiOpSender =  (Account:0;Amount:0;N_Operation:0;Payload:Nil;Signature:(r:Nil;s:Nil));
-  CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload:Nil);
-  CT_TMultiOpChangeInfo_NUL : TMultiOpChangeInfo = (Account:0;N_Operation:0;Changes_type:[];New_Accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);New_Name:Nil;New_Type:0;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;Fee:0;Signature:(r:Nil;s:Nil));
+  CT_TOperationPayload_NUL : TOperationPayload = (payload_type:0;payload_raw:Nil);
+  CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:(payload_type:0;payload_raw:nil);PrintablePayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
+  CT_TMultiOpSender_NUL : TMultiOpSender =  (Account:0;Amount:0;N_Operation:0;Payload:(payload_type:0;payload_raw:Nil);Signature:(r:Nil;s:Nil));
+  CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload:(payload_type:0;payload_raw:Nil));
+  CT_TMultiOpChangeInfo_NUL : TMultiOpChangeInfo = (Account:0;N_Operation:0;Changes_type:[];New_Accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);New_Name:Nil;New_Type:0;New_Data:Nil;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;
+    Hashed_secret:Nil;
+    Fee:0;Signature:(r:Nil;s:Nil));
   CT_TOpChangeAccountInfoType_Txt : Array[Low(TOpChangeAccountInfoType)..High(TOpChangeAccountInfoType)] of String = ('public_key','account_name','account_type','list_for_public_sale','list_for_private_sale', 'delist', 'account_data','list_for_account_swap','list_for_coin_swap');
   CT_TOpChangeAccountInfoType_Txt : Array[Low(TOpChangeAccountInfoType)..High(TOpChangeAccountInfoType)] of String = ('public_key','account_name','account_type','list_for_public_sale','list_for_private_sale', 'delist', 'account_data','list_for_account_swap','list_for_coin_swap');
 
 
 implementation
 implementation
@@ -1212,6 +1229,11 @@ Begin
         errors := 'Bank blockcount<>OperationBlock.Block';
         errors := 'Bank blockcount<>OperationBlock.Block';
         exit;
         exit;
       end;
       end;
+      if OperationBlock.protocol_version <> op.ProtocolVersion then begin
+        errors := Format('Operation protocol:%d <> current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
+        Tlog.NewLog(lterror,ClassName,errors);
+        Exit;
+      end;
       // Only process when in current address, prevent do it when reading operations from file
       // Only process when in current address, prevent do it when reading operations from file
       if FOperationsHashTree.CanAddOperationToHashTree(op) then begin
       if FOperationsHashTree.CanAddOperationToHashTree(op) then begin
         Result := op.DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, errors);
         Result := op.DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, errors);
@@ -1285,10 +1307,12 @@ begin
   FStreamPoW.WriteBuffer(FDigest_Part3[Low(FDigest_Part3)],Length(FDigest_Part3));
   FStreamPoW.WriteBuffer(FDigest_Part3[Low(FDigest_Part3)],Length(FDigest_Part3));
   FStreamPoW.Write(FOperationBlock.timestamp,4);
   FStreamPoW.Write(FOperationBlock.timestamp,4);
   FStreamPoW.Write(FOperationBlock.nonce,4);
   FStreamPoW.Write(FOperationBlock.nonce,4);
-  if CT_ACTIVATE_RANDOMHASH_V4 AND (FOperationBlock.protocol_version >= CT_PROTOCOL_4) then
-    TCrypto.DoRandomHash(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW)
-  else
-    TCrypto.DoDoubleSha256(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
+  if CT_ACTIVATE_RANDOMHASH_V4 AND (FOperationBlock.protocol_version >= CT_PROTOCOL_4) then begin
+    if (FOperationBlock.protocol_version < CT_PROTOCOL_5) then
+      TCrypto.DoRandomHash(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW)
+    else
+      TCrypto.DoRandomHash2(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
+  end else TCrypto.DoDoubleSha256(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
 end;
 end;
 
 
 procedure TPCOperationsComp.Calc_Digest_Parts;
 procedure TPCOperationsComp.Calc_Digest_Parts;
@@ -1348,6 +1372,7 @@ begin
         FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
         FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
       end;
       end;
       FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
       FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
+      FOperationBlock.previous_proof_of_work := FBank.LastOperationBlock.proof_of_work;
       If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
       If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
         FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
         FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
     end else begin
     end else begin
@@ -1357,6 +1382,7 @@ begin
       FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Nothing for first line
       FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Nothing for first line
       FOperationBlock.protocol_version := CT_PROTOCOL_1;
       FOperationBlock.protocol_version := CT_PROTOCOL_1;
       FOperationsHashTree.Max0feeOperationsBySigner := -1;
       FOperationsHashTree.Max0feeOperationsBySigner := -1;
+      FOperationBlock.previous_proof_of_work := Nil;
     end;
     end;
     FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
     FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
     FOperationBlock.fee := 0;
     FOperationBlock.fee := 0;
@@ -1465,20 +1491,7 @@ end;
 class function TPCOperationsComp.EqualsOperationBlock(const OperationBlock1,
 class function TPCOperationsComp.EqualsOperationBlock(const OperationBlock1,
   OperationBlock2: TOperationBlock): Boolean;
   OperationBlock2: TOperationBlock): Boolean;
 begin
 begin
-
-  Result := (OperationBlock1.block=OperationBlock2.block)
-           And (TAccountComp.EqualAccountKeys(OperationBlock1.account_key,OperationBlock2.account_key))
-           And (OperationBlock1.reward=OperationBlock2.reward)
-           And (OperationBlock1.fee=OperationBlock2.fee)
-           And (OperationBlock1.protocol_version=OperationBlock2.protocol_version)
-           And (OperationBlock1.protocol_available=OperationBlock2.protocol_available)
-           And (OperationBlock1.timestamp=OperationBlock2.timestamp)
-           And (OperationBlock1.compact_target=OperationBlock2.compact_target)
-           And (OperationBlock1.nonce=OperationBlock2.nonce)
-           And (TBaseType.Equals(OperationBlock1.block_payload,OperationBlock2.block_payload))
-           And (TBaseType.Equals(OperationBlock1.initial_safe_box_hash,OperationBlock2.initial_safe_box_hash))
-           And (TBaseType.Equals(OperationBlock1.operations_hash,OperationBlock2.operations_hash))
-           And (TBaseType.Equals(OperationBlock1.proof_of_work,OperationBlock2.proof_of_work));
+  Result := TAccountComp.EqualOperationBlocks(OperationBlock1,OperationBlock2);
 end;
 end;
 
 
 function TPCOperationsComp.GetAccountKey: TAccountKey;
 function TPCOperationsComp.GetAccountKey: TAccountKey;
@@ -1556,6 +1569,7 @@ Var i : Integer;
   soob : Byte;
   soob : Byte;
   raw: TRawBytes;
   raw: TRawBytes;
   load_protocol_version : Word;
   load_protocol_version : Word;
+  LLoadPreviousUpdatedBlocks : Boolean;
 begin
 begin
   Lock;
   Lock;
   Try
   Try
@@ -1581,6 +1595,7 @@ begin
     // - Value 4 means that is loading from storage using protocol v2 (so, includes always operations)
     // - Value 4 means that is loading from storage using protocol v2 (so, includes always operations)
     // - Value 5 means that is loading from storage using TAccountPreviousBlockInfo
     // - Value 5 means that is loading from storage using TAccountPreviousBlockInfo
     load_protocol_version := CT_PROTOCOL_1;
     load_protocol_version := CT_PROTOCOL_1;
+    LLoadPreviousUpdatedBlocks := False;
     if (soob in [0,2]) then FIsOnlyOperationBlock:=false
     if (soob in [0,2]) then FIsOnlyOperationBlock:=false
     else if (soob in [1,3]) then FIsOnlyOperationBlock:=true
     else if (soob in [1,3]) then FIsOnlyOperationBlock:=true
     else if (soob in [4]) then begin
     else if (soob in [4]) then begin
@@ -1589,6 +1604,7 @@ begin
     end else if (soob in [5]) then begin
     end else if (soob in [5]) then begin
       FIsOnlyOperationBlock:=False;
       FIsOnlyOperationBlock:=False;
       load_protocol_version := CT_PROTOCOL_3;
       load_protocol_version := CT_PROTOCOL_3;
+      LLoadPreviousUpdatedBlocks := True;
     end else begin
     end else begin
       errors := 'Invalid value in protocol header! Found:'+inttostr(soob)+' - Check if your application version is Ok';
       errors := 'Invalid value in protocol header! Found:'+inttostr(soob)+' - Check if your application version is Ok';
       exit;
       exit;
@@ -1616,26 +1632,26 @@ begin
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.initial_safe_box_hash) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.initial_safe_box_hash) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.operations_hash) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.operations_hash) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.proof_of_work) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.proof_of_work) < 0 then exit;
+    if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+      if TStreamOp.ReadAnsiString(Stream, FOperationBlock.previous_proof_of_work) < 0 then exit;
+      load_protocol_version := FOperationBlock.protocol_version;
+    end;
     If FIsOnlyOperationBlock then begin
     If FIsOnlyOperationBlock then begin
       Result := true;
       Result := true;
       exit;
       exit;
     end;
     end;
     //
     //
-    if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
-      load_protocol_version := FOperationBlock.protocol_version;
-    end;
-
     // Fee will be calculated for each operation. Set it to 0 and check later for integrity
     // Fee will be calculated for each operation. Set it to 0 and check later for integrity
     lastfee := OperationBlock.fee;
     lastfee := OperationBlock.fee;
     FOperationBlock.fee := 0;
     FOperationBlock.fee := 0;
     if FOperationBlock.protocol_version>=CT_PROTOCOL_4 then begin
     if FOperationBlock.protocol_version>=CT_PROTOCOL_4 then begin
       FOperationsHashTree.Max0feeOperationsBySigner := 1;
       FOperationsHashTree.Max0feeOperationsBySigner := 1;
     end else FOperationsHashTree.Max0feeOperationsBySigner := -1;
     end else FOperationsHashTree.Max0feeOperationsBySigner := -1;
-    Result := FOperationsHashTree.LoadOperationsHashTreeFromStream(Stream,LoadingFromStorage,load_protocol_version,FPreviousUpdatedBlocks,errors);
+    Result := FOperationsHashTree.LoadOperationsHashTreeFromStream(Stream,LoadingFromStorage,FOperationBlock.protocol_version,load_protocol_version,FPreviousUpdatedBlocks,errors);
     if not Result then begin
     if not Result then begin
       exit;
       exit;
     end;
     end;
-    If load_protocol_version>=CT_PROTOCOL_3 then begin
+    if LLoadPreviousUpdatedBlocks then begin
       Result := FPreviousUpdatedBlocks.LoadFromStream(Stream);
       Result := FPreviousUpdatedBlocks.LoadFromStream(Stream);
       If Not Result then begin
       If Not Result then begin
         errors := 'Invalid PreviousUpdatedBlock stream';
         errors := 'Invalid PreviousUpdatedBlock stream';
@@ -1722,14 +1738,17 @@ begin
         FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
         FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
       end;
       end;
       FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
       FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
-      If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
+      If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then begin
         FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
         FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
+      end;
+      FOperationBlock.previous_proof_of_work := FBank.LastOperationBlock.proof_of_work;
     end else begin
     end else begin
       FOperationBlock.block := 0;
       FOperationBlock.block := 0;
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(0);
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(0);
       FOperationBlock.compact_target := CT_MinCompactTarget_v1;
       FOperationBlock.compact_target := CT_MinCompactTarget_v1;
       FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash;
       FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash;
       FOperationBlock.protocol_version := CT_PROTOCOL_1;
       FOperationBlock.protocol_version := CT_PROTOCOL_1;
+      FOperationBlock.previous_proof_of_work := Nil;
     end;
     end;
     FOperationBlock.proof_of_work:=Nil;
     FOperationBlock.proof_of_work:=Nil;
     FOperationBlock.protocol_available := CT_BlockChain_Protocol_Available;
     FOperationBlock.protocol_available := CT_BlockChain_Protocol_Available;
@@ -1746,17 +1765,22 @@ begin
       lastn := FOperationsHashTree.OperationsCount;
       lastn := FOperationsHashTree.OperationsCount;
       for i:=0 to lastn-1 do begin
       for i:=0 to lastn-1 do begin
         op := FOperationsHashTree.GetOperation(i);
         op := FOperationsHashTree.GetOperation(i);
-        if (aux.CanAddOperationToHashTree(op)) then begin
-          if (op.DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors)) then begin
-            if aux.AddOperationToHashTree(op) then begin
-              inc(n);
-              inc(FOperationBlock.fee,op.OperationFee);
-              {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString){$ENDIF};
-            end else begin
-              TLog.NewLog(lterror,ClassName,Format('Undo operation.DoExecute at Sanitize due limits reached. Executing %d operations',[aux.OperationsCount]));
-              FPreviousUpdatedBlocks.Clear;
-              FSafeBoxTransaction.Rollback;
-              for iUndo := 0 to aux.OperationsCount-1 do aux.GetOperation(iUndo).DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, auxs);
+        if OperationBlock.protocol_version <> op.ProtocolVersion then begin
+          errors := Format('Sanitize Operation protocol:%d <> current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
+          Tlog.NewLog(lterror,ClassName,errors);
+        end else begin
+          if (aux.CanAddOperationToHashTree(op)) then begin
+            if (op.DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors)) then begin
+              if aux.AddOperationToHashTree(op) then begin
+                inc(n);
+                inc(FOperationBlock.fee,op.OperationFee);
+                {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString){$ENDIF};
+              end else begin
+                TLog.NewLog(lterror,ClassName,Format('Undo operation.DoExecute at Sanitize due limits reached. Executing %d operations',[aux.OperationsCount]));
+                FPreviousUpdatedBlocks.Clear;
+                FSafeBoxTransaction.Rollback;
+                for iUndo := 0 to aux.OperationsCount-1 do aux.GetOperation(iUndo).DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, auxs);
+              end;
             end;
             end;
           end;
           end;
         end;
         end;
@@ -1771,7 +1795,7 @@ begin
     Calc_Digest_Parts; // Does not need to recalc PoW
     Calc_Digest_Parts; // Does not need to recalc PoW
     Unlock;
     Unlock;
   End;
   End;
-  if (n>0) then begin
+  if (n>0) or (lastn<>n) then begin
     TLog.NewLog(ltdebug,Classname,Format('Sanitize operations (before %d - after %d)',[lastn,n]));
     TLog.NewLog(ltdebug,Classname,Format('Sanitize operations (before %d - after %d)',[lastn,n]));
   end;
   end;
 end;
 end;
@@ -1827,6 +1851,9 @@ begin
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.initial_safe_box_hash);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.initial_safe_box_hash);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.operations_hash);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.operations_hash);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.proof_of_work);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.proof_of_work);
+    if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+      TStreamOp.WriteAnsiString(Stream, FOperationBlock.previous_proof_of_work);
+    end;
     { Basic size calculation:
     { Basic size calculation:
     protocols : 2 words = 4 bytes
     protocols : 2 words = 4 bytes
     block : 4 bytes
     block : 4 bytes
@@ -1867,6 +1894,9 @@ begin
   TStreamOp.WriteAnsiString(Stream, OperationBlock.initial_safe_box_hash);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.initial_safe_box_hash);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.operations_hash);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.operations_hash);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.proof_of_work);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.proof_of_work);
+  if OperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+    TStreamOp.WriteAnsiString(Stream, OperationBlock.previous_proof_of_work);
+  end;
   Result := true;
   Result := true;
 end;
 end;
 
 
@@ -1989,8 +2019,12 @@ begin
     TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(SafeBoxTransaction,OperationsHashTree,Nil);
     TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(SafeBoxTransaction,OperationsHashTree,Nil);
     //
     //
     for i := 0 to Count - 1 do begin
     for i := 0 to Count - 1 do begin
+      if (Operation[i].ProtocolVersion<>OperationBlock.protocol_version) then begin
+        errors := 'Error executing operation invalid protocol at '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors+' Op:'+Operation[i].ToString;
+        exit;
+      end;
       If Not Operation[i].DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors) then begin
       If Not Operation[i].DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors) then begin
-        errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors;
+        errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors+' Op:'+Operation[i].ToString;
         exit;
         exit;
       end;
       end;
     end;
     end;
@@ -2593,7 +2627,7 @@ begin
   Index := L;
   Index := L;
 end;
 end;
 
 
-function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; LoadProtocolVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors: String): Boolean;
+function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors: String): Boolean;
 Var c, i: Cardinal;
 Var c, i: Cardinal;
   OpType: Cardinal;
   OpType: Cardinal;
   bcop: TPCOperation;
   bcop: TPCOperation;
@@ -2626,10 +2660,10 @@ begin
         errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c) + ' optype not valid:' + InttoHex(OpType, 4);
         errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c) + ' optype not valid:' + InttoHex(OpType, 4);
         exit;
         exit;
       end;
       end;
-      bcop := OpClass.Create(LoadProtocolVersion);
+      bcop := OpClass.Create(ASetOperationsToProtocolVersion);
       Try
       Try
         if LoadingFromStorage then begin
         if LoadingFromStorage then begin
-          If not bcop.LoadFromStorage(Stream,LoadProtocolVersion,PreviousUpdatedBlocks) then begin
+          If not bcop.LoadFromStorage(Stream,ALoadVersion,PreviousUpdatedBlocks) then begin
             errors := 'Invalid operation load from storage ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
             errors := 'Invalid operation load from storage ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
             exit;
             exit;
           end;
           end;
@@ -3071,7 +3105,7 @@ begin
   //
   //
 end;
 end;
 
 
-function TPCOperation.IsValidECDSASignature(const PubKey: TECDSA_Public; current_protocol: Word; const Signature: TECDSA_SIG): Boolean;
+function TPCOperation.IsValidECDSASignature(const PubKey: TECDSA_Public; const Signature: TECDSA_SIG): Boolean;
 begin
 begin
   // Will reuse FHasValidSignature if checked previously and was True
   // Will reuse FHasValidSignature if checked previously and was True
   // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
   // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
@@ -3083,7 +3117,7 @@ begin
     end;
     end;
   end;
   end;
   if (Not FHasValidSignature) then begin
   if (Not FHasValidSignature) then begin
-    FHasValidSignature := TCrypto.ECDSAVerify(PubKey,GetDigestToSign(current_protocol),Signature);
+    FHasValidSignature := TCrypto.ECDSAVerify(PubKey,GetDigestToSign,Signature);
     If FHasValidSignature then begin;
     If FHasValidSignature then begin;
       FUsedPubkeyForSignature := PubKey;
       FUsedPubkeyForSignature := PubKey;
     end;
     end;
@@ -3126,6 +3160,27 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TPCOperation.LoadOperationPayloadFromStream(const AStream: TStream; out APayload: TOperationPayload): Boolean;
+begin
+  APayload := CT_TOperationPayload_NUL;
+  if FProtocolVersion>=CT_PROTOCOL_5 then begin
+    // payload_type will only be available on protocol 5
+    if AStream.Read(APayload.payload_type,SizeOf(APayload.payload_type))<>SizeOf(APayload.payload_type) then Exit(False);
+  end;
+  if TStreamOp.ReadAnsiString(AStream,APayload.payload_raw)<0 then Exit(False);
+  Result := True;
+end;
+
+function TPCOperation.SaveOperationPayloadToStream(const AStream: TStream; const APayload: TOperationPayload): Boolean;
+begin
+  if FProtocolVersion>=CT_PROTOCOL_5 then begin
+    // payload_type will only be available on protocol 5
+    AStream.Write(APayload.payload_type,SizeOf(APayload.payload_type));
+  end;
+  TStreamOp.WriteAnsiString(AStream,APayload.payload_raw);
+  Result := True;
+end;
+
 class function TPCOperation.OperationHash_OLD(op: TPCOperation; Block : Cardinal): TRawBytes;
 class function TPCOperation.OperationHash_OLD(op: TPCOperation; Block : Cardinal): TRawBytes;
   { OperationHash is a 32 bytes value.
   { OperationHash is a 32 bytes value.
     First 4 bytes (0..3) are Block in little endian
     First 4 bytes (0..3) are Block in little endian
@@ -3188,6 +3243,7 @@ end;
 
 
 class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume): Boolean;
 class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume): Boolean;
 Var s : String;
 Var s : String;
+  LOpToText : String;
 begin
 begin
   OperationResume := CT_TOperationResume_NUL;
   OperationResume := CT_TOperationResume_NUL;
   OperationResume.Block:=Block;
   OperationResume.Block:=Block;
@@ -3227,6 +3283,53 @@ begin
           OperationResume.Fee := 0;
           OperationResume.Fee := 0;
           Result := true;
           Result := true;
         end else exit;
         end else exit;
+      end else if (TOpTransaction(Operation).Data.opTransactionStyle = transaction_with_auto_atomic_swap) then begin
+        if TOpTransaction(Operation).Data.new_accountkey.EC_OpenSSL_NID=0 then begin
+          // COIN SWAP
+          LOpToText := Format('COIN SWAP %s PASC from %s to %s',[
+            TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice),
+            TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+            TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.SellerAccount)]);
+        end else begin
+          // ACCOUNT SWAP
+          LOpToText := Format('ACCOUNT SWAP %s to new PublicKey %s',[
+            TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+            TAccountComp.AccountPublicKeyExport(TOpTransaction(Operation).Data.new_accountkey)]);
+        end;
+        if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
+          // The sender of the transaction
+          OperationResume.OpSubtype := CT_OpSubtype_SwapTransactionSender;
+          OperationResume.OperationTxt := Format('Tx-Out %s PASC from %s to %s with %s',
+             [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+             LOpToText]);
+          If (TOpTransaction(Operation).Data.sender=TOpTransaction(Operation).Data.SellerAccount) then begin
+            // Valid calc when sender is the same than seller
+            OperationResume.Amount := (Int64(TOpTransaction(Operation).Data.amount) - (TOpTransaction(Operation).Data.AccountPrice)) * (-1);
+          end else OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) * (-1);
+          Result := true;
+        end else if TOpTransaction(Operation).Data.target=Affected_account_number then begin
+          OperationResume.OpSubtype := CT_OpSubtype_SwapTransactionTarget;
+          OperationResume.OperationTxt := Format('Tx-In %s PASC from %s to %s with %s',
+             [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+             LOpToText]);
+          OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) - Int64(TOpTransaction(Operation).Data.AccountPrice);
+          OperationResume.Fee := 0;
+          Result := true;
+        end else if TOpTransaction(Operation).Data.SellerAccount=Affected_account_number then begin
+          OperationResume.OpSubtype := CT_OpSubtype_BuyTransactionSeller;
+          OperationResume.OperationTxt := Format('Tx-In seller receiving %s PASC from Tx between %s to %s with %s',
+             [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+             LOpToText]);
+          OperationResume.Amount := TOpTransaction(Operation).Data.AccountPrice;
+          OperationResume.Fee := 0;
+          Result := true;
+        end else exit;
       end else begin
       end else begin
         if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
         if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
           OperationResume.OpSubtype := CT_OpSubtype_TransactionSender;
           OperationResume.OpSubtype := CT_OpSubtype_TransactionSender;
@@ -3322,6 +3425,9 @@ begin
         OperationResume.Fee := 0;
         OperationResume.Fee := 0;
         Result := true;
         Result := true;
       end else exit;
       end else exit;
+      if (TOpBuyAccount(Operation).Data.sender = TOpBuyAccount(Operation).Data.target) then begin
+        OperationResume.Amount := TOpBuyAccount(Operation).Data.AccountPrice;
+      end;
     End;
     End;
     CT_Op_ChangeAccountInfo : Begin
     CT_Op_ChangeAccountInfo : Begin
       OperationResume.DestAccount := Operation.DestinationAccount;
       OperationResume.DestAccount := Operation.DestinationAccount;
@@ -3337,6 +3443,10 @@ begin
         if s<>'' then s:=s+',';
         if s<>'' then s:=s+',';
         s := s + 'type';
         s := s + 'type';
       end;
       end;
+      if (account_data in TOpChangeAccountInfo(Operation).Data.changes_type) then begin
+        if s<>'' then s:=s+',';
+        s := s + 'data';
+      end;
       OperationResume.OperationTxt:= 'Changed '+s+' of account '+TAccountComp.AccountNumberToAccountTxtNumber(Operation.DestinationAccount);
       OperationResume.OperationTxt:= 'Changed '+s+' of account '+TAccountComp.AccountNumberToAccountTxtNumber(Operation.DestinationAccount);
       OperationResume.OpSubtype:=CT_OpSubtype_ChangeAccountInfo;
       OperationResume.OpSubtype:=CT_OpSubtype_ChangeAccountInfo;
       Result := True;
       Result := True;
@@ -3354,8 +3464,8 @@ begin
   else Exit;
   else Exit;
   end;
   end;
   OperationResume.OriginalPayload := Operation.OperationPayload;
   OperationResume.OriginalPayload := Operation.OperationPayload;
-  If TCrypto.IsHumanReadable(OperationResume.OriginalPayload) then OperationResume.PrintablePayload := OperationResume.OriginalPayload.ToPrintable
-  else OperationResume.PrintablePayload := TCrypto.ToHexaString(OperationResume.OriginalPayload);
+  If TCrypto.IsHumanReadable(OperationResume.OriginalPayload.payload_raw) then OperationResume.PrintablePayload := OperationResume.OriginalPayload.payload_raw.ToPrintable
+  else OperationResume.PrintablePayload := TCrypto.ToHexaString(OperationResume.OriginalPayload.payload_raw);
   OperationResume.OperationHash:=TPCOperation.OperationHashValid(Operation,Block);
   OperationResume.OperationHash:=TPCOperation.OperationHashValid(Operation,Block);
   if (Block>0) And (Block<CT_Protocol_Upgrade_v2_MinBlock) then begin
   if (Block>0) And (Block<CT_Protocol_Upgrade_v2_MinBlock) then begin
     OperationResume.OperationHash_OLD:=TPCOperation.OperationHash_OLD(Operation,Block);
     OperationResume.OperationHash_OLD:=TPCOperation.OperationHash_OLD(Operation,Block);

+ 24 - 15
src/core/UConst.pas

@@ -54,12 +54,12 @@ Const
 
 
   CT_FirstReward: UInt64 = 1000000; // 4 decimals... First reward = 100,0000
   CT_FirstReward: UInt64 = 1000000; // 4 decimals... First reward = 100,0000
   CT_MinReward: UInt64 = 10000; // 4 decimals... Min reward = 1,0000
   CT_MinReward: UInt64 = 10000; // 4 decimals... Min reward = 1,0000
-  CT_NewLineRewardDecrease: Cardinal = {$IFDEF PRODUCTION}210240{$ELSE}{$IFDEF TESTNET}20000{$ENDIF}{$ENDIF};
+  CT_NewLineRewardDecrease: Cardinal = {$IFDEF PRODUCTION}210240{$ELSE}{$IFDEF TESTNET}10000{$ENDIF}{$ENDIF};
 
 
-  CT_WaitNewBlocksBeforeTransaction = 100;
+  CT_WaitNewBlocksBeforeTransaction = {$IFDEF PRODUCTION}100{$ELSE}{$IFDEF TESTNET}10{$ENDIF}{$ENDIF};
 
 
-  CT_RecoverFoundsWaitInactiveCount = 420480;  // After 4 years... if an account has no operations, money will be a reward for a miner!
-  CT_MaxFutureBlocksLockedAccount = 105120; // Maximum future blocks an account can be locked
+  CT_RecoverFoundsWaitInactiveCount = {$IFDEF PRODUCTION}420480{$ELSE}{$IFDEF TESTNET}1000{$ENDIF}{$ENDIF};  // After 4 years... if an account has no operations, money will be a reward for a miner!
+  CT_MaxFutureBlocksLockedAccount = {$IFDEF PRODUCTION}105120{$ELSE}{$IFDEF TESTNET}CT_RecoverFoundsWaitInactiveCount{$ENDIF}{$ENDIF}; // Maximum future blocks an account can be locked
 
 
   CT_MaxTransactionAmount = 1000000000000; // ... can be deleted
   CT_MaxTransactionAmount = 1000000000000; // ... can be deleted
   CT_MaxTransactionFee = 100000000;
   CT_MaxTransactionFee = 100000000;
@@ -75,6 +75,8 @@ Const
   CT_CompactTarget_Reset_v4: Cardinal = // First compact target of block if using Protocol 4 and RandomHash is Active
   CT_CompactTarget_Reset_v4: Cardinal = // First compact target of block if using Protocol 4 and RandomHash is Active
     {$IFDEF PRODUCTION}$16000000{$ELSE}$08000000{$ENDIF};
     {$IFDEF PRODUCTION}$16000000{$ELSE}$08000000{$ENDIF};
   {$ENDIF}
   {$ENDIF}
+  CT_MinCompactTarget_v5: Cardinal = // Minimum compact target of block if using Protocol 5 or higher
+    {$IFDEF PRODUCTION}$12000000{$ELSE}{$IFDEF TESTNET}$10000000{$ELSE}{$ENDIF}{$ENDIF};
 
 
 
 
   CT_CalcNewTargetBlocksAverage: Cardinal = 100;
   CT_CalcNewTargetBlocksAverage: Cardinal = 100;
@@ -116,16 +118,16 @@ Const
   CT_PROTOCOL_3 = 3;
   CT_PROTOCOL_3 = 3;
   CT_PROTOCOL_4 = 4;
   CT_PROTOCOL_4 = 4;
   CT_PROTOCOL_5 = 5;
   CT_PROTOCOL_5 = 5;
-  CT_BUILD_PROTOCOL = CT_PROTOCOL_4;
+  CT_BUILD_PROTOCOL = CT_PROTOCOL_5;
 
 
-  CT_BlockChain_Protocol_Available: Word = 4; // Protocol 4 flag
-  CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}47{$ENDIF};
-  CT_Protocol_Upgrade_v3_MinBlock = {$IFDEF PRODUCTION}210000{$ELSE}48{$ENDIF};
-  CT_Protocol_Upgrade_v4_MinBlock = {$IFDEF PRODUCTION}260000{$ELSE}49{$ENDIF};
-  CT_Protocol_Upgrade_v5_MinBlock = {$IFDEF PRODUCTION}999999999{$ELSE}50{$ENDIF}; // TODO Need define v5 for production!
+  CT_BlockChain_Protocol_Available: Word = 5; // Protocol 5 flag
+  CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}50{$ENDIF};
+  CT_Protocol_Upgrade_v3_MinBlock = {$IFDEF PRODUCTION}210000{$ELSE}250{$ENDIF};
+  CT_Protocol_Upgrade_v4_MinBlock = {$IFDEF PRODUCTION}260000{$ELSE}400{$ENDIF};
+  CT_Protocol_Upgrade_v5_MinBlock = {$IFDEF PRODUCTION}999999999{$ELSE}500{$ENDIF}; // TODO Need define v5 for production!
 
 
 
 
-  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000001{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000003{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
 
 
   CT_NetProtocol_Version: Word = $0009; // Version 4.0.2 Will allow only net protocol 9
   CT_NetProtocol_Version: Word = $0009; // Version 4.0.2 Will allow only net protocol 9
   // IMPORTANT NOTE!!!
   // IMPORTANT NOTE!!!
@@ -136,7 +138,9 @@ Const
 
 
   CT_SafeBoxBankVersion : Word = 3; // Protocol 2 upgraded safebox version from 2 to 3
   CT_SafeBoxBankVersion : Word = 3; // Protocol 2 upgraded safebox version from 2 to 3
 
 
-  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET_5.Beta.1'{$ENDIF}; //
+  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET_5.Beta.3'{$ENDIF}; //
+
+  CT_PascalCoin_Data_Folder : String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET_5.Beta.3'{$ENDIF}; //
 
 
   CT_PseudoOp_Reward = $0;
   CT_PseudoOp_Reward = $0;
   // Value of Operations type in Protocol 1
   // Value of Operations type in Protocol 1
@@ -156,8 +160,10 @@ Const
 
 
   CT_Protocol_v3_PIP11_Percent = 20; // PIP-0011 20% Percent proposed and voted by PIP-0011
   CT_Protocol_v3_PIP11_Percent = 20; // PIP-0011 20% Percent proposed and voted by PIP-0011
 
 
-  CT_Hardcoded_RandomHash_Table_Filename = 'HardcodedRH_63300.randomhash';
-  CT_Hardcoded_RandomHash_Table_HASH = '1908BAB3D3A2EF04ECA9F0AEE9E2BD6423D2A921B5880A1CBA8A748D11424418';
+  {$IFDEF PRODUCTION}
+  CT_Hardcoded_RandomHash_Table_Filename = 'HardcodedRH_75800.randomhash';
+  CT_Hardcoded_RandomHash_Table_HASH = '0A56291E8368AC855227B67A2F6CBEEFD2DB4CAE3CB8A473A7F6663C63368D0E';
+  {$ENDIF}
 
 
   CT_PseudoOpSubtype_Miner                = 1;
   CT_PseudoOpSubtype_Miner                = 1;
   CT_PseudoOpSubtype_Developer            = 2;
   CT_PseudoOpSubtype_Developer            = 2;
@@ -167,6 +173,9 @@ Const
   CT_OpSubtype_BuyTransactionBuyer        = 13;
   CT_OpSubtype_BuyTransactionBuyer        = 13;
   CT_OpSubtype_BuyTransactionTarget       = 14;
   CT_OpSubtype_BuyTransactionTarget       = 14;
   CT_OpSubtype_BuyTransactionSeller       = 15;
   CT_OpSubtype_BuyTransactionSeller       = 15;
+  CT_OpSubtype_SwapTransactionSender      = 16;
+  CT_OpSubtype_SwapTransactionTarget      = 17;
+  CT_OpSubtype_SwapTransactionReceiver    = 18;
   CT_OpSubtype_ChangeKey                  = 21;
   CT_OpSubtype_ChangeKey                  = 21;
   CT_OpSubtype_Recover                    = 31;
   CT_OpSubtype_Recover                    = 31;
   CT_OpSubtype_ListAccountForPublicSale   = 41;
   CT_OpSubtype_ListAccountForPublicSale   = 41;
@@ -186,7 +195,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
   CT_OpSubtype_Data_Receiver              = 104;
 
 
-  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'4.0.3.2'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.Beta.1'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'4.1'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.Beta.3'{$ELSE}{$ENDIF}{$ENDIF};
 
 
   CT_Discover_IPs = {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us'
   CT_Discover_IPs = {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us'
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};

+ 42 - 10
src/core/UCrypto.pas

@@ -39,7 +39,7 @@ uses
   ClpBigInteger,
   ClpBigInteger,
   ClpCryptoLibTypes,
   ClpCryptoLibTypes,
   {$ENDIF}
   {$ENDIF}
-  URandomHash, UBaseTypes, UPCDataTypes;
+  URandomHash, URandomHash2, UBaseTypes, UPCDataTypes;
 
 
 Type
 Type
   ECryptoException = Class(Exception);
   ECryptoException = Class(Exception);
@@ -87,8 +87,11 @@ Type
     class function DoDoubleSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
     class function DoDoubleSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
     class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
     class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
     class function DoRandomHash(const TheMessage : TRawBytes) : TRawBytes; overload;
     class function DoRandomHash(const TheMessage : TRawBytes) : TRawBytes; overload;
-    class procedure DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
-    class procedure DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
+    class function DoRandomHash2(const TheMessage : TRawBytes) : TRawBytes; overload;
+    class procedure DoRandomHash(p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes); overload;
+    class procedure DoRandomHash2(p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes); overload;
+    class procedure DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes); overload;
+    class procedure DoRandomHash2(AHasher : TRandomHash2Fast; p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes); overload;
     class function DoRipeMD160_HEXASTRING(const TheMessage : TRawBytes) : TRawBytes; overload;
     class function DoRipeMD160_HEXASTRING(const TheMessage : TRawBytes) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(const TheMessage : TRawBytes) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(const TheMessage : TRawBytes) : TRawBytes; overload;
@@ -152,7 +155,7 @@ Const
 implementation
 implementation
 
 
 uses
 uses
-  ULog, UConst, UAccounts;
+  ULog, UConst, UAccounts, UCommon;
 
 
 Var _initialized : Boolean = false;
 Var _initialized : Boolean = false;
 
 
@@ -769,28 +772,57 @@ begin
   Result := TRandomHashFast.Compute(TheMessage);
   Result := TRandomHashFast.Compute(TheMessage);
 end;
 end;
 
 
-class procedure TCrypto.DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
+class function TCrypto.DoRandomHash2(const TheMessage: TRawBytes): TRawBytes;
+begin
+  Result := TRandomHash2.Compute(TheMessage);
+end;
+
+class procedure TCrypto.DoRandomHash(p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes);
 var
 var
   LInput : TBytes;
   LInput : TBytes;
   LResult : TBytes;
   LResult : TBytes;
 begin
 begin
-  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
+  if Length(AResult) <> 32 then SetLength(AResult, 32);
   SetLength(LInput, plength);
   SetLength(LInput, plength);
   Move(p^, LInput[0], plength);
   Move(p^, LInput[0], plength);
   LResult := TRandomHashFast.Compute(LInput);
   LResult := TRandomHashFast.Compute(LInput);
-  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
+  Move(LResult[0], AResult[Low(AResult)], 32);
 end;
 end;
 
 
-class procedure TCrypto.DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
+class procedure TCrypto.DoRandomHash2(p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes);
 var
 var
   LInput : TBytes;
   LInput : TBytes;
   LResult : TBytes;
   LResult : TBytes;
 begin
 begin
-  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
+  if Length(AResult) <> 32 then SetLength(AResult, 32);
+  SetLength(LInput, plength);
+  Move(p^, LInput[0], plength);
+  LResult := TRandomHash2.Compute(LInput);
+  Move(LResult[0], AResult[Low(AResult)], 32);
+end;
+
+class procedure TCrypto.DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes);
+var
+  LInput : TBytes;
+  LResult : TBytes;
+begin
+  if Length(AResult) <> 32 then SetLength(AResult, 32);
   SetLength(LInput, plength);
   SetLength(LInput, plength);
   Move(p^, LInput[0], plength);
   Move(p^, LInput[0], plength);
   LResult := AFastHasher.Hash(LInput);
   LResult := AFastHasher.Hash(LInput);
-  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
+  Move(LResult[0], AResult[Low(AResult)], 32);
+end;
+
+class procedure TCrypto.DoRandomHash2(AHasher : TRandomHash2Fast; p : PAnsiChar; plength : Cardinal; out AResult : TRawBytes);
+var
+  LInput : TBytes;
+  LResult : TBytes;
+begin
+  if Length(AResult) <> 32 then SetLength(AResult, 32);
+  SetLength(LInput, plength);
+  Move(p^, LInput[0], plength);
+  LResult := AHasher.Hash(LInput);
+  Move(LResult[0], AResult[Low(AResult)], 32);
 end;
 end;
 
 
 { TBigNum }
 { TBigNum }

+ 5 - 2
src/core/UFileStorage.pas

@@ -316,15 +316,18 @@ procedure TFileStorage.DoLoadPendingBufferOperations(OperationsHashTree : TOpera
 Var fs : TFileStream;
 Var fs : TFileStream;
   errors : String;
   errors : String;
   n : Integer;
   n : Integer;
+  LCurrentProtocol : Word;
 begin
 begin
   LockBlockChainStream;
   LockBlockChainStream;
   Try
   Try
     fs := GetPendingBufferOperationsStream;
     fs := GetPendingBufferOperationsStream;
     fs.Position:=0;
     fs.Position:=0;
     if fs.Size>0 then begin
     if fs.Size>0 then begin
-      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,CT_PROTOCOL_3,Nil,errors) then begin
+      if Assigned(Bank) then LCurrentProtocol := Bank.SafeBox.CurrentProtocol
+      else LCurrentProtocol := CT_BUILD_PROTOCOL;
+      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,LCurrentProtocol,LCurrentProtocol, Nil,errors) then begin
         TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
         TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
-      end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR: loaded operations:%d errors:%s',[OperationsHashTree.OperationsCount,errors]));
+      end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR (Protocol %d): loaded operations:%d errors:%s',[LCurrentProtocol,OperationsHashTree.OperationsCount,errors]));
     end;
     end;
   finally
   finally
     UnlockBlockChainStream;
     UnlockBlockChainStream;

+ 28 - 1
src/core/UNetProtection.pas

@@ -31,9 +31,11 @@ unit UNetProtection;
   {$MODE Delphi}
   {$MODE Delphi}
 {$ENDIF}
 {$ENDIF}
 
 
+{$I ./../config.inc}
+
 interface
 interface
 
 
-Uses SysUtils, Classes, UJSONFunctions, UThread, ULog, UTime,
+Uses SysUtils, Classes, UJSONFunctions, UThread, ULog, UTime, UBaseTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 
 
@@ -57,6 +59,7 @@ Type
     FMaxStatsLifetime: Integer;
     FMaxStatsLifetime: Integer;
     FMaxStatsCount: Integer;
     FMaxStatsCount: Integer;
     FDeletedStatsCount: Int64;
     FDeletedStatsCount: Int64;
+    FLastCleanedTC : TTickCount;
     function Find(lockedList : TList<Pointer>; const ip : String; var Index: Integer): Boolean;
     function Find(lockedList : TList<Pointer>; const ip : String; var Index: Integer): Boolean;
     procedure SetMaxStatsLifetime(const Value: Integer);
     procedure SetMaxStatsLifetime(const Value: Integer);
     procedure CleanLastStatsByUpdatedTimestamp(minTimestamp : Integer);
     procedure CleanLastStatsByUpdatedTimestamp(minTimestamp : Integer);
@@ -75,6 +78,7 @@ Type
     function Count : Integer;
     function Count : Integer;
     property MaxStatsLifetime : Integer read FMaxStatsLifetime write SetMaxStatsLifetime;
     property MaxStatsLifetime : Integer read FMaxStatsLifetime write SetMaxStatsLifetime;
     property MaxStatsCount : Integer read FMaxStatsCount write SetMaxStatsCount;
     property MaxStatsCount : Integer read FMaxStatsCount write SetMaxStatsCount;
+    function CleanLastStats : Integer;
   End;
   End;
 
 
 implementation
 implementation
@@ -83,6 +87,19 @@ implementation
 
 
 Type PIpInfo = ^TIpInfo;
 Type PIpInfo = ^TIpInfo;
 
 
+function TIpInfos.CleanLastStats : Integer;
+var LLastCleanedCount : Integer;
+  LCurrTimestamp : Integer;
+begin
+  LCurrTimestamp := UnivDateTimeToUnix( DateTime2UnivDateTime(now) );
+  LLastCleanedCount := FDeletedStatsCount;
+  CleanLastStatsByUpdatedTimestamp(LCurrTimestamp - FMaxStatsLifetime);
+  Result := FDeletedStatsCount-LLastCleanedCount;
+  if (LLastCleanedCount<>FDeletedStatsCount) then begin
+    TLog.NewLog(ltInfo,ClassName,Format('Cleaned %d old stats',[(FDeletedStatsCount-LLastCleanedCount)]));
+  end;
+end;
+
 procedure TIpInfos.CleanLastStatsByUpdatedTimestamp(minTimestamp: Integer);
 procedure TIpInfos.CleanLastStatsByUpdatedTimestamp(minTimestamp: Integer);
 var jsonOpType, relJsonOpType, relJsonNetTransferType : TPCJSONObject;
 var jsonOpType, relJsonOpType, relJsonNetTransferType : TPCJSONObject;
   lasts : TPCJSONArray;
   lasts : TPCJSONArray;
@@ -118,6 +135,7 @@ begin
         end;
         end;
       end;
       end;
     end;
     end;
+    FLastCleanedTC := TPlatform.GetTickCount;
   Finally
   Finally
     FThreadList.UnlockList;
     FThreadList.UnlockList;
   End;
   End;
@@ -137,6 +155,7 @@ begin
     end;
     end;
     FDeletedStatsCount := 0;
     FDeletedStatsCount := 0;
     list.Clear;
     list.Clear;
+    FLastCleanedTC := TPlatform.GetTickCount;
   Finally
   Finally
     FThreadList.UnlockList;
     FThreadList.UnlockList;
   End;
   End;
@@ -154,6 +173,7 @@ begin
   FMaxStatsLifetime := 60*60*24; // Last values by 24 hours by default
   FMaxStatsLifetime := 60*60*24; // Last values by 24 hours by default
   FMaxStatsCount := 1000; // Max 1000 last stats by default
   FMaxStatsCount := 1000; // Max 1000 last stats by default
   FDeletedStatsCount := 0;
   FDeletedStatsCount := 0;
+  FLastCleanedTC := TPlatform.GetTickCount;
 end;
 end;
 
 
 destructor TIpInfos.Destroy;
 destructor TIpInfos.Destroy;
@@ -356,8 +376,15 @@ begin
         end;
         end;
       end;
       end;
     end;
     end;
+    if TPlatform.GetElapsedMilliseconds( FLastCleanedTC ) > (FMaxStatsLifetime * 1000) then begin ///  Clean stats auto
+      CleanLastStats;
+    end;
   Finally
   Finally
     Unlock;
     Unlock;
+    {$IFDEF TESTNET}
+    // For testing purposes, TESTNET will log when reaches limits but will not return a True value
+    Result := False;
+    {$ENDIF}
   End;
   End;
   setLength(countLimitsValues,0);
   setLength(countLimitsValues,0);
 end;
 end;

+ 18 - 8
src/core/UNetProtocol.pas

@@ -921,7 +921,7 @@ begin
         // Updated netConnection
         // Updated netConnection
         if Assigned(P^.netConnection) then begin
         if Assigned(P^.netConnection) then begin
           // Delete old value
           // Delete old value
-          if Not DeleteNetConnection(P^.netConnection) then TLog.NewLog(lterror,Classname,'DEV ERROR 20180205-1');
+          if Not DeleteNetConnection(P^.netConnection) then TLog.NewLog(lterror,Classname,Format('DEV ERROR 20180205-1 %s %d',[nodeServerAddress.ip,nodeServerAddress.port]));
         end;
         end;
       end;
       end;
       P^ := nodeServerAddress;
       P^ := nodeServerAddress;
@@ -1592,7 +1592,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         BlocksList.Free;
         BlocksList.Free;
       end;
       end;
     until (distinctmin=distinctmax);
     until (distinctmin=distinctmax);
-    Result := (OperationBlock.proof_of_work <> CT_OperationBlock_NUL.proof_of_work);
+    Result := (Not TBaseType.Equals(OperationBlock.proof_of_work,CT_OperationBlock_NUL.proof_of_work));
   End;
   End;
 
 
   procedure GetNewBank(start_block : Int64);
   procedure GetNewBank(start_block : Int64);
@@ -1647,7 +1647,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       repeat
       repeat
         BlocksList := TList<TPCOperationsComp>.Create;
         BlocksList := TList<TPCOperationsComp>.Create;
         try
         try
-          finished := NOT Do_GetOperationsBlock(Bank,start,start + 50,30000,false,BlocksList);
+          finished := NOT Do_GetOperationsBlock(Bank,start,start + 100,30000,false,BlocksList);
           i := 0;
           i := 0;
           while (i<BlocksList.Count) And (Not finished) do begin
           while (i<BlocksList.Count) And (Not finished) do begin
             OpComp := TPCOperationsComp(BlocksList[i]);
             OpComp := TPCOperationsComp(BlocksList[i]);
@@ -1779,6 +1779,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     headerdata : TNetHeaderData;
     headerdata : TNetHeaderData;
     request_id : Cardinal;
     request_id : Cardinal;
     c : Cardinal;
     c : Cardinal;
+    LRandomMilis : Integer;
   Begin
   Begin
     Result := False;
     Result := False;
     sendData := TMemoryStream.Create;
     sendData := TMemoryStream.Create;
@@ -1791,9 +1792,15 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       if (c>=safebox_blockscount) then c := safebox_blockscount-1;
       if (c>=safebox_blockscount) then c := safebox_blockscount-1;
       sendData.Write(c,SizeOf(c));
       sendData.Write(c,SizeOf(c));
       if (from_block>c) or (c>=safebox_blockscount) then begin
       if (from_block>c) or (c>=safebox_blockscount) then begin
-        errors := 'ERROR DEV 20170727-1';
+        errors := Format('ERROR DEV 20170727-1 fromblock:%d c:%d safebox_blockscount:%d',[from_block,c,safebox_blockscount]);
         Exit;
         Exit;
       end;
       end;
+      if Connection.NetProtocolVersion.protocol_version<9 then begin
+        // On old versions of nodes, must wait some seconds in order to do not reach max calls limits (limited to 30 calls in 30 seconds, so, at least wait 1 second per call)
+        LRandomMilis := 1000 + Random(500);
+        TLog.NewLog(ltDebug,CT_LogSender,Format('Sleep %d miliseconds prior to Call to GetSafeBox from blocks %d to %d of %d',[LRandomMilis,from_block,c,safebox_blockscount]));
+        Sleep(LRandomMilis);
+      end;
       TLog.NewLog(ltDebug,CT_LogSender,Format('Call to GetSafeBox from blocks %d to %d of %d',[from_block,c,safebox_blockscount]));
       TLog.NewLog(ltDebug,CT_LogSender,Format('Call to GetSafeBox from blocks %d to %d of %d',[from_block,c,safebox_blockscount]));
       request_id := TNetData.NetData.NewRequestId;
       request_id := TNetData.NetData.NewRequestId;
       if Connection.DoSendAndWaitForResponse(CT_NetOp_GetSafeBox,request_id,sendData,receiveData,30000,headerdata) then begin
       if Connection.DoSendAndWaitForResponse(CT_NetOp_GetSafeBox,request_id,sendData,receiveData,30000,headerdata) then begin
@@ -3259,7 +3266,7 @@ begin
       //
       //
       opht := TOperationsHashTree.Create;
       opht := TOperationsHashTree.Create;
       try
       try
-        If Not opht.LoadOperationsHashTreeFromStream(dataReceived,False,0,Nil,errors) then begin
+        If Not opht.LoadOperationsHashTreeFromStream(dataReceived,False,TNode.Node.Bank.SafeBox.CurrentProtocol,TNode.Node.Bank.SafeBox.CurrentProtocol,Nil,errors) then begin
           DisconnectInvalidClient(False,'Invalid operations hash tree stream: '+errors);
           DisconnectInvalidClient(False,'Invalid operations hash tree stream: '+errors);
           Exit;
           Exit;
         end;
         end;
@@ -3610,6 +3617,9 @@ Begin
       If (isFirstHello) And (HeaderData.header_type = ntp_response) then begin
       If (isFirstHello) And (HeaderData.header_type = ntp_response) then begin
         DoProcess_GetPendingOperations;
         DoProcess_GetPendingOperations;
       end;
       end;
+      if (isFirstHello) then begin
+        TLog.NewLog(ltInfo,ClassName,Format('New connection from %s version %s protocol (%d,%d)',[ClientRemoteAddr,ClientAppVersion,NetProtocolVersion.protocol_version,NetProtocolVersion.protocol_available]));
+      end;
     end else begin
     end else begin
       TLog.NewLog(lterror,Classname,'Error decoding operations of HELLO: '+errors);
       TLog.NewLog(lterror,Classname,'Error decoding operations of HELLO: '+errors);
       DisconnectInvalidClient(false,'Error decoding operations of HELLO: '+errors);
       DisconnectInvalidClient(false,'Error decoding operations of HELLO: '+errors);
@@ -3770,7 +3780,7 @@ var operationsComp : TPCOperationsComp;
     end;
     end;
     // Now we have nfpboarr with full data
     // Now we have nfpboarr with full data
     for i := 0 to High(nfpboarr) do begin
     for i := 0 to High(nfpboarr) do begin
-      auxOp := TPCOperation.GetOperationFromStreamData(Self.FNetProtocolVersion.protocol_version,  nfpboarr[i].opStreamData );
+      auxOp := TPCOperation.GetOperationFromStreamData(original_OperationBlock.protocol_version,  nfpboarr[i].opStreamData );
       if not Assigned(auxOp) then begin
       if not Assigned(auxOp) then begin
         errors := Format('Op index not available (%d/%d) OpReference:%d size:%d',[i,High(nfpboarr),nfpboarr[i].opReference,Length(nfpboarr[i].opStreamData)]);
         errors := Format('Op index not available (%d/%d) OpReference:%d size:%d',[i,High(nfpboarr),nfpboarr[i].opReference,Length(nfpboarr[i].opStreamData)]);
         Exit;
         Exit;
@@ -3941,7 +3951,7 @@ begin
                   CT_NetOp_GetBlocks : Begin
                   CT_NetOp_GetBlocks : Begin
                     if HeaderData.header_type=ntp_request then begin
                     if HeaderData.header_type=ntp_request then begin
                       if TNetData.NetData.IpInfos.ReachesLimits(Client.RemoteHost,CT_NetTransferType[HeaderData.header_type],TNetData.OperationToText(HeaderData.operation),HeaderData.buffer_data_length,
                       if TNetData.NetData.IpInfos.ReachesLimits(Client.RemoteHost,CT_NetTransferType[HeaderData.header_type],TNetData.OperationToText(HeaderData.operation),HeaderData.buffer_data_length,
-                        TArray<TLimitLifetime>.Create(TLimitLifetime.Create(300,100,0),TLimitLifetime.Create(10,5,0))) then DisconnectInvalidClient(False,Format('Reached limit %s',[TNetData.OperationToText(HeaderData.operation)]))
+                        TArray<TLimitLifetime>.Create(TLimitLifetime.Create(5,10,0))) then DisconnectInvalidClient(False,Format('Reached limit %s',[TNetData.OperationToText(HeaderData.operation)]))
                       else DoProcess_GetBlocks_Request(HeaderData,ReceiveDataBuffer)
                       else DoProcess_GetBlocks_Request(HeaderData,ReceiveDataBuffer)
                     end else if HeaderData.header_type=ntp_response then begin
                     end else if HeaderData.header_type=ntp_response then begin
                       DoProcess_GetBlocks_Response(HeaderData,ReceiveDataBuffer);
                       DoProcess_GetBlocks_Response(HeaderData,ReceiveDataBuffer);
@@ -3950,7 +3960,7 @@ begin
                   CT_NetOp_GetBlockHeaders : Begin
                   CT_NetOp_GetBlockHeaders : Begin
                     if HeaderData.header_type=ntp_request then begin
                     if HeaderData.header_type=ntp_request then begin
                       if TNetData.NetData.IpInfos.ReachesLimits(Client.RemoteHost,CT_NetTransferType[HeaderData.header_type],TNetData.OperationToText(HeaderData.operation),HeaderData.buffer_data_length,
                       if TNetData.NetData.IpInfos.ReachesLimits(Client.RemoteHost,CT_NetTransferType[HeaderData.header_type],TNetData.OperationToText(HeaderData.operation),HeaderData.buffer_data_length,
-                        TArray<TLimitLifetime>.Create(TLimitLifetime.Create(30,30,0))) then DisconnectInvalidClient(False,Format('Reached limit %s',[TNetData.OperationToText(HeaderData.operation)]))
+                        TArray<TLimitLifetime>.Create(TLimitLifetime.Create(10,30,0))) then DisconnectInvalidClient(False,Format('Reached limit %s',[TNetData.OperationToText(HeaderData.operation)]))
                       else DoProcess_GetOperationsBlock_Request(HeaderData,ReceiveDataBuffer)
                       else DoProcess_GetOperationsBlock_Request(HeaderData,ReceiveDataBuffer)
                     end else TLog.NewLog(ltdebug,Classname,'Received old response of: '+TNetData.HeaderDataToText(HeaderData));
                     end else TLog.NewLog(ltdebug,Classname,'Received old response of: '+TNetData.HeaderDataToText(HeaderData));
                   End;
                   End;

+ 13 - 7
src/core/UNode.pas

@@ -123,6 +123,7 @@ Type
     Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
     Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
     procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
     procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
     class function NodeVersion : String;
     class function NodeVersion : String;
+    class function GetPascalCoinDataFolder : String;
   End;
   End;
 
 
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
@@ -203,7 +204,7 @@ Type
 
 
 implementation
 implementation
 
 
-Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator;
+Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator, UFolderHelper;
 
 
 var _Node : TNode;
 var _Node : TNode;
 
 
@@ -1005,15 +1006,15 @@ begin
   if account_number>=Bank.SafeBox.AccountsCount then Exit;
   if account_number>=Bank.SafeBox.AccountsCount then Exit;
   if StartOperation>EndOperation then Exit;
   if StartOperation>EndOperation then Exit;
   acc := Bank.SafeBox.Account(account_number);
   acc := Bank.SafeBox.Account(account_number);
-  if (acc.updated_block>0) Or (acc.account=0) then Begin
-    if (SearchBackwardsStartingAtBlock=0) Or (SearchBackwardsStartingAtBlock>=acc.updated_block) then begin
-      startBlock := acc.updated_block;
+  if (acc.updated_on_block>0) Or (acc.account=0) then Begin
+    if (SearchBackwardsStartingAtBlock=0) Or (SearchBackwardsStartingAtBlock>=acc.updated_on_block) then begin
+      startBlock := acc.updated_on_block;
       lastBalance := acc.balance;
       lastBalance := acc.balance;
     end else begin
     end else begin
       startBlock := SearchBackwardsStartingAtBlock;
       startBlock := SearchBackwardsStartingAtBlock;
       lastBalance := -1;
       lastBalance := -1;
     end;
     end;
-    DoGetFromBlock(startBlock,lastBalance,MaxDepth,0,startBlock<>acc.updated_block);
+    DoGetFromBlock(startBlock,lastBalance,MaxDepth,0,startBlock<>acc.updated_on_block);
   end;
   end;
 end;
 end;
 
 
@@ -1092,7 +1093,7 @@ begin
           end;
           end;
         end;
         end;
       end;
       end;
-      block := Bank.SafeBox.Account(account).updated_block;
+      block := Bank.SafeBox.Account(account).updated_on_block;
     finally
     finally
       UnlockMempoolRead;
       UnlockMempoolRead;
     end;
     end;
@@ -1207,7 +1208,7 @@ begin
       UnlockMempoolRead;
       UnlockMempoolRead;
     End;
     End;
     // block=0 and not found... start searching at block updated by account updated_block
     // block=0 and not found... start searching at block updated by account updated_block
-    block := Bank.SafeBox.Account(account).updated_block;
+    block := Bank.SafeBox.Account(account).updated_on_block;
     if Bank.SafeBox.Account(account).n_operation<n_operation then exit; // n_operation is greater than found in safebox
     if Bank.SafeBox.Account(account).n_operation<n_operation then exit; // n_operation is greater than found in safebox
   end;
   end;
   if (block=0) or (block>=Bank.BlocksCount) then exit;
   if (block=0) or (block>=Bank.BlocksCount) then exit;
@@ -1289,6 +1290,11 @@ begin
   end;
   end;
 end;
 end;
 
 
+class function TNode.GetPascalCoinDataFolder: String;
+begin
+  Result := TFolderHelper.GetDataFolder(CT_PascalCoin_Data_Folder);
+end;
+
 function TNode.LockMempoolRead: TPCOperationsComp;
 function TNode.LockMempoolRead: TPCOperationsComp;
 begin
 begin
   FLockMempool.Acquire;
   FLockMempool.Acquire;

File diff suppressed because it is too large
+ 259 - 149
src/core/UOpTransaction.pas


+ 7 - 4
src/core/UPCOperationsSignatureValidator.pas

@@ -150,7 +150,7 @@ class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures
 var LList : TList<TPCOperation>;
 var LList : TList<TPCOperation>;
   i : Integer;
   i : Integer;
   LMultiThreadValidator : TPCOperationsSignatureValidator;
   LMultiThreadValidator : TPCOperationsSignatureValidator;
-  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LValidatedOk, LValidatedError, LValidatedTotal, LGlobalOperationsCount : Integer;
   LTC : TTickCount;
   LTC : TTickCount;
 begin
 begin
   if _Cpus<=0 then begin
   if _Cpus<=0 then begin
@@ -158,9 +158,11 @@ begin
   end;
   end;
   if _Cpus<=1 then Exit;
   if _Cpus<=1 then Exit;
 
 
+  LGlobalOperationsCount := 0;
   LList := TList<TPCOperation>.Create;
   LList := TList<TPCOperation>.Create;
   Try
   Try
     for i := 0 to APCOperationsCompList.Count-1 do begin
     for i := 0 to APCOperationsCompList.Count-1 do begin
+      Inc(LGlobalOperationsCount, APCOperationsCompList[i].Count );
       APCOperationsCompList[i].OperationsHashTree.GetOperationsList(LList,True);
       APCOperationsCompList[i].OperationsHashTree.GetOperationsList(LList,True);
     end;
     end;
     LTC := TPlatform.GetTickCount;
     LTC := TPlatform.GetTickCount;
@@ -171,7 +173,7 @@ begin
       LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
       LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
       LTC := TPlatform.GetElapsedMilliseconds(LTC);
       LTC := TPlatform.GetElapsedMilliseconds(LTC);
       if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
       if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
-        TLog.NewLog(ltdebug,ClassName,Format('Validated %d operations from %d Blocks with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,APCOperationsCompList.Count,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+        TLog.NewLog(ltdebug,ClassName,Format('Validated %d/%d operations from %d Blocks with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LGlobalOperationsCount,APCOperationsCompList.Count,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
       end;
       end;
     finally
     finally
       LMultiThreadValidator.Free;
       LMultiThreadValidator.Free;
@@ -187,7 +189,7 @@ end;
 class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(
 class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(
   ASafeBoxTransaction: TPCSafeBoxTransaction; AOperationsHashTree: TOperationsHashTree; AProgressNotify : TProgressNotify);
   ASafeBoxTransaction: TPCSafeBoxTransaction; AOperationsHashTree: TOperationsHashTree; AProgressNotify : TProgressNotify);
 var LMultiThreadValidator : TPCOperationsSignatureValidator;
 var LMultiThreadValidator : TPCOperationsSignatureValidator;
-  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LValidatedOk, LValidatedError, LValidatedTotal, LGlobalOperationsCount : Integer;
   LTC : TTickCount;
   LTC : TTickCount;
   LList : TList<TPCOperation>;
   LList : TList<TPCOperation>;
 begin
 begin
@@ -197,6 +199,7 @@ begin
   if _Cpus<=1 then Exit;
   if _Cpus<=1 then Exit;
   if AOperationsHashTree.OperationsCount<_Cpus then Exit;   // If less than cpus, no need for multithreading...
   if AOperationsHashTree.OperationsCount<_Cpus then Exit;   // If less than cpus, no need for multithreading...
 
 
+  LGlobalOperationsCount := AOperationsHashTree.OperationsCount;
   LTC := TPlatform.GetTickCount;
   LTC := TPlatform.GetTickCount;
   LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction,AProgressNotify);
   LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction,AProgressNotify);
   try
   try
@@ -210,7 +213,7 @@ begin
       LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
       LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
       LTC := TPlatform.GetElapsedMilliseconds(LTC);
       LTC := TPlatform.GetElapsedMilliseconds(LTC);
       if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
       if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
-        TLog.NewLog(ltdebug,ClassName,Format('Validated %d operations with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+        TLog.NewLog(ltdebug,ClassName,Format('Validated %d/%d operations with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LGlobalOperationsCount,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
       end;
       end;
     Finally
     Finally
       LList.Free;
       LList.Free;

+ 274 - 0
src/core/UPCRPCFindAccounts.pas

@@ -0,0 +1,274 @@
+unit UPCRPCFindAccounts;
+
+{ Copyright (c) 2019 by PascalCoin developers, orignal code by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ./../config.inc}
+
+Uses classes, SysUtils,
+  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  URPC, UCrypto, UWallet, UBlockChain, ULog;
+
+
+Type
+  TRPCFindAccounts = Class
+  private
+  public
+    class function FindAccounts(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+  End;
+
+implementation
+
+
+{ TRPCFindAccounts }
+
+class function TRPCFindAccounts.FindAccounts(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+
+{  RPC "findaccounts"
+### findaccounts
+Find accounts by name/type and returns them as an array of "Account Object"
+
+##### Params
+- `name` : String - Name to search
+- `namesearchtype` : String - One of those values
+  - `exact` : `name` must match (DEFAULT OPTION)
+  - `startswith` :
+  - `not-startswith` :
+  - `contains` :
+  - `not-contains` :
+  - `endswith` :
+  - `not-endswith` :
+- `exact` : Boolean (DEPRECATED, use `namesearchtype` instead, True by default) - If False and `name` has value will return accounts starting with `name`
+- `type` : Integer - If has value, will return accounts with same type
+- `min_balance`,`max_balance` : PASCURRENCY - If have value, will filter by current account balance
+- `enc_pubkey` or `b58_pubkey` : HEXASTRING or String - Will return accounts with this public key. **NOTE:** When searching by public key the `start` param value is the position of indexed public keys list instead of accounts numbers
+- `statustype` : String - One of those values
+  - `all` : (Default option)
+  - `for-sale`
+  - `for-public-sale`
+  - `for-private-sale`
+  - `for-swap`
+  - `for-account-swap`
+  - `for-coin-swap`
+  - `for-sale-swap`
+  - `not-for-sale-swap`
+- `listed` : Boolean (DEPRECATED, use `statustype` instead, False by default) - If True returns only for sale accounts
+- `start` : Integer - Start account (by default, 0) - **NOTE:** Is the "start account number", when executing multiple calls you must set `start` value to the latest returned account number + 1 (Except if searching by public key, see below)
+- `max` : Integer - Max of accounts returned in array (by default, 100)
+
+}
+
+type
+  TSearchAccountNameType = (st_exact, st_startswith, st_contains, st_endswith, st_not_startswith, st_not_contains, st_not_endswith);
+  TSearchAccountStatusType = (ss_all, ss_for_sale, ss_for_public_sale, ss_for_private_sale, ss_for_swap, ss_for_account_swap, ss_for_coin_swap, ss_for_sale_or_swap, ss_not_for_sale_or_swap);
+
+  function _SearchValidName(const ASearchName : String; const AAccountName : String; ASearchType : TSearchAccountNameType) : Boolean;
+  var i : Integer;
+  begin
+    if (ASearchName.Length=0) then Exit(True); // If nothing to search, allways TRUE
+    // Here we know that ASearchName has a value
+    if (AAccountName.Length=0) then Exit(False); // If account has NO NAME, allways FALSE
+    if (ASearchType=st_exact) then begin
+      Exit( AAccountName.Equals(ASearchName) );  // Must match
+    end;
+
+    i := AAccountName.IndexOf(ASearchName);
+    Result :=
+      ((i=0) and (ASearchType in [st_startswith])) // found at first position
+      or
+      ((i>=0) and (ASearchType in [st_contains])) // found in any pos
+      or
+      ((i=(AAccountName.Length-1)) and (ASearchType in [st_endswith])) // found at last position
+      or
+      ((i<0) and (ASearchType in [st_not_startswith, st_not_contains, st_not_endswith])) // not found and must not contain in any pos
+      or
+      ((i>=1) and (ASearchType in [st_not_startswith])) // not found at first position
+      or
+      ((i<(AAccountName.Length-1)) and (ASearchType in [st_not_endswith])); // not found at last position
+  end;
+
+var
+  LAccountName : String;
+  LSearchByNameType : TSearchAccountNameType;
+  LAccountType : Integer;
+  LSearchByStatusType : TSearchAccountStatusType;
+  LAccountBalanceMin : Int64;
+  LAccountBalanceMax : Int64;
+
+  function _IsValidAccount(const AAccount : TAccount) : Boolean;
+  begin
+    if (LAccountType <> -1) AND (Integer(AAccount.account_type) <> LAccountType) then Exit(False);
+    if (Not _SearchValidName(LAccountName,AAccount.name.ToString,LSearchByNameType)) then Exit(False);
+    case LSearchByStatusType of
+      ss_for_sale :        if Not TAccountComp.IsAccountForSale(AAccount.accountInfo) then Exit(False);
+      ss_for_public_sale:  if Not TAccountComp.IsAccountForPublicSale(AAccount.accountInfo) then Exit(False);
+      ss_for_private_sale: if Not TAccountComp.IsAccountForPrivateSale(AAccount.accountInfo) then Exit(False);
+      ss_for_swap:         if Not TAccountComp.IsAccountForSwap(AAccount.accountInfo) then Exit(False);
+      ss_for_account_swap: if Not TAccountComp.IsAccountForAccountSwap(AAccount.accountInfo) then Exit(False);
+      ss_for_coin_swap:    if Not TAccountComp.IsAccountForCoinSwap(AAccount.accountInfo) then Exit(False);
+      ss_for_sale_or_swap: if Not TAccountComp.IsAccountForSwap(AAccount.accountInfo) then Exit(False);
+      ss_not_for_sale_or_swap: if TAccountComp.IsAccountForSwap(AAccount.accountInfo) then Exit(False);
+    end;
+    if ((LAccountBalanceMin>=0) AND (AAccount.balance<LAccountBalanceMin)) then Exit(False);
+    if ((LAccountBalanceMax>=0) AND (AAccount.balance>LAccountBalanceMax)) then Exit(False);
+    Result := True;
+  end;
+
+var
+  LString : String;
+  LAccountNumber : Integer;
+  LRaw : TRawBytes;
+  LSearchByPubkey : Boolean;
+  LStart, LMax,
+  iPubKey : Integer;
+  LAccount : TAccount;
+  i : Integer;
+  LErrors : String;
+  LAccPubKey : TAccountKey;
+  LOutput : TPCJSONArray;
+
+begin
+  // Get Parameters
+  Result := False;
+  LAccountName := LowerCase(AInputParams.AsString('name', '')); // Convert to lowercase...
+  if AInputParams.IndexOfName('namesearchtype')>=0 then begin
+    LString := AInputParams.AsString('namesearchtype','');
+    if (AnsiSameStr(LString,'exact')) then LSearchByNameType := st_exact
+    else if (AnsiSameStr(LString,'startswith')) then LSearchByNameType := st_startswith
+    else if (AnsiSameStr(LString,'not-startswith')) then LSearchByNameType := st_not_startswith
+    else if (AnsiSameStr(LString,'contains')) then LSearchByNameType := st_contains
+    else if (AnsiSameStr(LString,'not-contains')) then LSearchByNameType := st_not_contains
+    else if (AnsiSameStr(LString,'endswith')) then LSearchByNameType := st_endswith
+    else if (AnsiSameStr(LString,'not-endswith')) then LSearchByNameType := st_not_endswith
+    else begin
+      AErrorNum := CT_RPC_ErrNum_InvalidData;
+      AErrorDesc := Format('Invalid "namesearchtype" value: "%s"',[LString]);
+      Exit(False);
+    end;
+  end else begin
+    if AInputParams.AsBoolean('exact',True) then LSearchByNameType := st_exact
+    else LSearchByNameType := st_startswith;
+  end;
+  LAccountType := AInputParams.AsInteger('type', -1);
+  LStart := AInputParams.AsInteger('start', 0);
+  LMax := AInputParams.AsInteger('max', 100);
+  if AInputParams.IndexOfName('statustype')>=0 then begin
+    LString := AInputParams.AsString('statustype','all');
+    if (AnsiSameStr(LString,'all')) then LSearchByStatusType := ss_all
+    else if (AnsiSameStr(LString,'for-sale')) then LSearchByStatusType := ss_for_sale
+    else if (AnsiSameStr(LString,'for-public-sale')) then LSearchByStatusType := ss_for_public_sale
+    else if (AnsiSameStr(LString,'for-private-sale')) then LSearchByStatusType := ss_for_private_sale
+    else if (AnsiSameStr(LString,'for-swap')) then LSearchByStatusType := ss_for_swap
+    else if (AnsiSameStr(LString,'for-account-swap')) then LSearchByStatusType := ss_for_account_swap
+    else if (AnsiSameStr(LString,'for-coin-swap')) then LSearchByStatusType := ss_for_coin_swap
+    else if (AnsiSameStr(LString,'for-sale-swap')) then LSearchByStatusType := ss_for_sale_or_swap
+    else if (AnsiSameStr(LString,'not-for-sale-swap')) then LSearchByStatusType := ss_not_for_sale_or_swap
+    else begin
+      AErrorNum := CT_RPC_ErrNum_InvalidData;
+      AErrorDesc := Format('Invalid "statustype" value: "%s"',[LString]);
+      Exit(False);
+    end;
+  end else begin
+    if AInputParams.AsBoolean('listed',False) then LSearchByStatusType := ss_for_sale_or_swap
+    else LSearchByStatusType := ss_all;
+  end;
+
+  LAccountBalanceMin := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('min_balance',-1));
+  LAccountBalanceMax := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('max_balance',-1));
+
+  // Validate Parameters
+  if (Length(LAccountName)>0) And (LSearchByNameType = st_exact) then begin
+    LRaw.FromString( LAccountName );
+    if not ASender.Node.Bank.SafeBox.ValidAccountName(LRaw, LErrors) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidAccountName;
+      AErrorDesc := LErrors;
+      exit;
+    end;
+  end;
+
+  if LStart < 0 then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := '"start" param must be >=0';
+    exit;
+  end;
+  if LMax <= 0 then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := '"max" param must be greater than zero';
+    exit;
+  end;
+
+  // Declare return result (empty by default)
+  LOutput := AJSONResponse.GetAsArray('result');
+
+  // Search by accPubKey (if provided)
+  If TPascalCoinJSONComp.CapturePubKey(AInputParams, '',LAccPubKey,LErrors) then begin
+    // Must match accPubKey
+    if (Not Assigned(ASender.Node.Bank.SafeBox.OrderedAccountKeysList)) then begin
+      AErrorNum := CT_RPC_ErrNum_NotImplemented;
+      AErrorDesc := 'Not allowed search by public key';
+      Exit;
+    end;
+    LSearchByPubkey := True;
+    iPubKey := ASender.Node.Bank.SafeBox.OrderedAccountKeysList.IndexOfAccountKey(LAccPubKey);
+    if (iPubKey<0) then begin
+      // No account available with this pubkey, exit
+      Exit;
+    end;
+  end else LSearchByPubkey := False;
+  // Search by name
+  if ((Length(LAccountName)>0) AND (LSearchByNameType in [st_exact] )) then begin
+    LAccountNumber := ASender.Node.Bank.SafeBox.FindAccountByName(LAccountName);
+    if LAccountNumber >= 0 then begin
+      LAccount := ASender.Node.GetMempoolAccount(LAccountNumber);
+      if (_IsValidAccount(LAccount)) and
+        ((Not LSearchByPubkey) OR (TAccountComp.EqualAccountKeys(LAccPubKey,LAccount.accountInfo.accountKey))) then begin
+         TPascalCoinJSONComp.FillAccountObject(LAccount,LOutput.GetAsObject(LOutput.Count));
+      end;
+    end;
+  end else begin
+    // Search by type-forSale-balance
+    for i := LStart to ASender.Node.Bank.AccountsCount - 1 do begin
+      if (LSearchByPubkey) then begin
+        if (i>=ASender.Node.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[iPubKey].Count) then Break;
+        LAccount := ASender.Node.GetMempoolAccount( ASender.Node.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[iPubKey].Get(i) );
+      end else begin
+        LAccount := ASender.Node.GetMempoolAccount(i);
+      end;
+
+      if (_IsValidAccount(LAccount)) then begin
+        TPascalCoinJSONComp.FillAccountObject(LAccount,LOutput.GetAsObject(LOutput.Count));
+        if LOutput.Count>=LMax then break;
+      end;
+    end;
+  end;
+  Result := True;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('findaccounts',TRPCFindAccounts.FindAccounts);
+finalization
+  TRPCProcess.UnregisterProcessMethod('findaccounts');
+end.

+ 449 - 0
src/core/UPCRPCOpData.pas

@@ -0,0 +1,449 @@
+unit UPCRPCOpData;
+
+{ Copyright (c) 2019 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I config.inc}
+
+Uses classes, SysUtils,
+  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  URPC, UCrypto, UWallet, UBlockChain, ULog;
+
+
+Type
+  TRPCOpData = Class
+  private
+    class Function CreateOpData(ARPCProcess : TRPCProcess; ACurrent_protocol : Word;
+       AInputParams : TPCJSONObject;
+       const AAccountSignerPublicKey : TAccountKey;
+       AAccountSigner, AAccountSender, AAccountTarget, ASender_last_n_operation : Cardinal;
+       Const AOperationPayload : TOperationPayload;
+       var AErrorNum: Integer; var AErrorDesc: String; var AOpData : TOpData): Boolean;
+  public
+    class function OpData_SendOpData(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function OpData_SignOpData(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function OpData_FindOpDataOperations(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+  End;
+
+implementation
+
+{ TRPCOpData }
+
+class function TRPCOpData.CreateOpData(ARPCProcess: TRPCProcess;
+  ACurrent_protocol: Word;
+  AInputParams : TPCJSONObject;
+  const AAccountSignerPublicKey : TAccountKey;
+  AAccountSigner, AAccountSender, AAccountTarget, ASender_last_n_operation: Cardinal;
+  const AOperationPayload: TOperationPayload;
+  var AErrorNum: Integer; var AErrorDesc: String; var AOpData : TOpData): Boolean;
+var LSignerKey : TECPrivateKey;
+  LGUID : TGUID;
+  LStringGuid : String;
+begin
+  Result := False;
+  AOpData := Nil;
+  if (Not ARPCProcess.RPCServer.AllowUsePrivateKeys) then begin
+    // Protection when server is locked to avoid private keys call
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit;
+  end;
+  If Not ARPCProcess.RPCServer.WalletKeys.IsValidPassword then begin
+    AErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+    AErrorDesc := 'Wallet is password protected. Unlock first';
+    Exit;
+  end;
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(AAccountSignerPublicKey,LSignerKey,AErrorNum,AErrorDesc) then Exit;
+
+  LStringGuid := AInputParams.AsString('guid','');
+  if LStringGuid<>'' then begin
+    try
+      LGUID := StringToGUID(LStringGuid);
+    except
+      On E:Exception do begin
+        AErrorNum := CT_RPC_ErrNum_InvalidData;
+        AErrordesc := 'Invalid "guid" value '+E.Message;
+        Exit;
+      end;
+    end;
+  end else LGUID := CT_TOpDataData_NUL.guid;
+
+  AOpData := TOpData.CreateOpData(ACurrent_protocol,
+    AAccountSigner,
+    AAccountSender,
+    AAccountTarget,
+    LSignerKey,
+    ASender_last_n_operation+1,
+    AInputParams.AsInteger('data_type',0),
+    AInputParams.AsInteger('data_sequence',0),
+    LGUID,
+    TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0)),
+    TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0)),
+    AOperationPayload);
+  Result := True;
+end;
+
+class function TRPCOpData.OpData_FindOpDataOperations(
+  const ASender: TRPCProcess; const AMethodName: String; AInputParams,
+  AJSONResponse: TPCJSONObject; var AErrorNum: Integer;
+  var AErrorDesc: String): Boolean;
+
+    Procedure DoFindFromBlock(ABlock_number : Integer;
+      ASearchedAccount_number: Cardinal;
+      AStartOperation, AEndOperation: Integer;
+      AAct_depth : Integer; AFirst_Block_Is_Unknown : Boolean;
+      ASearchBySender : Boolean; ASearchSender : Cardinal;
+      ASearchByTarget : Boolean; ASearchTarget : Cardinal;
+      ASearchByGUID : Boolean; ASearchGUID : TGUID;
+      ASearchByDataSequence : Boolean; ASearchDataSequence : Word;
+      ASearchByDataType : Boolean; ASearchDataType : Word;
+      AOperationsResumeList : TOperationsResumeList
+      );
+
+      Function EqualGUIDs(const AGuid_A, AGuid_B : TGUID) : Boolean;
+      var i : Integer;
+      begin
+        Result := (AGuid_A.D1 = AGuid_B.D1)
+          and (AGuid_A.D2 = AGuid_B.D2)
+          and (AGuid_A.D3 = AGuid_B.D3);
+        i := Low(AGuid_A.D4);
+        while (Result) and (i<=High(AGuid_A.D4)) do begin
+          Result := (AGuid_A.D4[i] = AGuid_B.D4[i]);
+          inc(i);
+        end;
+      end;
+
+    var LOpComp : TPCOperationsComp;
+      LOperation : TPCOperation;
+      LOpData : TOpData;
+      LOperationResume : TOperationResume;
+      LList : TList<Cardinal>;
+      i : Integer;
+      LLast_block_number : Integer;
+      LFound_in_block : Boolean;
+      LFoundCounter : Integer;
+    begin
+      if (AAct_depth<=0) then exit;
+      LFoundCounter := 0;
+      LOpComp := TPCOperationsComp.Create(Nil);
+      Try
+        LList := TList<Cardinal>.Create;
+        try
+          LLast_block_number := ABlock_number+1;
+          while (LLast_block_number>ABlock_number) And (AAct_depth>0)
+            And (ABlock_number >= (ASearchedAccount_number DIV CT_AccountsPerBlock))
+            And (LFoundCounter <= AEndOperation) do begin
+
+            if Assigned(ASender) then begin
+              // Threading protection
+              if ASender.Terminated then Exit;
+            end;
+            LFound_in_block := False;
+            LLast_block_number := ABlock_number;
+            LList.Clear;
+            If not ASender.Node.Bank.Storage.LoadBlockChainBlock(LOpComp,ABlock_number) then begin
+              TLog.NewLog(ltdebug,ClassName,'Block '+inttostr(ABlock_number)+' not found. Cannot read operations');
+              Exit;
+            end;
+            LFound_in_block := LOpComp.OperationsHashTree.GetOperationsAffectingAccount(ASearchedAccount_number,LList) > 0;
+              // Reverse order:
+            for i := LList.Count - 1 downto 0 do begin
+              LOperation := LOpComp.Operation[LList.Items[i]];
+              if LOperation is TOpData then begin
+                //
+                LOpData := TOpData( LOperation );
+                // Search by filter:
+                if ((Not ASearchBySender) Or (ASearchSender = LOpData.Data.account_sender))
+                   and ((Not ASearchByTarget) Or (ASearchTarget = LOpData.Data.account_target))
+                   and ((Not ASearchByGUID) Or (EqualGUIDs(ASearchGUID,LOpData.Data.guid)))
+                   and ((Not ASearchByDataSequence) Or (ASearchDataSequence = LOpData.Data.dataSequence))
+                   and ((Not ASearchByDataType) Or (ASearchDataType = LOpData.Data.dataType))
+                then begin
+                  if (LFoundCounter>=AStartOperation) And (LFoundCounter<=AEndOperation) then begin
+                    If TPCOperation.OperationToOperationResume(ABlock_number,LOpData,False,LOpData.SignerAccount,LOperationResume) then begin
+                      LOperationResume.Balance:=-1;
+                      LOperationResume.NOpInsideBlock:=LList.Items[i];
+                      LOperationResume.Block:=ABlock_number;
+                      AOperationsResumeList.Add(LOperationResume);
+                    end;
+                  end;
+                  inc(LFoundCounter);
+                end;
+              end;
+            end; // For LList...
+            //
+            dec(AAct_depth);
+            If (Not LFound_in_block) And (AFirst_Block_Is_Unknown) then begin
+              Dec(ABlock_number);
+            end else begin
+              ABlock_number := LOpComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(ASearchedAccount_number,ABlock_number);
+            end;
+            LOpComp.Clear(true);
+          end;
+        finally
+          LList.Free;
+        end;
+      Finally
+        LOpComp.Free;
+      End;
+    end;
+
+Var LAccount : TAccount;
+  LStartBlock : Cardinal;
+  LMaxDepth : Integer;
+  LSearchedAccount_number: Cardinal;
+  LStartOperation, LMaxOperations: Integer;
+  LFirst_Block_Is_Unknown : Boolean;
+  LSearchBySender : Boolean;
+  LSearchByTarget : Boolean;
+  LSearchByGUID : Boolean; LSearchGUID : TGUID;
+  LSearchByDataSequence : Boolean; LSearchDataSequence : Word;
+  LSearchByDataType : Boolean; LSearchDataType : Word;
+  LOperationsResumeList : TOperationsResumeList;
+  LSender, LTarget : Cardinal;
+  LResultArray : TPCJSONArray;
+  i : Integer;
+begin
+  Result := False;
+
+  LSender := AInputParams.AsCardinal('sender',CT_MaxAccount);
+  LTarget := AInputParams.AsCardinal('target',CT_MaxAccount);
+  LSearchedAccount_number := CT_MaxAccount;
+  LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
+  LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
+  if (LSearchBySender) then begin
+    LSearchedAccount_number := LSender;
+  end else if (LSearchByTarget) then begin
+    LSearchedAccount_number := LTarget;
+  end else begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := 'Must provide "sender" or "target" valid values';
+    Exit;
+  end;
+
+  if AInputParams.IndexOfName('guid')>=0 then begin
+    try
+      LSearchGUID := StringToGUID( AInputParams.AsString('guid','') );
+      LSearchByGUID := True;
+    except
+      on E:Exception do begin
+        AErrorNum := CT_RPC_ErrNum_InvalidData;
+        AErrorDesc := 'Invalid "guid" param '+E.Message;
+        Exit;
+      end;
+    end;
+  end else LSearchByGUID := False;
+
+  if AInputParams.IndexOfName('data_sequence')>=0 then begin
+    LSearchByDataSequence := True;
+    LSearchDataSequence := AInputParams.AsInteger('data_sequence',0);
+  end else LSearchByDataSequence := False;
+
+  if AInputParams.IndexOfName('data_type')>=0 then begin
+    LSearchByDataType := True;
+    LSearchDataType := AInputParams.AsInteger('data_type',0);
+  end else LSearchByDataType := False;
+
+  LMaxDepth := AInputParams.AsInteger('depth',1000);
+  LStartOperation := AInputParams.AsInteger('start',0);
+  LMaxOperations := AInputParams.AsInteger('max',100);
+  if AInputParams.IndexOfName('startblock')>=0 then begin
+    LStartBlock := AInputParams.AsInteger('startblock',100);
+    LFirst_Block_Is_Unknown := True;
+  end else begin
+    if not ASender.RPCServer.GetMempoolAccount(LSearchedAccount_number,LAccount) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidData;
+      AErrorDesc := 'Invalid account';
+      Exit;
+    end;
+    LFirst_Block_Is_Unknown := False;
+    LStartBlock := LAccount.updated_on_block;
+    if LStartBlock>=ASender.Node.Bank.BlocksCount then Dec(LStartBlock); // If its updated on mempool, don't look the mempool
+  end;
+
+  LOperationsResumeList := TOperationsResumeList.Create;
+  try
+    DoFindFromBlock(LStartBlock,
+      LSearchedAccount_number,
+      LStartOperation, LStartOperation + LMaxOperations,
+      LMaxDepth, LFirst_Block_Is_Unknown,
+      LSearchBySender, LSender,
+      LSearchByTarget, LTarget,
+      LSearchByGUID, LSearchGUID,
+      LSearchByDataSequence, LSearchDataSequence,
+      LSearchByDataType, LSearchDataType,
+      LOperationsResumeList
+      );
+    //
+    LResultArray := AJSONResponse.GetAsArray('result');
+
+    for i := 0 to LOperationsResumeList.Count-1 do begin
+      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,LResultArray.GetAsObject( LResultArray.Count ));
+    end;
+    Result := True;
+  finally
+    LOperationsResumeList.Free;
+  end;
+end;
+
+class function TRPCOpData.OpData_SendOpData(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LOpData : TOpData;
+  LSigner, LSender, LTarget : TAccount;
+  LOperationPayload : TOperationPayload;
+  LErrors : String;
+  LOPR : TOperationResume;
+begin
+  Result := False;
+  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('sender',CT_MaxAccount),LSender) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    AErrorDesc := 'Invalid "sender"';
+    Exit;
+  end;
+  if (AInputParams.IndexOfName('signer')>=0) then begin
+    if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('signer',CT_MaxAccount),LSigner) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      AErrorDesc := 'Invalid "signer"';
+      Exit;
+    end;
+  end else LSigner := LSender; // If no "signer" param, then use "sender" as signer by default
+  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('target',CT_MaxAccount),LTarget) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    AErrorDesc := 'Invalid "target"';
+    Exit;
+  end;
+
+  if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
+    TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+    AInputParams.AsString('payload_method','none'),
+    AInputParams.AsString('pwd',''),
+    LSender.accountInfo.accountKey,
+    LTarget.accountInfo.accountKey,
+    LOperationPayload,AErrorNum,AErrorDesc) Then Exit;
+
+
+  if Not CreateOpData(ASender,
+    ASender.Node.Bank.SafeBox.CurrentProtocol,
+    AInputParams,
+    LSigner.accountInfo.accountKey,
+    LSigner.account,
+    LSender.account,
+    LTarget.account,
+    LSigner.n_operation,
+    LOperationPayload,
+    AErrorNum, AErrorDesc, LOpData) then Exit;
+
+  if LOpData=nil then Exit;
+  try
+    If not ASender.Node.AddOperation(Nil,LOpData,LErrors) then begin
+      AErrorDesc := 'Error adding operation: '+LErrors;
+      AErrorNum := CT_RPC_ErrNum_InvalidOperation;
+      Exit;
+    end;
+    TPCOperation.OperationToOperationResume(0,LOpData,False,LSender.account,LOPR);
+    TPascalCoinJSONComp.FillOperationObject(LOPR,ASender.Node.Bank.BlocksCount,AJSONResponse.GetAsObject('result'));
+    Result := True;
+  finally
+    LOpData.free;
+  end;
+end;
+
+class function TRPCOpData.OpData_SignOpData(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LOpData : TOpData;
+  LOperationPayload : TOperationPayload;
+  LErrors : String;
+  LOPR : TOperationResume;
+  LEncodePayloadType : String;
+  LPayloadPubkey, LSignerPubkey : TAccountKey;
+  LOperationsHashTree : TOperationsHashTree;
+  LSigner : Cardinal;
+begin
+  Result := False;
+
+  if Not TPascalCoinJSONComp.HexaStringToOperationsHashTree(AInputParams.AsString('rawoperations',''),CT_BUILD_PROTOCOL,LOperationsHashTree,LErrors) then begin
+    AErrorNum:=CT_RPC_ErrNum_InvalidData;
+    AErrorDesc:= 'Error decoding param "rawoperations": '+LErrors;
+    Exit;
+  end;
+  try
+
+    LEncodePayloadType := AInputParams.AsString('payload_method','none');
+    if (LEncodePayloadType='sender') then begin
+      if Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'sender_',LPayloadPubkey,AErrorDesc) then begin
+        AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+        Exit;
+      end;
+    end else if (LEncodePayloadType='dest') then begin
+      if Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'target_',LPayloadPubkey,AErrorDesc) then begin
+        AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+        Exit;
+      end;
+    end;
+
+    if Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'signer_',LSignerPubkey,AErrorDesc) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+      Exit;
+    end;
+
+    if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
+      TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+      AInputParams.AsString('payload_method','dest'),
+      AInputParams.AsString('pwd',''),
+      LPayloadPubkey,
+      LPayloadPubkey,
+      LOperationPayload,AErrorNum,AErrorDesc) then Exit;
+
+    if Not CreateOpData(ASender,
+      AInputParams.AsInteger('protocol',CT_BUILD_PROTOCOL),
+      AInputParams,
+      LSignerPubkey,
+      AInputParams.AsCardinal('signer',CT_MaxAccount),
+      AInputParams.AsCardinal('sender',CT_MaxAccount),
+      AInputParams.AsCardinal('target',CT_MaxAccount),
+      AInputParams.AsCardinal('last_n_operation',0),
+      LOperationPayload,
+      AErrorNum, AErrorDesc, LOpData) then Exit;
+    if LOpData=nil then Exit;
+    try
+      LOperationsHashTree.AddOperationToHashTree(LOpData);
+      TPascalCoinJSONComp.FillOperationsHashTreeObject(LOperationsHashTree,AJSONResponse.GetAsObject('result'));
+      Result := true;
+    finally
+      LOpData.Free;
+    end;
+  finally
+    LOperationsHashTree.Free;
+  end;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('senddata',TRPCOpData.OpData_SendOpData);
+  TRPCProcess.RegisterProcessMethod('signdata',TRPCOpData.OpData_SignOpData);
+  TRPCProcess.RegisterProcessMethod('finddataoperations',TRPCOpData.OpData_FindOpDataOperations);
+finalization
+  TRPCProcess.UnregisterProcessMethod('senddata');
+  TRPCProcess.UnregisterProcessMethod('signdata');
+  TRPCProcess.UnregisterProcessMethod('finddataoperations');
+end.

+ 11 - 5
src/core/UPCSafeBoxRootHash.pas

@@ -80,6 +80,10 @@ unit UPCSafeBoxRootHash;
 
 
 }
 }
 
 
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
 interface
 interface
 
 
 {$I config.inc}
 {$I config.inc}
@@ -377,7 +381,7 @@ constructor TBytesBuffer32Safebox.Create(ADefaultIncrement: Integer);
 begin
 begin
   FNextLevelBytesBuffer := Nil;
   FNextLevelBytesBuffer := Nil;
   FSafeBoxHashCalcType := sbh_Single_Sha256;
   FSafeBoxHashCalcType := sbh_Single_Sha256;
-  inherited;
+  inherited Create(ADefaultIncrement);
 end;
 end;
 
 
 destructor TBytesBuffer32Safebox.Destroy;
 destructor TBytesBuffer32Safebox.Destroy;
@@ -446,18 +450,20 @@ begin
 end;
 end;
 
 
 procedure TBytesBuffer32Safebox.RedoNextLevelsForMerkleRootHash;
 procedure TBytesBuffer32Safebox.RedoNextLevelsForMerkleRootHash;
-var i, j : Integer;
+var i : Integer;
+  LNextDefaultIncrement : Integer;
 begin
 begin
   if (Self.Length<64) or ((Self.Length MOD 32)<>0) then begin
   if (Self.Length<64) or ((Self.Length MOD 32)<>0) then begin
     FreeAndNil(FNextLevelBytesBuffer);
     FreeAndNil(FNextLevelBytesBuffer);
     Exit;
     Exit;
   end;
   end;
   if Not Assigned(FNextLevelBytesBuffer) then begin
   if Not Assigned(FNextLevelBytesBuffer) then begin
-    FNextLevelBytesBuffer := TBytesBuffer32Safebox.Create(32*1000);
+    if (DefaultIncrement >= 64) then LNextDefaultIncrement := DefaultIncrement DIV 2
+    else LNextDefaultIncrement := 32;
+    FNextLevelBytesBuffer := TBytesBuffer32Safebox.Create(LNextDefaultIncrement);
     FNextLevelBytesBuffer.SafeBoxHashCalcType := Self.SafeBoxHashCalcType;
     FNextLevelBytesBuffer.SafeBoxHashCalcType := Self.SafeBoxHashCalcType;
   end;
   end;
-  j := Self.Length DIV 64;
-  for i := 0 to ((Self.Length DIV 64)-1) do begin
+  for i := 0 to (((Self.Length+32) DIV 64)-1) do begin
     NotifyUpdated( (i*64), 32);
     NotifyUpdated( (i*64), 32);
   end;
   end;
 end;
 end;

+ 6 - 4
src/core/UPCTNetDataExtraMessages.pas

@@ -134,7 +134,7 @@ var LSenderPublicKey : TAccountKey;
   LIndexKey : Integer;
   LIndexKey : Integer;
   LAccount : TAccount;
   LAccount : TAccount;
   LOpChangeKey : TOpChangeKey;
   LOpChangeKey : TOpChangeKey;
-  LPayload : TRawBytes;
+  LPayload : TOperationPayload;
   LErrors : String;
   LErrors : String;
   LWord : Word;
   LWord : Word;
 begin
 begin
@@ -146,7 +146,8 @@ begin
   if TStreamOp.ReadAccountKey(AReceivedData,LSenderPublicKey)<=0 then Exit;
   if TStreamOp.ReadAccountKey(AReceivedData,LSenderPublicKey)<=0 then Exit;
   if Not RandomGetWalletKeysAccount(FNode.Bank.SafeBox,FWalletKeys,0,10000,LIndexKey,LAccount) then Exit;
   if Not RandomGetWalletKeysAccount(FNode.Bank.SafeBox,FWalletKeys,0,10000,LIndexKey,LAccount) then Exit;
   // Send
   // Send
-  LPayload.FromString('Free Account to '+ASenderConnection.Client.RemoteHost);
+  LPayload := CT_TOperationPayload_NUL;
+  LPayload.payload_raw.FromString('Free Account to '+ASenderConnection.Client.RemoteHost);
   LOpChangeKey := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,
   LOpChangeKey := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,
     LAccount.account,FWalletKeys.Key[LIndexKey].PrivateKey,LSenderPublicKey,0,LPayload);
     LAccount.account,FWalletKeys.Key[LIndexKey].PrivateKey,LSenderPublicKey,0,LPayload);
   try
   try
@@ -168,7 +169,7 @@ var LSenderAccount : Cardinal;
   LIndexKey : Integer;
   LIndexKey : Integer;
   LAccount : TAccount;
   LAccount : TAccount;
   LOpTransaction : TOpTransaction;
   LOpTransaction : TOpTransaction;
-  LPayload : TRawBytes;
+  LPayload : TOperationPayload;
   LErrors : String;
   LErrors : String;
   LSendAmount : Int64;
   LSendAmount : Int64;
   LInt : Integer;
   LInt : Integer;
@@ -187,7 +188,8 @@ begin
   if LSendAmount<=0 then Exit;
   if LSendAmount<=0 then Exit;
 
 
   // Send
   // Send
-  LPayload.FromString('Free Money to '+ASenderConnection.Client.RemoteHost);
+  LPayload := CT_TOperationPayload_NUL;
+  LPayload.payload_raw.FromString('Free Money to '+ASenderConnection.Client.RemoteHost);
   LOpTransaction := TOpTransaction.CreateTransaction(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,
   LOpTransaction := TOpTransaction.CreateTransaction(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,
     LSenderAccount,FWalletKeys.Key[LIndexKey].PrivateKey,LSendAmount,0,LPayload);
     LSenderAccount,FWalletKeys.Key[LIndexKey].PrivateKey,LSendAmount,0,LPayload);
   try
   try

+ 105 - 26
src/core/UPoolMinerThreads.pas

@@ -25,7 +25,7 @@ interface
 {$I config.inc}
 {$I config.inc}
 
 
 uses
 uses
-  Classes, SysUtils, syncobjs, UThread, UPoolMining, UAccounts, UCrypto, ULog, UBlockChain, USha256, URandomHash, UBaseTypes, UCommon,
+  Classes, SysUtils, syncobjs, UThread, UPoolMining, UAccounts, UCrypto, ULog, UBlockChain, USha256, URandomHash, URandomHash2, UBaseTypes, UCommon,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type
@@ -157,6 +157,7 @@ Type
   private
   private
     FCPUDeviceThread : TCPUDeviceThread;
     FCPUDeviceThread : TCPUDeviceThread;
     FLock : TCriticalSection;
     FLock : TCriticalSection;
+    FJobNum : Integer;
   protected
   protected
     FCurrentMinerValuesForWork : TMinerValuesForWork;
     FCurrentMinerValuesForWork : TMinerValuesForWork;
     FInternalSha256 : TSHA256HASH;
     FInternalSha256 : TSHA256HASH;
@@ -498,7 +499,10 @@ begin
   FLastDigest := digest;
   FLastDigest := digest;
   LUseRandomHash := TPoolMinerThread.UseRandomHash(usedMinerValuesForWork.version);
   LUseRandomHash := TPoolMinerThread.UseRandomHash(usedMinerValuesForWork.version);
   if LUseRandomHash then
   if LUseRandomHash then
-    LHash := TCrypto.DoRandomHash(digest)
+    if (usedMinerValuesForWork.version >= CT_PROTOCOL_5) then
+      LHash := TCrypto.DoRandomHash2(digest)
+    else
+      LHash := TCrypto.DoRandomHash(digest)
   else
   else
     LHash := TCrypto.DoSha256(TCrypto.DoSha256(digest));
     LHash := TCrypto.DoSha256(TCrypto.DoSha256(digest));
   if (TBaseType.BinStrComp(LHash,usedMinerValuesForWork.target_pow)<=0) then begin
   if (TBaseType.BinStrComp(LHash,usedMinerValuesForWork.target_pow)<=0) then begin
@@ -745,6 +749,7 @@ begin
       cpu := TCPUOpenSSLMinerThread(l[i]);
       cpu := TCPUOpenSSLMinerThread(l[i]);
       cpu.FLock.Acquire;
       cpu.FLock.Acquire;
       try
       try
+        Inc(cpu.FJobNum);
         cpu.FCurrentMinerValuesForWork := FMinerValuesForWork;
         cpu.FCurrentMinerValuesForWork := FMinerValuesForWork;
         cpu.FInternalSha256 := sflc;
         cpu.FInternalSha256 := sflc;
         cpu.FInternalChunk := lc;
         cpu.FInternalChunk := lc;
@@ -781,17 +786,28 @@ end;
 { TCPUOpenSSLMinerThread }
 { TCPUOpenSSLMinerThread }
 
 
 procedure TCPUOpenSSLMinerThread.BCExecute;
 procedure TCPUOpenSSLMinerThread.BCExecute;
+
+type
+  TNonceResult = record
+    Nonce : UInt32;
+    PoW : TBytes;
+  end;
+
 Var
 Var
   ts : Cardinal;
   ts : Cardinal;
-  i,roundsToDo : Integer;
+  i, j, roundsToDo, LRoundsPerformed : Integer;
   nonce : Cardinal;
   nonce : Cardinal;
   baseRealTC,baseHashingTC,finalHashingTC : TTickCount;
   baseRealTC,baseHashingTC,finalHashingTC : TTickCount;
   resultPoW : TRawBytes;
   resultPoW : TRawBytes;
-  //
+  LRoundJobNum : Integer;
   AuxStats : TMinerStats;
   AuxStats : TMinerStats;
   dstep : Integer;
   dstep : Integer;
-  LUseRandomHash : boolean;
+  LUseRandomHash, LUseCachedHash : boolean;
   LRandomHasher : TRandomHashFast;
   LRandomHasher : TRandomHashFast;
+  LRandomHasher2 : TRandomHash2Fast;
+  LCachedItem : TRandomHash2Fast.TCachedHash;
+  LNonceResult : TNonceResult;
+  LResultsToCheck : TList<TNonceResult>;
   LDisposables : TDisposables;
   LDisposables : TDisposables;
 begin
 begin
   DebugStep := '----------';
   DebugStep := '----------';
@@ -799,6 +815,11 @@ begin
   nonce := 0;
   nonce := 0;
   dstep := 0;
   dstep := 0;
   LRandomHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
   LRandomHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  LRandomHasher2 := LDisposables.AddObject( TRandomHash2Fast.Create ) as TRandomHash2Fast;
+  LResultsToCheck := LDisposables.AddObject( TList<TNonceResult>.Create ) as TList<TNonceResult>;
+  LRandomHasher2.EnableCaching := True;
+  LRandomHasher2.Cache.EnablePartiallyComputed:=True;
+  LUseCachedHash := False;
   Try
   Try
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
       Try
       Try
@@ -812,7 +833,10 @@ begin
         try
         try
           LUseRandomHash := TPoolMinerThread.UseRandomHash(FCurrentMinerValuesForWork.version);
           LUseRandomHash := TPoolMinerThread.UseRandomHash(FCurrentMinerValuesForWork.version);
           if (LUseRandomHash) then begin
           if (LUseRandomHash) then begin
-            roundsToDo := 20;
+            if FCurrentMinerValuesForWork.version < CT_PROTOCOL_5 then
+              roundsToDo := 20
+            else
+              roundsToDo := 200;
           end else begin
           end else begin
             roundsToDo := 10000;
             roundsToDo := 10000;
           end;
           end;
@@ -826,37 +850,91 @@ begin
           // Timestamp
           // Timestamp
           ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
           ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
           if ts<=FCurrentMinerValuesForWork.timestamp then ts := FCurrentMinerValuesForWork.timestamp+1;
           if ts<=FCurrentMinerValuesForWork.timestamp then ts := FCurrentMinerValuesForWork.timestamp+1;
-
           If FDigestStreamMsg.Size>8 then begin
           If FDigestStreamMsg.Size>8 then begin
             if FCPUDeviceThread.FUseOpenSSLFunctions OR LUseRandomHash then begin
             if FCPUDeviceThread.FUseOpenSSLFunctions OR LUseRandomHash then begin
+
+              // update timestamp
               FDigestStreamMsg.Position:=FDigestStreamMsg.Size - 8;
               FDigestStreamMsg.Position:=FDigestStreamMsg.Size - 8;
               FDigestStreamMsg.Write(ts,4);
               FDigestStreamMsg.Write(ts,4);
+              LRandomHasher2.Cache.Clear;
+              LUseCachedHash:=False;
               baseHashingTC:=TPlatform.GetTickCount;
               baseHashingTC:=TPlatform.GetTickCount;
               dstep := 4;
               dstep := 4;
+              LRoundJobNum := FJobNum;
+              LRoundsPerformed := 0;
               for i := 1 to roundsToDo do begin
               for i := 1 to roundsToDo do begin
+                LResultsToCheck.Clear;
+                if LRoundJobNum <> FJobNum then
+                  break;
+
+                // write nonce
                 FDigestStreamMsg.Position := FDigestStreamMsg.Size - 4;
                 FDigestStreamMsg.Position := FDigestStreamMsg.Size - 4;
                 FDigestStreamMsg.Write(nonce,4);
                 FDigestStreamMsg.Write(nonce,4);
+
+                // perform the hash
                 if LUseRandomHash then begin
                 if LUseRandomHash then begin
-                  // Note if i > 1 then FDigestStreamMsg.Memory == LHasher.NextHeader (needs to be for CPU optimization to work)
-                  TCrypto.DoRandomHash(LRandomHasher,FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
-                end else
+                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then begin
+                    // Note if i > 1 then FDigestStreamMsg.Memory == LHasher.NextHeader (needs to be for CPU optimization to work)
+                    TCrypto.DoRandomHash(LRandomHasher,FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
+                    LNonceResult.Nonce := nonce;
+                    LNonceResult.PoW := resultPoW;
+                    LResultsToCheck.Add(LNonceResult);
+                    Inc(LRoundsPerformed);
+                  end else begin
+                    if LUseCachedHash then begin
+                      //if NOT BytesEqual(FDigestStreamMsg.ToBytes, LCachedItem.Header) then raise Exception.Create('Cache consistency bug');
+                      resultPow := LRandomHasher2.ResumeHash(LCachedItem);
+                      LUseCachedHash := False;
+                    end else
+                      resultPoW := LRandomHasher2.Hash(FDigestStreamMsg.ToBytes);
+                    LNonceResult.Nonce := nonce;
+                    LNonceResult.PoW := resultPoW;
+                    LResultsToCheck.Add(LNonceResult);
+                    Inc(LRoundsPerformed);
+                    while LRandomHasher2.Cache.HasComputedHash do begin
+                      LCachedItem := LRandomHasher2.Cache.PopComputedHash;
+                      LNonceResult.Nonce := LCachedItem.Nonce;
+                      LNonceResult.PoW := LCachedItem.RoundOutputs[0];
+                      LResultsToCheck.Add(LNonceResult);
+                      Inc(LRoundsPerformed);
+                    end;
+                  end
+                end else begin
                   TCrypto.DoDoubleSha256(FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
                   TCrypto.DoDoubleSha256(FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
-                if (TBaseType.BinStrComp(resultPoW,FCurrentMinerValuesForWork.target_pow)<0) then begin
-                  if (Terminated) Or (FCPUDeviceThread.Terminated) then exit;
-                  dstep := 5;
-                  FLock.Release;
-                  try
-                    dstep := 6;
-                    FCPUDeviceThread.FoundNOnce(FCurrentMinerValuesForWork, ts, nonce);
-                    dstep := 7;
-                  finally
-                    FLock.Acquire;
+                  LNonceResult.Nonce := nonce;
+                  LNonceResult.PoW := resultPoW;
+                  LResultsToCheck.Add(LNonceResult);
+                  Inc(LRoundsPerformed);
+                end;
+
+                // check results
+                for j:= 0 to LResultsToCheck.Count - 1 do begin
+                  if (TBaseType.BinStrComp(LResultsToCheck[j].PoW,FCurrentMinerValuesForWork.target_pow)<0) then begin
+                    if (Terminated) Or (FCPUDeviceThread.Terminated) then exit;
+                    dstep := 5;
+                    FLock.Release;
+                    try
+                      dstep := 6;
+                      FCPUDeviceThread.FoundNOnce(FCurrentMinerValuesForWork, ts, LResultsToCheck[j].Nonce);
+                      dstep := 7;
+                    finally
+                      FLock.Acquire;
+                    end;
+                    dstep := 8;
                   end;
                   end;
-                  dstep := 8;
                 end;
                 end;
-                if LUseRandomHash then
-                  nonce := LRandomHasher.NextNonce
-                else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
+
+                // select next nonce
+                if LUseRandomHash then begin
+                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then
+                    nonce := LRandomHasher.NextNonce
+                  else if LRandomHasher2.Cache.HasNextPartiallyComputedHash then begin
+                    LCachedItem := LRandomHasher2.Cache.PopNextPartiallyComputedHash;
+                    LUseCachedHash := True;
+                    Nonce := LCachedItem.Nonce;
+                  end else
+                    nonce := Random(FMaxNOnce - FMinNOnce) + FMinNOnce;
+                end else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
               end;
               end;
               finalHashingTC:=TPlatform.GetTickCount;
               finalHashingTC:=TPlatform.GetTickCount;
             end else begin
             end else begin
@@ -872,12 +950,12 @@ begin
                     FLock.Acquire;
                     FLock.Acquire;
                   end;
                   end;
                 end;
                 end;
-                if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
+                if(nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
               end;
               end;
               finalHashingTC:=TPlatform.GetTickCount;
               finalHashingTC:=TPlatform.GetTickCount;
             end;
             end;
             AuxStats.Miners:=FCPUDeviceThread.FCPUs;
             AuxStats.Miners:=FCPUDeviceThread.FCPUs;
-            AuxStats.RoundsCount:=roundsToDo;
+            AuxStats.RoundsCount:=LRoundsPerformed;
             AuxStats.WorkingMillisecondsTotal:=TPlatform.GetTickCount - baseRealTC;
             AuxStats.WorkingMillisecondsTotal:=TPlatform.GetTickCount - baseRealTC;
             AuxStats.WorkingMillisecondsHashing:= finalHashingTC - baseHashingTC;
             AuxStats.WorkingMillisecondsHashing:= finalHashingTC - baseHashingTC;
             dstep := 9;
             dstep := 9;
@@ -905,6 +983,7 @@ begin
   FDigestStreamMsg := TMemoryStream.Create;
   FDigestStreamMsg := TMemoryStream.Create;
   FMinNOnce := 0; FMaxNOnce:=$FFFFFFFF;
   FMinNOnce := 0; FMaxNOnce:=$FFFFFFFF;
   FResetNOnce:=True;
   FResetNOnce:=True;
+  FJobNum := 0;
   inherited Create(false);
   inherited Create(false);
 end;
 end;
 
 

+ 4 - 1
src/core/UPoolMining.pas

@@ -690,6 +690,7 @@ begin
       response_result.GetAsVariant('initial_sbh').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.initial_safe_box_hash );
       response_result.GetAsVariant('initial_sbh').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.initial_safe_box_hash );
       response_result.GetAsVariant('operations_hash').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.operations_hash );
       response_result.GetAsVariant('operations_hash').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.operations_hash );
       response_result.GetAsVariant('pow').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.proof_of_work );
       response_result.GetAsVariant('pow').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.proof_of_work );
+      response_result.GetAsVariant('previous_pow').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.previous_proof_of_work );
       Client.SendJSONRPCResponse(response_result,id_value);
       Client.SendJSONRPCResponse(response_result,id_value);
     Finally
     Finally
       response_result.Free;
       response_result.Free;
@@ -837,7 +838,9 @@ begin
             TLog.NewLog(ltInfo,ClassName,sJobInfo+' - Found a solution for block '+IntToStr(nbOperations.OperationBlock.block));
             TLog.NewLog(ltInfo,ClassName,sJobInfo+' - Found a solution for block '+IntToStr(nbOperations.OperationBlock.block));
           end else TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Calculated Pow > Min PoW ->  %s > %s',
           end else TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Calculated Pow > Min PoW ->  %s > %s',
             [TCrypto.ToHexaString(P^.OperationsComp.OperationBlock.proof_of_work),TCrypto.ToHexaString(_targetPoW)]));
             [TCrypto.ToHexaString(P^.OperationsComp.OperationBlock.proof_of_work),TCrypto.ToHexaString(_targetPoW)]));
-        end else TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Timestamp %d < MinTimestamp %d',[_timestamp,P^.SentMinTimestamp]));
+        end else begin
+          TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Timestamp %d < MinTimestamp %d',[_timestamp,P^.SentMinTimestamp]));
+        end;
         dec(i);
         dec(i);
       end;
       end;
     Finally
     Finally

File diff suppressed because it is too large
+ 359 - 204
src/core/URPC.pas


+ 41 - 18
src/core/URandomHash.pas

@@ -103,7 +103,9 @@ unit URandomHash;
 
 
 interface
 interface
 
 
-uses {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, SysUtils, HlpIHash, HlpBits, HlpHashFactory;
+uses {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+     SysUtils, HlpIHash, HlpBits, HlpHashFactory,
+     UCommon;
 
 
 type
 type
 
 
@@ -115,7 +117,7 @@ type
       M = (10 * 1024) * 5; // The memory expansion unit (in bytes), total bytes per nonce = M * (2^N (N-2) + 2)
       M = (10 * 1024) * 5; // The memory expansion unit (in bytes), total bytes per nonce = M * (2^N (N-2) + 2)
 
 
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
-      FMurmurHash3_x86_32 : IHash;
+      FMurmur3 : IHash;
       FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
       FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
       function ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
       function ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
       function MemTransform1(const AChunk: TBytes): TBytes; inline;
       function MemTransform1(const AChunk: TBytes): TBytes; inline;
@@ -159,6 +161,7 @@ type
         FComputedIndex : Integer;
         FComputedIndex : Integer;
         FChecksum : UInt32;
         FChecksum : UInt32;
         FMurMur3 : IHash;
         FMurMur3 : IHash;
+
         function GetCount : Integer;
         function GetCount : Integer;
         function CalculateChecksum : UInt32;
         function CalculateChecksum : UInt32;
       public
       public
@@ -182,12 +185,14 @@ type
     private
     private
       function GetCachedHeader : TBytes;
       function GetCachedHeader : TBytes;
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
-      FMurmurHash3_x86_32 : IHash;
+      FMurmur3 : IHash;
       FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
       FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
       FCachedHeader : TBytes;
       FCachedHeader : TBytes;
       FCachedNonce : UInt32;
       FCachedNonce : UInt32;
       FCachedOutput : TChecksummedByteCollection;
       FCachedOutput : TChecksummedByteCollection;
-
+      FMemStats : TStatistics;
+      FCaptureMemStats : Boolean;
+      FEnableCache : Boolean;
       procedure MemTransform1(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform1(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform2(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform2(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform3(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform3(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
@@ -206,6 +211,9 @@ type
     public
     public
       property NextHeader : TBytes read GetCachedHeader;
       property NextHeader : TBytes read GetCachedHeader;
       property NextNonce : UInt32 read FCachedNonce;
       property NextNonce : UInt32 read FCachedNonce;
+      property MemStats : TStatistics read FMemStats;
+      property CaptureMemStats : Boolean read FCaptureMemStats write FCaptureMemStats;
+      property EnableCache : Boolean read FEnableCache write FEnableCache;
       constructor Create;
       constructor Create;
       destructor Destroy; override;
       destructor Destroy; override;
       function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
       function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
@@ -263,7 +271,7 @@ resourcestring
 
 
 implementation
 implementation
 
 
-uses UCommon, UMemory;
+uses UMemory;
 
 
 { Global Functions }
 { Global Functions }
 
 
@@ -279,7 +287,7 @@ end;
 
 
 constructor TRandomHash.Create;
 constructor TRandomHash.Create;
 begin
 begin
-  FMurmurHash3_x86_32 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  FMurmur3 := THashFactory.THash32.CreateMurmurHash3_x86_32();
   FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
   FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
   FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
   FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
   FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
   FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
@@ -303,7 +311,7 @@ end;
 destructor TRandomHash.Destroy;
 destructor TRandomHash.Destroy;
 var i : integer;
 var i : integer;
 begin
 begin
- FMurmurHash3_x86_32 := nil;
+ FMurmur3 := nil;
  for i := Low(FHashAlg) to High(FHashAlg) do
  for i := Low(FHashAlg) to High(FHashAlg) do
    FHashAlg[i] := nil;
    FHashAlg[i] := nil;
  inherited Destroy;
  inherited Destroy;
@@ -389,19 +397,19 @@ end;
 
 
 function TRandomHash.Checksum(const AInput: TBytes): UInt32;
 function TRandomHash.Checksum(const AInput: TBytes): UInt32;
 begin
 begin
-  Result := FMurmurHash3_x86_32.ComputeBytes(AInput).GetUInt32;
+  Result := FMurmur3.ComputeBytes(AInput).GetUInt32;
 end;
 end;
 
 
 function TRandomHash.Checksum(const AInput : TArray<TBytes>): UInt32;
 function TRandomHash.Checksum(const AInput : TArray<TBytes>): UInt32;
 var
 var
   i: Int32;
   i: Int32;
 begin
 begin
-  FMurmurHash3_x86_32.Initialize;
+  FMurmur3.Initialize;
   for i := Low(AInput) to High(AInput) do
   for i := Low(AInput) to High(AInput) do
   begin
   begin
-    FMurmurHash3_x86_32.TransformBytes(AInput[i]);
+    FMurmur3.TransformBytes(AInput[i]);
   end;
   end;
-  Result := FMurmurHash3_x86_32.TransformFinal.GetUInt32;
+  Result := FMurmur3.TransformFinal.GetUInt32;
 end;
 end;
 
 
 function TRandomHash.Compress(const AInputs : TArray<TBytes>): TBytes;
 function TRandomHash.Compress(const AInputs : TArray<TBytes>): TBytes;
@@ -585,7 +593,7 @@ end;
 
 
 constructor TRandomHashFast.Create;
 constructor TRandomHashFast.Create;
 begin
 begin
-  FMurmurHash3_x86_32 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  FMurmur3 := THashFactory.THash32.CreateMurmurHash3_x86_32();
   FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
   FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
   FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
   FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
   FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
   FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
@@ -604,12 +612,15 @@ begin
   FHashAlg[15] := THashFactory.TCrypto.CreateMD5();
   FHashAlg[15] := THashFactory.TCrypto.CreateMD5();
   FHashAlg[16] := THashFactory.TCrypto.CreateRadioGatun32();
   FHashAlg[16] := THashFactory.TCrypto.CreateRadioGatun32();
   FHashAlg[17] := THashFactory.TCrypto.CreateWhirlPool();
   FHashAlg[17] := THashFactory.TCrypto.CreateWhirlPool();
+  FEnableCache := True;
+  FCaptureMemStats := False;
+  FMemStats.Reset;
 end;
 end;
 
 
 destructor TRandomHashFast.Destroy;
 destructor TRandomHashFast.Destroy;
 var i : integer;
 var i : integer;
 begin
 begin
- FMurmurHash3_x86_32 := nil;
+ FMurmur3 := nil;
  for i := Low(FHashAlg) to High(FHashAlg) do
  for i := Low(FHashAlg) to High(FHashAlg) do
    FHashAlg[i] := nil;
    FHashAlg[i] := nil;
  if Assigned(FCachedOutput) then
  if Assigned(FCachedOutput) then
@@ -629,11 +640,23 @@ end;
 function TRandomHashFast.Hash(const ABlockHeader: TBytes): TBytes;
 function TRandomHashFast.Hash(const ABlockHeader: TBytes): TBytes;
 var
 var
   LAllOutputs: TChecksummedByteCollection;
   LAllOutputs: TChecksummedByteCollection;
-  LSeed, LTemp: UInt32;
+  LSeed, LTemp, LSize: UInt32;
+  i : Integer;
   LDisposables : TDisposables;
   LDisposables : TDisposables;
 begin
 begin
   LAllOutputs := LDisposables.AddObject( Hash(ABlockHeader, N) ) as TChecksummedByteCollection;
   LAllOutputs := LDisposables.AddObject( Hash(ABlockHeader, N) ) as TChecksummedByteCollection;
   Result := FHashAlg[0].ComputeBytes(Compress(LAllOutputs)).GetBytes;
   Result := FHashAlg[0].ComputeBytes(Compress(LAllOutputs)).GetBytes;
+
+  if FCaptureMemStats then begin
+    LSize := 0;
+    for i := 0 to LAllOutputs.Count - 1 do
+      Inc(LSize, Length(LAllOutputs.Get(i)));
+
+    if Assigned(FCachedOutput) then
+      for i := 0 to FCachedOutput.Count - 1 do
+        Inc(LSize, Length(FCachedOutput.Get(i)));
+    FMemStats.AddDatum(LSize);
+  end;
 end;
 end;
 
 
 function TRandomHashFast.Hash(const ABlockHeader: TBytes; ARound: Int32) : TChecksummedByteCollection;
 function TRandomHashFast.Hash(const ABlockHeader: TBytes; ARound: Int32) : TChecksummedByteCollection;
@@ -678,7 +701,7 @@ begin
     LDisposables.AddObject(LNeighbourOutputs);
     LDisposables.AddObject(LNeighbourOutputs);
 
 
     // Cache neighbour nonce n-1 calculation if on final round (neighbour will be next nonce)
     // Cache neighbour nonce n-1 calculation if on final round (neighbour will be next nonce)
-    if (ARound = N) then begin
+    if FEnableCache AND (ARound = N) then begin
       FCachedNonce := LNeighbourNonce;
       FCachedNonce := LNeighbourNonce;
       FCachedHeader := LNeighbourNonceHeader;
       FCachedHeader := LNeighbourNonceHeader;
       if Assigned(FCachedOutput) then
       if Assigned(FCachedOutput) then
@@ -740,9 +763,9 @@ end;
 
 
 function TRandomHashFast.Checksum(const AInput: TBytes; AOffset, ALength: Integer): UInt32;
 function TRandomHashFast.Checksum(const AInput: TBytes; AOffset, ALength: Integer): UInt32;
 begin
 begin
-  FMurmurHash3_x86_32.Initialize;
-  FMurmurHash3_x86_32.TransformBytes(AInput, AOffset, ALength);
-  Result := FMurmurHash3_x86_32.TransformFinal.GetUInt32();
+  FMurmur3.Initialize;
+  FMurmur3.TransformBytes(AInput, AOffset, ALength);
+  Result := FMurmur3.TransformFinal.GetUInt32();
 end;
 end;
 
 
 function TRandomHashFast.Compress(const AInputs : TChecksummedByteCollection): TBytes;
 function TRandomHashFast.Compress(const AInputs : TChecksummedByteCollection): TBytes;

+ 1144 - 0
src/core/URandomHash2.pas

@@ -0,0 +1,1144 @@
+unit URandomHash2;
+
+{ Copyright (c) 2019 by Herman Schoenfeld
+
+  RandomHash2 Reference Implementation
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{%region Compiler Directives}
+{$IFDEF FPC}
+  {$UNDEF DELPHI}
+  {$MODE delphi}
+  {$DEFINE USE_UNROLLED_VARIANT}
+  {$OVERFLOWCHECKS OFF}
+  {$RANGECHECKS OFF}
+  {$POINTERMATH ON}
+  {$WARNINGS OFF}
+  {$HINTS OFF}
+  {$NOTES OFF}
+  {$OPTIMIZATION LEVEL3}
+  {$OPTIMIZATION PEEPHOLE}
+  {$OPTIMIZATION REGVAR}
+  {$OPTIMIZATION LOOPUNROLL}
+  {$OPTIMIZATION STRENGTH}
+  {$OPTIMIZATION CSE}
+  {$OPTIMIZATION DFA}
+  {$IFDEF CPUI386}
+    {$OPTIMIZATION USEEBP}
+  {$ENDIF}
+  {$IFDEF CPUX86_64}
+    {$OPTIMIZATION USERBP}
+  {$ENDIF}
+{$ELSE}
+  {$DEFINE USE_UNROLLED_VARIANT}
+  {$DEFINITIONINFO ON}
+  {$HINTS OFF}
+  {$OVERFLOWCHECKS OFF}
+  {$RANGECHECKS OFF}
+  {$POINTERMATH ON}
+  {$STRINGCHECKS OFF}
+  {$WARN DUPLICATE_CTOR_DTOR OFF}
+  // 2010 only
+  {$IF CompilerVersion = 21.0}
+  {$DEFINE DELPHI2010}
+  {$IFEND}
+  // 2010 and Above
+  {$IF CompilerVersion >= 21.0}
+  {$DEFINE DELPHI2010_UP}
+  {$IFEND}
+  // XE and Above
+  {$IF CompilerVersion >= 22.0}
+  {$DEFINE DELPHIXE_UP}
+  {$IFEND}
+  // XE2 and Above
+  {$IF CompilerVersion >= 23.0}
+  {$DEFINE DELPHIXE2_UP}
+  {$DEFINE HAS_UNITSCOPE}
+  {$IFEND}
+  // XE3 and Below
+  {$IF CompilerVersion <= 24.0}
+  {$DEFINE DELPHIXE3_DOWN}
+  {$IFEND}
+  // XE3 and Above
+  {$IF CompilerVersion >= 24.0}
+  {$DEFINE DELPHIXE3_UP}
+  {$LEGACYIFEND ON}
+  {$ZEROBASEDSTRINGS OFF}
+  {$IFEND}
+  // XE7 and Above
+  {$IF CompilerVersion >= 28.0}
+  {$DEFINE DELPHIXE7_UP}
+  {$IFEND}
+  // 10.2 Tokyo and Above
+  {$IF CompilerVersion >= 32.0}
+  {$DEFINE DELPHI10.2_TOKYO_UP}
+  {$IFEND}
+  // 2010 and Above
+  {$IFNDEF DELPHI2010_UP}
+  {$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'}
+  {$ENDIF}
+  // 10.2 Tokyo and Above
+  {$IFDEF DELPHI10.2_TOKYO_UP}
+  {$WARN COMBINING_SIGNED_UNSIGNED OFF}
+  {$WARN COMBINING_SIGNED_UNSIGNED64 OFF}
+  {$ENDIF}
+{$ENDIF}
+{%endregion}
+
+interface
+
+uses {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UCommon, SysUtils, HlpIHash, HlpBits, HlpHashFactory;
+
+type
+
+  { TRandomHash2 }
+
+  TRandomHash2 = class sealed(TObject)
+    const
+      MIN_N = 2; // Min-number of hashing rounds required to compute a nonce
+      MAX_N = 4; // Max-number of hashing rounds required to compute a nonce
+      MIN_J = 1; // Min-number of dependent neighbouring nonces required to evaluate a nonce round
+      MAX_J = 8; // Max-number of dependent neighbouring nonces required to evaluate a nonce round
+      M = 64;    // The memory expansion unit (in bytes)
+      NUM_HASH_ALGO = 77;
+      SHA2_256_IX = 47;
+
+    {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
+      FHashAlg : array[0..NUM_HASH_ALGO-1] of IHash;  // declared here to avoid race-condition during mining
+
+      function MemTransform1(const AChunk: TBytes): TBytes; inline;
+      function MemTransform2(const AChunk: TBytes): TBytes; inline;
+      function MemTransform3(const AChunk: TBytes): TBytes; inline;
+      function MemTransform4(const AChunk: TBytes): TBytes; inline;
+      function MemTransform5(const AChunk: TBytes): TBytes; inline;
+      function MemTransform6(const AChunk: TBytes): TBytes; inline;
+      function MemTransform7(const AChunk: TBytes): TBytes; inline;
+      function MemTransform8(const AChunk: TBytes): TBytes; inline;
+      function Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32) : TBytes;
+      function Compress(const AInputs: TArray<TBytes>; ASeed : UInt32): TBytes; inline;
+      function ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes; inline;
+      function CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean; overload;
+    public
+      constructor Create;
+      destructor Destroy; override;
+      function TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+      function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
+      class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
+  end;
+
+ { TRandomHash2Fast }
+
+  TRandomHash2Fast = class sealed(TObject)
+    const
+      MIN_N = 2; // Min-number of hashing rounds required to compute a nonce
+      MAX_N = 4; // Max-number of hashing rounds required to compute a nonce
+      MIN_J = 1; // Min-number of dependent neighbouring nonces required to evaluate a nonce round
+      MAX_J = 8; // Max-number of dependent neighbouring nonces required to evaluate a nonce round
+      M = 64;    // The memory expansion unit (in bytes)
+      NUM_HASH_ALGO = 77;
+      SHA2_256_IX = 47;
+      public type
+
+        TCachedHash = record
+          Nonce : UInt32;
+          Level : Integer;
+          Header : TBytes;
+          RoundOutputs : TArray<TBytes>;
+        end;
+        PCachedHash = ^TCachedHash;
+
+        TCache = class
+          private
+            FEnablePartiallyComputed : boolean;          
+            FHeaderTemplate : TBytes;
+            FComputed : TList<TCachedHash>;
+            FPartiallyComputed : TList<TCachedHash>;
+            procedure PreProcessNewHash(const AHeader : TBytes);
+          public
+            constructor Create;
+            destructor Destroy;
+            property EnablePartiallyComputed : boolean read FEnablePartiallyComputed write FEnablePartiallyComputed;
+            procedure AddPartiallyComputed(const AHeader : TBytes; ALevel : Integer; const AOutputs : TArray<TBytes>); inline;
+            procedure AddFullyComputed(const AHeader : TBytes; ALevel : Integer; const AHash : TBytes); inline;
+            procedure Clear;
+            function HasComputedHash : Boolean; inline;
+            function PopComputedHash : TCachedHash; inline;
+            function HasNextPartiallyComputedHash : Boolean; inline;
+            function PeekNextPartiallyComputedHash : TCachedHash; inline;
+            function PopNextPartiallyComputedHash : TCachedHash; inline;
+            function ComputeMemorySize : Integer;
+        end;
+
+    {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
+      FHashAlg : array[0..NUM_HASH_ALGO-1] of IHash;  // declared here to avoid race-condition during mining
+      FCache : TCache;
+      FMemStats : TStatistics;
+      FCaptureMemStats : Boolean;
+      FEnableCaching : Boolean;
+
+      procedure MemTransform1(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform2(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform3(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform4(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform5(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform6(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform7(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform8(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      function Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32) : TBytes;
+      function Compress(const AInputs: TArray<TBytes>; ASeed : UInt32): TBytes;
+      function ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes;
+      function CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; APCachedHash : PCachedHash; out ARoundOutputs : TArray<TBytes>) : Boolean; overload;
+    public
+      constructor Create;
+      destructor Destroy; override;
+      property Cache : TCache read FCache;
+      property EnableCaching : Boolean read FEnableCaching write FEnableCaching;
+      property CaptureMemStats : Boolean read FCaptureMemStats write FCaptureMemStats;
+      property MemStats : TStatistics read FMemStats;
+      function TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+      function Hash(const ABlockHeader: TBytes): TBytes; overload;
+      function ResumeHash(const ACachedHash : TCachedHash): TBytes; overload;
+      class function Compute(const ABlockHeader: TBytes): TBytes; overload; static;
+  end;
+
+ { ERandomHash2 }
+
+  ERandomHash2 = class(Exception);
+
+resourcestring
+  SUnSupportedHash = 'Unsupported Hash Selected';
+  SInvalidRound = 'Round must be between 0 and N inclusive';
+  SOverlappingArgs = 'Overlapping read/write regions';
+  SBufferTooSmall = 'Buffer too small to apply memory transform';
+  SBlockHeaderTooSmallForNonce = 'Buffer too small to contain nonce';
+
+implementation
+
+uses UMemory, URandomHash;
+
+{ TRandomHash2 }
+
+constructor TRandomHash2.Create;
+begin
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool();
+end;
+
+destructor TRandomHash2.Destroy;
+var i : integer;
+begin
+  for i := Low(FHashAlg) to High(FHashAlg) do
+    FHashAlg[i] := nil;
+  inherited Destroy;
+end;
+
+class function TRandomHash2.Compute(const ABlockHeader: TBytes): TBytes;
+var
+  LHasher : TRandomHash2;
+  LDisposables : TDisposables;
+begin
+ LHasher := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+ Result := LHasher.Hash(ABlockHeader);
+end;
+
+function TRandomHash2.TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+var
+  LOutputs: TArray<TBytes>;
+  LSeed: UInt32;
+begin
+  if NOT CalculateRoundOutputs(ABlockHeader, AMaxRound, LOutputs) then
+    Exit(False);
+  AHash := ComputeVeneerRound(LOutputs);
+  Result := True;
+end;
+
+function TRandomHash2.Hash(const ABlockHeader: TBytes): TBytes;
+begin
+  if NOT TryHash(ABlockHeader, MAX_N, Result) then
+    raise ERandomHash2.Create('Internal Error: 984F52997131417E8D63C43BD686F5B2'); // Should have found final round!
+end;
+
+function TRandomHash2.ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes;
+var
+  LSeed : UInt32;
+begin
+  LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
+  // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
+  Result := FHashAlg[SHA2_256_IX].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+end;
+
+function TRandomHash2.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean;
+var
+  LRoundOutputs: TList<TBytes>;
+  LNeighbourWasLastRound : Boolean;
+  LSeed, LNumNeighbours: UInt32;
+  LGen: TMersenne32;
+  LRoundInput, LNeighbourNonceHeader, LOutput : TBytes;
+  LParentOutputs, LNeighborOutputs, LToArray, LBuffs2: TArray<TBytes>;
+  LHashFunc: IHash;
+  i: Int32;
+  LDisposables : TDisposables;
+  LBuff : TBytes;
+begin
+  if (ARound < 1) or (ARound > MAX_N) then
+    raise EArgumentOutOfRangeException.CreateRes(@SInvalidRound);
+
+  LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
+  LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
+  if ARound = 1 then begin
+    LRoundInput := FHashAlg[SHA2_256_IX].ComputeBytes(ABlockHeader).GetBytes;
+    LSeed := GetLastDWordLE( LRoundInput );
+    LGen.Initialize(LSeed);
+  end else begin
+    if CalculateRoundOutputs(ABlockHeader, ARound - 1, LParentOutputs) = True then begin
+      // Previous round was the final round, so just return it's value
+      ARoundOutputs := LParentOutputs;
+      Exit(True);
+    end;
+
+    // Add parent round outputs to this round outputs
+    LSeed := GetLastDWordLE( LParentOutputs[High(LParentOutputs)] );
+    LGen.Initialize(LSeed);
+    LRoundOutputs.AddRange( LParentOutputs );
+
+    // Add neighbouring nonce outputs to this round outputs
+    LNumNeighbours := (LGen.NextUInt32 MOD (MAX_J - MIN_J)) + MIN_J;
+    for i := 1 to LNumNeighbours do begin
+      LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
+      LNeighbourWasLastRound := CalculateRoundOutputs(LNeighbourNonceHeader, ARound - 1, LNeighborOutputs);
+      LRoundOutputs.AddRange(LNeighborOutputs);
+    end;
+    // Compress the parent/neighbouring outputs to form this rounds input
+    LRoundInput := Compress( LRoundOutputs.ToArray, LGen.NextUInt32 );
+  end;
+
+  // Select a random hash function and hash the input to find the output
+  LHashFunc := FHashAlg[LGen.NextUInt32 mod NUM_HASH_ALGO];
+  LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
+
+  // Memory-expand the output, add to output list and return output list
+  LOutput := Expand(LOutput, MAX_N - ARound, LGen.NextUInt32);
+  LRoundOutputs.Add(LOutput);
+  ARoundOutputs := LRoundOutputs.ToArray;
+
+  // Determine if final round
+  Result := (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0));
+end;
+
+function TRandomHash2.Compress(const AInputs : TArray<TBytes>; ASeed : UInt32): TBytes;
+var
+  i: Int32;
+  LSource: TBytes;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  SetLength(Result, 100);
+  LGen := LDisposables.AddObject( TMersenne32.Create( ASeed ) ) as TMersenne32;
+  for i := 0 to 99 do
+  begin
+    LSource := AInputs[LGen.NextUInt32 mod Length(AInputs)];
+    Result[i] := LSource[LGen.NextUInt32 mod Length(LSource)];
+  end;
+end;
+
+function TRandomHash2.Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32): TBytes;
+var
+  LSize, LBytesToAdd: Int32;
+  LOutput, LNextChunk: TBytes;
+  LRandom: UInt32;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  LGen := LDisposables.AddObject( TMersenne32.Create (ASeed) ) as TMersenne32;
+  LSize := Length(AInput) + (AExpansionFactor * M);
+  LOutput := Copy(AInput);
+  LBytesToAdd := LSize - Length(AInput);
+
+  while LBytesToAdd > 0 do
+  begin
+    LNextChunk := Copy(LOutput);
+    if Length(LNextChunk) > LBytesToAdd then
+      SetLength(LNextChunk, LBytesToAdd);
+
+    LRandom := LGen.NextUInt32;
+    case LRandom mod 8 of
+      0: LOutput := ContencateBytes(LOutput, MemTransform1(LNextChunk));
+      1: LOutput := ContencateBytes(LOutput, MemTransform2(LNextChunk));
+      2: LOutput := ContencateBytes(LOutput, MemTransform3(LNextChunk));
+      3: LOutput := ContencateBytes(LOutput, MemTransform4(LNextChunk));
+      4: LOutput := ContencateBytes(LOutput, MemTransform5(LNextChunk));
+      5: LOutput := ContencateBytes(LOutput, MemTransform6(LNextChunk));
+      6: LOutput := ContencateBytes(LOutput, MemTransform7(LNextChunk));
+      7: LOutput := ContencateBytes(LOutput, MemTransform8(LNextChunk));
+    end;
+    LBytesToAdd := LBytesToAdd - Length(LNextChunk);
+  end;
+  Result := LOutput;
+end;
+
+function TRandomHash2.MemTransform1(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength : UInt32;
+  LState : UInt32;
+begin
+  // Seed XorShift32 with last byte
+  LState := GetLastDWordLE(AChunk);
+  if LState = 0 then
+    LState := 1;
+
+  // Select random bytes from input using XorShift32 RNG
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := AChunk[TXorShift32.Next(LState) MOD LChunkLength];
+end;
+
+function TRandomHash2.MemTransform2(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  Move(AChunk[LPivot + LOdd], Result[0], LPivot);
+  Move(AChunk[0], Result[LPivot + LOdd], LPivot);
+  // Set middle-byte for odd-length arrays
+  if LOdd = 1 then
+    Result[LPivot] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform3(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := AChunk[LChunkLength - i - 1];
+end;
+
+function TRandomHash2.MemTransform4(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := 0 to Pred(LPivot) do
+  begin
+    Result[(i * 2)] := AChunk[i];
+    Result[(i * 2) + 1] := AChunk[i + LPivot + LOdd];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[High(Result)] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform5(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := Low(AChunk) to Pred(LPivot) do
+  begin
+    Result[(i * 2)] := AChunk[i + LPivot + LOdd];
+    Result[(i * 2) + 1] := AChunk[i];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[High(Result)] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform6(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := 0 to Pred(LPivot) do
+  begin
+    Result[i] := AChunk[(i * 2)] xor AChunk[(i * 2) + 1];
+    Result[i + LPivot + LOdd] := AChunk[i] xor AChunk[LChunkLength - i - 1];
+  end;
+  // Set middle-byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[LPivot] := AChunk[High(AChunk)];
+end;
+
+function TRandomHash2.MemTransform7(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := TBits.RotateLeft8(AChunk[i], LChunkLength - i);
+end;
+
+function TRandomHash2.MemTransform8(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
+end;
+
+
+{ TRandomHash2Fast }
+
+constructor TRandomHash2Fast.Create;
+begin
+  FEnableCaching := False;
+  FCache := TCache.Create;
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool();
+  FMemStats.Reset;
+end;
+
+destructor TRandomHash2Fast.Destroy;
+var i : integer;
+begin
+  for i := Low(FHashAlg) to High(FHashAlg) do
+    FHashAlg[i] := nil;
+ FCache.Destroy;
+ inherited Destroy;
+end;
+
+class function TRandomHash2Fast.Compute(const ABlockHeader: TBytes): TBytes;
+var
+  LHasher : TRandomHash2Fast;
+  LDisposables : TDisposables;
+begin
+ LHasher := LDisposables.AddObject( TRandomHash2Fast.Create ) as TRandomHash2Fast;
+ Result := LHasher.Hash(ABlockHeader);
+end;
+
+function TRandomHash2Fast.TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+var
+  LOutputs: TArray<TBytes>;
+  LSeed: UInt32;
+begin
+  if NOT CalculateRoundOutputs(ABlockHeader, AMaxRound, nil, LOutputs) then
+    Exit(False);
+  AHash := ComputeVeneerRound(LOutputs);
+  Result := True;
+end;
+
+function TRandomHash2Fast.Hash(const ABlockHeader: TBytes): TBytes;
+begin
+  if NOT TryHash(ABlockHeader, MAX_N, Result) then
+    raise ERandomHash2.Create('Internal Error: 974F52882131417E8D63A43BD686E5B2'); // Should have found final round!
+end;
+
+
+function TRandomHash2Fast.ResumeHash(const ACachedHash : TCachedHash): TBytes;
+var
+  LOutputs: TArray<TBytes>;
+  LSeed: UInt32;
+begin
+  if NOT CalculateRoundOutputs(ACachedHash.Header, MAX_N, @ACachedHash, LOutputs) then
+    raise ERandomHash2.Create('Internal Error: 274E52882131417E8C63A43BD686E5B4'); // Should have found final round!
+  Result := ComputeVeneerRound(LOutputs);
+end;
+
+function TRandomHash2Fast.ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes;
+var
+  LSeed : UInt32;
+  LSize, i : Integer;
+begin
+  LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
+  // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
+  Result := FHashAlg[SHA2_256_IX].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+  if FCaptureMemStats then begin
+    LSize := 0;
+    for i :=  Low(ARoundOutputs) to High(ARoundOutputs) do
+      Inc(LSize, Length(ARoundOutputs[i]));
+    if FEnableCaching then
+      Inc(LSize, FCache.ComputeMemorySize);
+    FMemStats.AddDatum(LSize);
+  end;
+end;
+
+function TRandomHash2Fast.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; APCachedHash : PCachedHash; out ARoundOutputs : TArray<TBytes>) : Boolean;
+var
+  LRoundOutputs: TList<TBytes>;
+  LNeighbourWasLastRound : Boolean;
+  LSeed, LNumNeighbours: UInt32;
+  LGen: TMersenne32;
+  LRoundInput, LNeighbourNonceHeader, LOutput, LXX : TBytes;
+  LCachedHash : TCachedHash;
+  LParentOutputs, LNeighborOutputs, LToArray, LBuffs2: TArray<TBytes>;
+  LHashFunc: IHash;
+  i: Int32;
+  LDisposables : TDisposables;
+  LBuff : TBytes;
+begin
+  if (ARound < 1) or (ARound > MAX_N) then
+    raise EArgumentOutOfRangeException.CreateRes(@SInvalidRound);   
+
+  if Assigned(APCachedHash) AND (APCachedHash^.Level = ARound) AND (BytesEqual(APCachedHash^.Header, ABlockHeader))  then begin
+    ARoundOutputs := APCachedHash^.RoundOutputs;
+    Exit(False); // assume partially evaluated 
+  end;
+
+  LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
+  LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
+  if ARound = 1 then begin
+    LRoundInput := FHashAlg[SHA2_256_IX].ComputeBytes(ABlockHeader).GetBytes;
+    LSeed := GetLastDWordLE( LRoundInput );
+    LGen.Initialize(LSeed);
+  end else begin
+    if CalculateRoundOutputs(ABlockHeader, ARound - 1, APCachedHash, LParentOutputs) = True then begin
+      // Previous round was the final round, so just return it's value
+      ARoundOutputs := LParentOutputs;
+      Exit(True);
+    end;
+
+    // Add parent round outputs to this round outputs
+    LSeed := GetLastDWordLE( LParentOutputs[High(LParentOutputs)] );
+    LGen.Initialize(LSeed);
+    LRoundOutputs.AddRange( LParentOutputs );
+
+    // Add neighbouring nonce outputs to this round outputs
+    LNumNeighbours := (LGen.NextUInt32 MOD (MAX_J - MIN_J)) + MIN_J;
+    for i := 1 to LNumNeighbours do begin
+      LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
+      LNeighbourWasLastRound := CalculateRoundOutputs(LNeighbourNonceHeader, ARound - 1, nil, LNeighborOutputs);
+      LRoundOutputs.AddRange(LNeighborOutputs);
+
+      // If neighbour was a fully evaluated nonce, cache it for re-use
+      if FEnableCaching then 
+        if LNeighbourWasLastRound then
+          FCache.AddFullyComputed(LNeighbourNonceHeader, ARound - 1, ComputeVeneerRound(LNeighborOutputs) )
+        else
+          FCache.AddPartiallyComputed(LNeighbourNonceHeader, ARound - 1, LNeighborOutputs );
+    end;
+    // Compress the parent/neighbouring outputs to form this rounds input
+    LRoundInput := Compress( LRoundOutputs.ToArray, LGen.NextUInt32 );
+  end;
+
+  // Select a random hash function and hash the input to find the output
+  LHashFunc := FHashAlg[LGen.NextUInt32 mod NUM_HASH_ALGO];
+  LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
+
+  // Memory-expand the output, add to output list and return output list
+  LOutput := Expand(LOutput, MAX_N - ARound, LGen.NextUInt32);
+  LRoundOutputs.Add(LOutput);
+  ARoundOutputs := LRoundOutputs.ToArray;
+
+  // Determine if final round
+  Result := (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0));
+end;
+
+function TRandomHash2Fast.Compress(const AInputs : TArray<TBytes>; ASeed : UInt32): TBytes;
+var
+  i: Int32;
+  LSource: TBytes;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  SetLength(Result, 100);
+  LGen := LDisposables.AddObject( TMersenne32.Create( ASeed ) ) as TMersenne32;
+  for i := 0 to 99 do
+  begin
+    LSource := AInputs[LGen.NextUInt32 mod Length(AInputs)];
+    Result[i] := LSource[LGen.NextUInt32 mod Length(LSource)];
+  end;
+end;
+
+function TRandomHash2Fast.Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32): TBytes;
+var
+  LOutput: TBytes;
+  LReadEnd, LCopyLen, LInputSize : UInt32;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  LInputSize := Length (AInput);
+  LGen := LDisposables.AddObject( TMersenne32.Create ( ASeed ) ) as TMersenne32;
+  SetLength(LOutput, LInputSize + (AExpansionFactor * M));
+
+  // Copy the genesis blob
+  Move(AInput[0], LOutput[0], LInputSize);
+  LReadEnd := LInputSize - 1;
+  LCopyLen := LInputSize;
+
+  while LReadEnd < Pred(Length(LOutput)) do
+  begin
+    if (LReadEnd + 1 + LCopyLen) > Length(LOutput) then
+      LCopyLen := Length(LOutput) - (LReadEnd + 1);
+    case LGen.NextUInt32 mod 8 of
+      0: MemTransform1(LOutput, 0, LReadEnd+1, LCopyLen);
+      1: MemTransform2(LOutput, 0, LReadEnd+1, LCopyLen);
+      2: MemTransform3(LOutput, 0, LReadEnd+1, LCopyLen);
+      3: MemTransform4(LOutput, 0, LReadEnd+1, LCopyLen);
+      4: MemTransform5(LOutput, 0, LReadEnd+1, LCopyLen);
+      5: MemTransform6(LOutput, 0, LReadEnd+1, LCopyLen);
+      6: MemTransform7(LOutput, 0, LReadEnd+1, LCopyLen);
+      7: MemTransform8(LOutput, 0, LReadEnd+1, LCopyLen);
+    end;
+    Inc(LReadEnd, LCopyLen);
+    Inc(LCopyLen, LCopyLen);
+  end;
+  Result := LOutput;
+end;
+
+procedure TRandomHash2Fast.MemTransform1(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd : UInt32;
+  LState : UInt32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+
+  // Seed XorShift32 with last byte
+  LState := GetDWordLE(ABuffer, LReadEnd-3);
+  if LState = 0 then
+    LState := 1;
+
+  // Select random bytes from input using XorShift32 RNG
+  for i := AWriteStart to LWriteEnd do
+    ABuffer[i] := ABuffer[AReadStart + (TXorShift32.Next(LState) MOD ALength)];
+end;
+
+procedure TRandomHash2Fast.MemTransform2(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  Move(ABuffer[AReadStart + LPivot + LOdd], ABuffer[AWriteStart], LPivot);
+  Move(ABuffer[AReadStart], ABuffer[AWriteStart + LPivot + LOdd], LPivot);
+  // Set middle-byte for odd-length arrays
+  if LOdd = 1 then
+    ABuffer[AWriteStart + LPivot] := ABuffer[AReadStart + LPivot];
+end;
+
+procedure TRandomHash2Fast.MemTransform3(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd: Int32;
+  LReadPtr, LWritePtr : PByte;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LReadPtr := PByte(ABuffer) + AReadStart;
+  LWritePtr := PByte(ABuffer) + LWriteEnd;
+  for i := 0 to Pred(ALength) do begin
+    LWritePtr^ := LReadPtr^;
+    Inc(LReadPtr);
+    Dec(LWritePtr);
+  end;
+end;
+
+procedure TRandomHash2Fast.MemTransform4(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  for i := 0 to Pred(LPivot) do
+  begin
+    ABuffer[AWriteStart + (i * 2)] := ABuffer[AReadStart + i];
+    ABuffer[AWriteStart + (i * 2) + 1] := ABuffer[AReadStart + i + LPivot + LOdd];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    ABuffer[LWriteEnd] := ABuffer[AReadStart + LPivot];
+end;
+
+procedure TRandomHash2Fast.MemTransform5(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  for i := 0 to Pred(LPivot) do
+  begin
+    ABuffer[AWriteStart + (i * 2)] := ABuffer[AReadStart + i + LPivot + LOdd];
+    ABuffer[AWriteStart + (i * 2) + 1] := ABuffer[AReadStart + i];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    ABuffer[LWriteEnd] := ABuffer[AReadStart + LPivot];
+end;
+
+procedure TRandomHash2Fast.MemTransform6(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  for i := 0 to Pred(LPivot) do
+  begin
+    ABuffer[AWriteStart + i] := ABuffer[AReadStart + (i * 2)] xor ABuffer[AReadStart + (i * 2) + 1];
+    ABuffer[AWriteStart + i + LPivot + LOdd] := ABuffer[AReadStart + i] xor ABuffer[AReadStart + ALength - i - 1];
+  end;
+  // Set middle-byte for odd-lengths
+  if LOdd = 1 THEN
+    ABuffer[AWriteStart + LPivot] := ABuffer[AReadStart + ALength - 1];
+end;
+
+procedure TRandomHash2Fast.MemTransform7(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  for i := 0 to Pred(ALength) do
+    ABuffer[AWriteStart + i] := TBits.RotateLeft8(ABuffer[AReadStart + i], ALength - i);
+end;
+
+procedure TRandomHash2Fast.MemTransform8(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LChunkLength, LReadEnd, LWriteEnd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  for i := 0 to Pred(ALength) do
+    ABuffer[AWriteStart + i] := TBits.RotateRight8(ABuffer[AReadStart + i], ALength - i);
+end;
+
+
+{ TRandomHash2Fast.TCache }
+
+constructor TRandomHash2Fast.TCache.Create;
+var i : Integer;
+begin
+  FEnablePartiallyComputed := false;
+  FComputed := TList<TCachedHash>.Create;
+  FComputed.Capacity := 100;
+  SetLength(Self.FHeaderTemplate, 0);
+  FPartiallyComputed := TList<TCachedHash>.Create;
+  FPartiallyComputed.Capacity := 1000;
+end;
+
+destructor TRandomHash2Fast.TCache.Destroy;
+var i : Integer;
+begin
+  FComputed.Clear;
+  FreeAndNil(FComputed);
+  FPartiallyComputed.Clear;
+  FreeAndNil(FPartiallyComputed);
+end;
+
+procedure TRandomHash2Fast.TCache.AddPartiallyComputed(const AHeader : TBytes; ALevel : Integer; const AOutputs : TArray<TBytes>);
+var
+  LItem : TCachedHash;
+begin
+  PreProcessNewHash(AHeader);
+  if NOT FEnablePartiallyComputed then
+    Exit;
+
+  // Only keep 10 level 3's partially calculated
+  if (ALevel < 3) OR (FPartiallyComputed.Count > 10) then Exit;
+    
+  LItem.Nonce := GetLastDWordLE(AHeader);
+  LItem.Header := AHeader;  
+  LItem.Level := ALevel;
+  LItem.RoundOutputs := AOutputs;
+  FPartiallyComputed.Add(LItem);
+end;
+
+procedure TRandomHash2Fast.TCache.AddFullyComputed(const AHeader : TBytes; ALevel : Integer; const AHash : TBytes);
+var
+  LItem : TCachedHash;
+begin
+  PreProcessNewHash(AHeader);
+  LItem.Nonce := GetLastDWordLE(AHeader);
+  LItem.Header := AHeader;
+  LItem.Level := ALevel;
+  LItem.RoundOutputs := TArray<TBytes>.Create( AHash );
+  FComputed.Add(LItem);
+end;
+
+procedure TRandomHash2Fast.TCache.PreProcessNewHash(const AHeader : TBytes);
+var i : integer;
+begin
+  // if header is a new template, flush cache
+  if NOT BytesEqual(FHeaderTemplate, AHeader, 0, 32 - 4) then begin
+    Clear;
+    FHeaderTemplate := SetLastDWordLE(AHeader, 0);
+  end;  
+end;
+
+procedure TRandomHash2Fast.TCache.Clear;
+begin
+  FComputed.Clear;
+  FComputed.Capacity := 100;
+  FPartiallyComputed.Clear;
+  FPartiallyComputed.Capacity := 1000;
+  SetLength(FHeaderTemplate, 0);
+end;
+
+function TRandomHash2Fast.TCache.HasComputedHash : Boolean;
+begin
+  Result := FComputed.Count > 0;
+end;
+
+function TRandomHash2Fast.TCache.PopComputedHash : TCachedHash;
+begin
+  Result := FComputed.Last;
+  FComputed.Delete(FComputed.Count - 1);
+end;
+
+function TRandomHash2Fast.TCache.HasNextPartiallyComputedHash : boolean;
+begin
+  Result := FPartiallyComputed.Count > 0;
+end;
+
+function TRandomHash2Fast.TCache.PeekNextPartiallyComputedHash : TCachedHash;
+var i : Integer;
+begin
+  if FPartiallyComputed.Count > 0 then begin
+    Result := FPartiallyComputed[0];
+    Exit;
+  end;
+  raise Exception.Create('Cache is empty');
+end;
+
+function TRandomHash2Fast.TCache.PopNextPartiallyComputedHash : TCachedHash;
+var 
+  i : Integer;
+  LList : TList<TCachedHash>;
+begin
+  if FPartiallyComputed.Count > 0 then begin
+    Result := FPartiallyComputed[0];
+    FPartiallyComputed.Delete(0);
+    Exit;
+  end;
+  raise Exception.Create('Cache is empty');
+end;
+
+function TRandomHash2Fast.TCache.ComputeMemorySize : Integer;
+var 
+  i,j,k : Integer;
+  LList : TList<TCachedHash>;
+  LItem : TCachedHash;
+begin
+  Result := Length(FHeaderTemplate);
+  Inc(Result, FComputed.Count * (4 + 4 + 32));
+  for j := 0 to FPartiallyComputed.Count - 1 do begin
+    LItem := FPartiallyComputed[j];
+    Inc(Result, 4);
+    Inc(Result, 4);
+    Inc(Result, 32);            
+    for k := Low(LItem.RoundOutputs) to High(LItem.RoundOutputs) do
+      Inc(Result, Length(LItem.RoundOutputs[k])); 
+  end;
+end;
+
+
+end.

+ 2 - 2
src/core/UServerApp.pas

@@ -281,7 +281,7 @@ begin
   // Check OpenSSL dll
   // Check OpenSSL dll
   if Not LoadSSLCrypt then raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
   if Not LoadSSLCrypt then raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
   TCrypto.InitCrypto;
   TCrypto.InitCrypto;
-  FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+  FWalletKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
   // Creating Node:
   // Creating Node:
   FNode := TNode.Node;
   FNode := TNode.Node;
   // RPC Server
   // RPC Server
@@ -291,7 +291,7 @@ begin
   FRPC.Active:=true;
   FRPC.Active:=true;
   // Check Database
   // Check Database
   FNode.Bank.StorageClass := TFileStorage;
   FNode.Bank.StorageClass := TFileStorage;
-  TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+  TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
   // Reading database
   // Reading database
   Log(sltInfo,'Reading database and constructing PascalCoin accounts');
   Log(sltInfo,'Reading database and constructing PascalCoin accounts');
   FNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
   FNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);

+ 2 - 2
src/core/USettings.pas

@@ -132,7 +132,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  Classes, SysUtils, UConst, UFolderHelper;
+  Classes, SysUtils, UConst, UNode;
 
 
 
 
 { TSettings }
 { TSettings }
@@ -140,7 +140,7 @@ uses
 class procedure TSettings.Load;
 class procedure TSettings.Load;
 begin
 begin
   FAppParams := TAppParams.Create(nil);
   FAppParams := TAppParams.Create(nil);
-  FAppParams.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+  FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
 end;
 end;
 
 
 class procedure TSettings.Save;
 class procedure TSettings.Save;

+ 53 - 24
src/core/UTxMultiOperation.pas

@@ -128,7 +128,7 @@ Type
     class function OpType : Byte; override;
     class function OpType : Byte; override;
     function OperationAmount : Int64; override;
     function OperationAmount : Int64; override;
     function OperationFee : Int64; override;
     function OperationFee : Int64; override;
-    function OperationPayload : TRawBytes; override;
+    function OperationPayload : TOperationPayload; override;
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     procedure SignerAccounts(list : TList<Cardinal>); override;
     procedure SignerAccounts(list : TList<Cardinal>); override;
     function IsSignerAccount(account : Cardinal) : Boolean; override;
     function IsSignerAccount(account : Cardinal) : Boolean; override;
@@ -155,7 +155,7 @@ Type
     //
     //
     Function toString : String; Override;
     Function toString : String; Override;
     Property Data : TOpMultiOperationData read FData;
     Property Data : TOpMultiOperationData read FData;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
 
 
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
   End;
@@ -318,7 +318,9 @@ var i : Integer;
   b : Byte;
   b : Byte;
 begin
 begin
   // Will save protocol info
   // Will save protocol info
-  w := CT_PROTOCOL_3;
+  if FProtocolVersion<CT_PROTOCOL_5 then
+    w := CT_PROTOCOL_3
+  else w := CT_PROTOCOL_5;
   stream.Write(w,SizeOf(w));
   stream.Write(w,SizeOf(w));
   // Save senders count
   // Save senders count
   w := Length(FData.txSenders);
   w := Length(FData.txSenders);
@@ -328,7 +330,7 @@ begin
     stream.Write(txs.Account,SizeOf(txs.Account));
     stream.Write(txs.Account,SizeOf(txs.Account));
     stream.Write(txs.Amount,SizeOf(txs.Amount));
     stream.Write(txs.Amount,SizeOf(txs.Amount));
     stream.Write(txs.N_Operation,SizeOf(txs.N_Operation));
     stream.Write(txs.N_Operation,SizeOf(txs.N_Operation));
-    TStreamOp.WriteAnsiString(stream,txs.Payload);
+    SaveOperationPayloadToStream(stream,txs.Payload);
     If FSaveSignatureValue then begin
     If FSaveSignatureValue then begin
       TStreamOp.WriteAnsiString(stream,txs.Signature.r);
       TStreamOp.WriteAnsiString(stream,txs.Signature.r);
       TStreamOp.WriteAnsiString(stream,txs.Signature.s);
       TStreamOp.WriteAnsiString(stream,txs.Signature.s);
@@ -341,7 +343,7 @@ begin
     txr := FData.txReceivers[i];
     txr := FData.txReceivers[i];
     stream.Write(txr.Account,SizeOf(txr.Account));
     stream.Write(txr.Account,SizeOf(txr.Account));
     stream.Write(txr.Amount,SizeOf(txr.Amount));
     stream.Write(txr.Amount,SizeOf(txr.Amount));
-    TStreamOp.WriteAnsiString(stream,txr.Payload);
+    SaveOperationPayloadToStream(stream,txr.Payload);
   end;
   end;
   // Save changes info count
   // Save changes info count
   w := Length(FData.changesInfo);
   w := Length(FData.changesInfo);
@@ -352,12 +354,17 @@ begin
     Stream.Write(chi.N_Operation,Sizeof(chi.N_Operation));
     Stream.Write(chi.N_Operation,Sizeof(chi.N_Operation));
     b := 0;
     b := 0;
     if (public_key in chi.Changes_type) then b:=b OR $01;
     if (public_key in chi.Changes_type) then b:=b OR $01;
-    if (account_name in chi.changes_type) then b:=b OR $02;
-    if (account_type in chi.changes_type) then b:=b OR $04;
+    if (account_name in chi.Changes_type) then b:=b OR $02;
+    if (account_type in chi.Changes_type) then b:=b OR $04;
+    if (account_data in chi.Changes_type) then b:=b OR $08;
+
     Stream.Write(b,Sizeof(b));
     Stream.Write(b,Sizeof(b));
     TStreamOp.WriteAccountKey(Stream,chi.New_Accountkey);
     TStreamOp.WriteAccountKey(Stream,chi.New_Accountkey);
     TStreamOp.WriteAnsiString(Stream,chi.New_Name);
     TStreamOp.WriteAnsiString(Stream,chi.New_Name);
     Stream.Write(chi.New_Type,Sizeof(chi.New_Type));
     Stream.Write(chi.New_Type,Sizeof(chi.New_Type));
+    if FProtocolVersion>=CT_PROTOCOL_5 then begin
+      TStreamOp.WriteAnsiString(Stream,chi.New_Data);
+    end;
     If FSaveSignatureValue then begin
     If FSaveSignatureValue then begin
       TStreamOp.WriteAnsiString(Stream,chi.Signature.r);
       TStreamOp.WriteAnsiString(Stream,chi.Signature.r);
       TStreamOp.WriteAnsiString(Stream,chi.Signature.s);
       TStreamOp.WriteAnsiString(Stream,chi.Signature.s);
@@ -368,7 +375,7 @@ end;
 
 
 function TOpMultiOperation.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
 function TOpMultiOperation.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
 var i : Integer;
 var i : Integer;
-  w : Word;
+  w, LSavedProtocol : Word;
   txs : TMultiOpSender;
   txs : TMultiOpSender;
   txr : TMultiOpReceiver;
   txr : TMultiOpReceiver;
   chi : TMultiOpChangeInfo;
   chi : TMultiOpChangeInfo;
@@ -394,8 +401,8 @@ begin
   Result := False;
   Result := False;
   Try
   Try
     // Read protocol info
     // Read protocol info
-    stream.Read(w,SizeOf(w));
-    If w<>CT_PROTOCOL_3 then Raise Exception.Create('Invalid protocol found');
+    stream.Read(LSavedProtocol,SizeOf(LSavedProtocol));
+    If (Not (LSavedProtocol in [CT_PROTOCOL_3,CT_PROTOCOL_5])) then Raise Exception.Create('Invalid protocol found '+IntToStr(LSavedProtocol));
     // Load senders
     // Load senders
     stream.Read(w,SizeOf(w));
     stream.Read(w,SizeOf(w));
     If w>CT_MAX_MultiOperation_Senders then Raise Exception.Create('Max senders');
     If w>CT_MAX_MultiOperation_Senders then Raise Exception.Create('Max senders');
@@ -406,7 +413,7 @@ begin
         stream.Read(txs.Account,SizeOf(txs.Account));
         stream.Read(txs.Account,SizeOf(txs.Account));
         stream.Read(txs.Amount,SizeOf(txs.Amount));
         stream.Read(txs.Amount,SizeOf(txs.Amount));
         stream.Read(txs.N_Operation,SizeOf(txs.N_Operation));
         stream.Read(txs.N_Operation,SizeOf(txs.N_Operation));
-        TStreamOp.ReadAnsiString(stream,txs.Payload);
+        LoadOperationPayloadFromStream(stream,txs.Payload);
         TStreamOp.ReadAnsiString(stream,txs.Signature.r);
         TStreamOp.ReadAnsiString(stream,txs.Signature.r);
         TStreamOp.ReadAnsiString(stream,txs.Signature.s);
         TStreamOp.ReadAnsiString(stream,txs.Signature.s);
         //
         //
@@ -422,7 +429,7 @@ begin
         txr := CT_TMultiOpReceiver_NUL;
         txr := CT_TMultiOpReceiver_NUL;
         stream.Read(txr.Account,SizeOf(txr.Account));
         stream.Read(txr.Account,SizeOf(txr.Account));
         stream.Read(txr.Amount,SizeOf(txr.Amount));
         stream.Read(txr.Amount,SizeOf(txr.Amount));
-        TStreamOp.ReadAnsiString(stream,txr.Payload);
+        LoadOperationPayloadFromStream(stream,txr.Payload);
         //
         //
         txreceivers[i] := txr;
         txreceivers[i] := txr;
       end;
       end;
@@ -441,11 +448,17 @@ begin
         if (b AND $01)=$01 then chi.changes_type:=chi.changes_type + [public_key];
         if (b AND $01)=$01 then chi.changes_type:=chi.changes_type + [public_key];
         if (b AND $02)=$02 then chi.changes_type:=chi.changes_type + [account_name];
         if (b AND $02)=$02 then chi.changes_type:=chi.changes_type + [account_name];
         if (b AND $04)=$04 then chi.changes_type:=chi.changes_type + [account_type];
         if (b AND $04)=$04 then chi.changes_type:=chi.changes_type + [account_type];
+        if (b AND $08)=$08 then chi.changes_type:=chi.changes_type + [account_data];
         // Check
         // Check
-        if (b AND $F8)<>0 then Exit;
+        if (LSavedProtocol=CT_PROTOCOL_3) and ((b AND $F8)<>0) then Exit;
+        if (b AND $F0)<>0 then Exit;
         TStreamOp.ReadAccountKey(Stream,chi.New_Accountkey);
         TStreamOp.ReadAccountKey(Stream,chi.New_Accountkey);
         TStreamOp.ReadAnsiString(Stream,chi.New_Name);
         TStreamOp.ReadAnsiString(Stream,chi.New_Name);
         Stream.Read(chi.New_Type,Sizeof(chi.New_Type));
         Stream.Read(chi.New_Type,Sizeof(chi.New_Type));
+        if (LSavedProtocol<>CT_PROTOCOL_3) then begin
+          TStreamOp.ReadAnsiString(Stream,chi.New_Data);
+        end;
+
         TStreamOp.ReadAnsiString(Stream,chi.Signature.r);
         TStreamOp.ReadAnsiString(Stream,chi.Signature.r);
         TStreamOp.ReadAnsiString(Stream,chi.Signature.s);
         TStreamOp.ReadAnsiString(Stream,chi.Signature.s);
         //
         //
@@ -488,7 +501,7 @@ begin
   Result := False;
   Result := False;
   // Do check it!
   // Do check it!
   Try
   Try
-    ophtosign := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
+    ophtosign := GetDigestToSign;
     // Tx verification
     // Tx verification
     For i:=Low(FData.txSenders) to High(FData.txSenders) do begin
     For i:=Low(FData.txSenders) to High(FData.txSenders) do begin
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
@@ -586,8 +599,8 @@ begin
       errors := Format('Invalid amount %d (0 or max: %d)',[txs.Amount,CT_MaxTransactionAmount]);
       errors := Format('Invalid amount %d (0 or max: %d)',[txs.Amount,CT_MaxTransactionAmount]);
       Exit;
       Exit;
     end;
     end;
-    if (length(txs.Payload)>CT_MaxPayloadSize) then begin
-      errors := 'Invalid Payload size:'+inttostr(length(txs.Payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
+    if (length(txs.Payload.payload_raw)>CT_MaxPayloadSize) then begin
+      errors := 'Invalid Payload size:'+inttostr(length(txs.Payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
       Exit;
       Exit;
     end;
     end;
     //
     //
@@ -623,8 +636,8 @@ begin
       errors := Format('Invalid amount %d (0 or max: %d)',[txr.Amount,CT_MaxTransactionAmount]);
       errors := Format('Invalid amount %d (0 or max: %d)',[txr.Amount,CT_MaxTransactionAmount]);
       Exit;
       Exit;
     end;
     end;
-    if (length(txr.Payload)>CT_MaxPayloadSize) then begin
-      errors := 'Invalid Payload size:'+inttostr(length(txr.Payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
+    if (length(txr.Payload.payload_raw)>CT_MaxPayloadSize) then begin
+      errors := 'Invalid Payload size:'+inttostr(length(txr.Payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
       Exit;
       Exit;
     end;
     end;
     //
     //
@@ -677,6 +690,18 @@ begin
         Exit;
         Exit;
       end;
       end;
     end;
     end;
+    // Account Data protection: (PIP-0024)
+    if (account_data in chi.Changes_type) then begin
+      if Length(chi.New_Data)>CT_MaxAccountDataSize then begin
+        errors := 'New data length ('+IntToStr(Length(chi.New_data))+') > '+IntToStr(CT_MaxAccountDataSize);
+        Exit;
+      end;
+    end else begin
+      if Length(chi.New_Data)<>0 then begin
+        errors := 'New data must be null when no data change';
+        Exit;
+      end;
+    end;
     If (chi.changes_type=[]) then begin
     If (chi.changes_type=[]) then begin
       errors := 'No change';
       errors := 'No change';
       Exit;
       Exit;
@@ -705,6 +730,7 @@ begin
       changer.accountInfo.price := 0;
       changer.accountInfo.price := 0;
       changer.accountInfo.account_to_pay := 0;
       changer.accountInfo.account_to_pay := 0;
       changer.accountInfo.new_publicKey := CT_TECDSA_Public_Nul;
       changer.accountInfo.new_publicKey := CT_TECDSA_Public_Nul;
+      changer.accountInfo.hashed_secret := Nil;
     end;
     end;
     If (account_name in chi.Changes_type) then begin
     If (account_name in chi.Changes_type) then begin
       changer.name := chi.New_Name;
       changer.name := chi.New_Name;
@@ -712,6 +738,9 @@ begin
     If (account_type in chi.Changes_type) then begin
     If (account_type in chi.Changes_type) then begin
       changer.account_type := chi.New_Type;
       changer.account_type := chi.New_Type;
     end;
     end;
+    If (account_data in chi.Changes_type) then begin
+      changer.account_data := chi.New_Data;
+    end;
     If Not AccountTransaction.UpdateAccountInfo(
     If Not AccountTransaction.UpdateAccountInfo(
            AccountPreviousUpdatedBlock,
            AccountPreviousUpdatedBlock,
            GetOpID,
            GetOpID,
@@ -755,7 +784,7 @@ begin
   If Not key.HasPrivateKey then begin
   If Not key.HasPrivateKey then begin
     exit;
     exit;
   end;
   end;
-  raw := GetDigestToSign(current_protocol);
+  raw := GetDigestToSign;
   Try
   Try
     _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
     _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
   Except
   Except
@@ -796,9 +825,9 @@ begin
   Result := FTotalFee;
   Result := FTotalFee;
 end;
 end;
 
 
-function TOpMultiOperation.OperationPayload: TRawBytes;
+function TOpMultiOperation.OperationPayload: TOperationPayload;
 begin
 begin
-  SetLength(Result,0);
+  Result := CT_TOperationPayload_NUL;
 end;
 end;
 
 
 function TOpMultiOperation.SignerAccount: Cardinal;
 function TOpMultiOperation.SignerAccount: Cardinal;
@@ -998,7 +1027,7 @@ begin
     // check valid Change type
     // check valid Change type
     for ct:=Low(TOpChangeAccountInfoType) to High(TOpChangeAccountInfoType) do begin
     for ct:=Low(TOpChangeAccountInfoType) to High(TOpChangeAccountInfoType) do begin
       case ct of
       case ct of
-        public_key,account_name,account_type : ; // Allowed
+        public_key,account_name,account_type,account_data : ; // Allowed
       else
       else
         if (ct in changes[i].Changes_type) then begin
         if (ct in changes[i].Changes_type) then begin
           Exit; // Not allowed multioperation change type
           Exit; // Not allowed multioperation change type
@@ -1078,7 +1107,7 @@ begin
      TAccountComp.FormatMoney(FTotalFee)]);
      TAccountComp.FormatMoney(FTotalFee)]);
 end;
 end;
 
 
-function TOpMultiOperation.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpMultiOperation.GetDigestToSign: TRawBytes;
 Var ms : TMemoryStream;
 Var ms : TMemoryStream;
   rb : TRawBytes;
   rb : TRawBytes;
   old : Boolean;
   old : Boolean;
@@ -1093,7 +1122,7 @@ begin
     finally
     finally
       FSaveSignatureValue:=old;
       FSaveSignatureValue:=old;
     end;
     end;
-    if (current_protocol<=CT_PROTOCOL_3) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
       ms.Position := 0;
       ms.Position := 0;
       SetLength(Result,ms.Size);
       SetLength(Result,ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);

+ 2 - 2
src/core/UWallet.pas

@@ -156,7 +156,7 @@ uses
   {$IFDEF INTERNAL_USE_FOLDERHELPER_UNIT}
   {$IFDEF INTERNAL_USE_FOLDERHELPER_UNIT}
   UFolderHelper,
   UFolderHelper,
   {$ENDIF}
   {$ENDIF}
-  UPCEncryption;
+  UPCEncryption, UNode;
 
 
 Const
 Const
   CT_PrivateKeyFile_Magic = 'TWalletKeys';
   CT_PrivateKeyFile_Magic = 'TWalletKeys';
@@ -634,7 +634,7 @@ begin
   try
   try
     if not Assigned(FKeys) then
     if not Assigned(FKeys) then
       FKeys := TWalletKeysExt.Create(nil);
       FKeys := TWalletKeysExt.Create(nil);
-    FKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+    FKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
   except
   except
     on E:Exception do begin
     on E:Exception do begin
       E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;
       E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;

+ 4 - 4
src/core/upcdaemon.pas

@@ -147,7 +147,7 @@ var
     TLog.NewLog(ltInfo,ClassName,'RPC server is active on port '+IntToStr(port));
     TLog.NewLog(ltInfo,ClassName,'RPC server is active on port '+IntToStr(port));
     If FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,true) then begin
     If FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,true) then begin
       FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,true);
       FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,true);
-      FRPC.LogFileName:= TFolderHelper.GetPascalCoinDataFolder+PathDelim+'pascalcoin_rpc.log';
+      FRPC.LogFileName:= TNode.GetPascalCoinDataFolder+PathDelim+'pascalcoin_rpc.log';
       TLog.NewLog(ltInfo,ClassName,'Activating RPC logs on file '+FRPC.LogFileName);
       TLog.NewLog(ltInfo,ClassName,'Activating RPC logs on file '+FRPC.LogFileName);
     end else begin
     end else begin
       FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,false);
       FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,false);
@@ -231,7 +231,7 @@ begin
         raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
         raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
       end;
       end;
       TCrypto.InitCrypto;
       TCrypto.InitCrypto;
-      FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+      FWalletKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
       // Creating Node:
       // Creating Node:
       FNode := TNode.Node;
       FNode := TNode.Node;
       {$IFDEF TESTNET}
       {$IFDEF TESTNET}
@@ -242,7 +242,7 @@ begin
       Try
       Try
         // Check Database
         // Check Database
         FNode.Bank.StorageClass := TFileStorage;
         FNode.Bank.StorageClass := TFileStorage;
-        TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+        TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
         TFileStorage(FNode.Bank.Storage).LowMemoryUsage := FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_LOWMEMORY,False);
         TFileStorage(FNode.Bank.Storage).LowMemoryUsage := FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_LOWMEMORY,False);
         // By default daemon will not download checkpoint except if specified on INI file
         // By default daemon will not download checkpoint except if specified on INI file
         TNetData.NetData.MinFutureBlocksToDownloadNewSafebox := FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINPENDINGBLOCKSTODOWNLOADCHECKPOINT,0);
         TNetData.NetData.MinFutureBlocksToDownloadNewSafebox := FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINPENDINGBLOCKSTODOWNLOADCHECKPOINT,0);
@@ -288,7 +288,7 @@ begin
   FIniFile := TIniFile.Create(ExtractFileDir(Application.ExeName)+PathDelim+'pascalcoin_daemon.ini');
   FIniFile := TIniFile.Create(ExtractFileDir(Application.ExeName)+PathDelim+'pascalcoin_daemon.ini');
   If FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,true) then begin
   If FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,true) then begin
     _FLog.SaveTypes:=CT_TLogTypes_ALL;
     _FLog.SaveTypes:=CT_TLogTypes_ALL;
-    _FLog.FileName:=TFolderHelper.GetPascalCoinDataFolder+PathDelim+'pascalcoin_'+FormatDateTime('yyyymmddhhnn',Now)+'.log';
+    _FLog.FileName:=TNode.GetPascalCoinDataFolder+PathDelim+'pascalcoin_'+FormatDateTime('yyyymmddhhnn',Now)+'.log';
     FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,true);
     FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,true);
   end else begin
   end else begin
     FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,false);
     FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,false);

+ 2 - 0
src/gui-classic/UFRMAbout.pas

@@ -22,6 +22,8 @@ unit UFRMAbout;
 
 
 interface
 interface
 
 
+{$I ../config.inc}
+
 uses
 uses
 {$IFnDEF FPC}
 {$IFnDEF FPC}
   pngimage, Windows,
   pngimage, Windows,

+ 10 - 7
src/gui-classic/UFRMAccountSelect.dfm

@@ -28,9 +28,9 @@ object FRMAccountSelect: TFRMAccountSelect
     object cbOnlyForSale: TCheckBox
     object cbOnlyForSale: TCheckBox
       Left = 10
       Left = 10
       Top = 65
       Top = 65
-      Width = 81
+      Width = 175
       Height = 19
       Height = 19
-      Caption = 'Only for sale'
+      Caption = 'Only for sale or swap'
       TabOrder = 2
       TabOrder = 2
     end
     end
     object bbSearch: TBitBtn
     object bbSearch: TBitBtn
@@ -102,7 +102,7 @@ object FRMAccountSelect: TFRMAccountSelect
     object cbOnlyForPrivateSaleToMe: TCheckBox
     object cbOnlyForPrivateSaleToMe: TCheckBox
       Left = 10
       Left = 10
       Top = 111
       Top = 111
-      Width = 148
+      Width = 175
       Height = 19
       Height = 19
       Caption = 'Only for private sale to me'
       Caption = 'Only for private sale to me'
       TabOrder = 4
       TabOrder = 4
@@ -110,7 +110,7 @@ object FRMAccountSelect: TFRMAccountSelect
     object cbAccountsBalance: TCheckBox
     object cbAccountsBalance: TCheckBox
       Left = 10
       Left = 10
       Top = 134
       Top = 134
-      Width = 104
+      Width = 167
       Height = 19
       Height = 19
       Caption = 'Accounts Balance'
       Caption = 'Accounts Balance'
       TabOrder = 5
       TabOrder = 5
@@ -118,7 +118,7 @@ object FRMAccountSelect: TFRMAccountSelect
     object cbMyAccounts: TCheckBox
     object cbMyAccounts: TCheckBox
       Left = 10
       Left = 10
       Top = 10
       Top = 10
-      Width = 81
+      Width = 175
       Height = 19
       Height = 19
       Caption = 'My Accounts'
       Caption = 'My Accounts'
       TabOrder = 0
       TabOrder = 0
@@ -136,7 +136,7 @@ object FRMAccountSelect: TFRMAccountSelect
     object cbOnlyForPublicSale: TCheckBox
     object cbOnlyForPublicSale: TCheckBox
       Left = 10
       Left = 10
       Top = 88
       Top = 88
-      Width = 111
+      Width = 175
       Height = 19
       Height = 19
       Caption = 'Only for public sale'
       Caption = 'Only for public sale'
       TabOrder = 3
       TabOrder = 3
@@ -160,7 +160,7 @@ object FRMAccountSelect: TFRMAccountSelect
     object cbAccountsName: TCheckBox
     object cbAccountsName: TCheckBox
       Left = 10
       Left = 10
       Top = 186
       Top = 186
-      Width = 93
+      Width = 167
       Height = 19
       Height = 19
       Caption = 'Accounts name'
       Caption = 'Accounts name'
       TabOrder = 8
       TabOrder = 8
@@ -257,6 +257,9 @@ object FRMAccountSelect: TFRMAccountSelect
     Width = 451
     Width = 451
     Height = 282
     Height = 282
     Align = alClient
     Align = alClient
+    ColCount = 6
+    FixedCols = 0
+    Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRowSelect]
     TabOrder = 2
     TabOrder = 2
   end
   end
 end
 end

+ 23 - 24
src/gui-classic/UFRMAccountSelect.lfm

@@ -14,8 +14,7 @@ object FRMAccountSelect: TFRMAccountSelect
   OnCreate = FormCreate
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.4.0'
-  Visible = False
+  LCLVersion = '1.8.0.6'
   object pnlAccountsTop: TPanel
   object pnlAccountsTop: TPanel
     Left = 395
     Left = 395
     Height = 282
     Height = 282
@@ -28,10 +27,10 @@ object FRMAccountSelect: TFRMAccountSelect
     TabOrder = 0
     TabOrder = 0
     object cbOnlyForSale: TCheckBox
     object cbOnlyForSale: TCheckBox
       Left = 10
       Left = 10
-      Height = 19
+      Height = 24
       Top = 65
       Top = 65
-      Width = 81
-      Caption = 'Only for sale'
+      Width = 149
+      Caption = 'Only for sale or Swap'
       TabOrder = 0
       TabOrder = 0
     end
     end
     object bbSearch: TBitBtn
     object bbSearch: TBitBtn
@@ -103,25 +102,25 @@ object FRMAccountSelect: TFRMAccountSelect
     end
     end
     object cbOnlyForPrivateSaleToMe: TCheckBox
     object cbOnlyForPrivateSaleToMe: TCheckBox
       Left = 10
       Left = 10
-      Height = 19
+      Height = 24
       Top = 111
       Top = 111
-      Width = 148
+      Width = 206
       Caption = 'Only for private sale to me'
       Caption = 'Only for private sale to me'
       TabOrder = 2
       TabOrder = 2
     end
     end
     object cbAccountsBalance: TCheckBox
     object cbAccountsBalance: TCheckBox
       Left = 10
       Left = 10
-      Height = 19
+      Height = 24
       Top = 134
       Top = 134
-      Width = 104
+      Width = 147
       Caption = 'Accounts Balance'
       Caption = 'Accounts Balance'
       TabOrder = 3
       TabOrder = 3
     end
     end
     object cbMyAccounts: TCheckBox
     object cbMyAccounts: TCheckBox
       Left = 10
       Left = 10
-      Height = 19
+      Height = 24
       Top = 10
       Top = 10
-      Width = 81
+      Width = 113
       Caption = 'My Accounts'
       Caption = 'My Accounts'
       OnChange = cbMyAccountsChange
       OnChange = cbMyAccountsChange
       OnClick = cbMyAccountsClick
       OnClick = cbMyAccountsClick
@@ -129,25 +128,25 @@ object FRMAccountSelect: TFRMAccountSelect
     end
     end
     object cbMyPrivateKeys: TComboBox
     object cbMyPrivateKeys: TComboBox
       Left = 10
       Left = 10
-      Height = 21
+      Height = 31
       Top = 35
       Top = 35
       Width = 176
       Width = 176
-      ItemHeight = 13
+      ItemHeight = 0
       OnChange = cbMyPrivateKeysChange
       OnChange = cbMyPrivateKeysChange
       Style = csDropDownList
       Style = csDropDownList
       TabOrder = 5
       TabOrder = 5
     end
     end
     object cbOnlyForPublicSale: TCheckBox
     object cbOnlyForPublicSale: TCheckBox
       Left = 10
       Left = 10
-      Height = 19
+      Height = 24
       Top = 88
       Top = 88
-      Width = 111
+      Width = 157
       Caption = 'Only for public sale'
       Caption = 'Only for public sale'
       TabOrder = 6
       TabOrder = 6
     end
     end
     object ebMinBalance: TEdit
     object ebMinBalance: TEdit
       Left = 16
       Left = 16
-      Height = 21
+      Height = 23
       Top = 160
       Top = 160
       Width = 73
       Width = 73
       OnExit = ebMinBalanceExit
       OnExit = ebMinBalanceExit
@@ -155,7 +154,7 @@ object FRMAccountSelect: TFRMAccountSelect
     end
     end
     object ebMaxBalance: TEdit
     object ebMaxBalance: TEdit
       Left = 103
       Left = 103
-      Height = 21
+      Height = 23
       Top = 160
       Top = 160
       Width = 73
       Width = 73
       OnExit = ebMaxBalanceExit
       OnExit = ebMaxBalanceExit
@@ -163,15 +162,15 @@ object FRMAccountSelect: TFRMAccountSelect
     end
     end
     object cbAccountsName: TCheckBox
     object cbAccountsName: TCheckBox
       Left = 10
       Left = 10
-      Height = 19
+      Height = 24
       Top = 186
       Top = 186
-      Width = 93
+      Width = 131
       Caption = 'Accounts name'
       Caption = 'Accounts name'
       TabOrder = 9
       TabOrder = 9
     end
     end
     object ebAccountName: TEdit
     object ebAccountName: TEdit
       Left = 18
       Left = 18
-      Height = 21
+      Height = 23
       Top = 209
       Top = 209
       Width = 158
       Width = 158
       TabOrder = 10
       TabOrder = 10
@@ -214,7 +213,7 @@ object FRMAccountSelect: TFRMAccountSelect
       Left = 5
       Left = 5
       Height = 13
       Height = 13
       Top = 10
       Top = 10
-      Width = 48
+      Width = 55
       Caption = 'Accounts:'
       Caption = 'Accounts:'
       ParentColor = False
       ParentColor = False
     end
     end
@@ -222,7 +221,7 @@ object FRMAccountSelect: TFRMAccountSelect
       Left = 60
       Left = 60
       Height = 13
       Height = 13
       Top = 10
       Top = 10
-      Width = 18
+      Width = 21
       Caption = '000'
       Caption = '000'
       ParentColor = False
       ParentColor = False
     end
     end
@@ -230,7 +229,7 @@ object FRMAccountSelect: TFRMAccountSelect
       Left = 136
       Left = 136
       Height = 13
       Height = 13
       Top = 10
       Top = 10
-      Width = 41
+      Width = 49
       Caption = 'Balance:'
       Caption = 'Balance:'
       ParentColor = False
       ParentColor = False
     end
     end
@@ -238,7 +237,7 @@ object FRMAccountSelect: TFRMAccountSelect
       Left = 200
       Left = 200
       Height = 13
       Height = 13
       Top = 10
       Top = 10
-      Width = 18
+      Width = 21
       Caption = '000'
       Caption = '000'
       ParentColor = False
       ParentColor = False
     end
     end

+ 31 - 10
src/gui-classic/UFRMAccountSelect.pas

@@ -42,7 +42,7 @@ type
     SafeBox : TPCSafeBox;
     SafeBox : TPCSafeBox;
     inWalletKeys : TWalletKeys;
     inWalletKeys : TWalletKeys;
     inAccountKey : TAccountKey;
     inAccountKey : TAccountKey;
-    onlyForSale,
+    onlyForSaleOrSwap,
     onlyForPublicSale,
     onlyForPublicSale,
     onlyForPrivateSaleToMe : Boolean;
     onlyForPrivateSaleToMe : Boolean;
     minBal,maxBal : Int64;
     minBal,maxBal : Int64;
@@ -173,19 +173,22 @@ procedure TSearchThread.BCExecute;
       If validAccKey then begin
       If validAccKey then begin
         isValid := TAccountComp.EqualAccountKeys(account.accountInfo.accountKey,FSearchValues.inAccountKey);
         isValid := TAccountComp.EqualAccountKeys(account.accountInfo.accountKey,FSearchValues.inAccountKey);
       end else if (Assigned(FSearchValues.inWalletKeys)) then begin
       end else if (Assigned(FSearchValues.inWalletKeys)) then begin
-        isValid := FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.accountKey)>=0;
+        isValid := (FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.accountKey)>=0) or (FSearchValues.onlyForPrivateSaleToMe);
       end;
       end;
-      If isValid And (FSearchValues.onlyForSale) then begin
-        isValid := TAccountComp.IsAccountForSale(account.accountInfo, LBlocksCount);
+      If isValid And (FSearchValues.onlyForSaleOrSwap) then begin
+        isValid := TAccountComp.IsAccountForSaleOrSwap(account.accountInfo);
       end;
       end;
       If IsValid and (FSearchValues.onlyForPublicSale) then begin
       If IsValid and (FSearchValues.onlyForPublicSale) then begin
         isValid := TAccountComp.IsAccountForPublicSale(account.accountInfo);
         isValid := TAccountComp.IsAccountForPublicSale(account.accountInfo);
       end;
       end;
       If IsValid and (FSearchValues.onlyForPrivateSaleToMe) then begin
       If IsValid and (FSearchValues.onlyForPrivateSaleToMe) then begin
-        isValid := ((TAccountComp.IsAccountForPrivateSale(account.accountInfo, LBlocksCount) OR
-                    TAccountComp.IsAccountForAccountSwap(account.accountInfo, LBlocksCount)) AND
-                    (Assigned(FSearchValues.inWalletKeys)) And (FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.new_publicKey)>=0)) OR
-                    (True {TODO: TAccountComp.IsAccountForCoinSwap(account.accountInfo, LBlocksCount) AND account.accountInfo.account_to_pay in [MyListOfAccounts]});
+        isValid := (
+          (TAccountComp.IsAccountForPrivateSale(account.accountInfo) OR (TAccountComp.IsAccountForAccountSwap(account.accountInfo)))
+          and
+          (Assigned(FSearchValues.inWalletKeys))
+          and
+          (FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.new_publicKey)>=0)
+          );
       end;
       end;
       If IsValid then begin
       If IsValid then begin
         IsValid := (account.balance>=FSearchValues.minBal) And ((FSearchValues.maxBal<0) Or (account.balance<=FSearchValues.maxBal));
         IsValid := (account.balance>=FSearchValues.minBal) And ((FSearchValues.maxBal<0) Or (account.balance<=FSearchValues.maxBal));
@@ -267,6 +270,7 @@ begin
 end;
 end;
 
 
 procedure TFRMAccountSelect.FormCreate(Sender: TObject);
 procedure TFRMAccountSelect.FormCreate(Sender: TObject);
+var LColumns : TAccountColumnArray;
 begin
 begin
   FSearchThread := TSearchThread.Create(false);
   FSearchThread := TSearchThread.Create(false);
   FSearchThread.OnSearchFinished := OnSearchFinished;
   FSearchThread.OnSearchFinished := OnSearchFinished;
@@ -279,6 +283,23 @@ begin
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid := TAccountsGrid.Create(Self);
   FAccountsGrid.DrawGrid := dgAccounts;
   FAccountsGrid.DrawGrid := dgAccounts;
   FAccountsGrid.OnUpdated:=OnAccountsGridUpdated;
   FAccountsGrid.OnUpdated:=OnAccountsGridUpdated;
+  FAccountsGrid.AllowMultiSelect := False;
+
+  SetLength(LColumns,6);
+  LColumns[0].ColumnType := act_account_number;
+  LColumns[0].width := 75;
+  LColumns[1].ColumnType := act_name;
+  LColumns[1].width := 80;
+  LColumns[2].ColumnType := act_balance;
+  LColumns[2].width := 80;
+  LColumns[3].ColumnType := act_n_operation;
+  LColumns[3].width := 40;
+  LColumns[4].ColumnType := act_type;
+  LColumns[4].width := 40;
+  LColumns[5].ColumnType := act_saleprice;
+  LColumns[5].width := 45;
+
+  FAccountsGrid.SetColumns( LColumns );
   //
   //
   cbMyAccounts.OnClick:=cbMyAccountsClick;
   cbMyAccounts.OnClick:=cbMyAccountsClick;
   cbMyPrivateKeys.OnClick:=cbMyAccountsClick;
   cbMyPrivateKeys.OnClick:=cbMyAccountsClick;
@@ -476,7 +497,7 @@ begin
   end else begin
   end else begin
     cbMyPrivateKeys.Font.Color := clGray;
     cbMyPrivateKeys.Font.Color := clGray;
   end;
   end;
-  searchValues.onlyForSale := (cbOnlyForSale.Checked);
+  searchValues.onlyForSaleOrSwap := (cbOnlyForSale.Checked);
   searchValues.onlyForPrivateSaleToMe := (cbOnlyForPrivateSaleToMe.Checked);
   searchValues.onlyForPrivateSaleToMe := (cbOnlyForPrivateSaleToMe.Checked);
   If searchValues.onlyForPrivateSaleToMe then begin
   If searchValues.onlyForPrivateSaleToMe then begin
     searchValues.inWalletKeys := FWalletKeys;
     searchValues.inWalletKeys := FWalletKeys;
@@ -508,7 +529,7 @@ begin
     ebAccountName.Font.Color := clGray;
     ebAccountName.Font.Color := clGray;
   end;
   end;
   If (searchValues.inAccountKey.EC_OpenSSL_NID=0) AND (searchValues.inWalletKeys=Nil) And (searchValues.maxBal<0) And (searchValues.minBal<=0) And
   If (searchValues.inAccountKey.EC_OpenSSL_NID=0) AND (searchValues.inWalletKeys=Nil) And (searchValues.maxBal<0) And (searchValues.minBal<=0) And
-     (Not searchValues.onlyForPrivateSaleToMe) And (Not searchValues.onlyForPublicSale) And (Not searchValues.onlyForSale) And
+     (Not searchValues.onlyForPrivateSaleToMe) And (Not searchValues.onlyForPublicSale) And (Not searchValues.onlyForSaleOrSwap) And
      (Length(searchValues.searchName)=0) then begin
      (Length(searchValues.searchName)=0) then begin
     FAccountsGrid.AccountsGridDatasource := acds_Node;
     FAccountsGrid.AccountsGridDatasource := acds_Node;
     lblAccountsCount.Caption := IntToStr(FAccountsGrid.Node.Bank.SafeBox.AccountsCount);
     lblAccountsCount.Caption := IntToStr(FAccountsGrid.Node.Bank.SafeBox.AccountsCount);

+ 90 - 0
src/gui-classic/UFRMDiagnosticTool.dfm

@@ -0,0 +1,90 @@
+object FRMDiagnosticTool: TFRMDiagnosticTool
+  Left = 0
+  Top = 0
+  Caption = 'FRMDiagnosticTool'
+  ClientHeight = 324
+  ClientWidth = 666
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  OnCreate = FormCreate
+  DesignSize = (
+    666
+    324)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object btnRH: TButton
+    Left = 8
+    Top = 8
+    Width = 130
+    Height = 25
+    Caption = 'Start Random Hash'
+    TabOrder = 0
+    OnClick = btnRHClick
+  end
+  object btnRH2: TButton
+    Left = 336
+    Top = 8
+    Width = 130
+    Height = 25
+    Caption = 'Start Random Hash 2'
+    TabOrder = 1
+    OnClick = btnRH2Click
+  end
+  object txtLog: TMemo
+    Left = 8
+    Top = 80
+    Width = 650
+    Height = 236
+    Anchors = [akLeft, akTop, akRight, akBottom]
+    TabOrder = 2
+  end
+  object btnRH2C: TButton
+    Left = 472
+    Top = 8
+    Width = 186
+    Height = 25
+    Caption = 'Start Random Hash 2 (Cached)'
+    TabOrder = 3
+    OnClick = btnRH2CClick
+  end
+  object btnEntropy: TButton
+    Left = 327
+    Top = 39
+    Width = 130
+    Height = 25
+    Caption = 'Entropy Tests'
+    TabOrder = 4
+    OnClick = btnEntropyClick
+  end
+  object btnRH2NonceScan: TButton
+    Left = 8
+    Top = 39
+    Width = 186
+    Height = 25
+    Caption = 'Start Random Hash 2 (Nonce Scan)'
+    TabOrder = 5
+    OnClick = btnRH2NonceScanClick
+  end
+  object txtScanLevel: TEdit
+    Left = 200
+    Top = 39
+    Width = 121
+    Height = 21
+    TabOrder = 6
+    TextHint = 'Enter Scan Level (2-4)'
+  end
+  object btnRHC: TButton
+    Left = 144
+    Top = 8
+    Width = 177
+    Height = 25
+    Caption = 'Start Random Hash (Cached)'
+    TabOrder = 7
+    OnClick = btnRHCClick
+  end
+end

+ 66 - 0
src/gui-classic/UFRMDiagnosticTool.lfm

@@ -0,0 +1,66 @@
+object FRMDiagnosticTool: TFRMDiagnosticTool
+  Left = 0
+  Height = 324
+  Top = 0
+  Width = 570
+  Caption = 'FRMDiagnosticTool'
+  ClientHeight = 324
+  ClientWidth = 570
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -12
+  Font.Name = 'Tahoma'
+  OnCreate = FormCreate
+  LCLVersion = '2.1.0.0'
+  object btnRH: TButton
+    Left = 8
+    Height = 26
+    Top = 8
+    Width = 130
+    Caption = 'Start Random Hash'
+    Font.Color = clWindowText
+    Font.Height = -12
+    Font.Name = 'Tahoma'
+    OnClick = btnRHClick
+    ParentFont = False
+    TabOrder = 0
+  end
+  object btnRH2: TButton
+    Left = 144
+    Height = 26
+    Top = 8
+    Width = 130
+    Caption = 'Start Random Hash 2'
+    Font.Color = clWindowText
+    Font.Height = -12
+    Font.Name = 'Tahoma'
+    OnClick = btnRH2Click
+    ParentFont = False
+    TabOrder = 1
+  end
+  object txtLog: TMemo
+    Left = 8
+    Height = 401
+    Top = 38
+    Width = 822
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Font.Color = clWindowText
+    Font.Height = -12
+    Font.Name = 'Tahoma'
+    ParentFont = False
+    TabOrder = 2
+  end
+  object btnRHC: TButton
+    Left = 280
+    Height = 26
+    Top = 8
+    Width = 186
+    Caption = 'Start Random Hash 2 (Cached)'
+    Font.Color = clWindowText
+    Font.Height = -12
+    Font.Name = 'Tahoma'
+    OnClick = btnRHCClick
+    ParentFont = False
+    TabOrder = 3
+  end
+end

+ 479 - 0
src/gui-classic/UFRMDiagnosticTool.pas

@@ -0,0 +1,479 @@
+unit UFRMDiagnosticTool;
+
+interface
+
+uses
+  Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls,
+  SysUtils, Variants, Classes, UThread, UMemory, URandomHash, URandomHash2,
+  {$IFNDEF FPC}
+  System.TimeSpan,
+  {$ENDIF}
+  UCommon;
+
+type
+  TAlgorithmThread = class;
+
+  TFRMDiagnosticTool = class(TForm)
+    btnRH: TButton;
+    btnRH2: TButton;
+    txtLog: TMemo;
+    btnRH2C: TButton;
+    btnEntropy: TButton;
+    btnRH2NonceScan: TButton;
+    txtScanLevel: TEdit;
+    btnRHC: TButton;
+    procedure btnRH2Click(Sender: TObject);
+    procedure btnRHClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure btnRH2CClick(Sender: TObject);
+    procedure btnEntropyClick(Sender: TObject);
+    procedure btnRH2NonceScanClick(Sender: TObject);
+    procedure btnRHCClick(Sender: TObject);
+  private
+    { Private declarations }
+    FDisposables : TDisposables;
+    FRHThread : TAlgorithmThread;
+    FRHCachedThread : TAlgorithmThread;
+    FRH2Thread : TAlgorithmThread;
+    FRH2CachedThread : TAlgorithmThread;
+    FRH2NonceScanThread : TAlgorithmThread;
+    procedure OnReport(const ATitle : String; ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+    procedure OnFinish(const ATitle : String; ATotalHashes : UInt32; const ATimeSpan : TTimeSpan; const AMemStats : TStatistics);
+  public
+    { Public declarations }
+  end;
+
+  { TAlgorithmNotify }
+
+  TAlgorithmNotify  = procedure (const ATitle : String; ATotalHashes : UInt32; const ATimeSpan : TTimeSpan) of object;
+
+  { TAlgorithmFinishNotify }
+
+  TAlgorithmFinishNotify  = procedure (const ATitle : String; ATotalHashes : UInt32; const ATimeSpan : TTimeSpan; const AMemStats : TStatistics) of object;
+
+  { TAlgorithmThread }
+
+  TAlgorithmThread = class(TPCThread)
+    private
+      FDisposables : TDisposables;
+      FNotifyHashes : UInt32;
+      FNotifyDuration : TTimeSpan;
+      FNotify : TAlgorithmNotify;
+      FNotifyFinish : TAlgorithmFinishNotify;
+      FExitLoop : Boolean;
+      procedure ThreadSafeNotify;
+    protected
+      FTitle : String;
+      FLastHash : TBytes;
+      procedure BCExecute; override;
+      function RandomizeNonce(const AHeader : TBytes): TBytes;
+      function TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean; virtual; abstract;
+      function GetMemStats : TStatistics; virtual; abstract;
+    public
+      property Notify : TAlgorithmNotify read FNotify write FNotify;
+      property NotifyFinish : TAlgorithmFinishNotify read FNotifyFinish write FNotifyFinish;
+      constructor Create(const ATitle : String); virtual;
+      destructor Destroy; override;
+      procedure Finish;
+  end;
+
+  { TRandomHashThread }
+
+  TRandomHashThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHashFast;
+    protected
+      function TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean; override;
+      function GetMemStats : TStatistics; override;
+    public
+      constructor Create; overload;
+  end;
+
+  { TRandomHashCachedThread }
+
+  TRandomHashCachedThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHashFast;
+    protected
+      function TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean; override;
+      function GetMemStats : TStatistics; override;
+    public
+      constructor Create; overload;
+  end;
+
+  { TRandomHash2Thread }
+
+  TRandomHash2Thread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2Fast;
+    protected
+      function TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean; override;
+      function GetMemStats : TStatistics; override;
+    public
+      constructor Create; overload;
+  end;
+
+  { TRandomHash2CachedThread }
+
+  TRandomHash2CachedThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2Fast;
+    protected
+      function TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean; override;
+      function GetMemStats : TStatistics; override;
+    public
+      constructor Create; overload;
+  end;
+
+  { TRandomHash2NonceScan }
+
+  TRandomHash2NonceScan = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2Fast;
+      FLevel : Integer;
+    protected
+      function TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean; override;
+      function GetMemStats : TStatistics; override;
+    public
+      property Level : Integer read FLevel write FLevel;
+      constructor Create; overload;
+  end;
+
+implementation
+
+uses UBaseTypes, UCrypto;
+
+{$R *.dfm}
+
+{ TAlgorithmThread }
+
+constructor TAlgorithmThread.Create(const ATitle : String);
+begin
+  Inherited Create(True);
+  FTitle := ATitle;
+  FExitLoop := False;
+  SetLength(FLastHash, 32);
+end;
+
+destructor TAlgorithmThread.Destroy;
+begin
+  Self.Suspended := True;
+  Finish;
+  Inherited Destroy;
+end;
+
+procedure TAlgorithmThread.Finish;
+begin
+  FExitLoop := True;
+end;
+
+procedure TAlgorithmThread.BCExecute;
+var
+ LTC : TTickCount;
+ LInput : TBytes;
+ LStartTime, LNotifyStartTime  : TDateTime;
+ LTotalHashes, LNotifyHashes : UInt32;
+begin
+  SetLength(LInput, 200);
+  while True do begin
+    LTotalHashes := 0;
+    LNotifyHashes := 0;
+    LStartTime := Now;
+    LNotifyStartTime := LStartTime;
+    LTC := TPlatform.GetTickCount;
+    while Not FExitLoop do begin
+     if TryNextRound (RandomizeNonce(LInput), FLastHash) then begin
+       inc(LTotalHashes);
+       inc(LNotifyHashes);
+       Move(FLastHash, LInput[High(LInput)-32], 32);  // randomize input
+     end;
+     if TPlatform.GetElapsedMilliseconds(LTC)>2500 then begin
+       FNotifyHashes := LNotifyHashes;
+       FNotifyDuration := TTimeSpan.Subtract(Now, LNotifyStartTime);
+       LNotifyHashes := 0;
+       LNotifyStartTime := Now;
+       Queue( ThreadSafeNotify );
+       LTC := TPlatform.GetTickCount;
+     end;
+    end;
+    if Assigned(FNotifyFinish) then
+      FNotifyFinish(FTitle, LTotalHashes, TTimeSpan.Subtract(Now, LStartTime), GetMemStats );
+    FExitLoop := False;
+    Suspended := True;
+  end;
+end;
+
+procedure TAlgorithmThread.ThreadSafeNotify;
+begin
+  if Assigned(FNotify) then
+    FNotify(FTitle, FNotifyHashes, FNotifyDuration);
+end;
+
+function TAlgorithmThread.RandomizeNonce(const AHeader : TBytes): TBytes;
+var
+  LNonce : UInt32;
+  LLen : Integer;
+begin
+  if Length(AHeader) < 4 then
+    Result := TBytes.Create(0,0,0,0)
+  else
+    Result := Copy(AHeader);
+  LNonce := Random(MaxInt);
+  // If digest not big enough to contain a nonce, just return the clone
+  LLen := Length(Result);
+  if LLen < 4 then
+    exit;
+
+  // Overwrite the nonce in little-endian
+  Result[LLen - 4] := Byte(LNonce);
+  Result[LLen - 3] := (LNonce SHR 8) AND 255;
+  Result[LLen - 2] := (LNonce SHR 16) AND 255;
+  Result[LLen - 1] := (LNonce SHR 24) AND 255;
+end;
+
+{ TRandomHashThread }
+
+constructor TRandomHashThread.Create;
+begin
+  Inherited Create('Random Hash');
+  FHasher := TRandomHashFast.Create;
+  FHasher.EnableCache := False;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHashThread.TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean;
+begin
+   AOutput := FHasher.Hash(AInput);
+   Result := True;
+end;
+
+function TRandomHashThread.GetMemStats : TStatistics;
+begin
+  Result := FHasher.MemStats;
+end;
+
+{ TRandomHashCachedThread }
+
+constructor TRandomHashCachedThread.Create;
+begin
+  Inherited Create('Random Hash (Cached)');
+  FHasher := TRandomHashFast.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHashCachedThread.TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean;
+begin
+  if Length(FHasher.NextHeader) > 0 then
+    AOutput := FHasher.Hash(FHasher.NextHeader)
+  else
+    AOutput := FHasher.Hash(AInput);
+  Result := True;
+end;
+
+function TRandomHashCachedThread.GetMemStats : TStatistics;
+begin
+  Result := FHasher.MemStats;
+end;
+
+{ TRandomHash2Thread }
+
+constructor TRandomHash2Thread.Create;
+begin
+  Inherited Create('Random Hash 2');
+  FHasher := TRandomHash2Fast.Create;
+  FHasher.EnableCaching := False;
+  FHasher.CaptureMemStats := True;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2Thread.TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean;
+begin
+  AOutput := FHasher.Hash(AInput);
+  Result := True;
+end;
+
+function TRandomHash2Thread.GetMemStats : TStatistics;
+begin
+  Result := FHasher.MemStats;
+end;
+
+{ TRandomHash2CachedThread }
+
+constructor TRandomHash2CachedThread.Create;
+begin
+  Inherited Create('Random Hash 2 (Cached)');
+  FHasher := TRandomHash2Fast.Create;
+  FHasher.EnableCaching := True;
+  FHasher.Cache.EnablePartiallyComputed := True;
+  FHasher.CaptureMemStats := True;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2CachedThread.TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean;
+begin
+  if FHasher.Cache.HasComputedHash then
+    AOutput := FHasher.Cache.PopComputedHash.RoundOutputs[0]
+  else if FHasher.Cache.HasNextPartiallyComputedHash then
+    AOutput := FHasher.ResumeHash(FHasher.Cache.PopNextPartiallyComputedHash)
+  else
+    AOutput := FHasher.Hash(AInput);
+  Result := True;
+end;
+
+function TRandomHash2CachedThread.GetMemStats : TStatistics;
+begin
+  Result := FHasher.MemStats;
+end;
+
+{ TRandomHash2NonceScan }
+
+constructor TRandomHash2NonceScan.Create;
+begin
+  Inherited Create('Random Hash 2 (Nonce Scan)');
+  FHasher := TRandomHash2Fast.Create;
+  FHasher.EnableCaching := True;
+  FHasher.Cache.EnablePartiallyComputed := True;
+  FHasher.CaptureMemStats := True;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2NonceScan.TryNextRound(const AInput : TBytes; out AOutput : TBytes) : Boolean;
+begin
+  if FHasher.Cache.HasComputedHash then begin
+    AOutput := FHasher.Cache.PopComputedHash.RoundOutputs[0];
+    Result := True;
+  end else if FHasher.Cache.HasNextPartiallyComputedHash then begin
+    AOutput := FHasher.ResumeHash(FHasher.Cache.PopNextPartiallyComputedHash);
+    Result := True;
+  end else if FHasher.TryHash(AInput, FLevel, AOutput) then begin
+    Result := True;
+  end else Result := False;
+end;
+
+function TRandomHash2NonceScan.GetMemStats : TStatistics;
+begin
+  Result := FHasher.MemStats;
+end;
+
+{ TFRMDiagnosicTool }
+
+procedure TFRMDiagnosticTool.FormCreate(Sender: TObject);
+begin
+  FRHThread := TRandomHashThread.Create;
+  FRHCachedThread := TRandomHashCachedThread.Create;
+  FRH2CachedThread := TRandomHash2CachedThread.Create;
+  FRH2Thread := TRandomHash2Thread.Create;
+  FRH2NonceScanThread := TRandomHash2NonceScan.Create;
+
+  FDisposables.AddObject(FRHThread);
+  FDisposables.AddObject(FRHCachedThread);
+  FDisposables.AddObject(FRH2Thread);
+  FDisposables.AddObject(FRH2CachedThread);
+  FDisposables.AddObject(FRH2NonceScanThread);
+
+  TAlgorithmThread(FRHThread).Notify := OnReport;
+  TAlgorithmThread(FRHCachedThread).Notify := OnReport;
+  TAlgorithmThread(FRH2Thread).Notify := OnReport;
+  TAlgorithmThread(FRH2CachedThread).Notify := OnReport;
+  TAlgorithmThread(FRH2NonceScanThread).Notify := OnReport;
+
+  TAlgorithmThread(FRHThread).NotifyFinish := OnFinish;
+  TAlgorithmThread(FRHCachedThread).NotifyFinish := OnFinish;
+  TAlgorithmThread(FRH2Thread).NotifyFinish := OnFinish;
+  TAlgorithmThread(FRH2CachedThread).NotifyFinish := OnFinish;
+  TAlgorithmThread(FRH2NonceScanThread).NotifyFinish := OnFinish;
+end;
+
+procedure TFRMDiagnosticTool.btnRHClick(Sender: TObject);
+begin
+  if FRHThread.Suspended then begin
+    FRHThread.Suspended := False;
+    btnRH.Caption := 'Stop Random Hash';
+  end else begin
+    FRHThread.Finish;
+    btnRH.Caption := 'Start Random Hash';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.btnRHCClick(Sender: TObject);
+begin
+  if FRHCachedThread.Suspended then begin
+    FRHCachedThread.Suspended := False;
+    btnRHC.Caption := 'Stop Random Hash (Cached)';
+  end else begin
+    FRHCachedThread.Finish;
+    btnRHC.Caption := 'Start Random Hash (Cached)';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.btnEntropyClick(Sender: TObject);
+var LIn, LOut : TBytes; i : Integer; TXT : String;
+begin
+  SetLength(LIn, 200);
+  TXT := '';
+  for I := 1 to 10 do begin
+    LOut := TRandomHash2.Compute(LIn);
+    TXT := TXT + Format('RH2( %s ) = %s %s', [ TCrypto.ToHexaString(LIn), TCrypto.ToHexaString(LOut), sLineBreak]);
+    LIn := SetLastDWordLE(LIn, I);
+  end;
+  txtLog.Text := TXT;
+end;
+
+procedure TFRMDiagnosticTool.btnRH2Click(Sender: TObject);
+begin
+  if FRH2Thread.Suspended then begin
+    FRH2Thread.Suspended := False;
+    btnRH2.Caption := 'Stop Random Hash 2';
+  end else begin
+    FRH2Thread.Finish;
+    btnRH2.Caption := 'Start Random Hash 2';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.btnRH2CClick(Sender: TObject);
+begin
+  if FRH2CachedThread.Suspended then begin
+    FRH2CachedThread.Suspended := False;
+    btnRHC.Caption := 'Stop Random Hash 2 (Cached)';
+  end else begin
+    FRH2CachedThread.Finish;
+    btnRHC.Caption := 'Start Random Hash 2 (Cached)';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.btnRH2NonceScanClick(Sender: TObject);
+var LLevel : Integer;
+begin
+  if FRH2NonceScanThread.Suspended then begin
+    if TryStrToInt(txtScanLevel.Text, LLevel) then begin
+      TRandomHash2NonceScan(FRH2NonceScanThread).Level := LLevel;
+      FRH2NonceScanThread.Suspended := False;
+      btnRH2NonceScan.Caption := 'Stop Random Hash 2 (NonceScan)';
+      txtScanLevel.Enabled := false;
+    end;
+  end else begin
+    FRH2NonceScanThread.Finish;
+    btnRH2NonceScan.Caption := 'Start Random Hash 2 (NonceScan)';
+    txtScanLevel.Enabled := true;
+  end;
+end;
+
+procedure TFRMDiagnosticTool.OnReport(const ATitle : String; ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+var
+ LHPS : Double;
+begin
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
+  txtLog.Text := txtLog.Text + Format('%s: %n H/S%s', [ATitle, LHPS, sLineBreak]);
+end;
+
+procedure TFRMDiagnosticTool.OnFinish(const ATitle : String; ATotalHashes : UInt32; const ATimeSpan : TTimeSpan; const AMemStats : TStatistics);
+var
+ LHPS : Double;
+ LMemStats : String;
+begin
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
+  LMemStats := Format('Samples: %d, Mean: %n, Std: %n, Min: %n, Max: %n', [AMemStats.SampleCount, AMemStats.Mean, AMemStats.SampleStandardDeviation, AMemStats.Minimum, AMemStats.Maximum]);
+  txtLog.Text := txtLog.Text + Format('FINISH: %s: %n Mean H/S, MemStats: %s%s', [ATitle, LHPS, LMemStats, sLineBreak]);
+end;
+
+end.

+ 3 - 0
src/gui-classic/UFRMDiagnosticTool.vlb

@@ -0,0 +1,3 @@
+[]
+Coordinates=0,0,91,36
+

+ 113 - 0
src/gui-classic/UFRMHashLock.lfm

@@ -0,0 +1,113 @@
+object FRMHashLock: TFRMHashLock
+  Left = 237
+  Height = 270
+  Top = 179
+  Width = 548
+  BorderStyle = bsDialog
+  Caption = 'Atomic Swap Hash-Lock '
+  ClientHeight = 270
+  ClientWidth = 548
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  OnCloseQuery = FormCloseQuery
+  OnCreate = FormCreate
+  Position = poOwnerFormCenter
+  LCLVersion = '1.8.0.6'
+  object lblError: TLabel
+    Left = 19
+    Height = 13
+    Top = 239
+    Width = 282
+    AutoSize = False
+    Caption = 'Errors detected'
+    Color = clBtnFace
+    Font.Color = clRed
+    Font.Height = -11
+    Font.Name = 'Tahoma'
+    ParentColor = False
+    ParentFont = False
+    Transparent = False
+  end
+  object btnCancel: TBitBtn
+    Left = 424
+    Height = 31
+    Top = 231
+    Width = 116
+    Anchors = [akRight, akBottom]
+    Kind = bkCancel
+    ModalResult = 2
+    OnClick = btnCancelClick
+    TabOrder = 0
+  end
+  object btnSet: TBitBtn
+    Left = 307
+    Height = 31
+    Top = 231
+    Width = 111
+    Anchors = [akRight, akBottom]
+    Caption = 'Set Hash-Lock'
+    Kind = bkOK
+    ModalResult = 1
+    OnClick = btnSetClick
+    TabOrder = 1
+  end
+  object GroupBox1: TGroupBox
+    Left = 8
+    Height = 83
+    Top = 14
+    Width = 532
+    Caption = 'Mode'
+    ClientHeight = 65
+    ClientWidth = 528
+    TabOrder = 2
+    object rbHashLock: TRadioButton
+      Left = 11
+      Height = 19
+      Top = 8
+      Width = 362
+      Caption = 'Hash-Lock - the 32 byte hash-lock provided to you by the counterparty'
+      TabOrder = 0
+    end
+    object rbHashLockKey: TRadioButton
+      Left = 11
+      Height = 19
+      Top = 31
+      Width = 386
+      Caption = 'Hash-Lock Key - a secret that will be hashed to create the 32 byte hash-lock'
+      TabOrder = 1
+    end
+  end
+  object GroupBox2: TGroupBox
+    Left = 8
+    Height = 122
+    Top = 103
+    Width = 532
+    Caption = 'Data'
+    ClientHeight = 104
+    ClientWidth = 528
+    TabOrder = 3
+    object Label2: TLabel
+      Left = 11
+      Height = 13
+      Top = 21
+      Width = 266
+      Caption = 'Please enter the hash lock data as a hexadecimal string'
+      ParentColor = False
+    end
+    object meHashLockData: TMemo
+      Left = 11
+      Height = 49
+      Top = 40
+      Width = 501
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      Font.Color = clBlack
+      Font.Height = -16
+      Font.Name = 'Tahoma'
+      ParentFont = False
+      TabOrder = 0
+      WantReturns = False
+    end
+  end
+end

+ 52 - 31
src/gui-classic/UFRMHashLock.pas

@@ -31,6 +31,7 @@ type
     FHashLock : T32Bytes;
     FHashLock : T32Bytes;
     function GetError : String;
     function GetError : String;
     procedure SetError(const AMsg : String);
     procedure SetError(const AMsg : String);
+    procedure UpdateInfo;
   public
   public
     { Public declarations }
     { Public declarations }
     property Error : String read GetError write SetError;
     property Error : String read GetError write SetError;
@@ -42,54 +43,35 @@ implementation
 
 
 uses UCrypto, UAccounts;
 uses UCrypto, UAccounts;
 
 
-{$R *.dfm}
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
+
 
 
 procedure TFRMHashLock.btnCancelClick(Sender: TObject);
 procedure TFRMHashLock.btnCancelClick(Sender: TObject);
 begin
 begin
   Error := '';
   Error := '';
+  ModalResult := mrCancel;
 end;
 end;
 
 
 procedure TFRMHashLock.btnSetClick(Sender: TObject);
 procedure TFRMHashLock.btnSetClick(Sender: TObject);
-var
-  LData : TRawBytes;
-  LErr : string;
 begin
 begin
-  Error := '';
-  if (NOT rbHashLock.Checked) AND (NOT rbHashLockKey.Checked) then begin
-    Error := 'Select the hash-lock mode';
-    Exit;
-  end;
-
-  if NOT TCrypto.IsHexString(meHashLockData.Text) then begin
-    Error := 'Data is not hexadecimal format';
-    Exit;
-  end;
-
-  LData := TCrypto.HexaToRaw(meHashLockData.Text);
-
-  if (rbHashLock.Checked) then begin
-    if Length(LData) <> 32 then begin
-      Error := 'Hash-lock must be 32bytes';
-      Exit;
-    end;
-    FHashLock := TBaseType.To32Bytes(LData);
-  end else if (rbHashLockKey.Checked) then begin
-    if NOT TAccountComp.IsValidHashLockKey(LData, LErr) then begin
-      Error := LErr;
-      Exit;
-    end;
-    FHashLock := TAccountComp.CalculateHashLock(LData);
-  end else Error := 'INTERNAL ERROR: 8356DE573BA748618EDD6603B22D9EAD';
+  UpdateInfo;
+  if Error='' then ModalResult := MrOk;
 end;
 end;
 
 
 procedure TFRMHashLock.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
 procedure TFRMHashLock.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
 begin
 begin
-  CanClose := Error = '';
+  //
 end;
 end;
 
 
 procedure TFRMHashLock.FormCreate(Sender: TObject);
 procedure TFRMHashLock.FormCreate(Sender: TObject);
 begin
 begin
   Error := '';
   Error := '';
+  btnSet.ModalResult := MrNone;
+  btnSet.OnClick := btnSetClick;
 end;
 end;
 
 
 function TFRMHashLock.GetError : String;
 function TFRMHashLock.GetError : String;
@@ -102,4 +84,43 @@ begin
   lblError.Caption := AMsg;
   lblError.Caption := AMsg;
 end;
 end;
 
 
+procedure TFRMHashLock.UpdateInfo;
+var
+  LData : TRawBytes;
+  LErr : string;
+begin
+  LErr := '';
+  Try
+    if (NOT rbHashLock.Checked) AND (NOT rbHashLockKey.Checked) then begin
+      LErr := 'Select the hash-lock mode';
+      Exit;
+    end;
+
+    if Length(Trim(meHashLockData.Text))<1 then begin
+      LErr := 'No Data value';
+      Exit;
+    end;
+
+    if NOT TCrypto.IsHexString(meHashLockData.Text) then begin
+      LErr := 'Data is not hexadecimal format';
+      Exit;
+    end;
+
+    LData := TCrypto.HexaToRaw(meHashLockData.Text);
+
+    if (rbHashLock.Checked) then begin
+      if Length(LData) <> 32 then begin
+        Lerr := 'Hash-lock must be 32bytes';
+        Exit;
+      end;
+      FHashLock := TBaseType.To32Bytes(LData);
+    end else if (rbHashLockKey.Checked) then begin
+      if NOT TAccountComp.IsValidHashLockKey(LData, LErr) then Exit;
+      FHashLock := TAccountComp.CalculateHashLock(LData);
+    end else LErr := 'INTERNAL ERROR: 8356DE573BA748618EDD6603B22D9EAD';
+  Finally
+    Error := LErr;
+  end;
+end;
+
 end.
 end.

+ 23 - 26
src/gui-classic/UFRMOperation.dfm

@@ -295,15 +295,11 @@ object FRMOperation: TFRMOperation
         Top = 7
         Top = 7
         Width = 524
         Width = 524
         Height = 204
         Height = 204
-        ActivePage = tsListAccount
+        ActivePage = tsBuyAccount
         TabOrder = 0
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
         object tsTransaction: TTabSheet
           Caption = 'Transaction'
           Caption = 'Transaction'
-          ExplicitLeft = 0
-          ExplicitTop = 0
-          ExplicitWidth = 0
-          ExplicitHeight = 0
           object lblDestAccount: TLabel
           object lblDestAccount: TLabel
             Left = 13
             Left = 13
             Top = 32
             Top = 32
@@ -390,10 +386,6 @@ object FRMOperation: TFRMOperation
         object tsChangePrivateKey: TTabSheet
         object tsChangePrivateKey: TTabSheet
           Caption = 'Change Key'
           Caption = 'Change Key'
           ImageIndex = 1
           ImageIndex = 1
-          ExplicitLeft = 0
-          ExplicitTop = 0
-          ExplicitWidth = 0
-          ExplicitHeight = 0
           object lblNewPrivateKey: TLabel
           object lblNewPrivateKey: TLabel
             Left = 57
             Left = 57
             Top = 40
             Top = 40
@@ -777,10 +769,6 @@ object FRMOperation: TFRMOperation
         object tsDelistAccount: TTabSheet
         object tsDelistAccount: TTabSheet
           Caption = 'Delist Account'
           Caption = 'Delist Account'
           ImageIndex = 3
           ImageIndex = 3
-          ExplicitLeft = 0
-          ExplicitTop = 0
-          ExplicitWidth = 0
-          ExplicitHeight = 0
           object lblDelistErrors: TLabel
           object lblDelistErrors: TLabel
             Left = 13
             Left = 13
             Top = 10
             Top = 10
@@ -801,10 +789,6 @@ object FRMOperation: TFRMOperation
         object tsBuyAccount: TTabSheet
         object tsBuyAccount: TTabSheet
           Caption = 'Buy Account'
           Caption = 'Buy Account'
           ImageIndex = 4
           ImageIndex = 4
-          ExplicitLeft = 0
-          ExplicitTop = 0
-          ExplicitWidth = 0
-          ExplicitHeight = 0
           object lblAccountToBuy: TLabel
           object lblAccountToBuy: TLabel
             Left = 13
             Left = 13
             Top = 32
             Top = 32
@@ -830,7 +814,7 @@ object FRMOperation: TFRMOperation
           object lblBuyAccountErrors: TLabel
           object lblBuyAccountErrors: TLabel
             Left = 13
             Left = 13
             Top = 10
             Top = 10
-            Width = 331
+            Width = 484
             Height = 13
             Height = 13
             AutoSize = False
             AutoSize = False
             Caption = 'Errors detected'
             Caption = 'Errors detected'
@@ -980,10 +964,6 @@ object FRMOperation: TFRMOperation
         end
         end
         object tsChangeInfo: TTabSheet
         object tsChangeInfo: TTabSheet
           Caption = 'Change Info'
           Caption = 'Change Info'
-          ExplicitLeft = 0
-          ExplicitTop = 0
-          ExplicitWidth = 0
-          ExplicitHeight = 0
           object lblChangeInfoErrors: TLabel
           object lblChangeInfoErrors: TLabel
             Left = 13
             Left = 13
             Top = 10
             Top = 10
@@ -1022,12 +1002,24 @@ object FRMOperation: TFRMOperation
             Color = clBtnFace
             Color = clBtnFace
             ParentColor = False
             ParentColor = False
           end
           end
+          object lblChangeAccountData: TLabel
+            Left = 13
+            Top = 87
+            Width = 100
+            Height = 13
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Change Data'
+            Color = clBtnFace
+            ParentColor = False
+          end
           object ebChangeName: TEdit
           object ebChangeName: TEdit
             Left = 118
             Left = 118
             Top = 29
             Top = 29
             Width = 258
             Width = 258
             Height = 21
             Height = 21
             TabOrder = 0
             TabOrder = 0
+            TextHint = 'Account name (null or 3..64 chars)'
           end
           end
           object ebChangeType: TEdit
           object ebChangeType: TEdit
             Left = 118
             Left = 118
@@ -1035,6 +1027,15 @@ object FRMOperation: TFRMOperation
             Width = 76
             Width = 76
             Height = 21
             Height = 21
             TabOrder = 1
             TabOrder = 1
+            TextHint = '0..65535'
+          end
+          object ebChangeAccountData: TEdit
+            Left = 118
+            Top = 84
+            Width = 386
+            Height = 21
+            TabOrder = 2
+            TextHint = 'Hexadecimal value (0..32 bytes)'
           end
           end
         end
         end
       end
       end
@@ -1049,10 +1050,6 @@ object FRMOperation: TFRMOperation
     object tsGlobalError: TTabSheet
     object tsGlobalError: TTabSheet
       ImageIndex = 1
       ImageIndex = 1
       TabVisible = False
       TabVisible = False
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 373
       object lblGlobalErrors: TLabel
       object lblGlobalErrors: TLabel
         Left = 40
         Left = 40
         Top = 50
         Top = 50

+ 412 - 294
src/gui-classic/UFRMOperation.lfm

@@ -1,12 +1,12 @@
 object FRMOperation: TFRMOperation
 object FRMOperation: TFRMOperation
-  Left = 772
-  Height = 493
-  Top = 245
+  Left = 564
+  Height = 513
+  Top = 267
   Width = 608
   Width = 608
   BorderIcons = [biSystemMenu]
   BorderIcons = [biSystemMenu]
   BorderStyle = bsSingle
   BorderStyle = bsSingle
   Caption = 'New Operation'
   Caption = 'New Operation'
-  ClientHeight = 493
+  ClientHeight = 513
   ClientWidth = 608
   ClientWidth = 608
   Color = clBtnFace
   Color = clBtnFace
   Font.Color = clWindowText
   Font.Color = clWindowText
@@ -15,8 +15,7 @@ object FRMOperation: TFRMOperation
   OnCreate = FormCreate
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.4.0'
-  Visible = False
+  LCLVersion = '1.8.0.6'
   object lblAccountCaption: TLabel
   object lblAccountCaption: TLabel
     Left = 25
     Left = 25
     Height = 13
     Height = 13
@@ -57,7 +56,7 @@ object FRMOperation: TFRMOperation
   object bbExecute: TBitBtn
   object bbExecute: TBitBtn
     Left = 300
     Left = 300
     Height = 31
     Height = 31
-    Top = 445
+    Top = 464
     Width = 130
     Width = 130
     Caption = 'Execute (F12)'
     Caption = 'Execute (F12)'
     Glyph.Data = {
     Glyph.Data = {
@@ -85,7 +84,7 @@ object FRMOperation: TFRMOperation
   object bbCancel: TBitBtn
   object bbCancel: TBitBtn
     Left = 465
     Left = 465
     Height = 31
     Height = 31
-    Top = 445
+    Top = 464
     Width = 116
     Width = 116
     Cancel = True
     Cancel = True
     Caption = 'Cancel'
     Caption = 'Cancel'
@@ -95,7 +94,7 @@ object FRMOperation: TFRMOperation
   end
   end
   object PageControlLocked: TPageControl
   object PageControlLocked: TPageControl
     Left = 25
     Left = 25
-    Height = 367
+    Height = 383
     Top = 72
     Top = 72
     Width = 556
     Width = 556
     ActivePage = tsOperation
     ActivePage = tsOperation
@@ -103,7 +102,7 @@ object FRMOperation: TFRMOperation
     TabOrder = 1
     TabOrder = 1
     object tsOperation: TTabSheet
     object tsOperation: TTabSheet
       Caption = 'Operation'
       Caption = 'Operation'
-      ClientHeight = 341
+      ClientHeight = 357
       ClientWidth = 548
       ClientWidth = 548
       TabVisible = False
       TabVisible = False
       object lblFee: TLabel
       object lblFee: TLabel
@@ -116,11 +115,11 @@ object FRMOperation: TFRMOperation
       end
       end
       object gbPayload: TGroupBox
       object gbPayload: TGroupBox
         Left = 13
         Left = 13
-        Height = 129
+        Height = 145
         Top = 207
         Top = 207
         Width = 521
         Width = 521
         Caption = ' Payload: '
         Caption = ' Payload: '
-        ClientHeight = 111
+        ClientHeight = 127
         ClientWidth = 517
         ClientWidth = 517
         TabOrder = 2
         TabOrder = 2
         object lblEncryptPassword: TLabel
         object lblEncryptPassword: TLabel
@@ -141,9 +140,9 @@ object FRMOperation: TFRMOperation
         end
         end
         object lblEncryptionErrors: TLabel
         object lblEncryptionErrors: TLabel
           Left = 255
           Left = 255
-          Height = 13
+          Height = 33
           Top = 87
           Top = 87
-          Width = 246
+          Width = 182
           AutoSize = False
           AutoSize = False
           Caption = 'Errors detected'
           Caption = 'Errors detected'
           Font.Color = clRed
           Font.Color = clRed
@@ -151,6 +150,7 @@ object FRMOperation: TFRMOperation
           Font.Name = 'Tahoma'
           Font.Name = 'Tahoma'
           ParentColor = False
           ParentColor = False
           ParentFont = False
           ParentFont = False
+          WordWrap = True
         end
         end
         object lblPayloadLength: TLabel
         object lblPayloadLength: TLabel
           Left = 352
           Left = 352
@@ -223,6 +223,14 @@ object FRMOperation: TFRMOperation
           OnClick = memoPayloadClick
           OnClick = memoPayloadClick
           TabOrder = 0
           TabOrder = 0
         end
         end
+        object cbPayloadAsHex: TCheckBox
+          Left = 444
+          Height = 19
+          Top = 92
+          Width = 54
+          Caption = 'As Hex'
+          TabOrder = 6
+        end
       end
       end
       object ebFee: TEdit
       object ebFee: TEdit
         Left = 90
         Left = 90
@@ -232,12 +240,12 @@ object FRMOperation: TFRMOperation
         TabOrder = 1
         TabOrder = 1
       end
       end
       object PageControlOpType: TPageControl
       object PageControlOpType: TPageControl
-        Left = 15
+        Left = 16
         Height = 167
         Height = 167
         Top = 11
         Top = 11
         Width = 521
         Width = 521
-        ActivePage = tsChangeInfo
-        TabIndex = 5
+        ActivePage = tsListAccount
+        TabIndex = 2
         TabOrder = 0
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
         object tsTransaction: TTabSheet
@@ -328,285 +336,375 @@ object FRMOperation: TFRMOperation
           ClientHeight = 141
           ClientHeight = 141
           ClientWidth = 513
           ClientWidth = 513
           ImageIndex = 1
           ImageIndex = 1
-          object gbChangeKey: TGroupBox
-            Left = 7
-            Height = 130
-            Top = 6
-            Width = 488
-            Caption = ' Change type: '
-            ClientHeight = 112
-            ClientWidth = 484
+          object lblNewPrivateKey: TLabel
+            Left = 61
+            Height = 13
+            Top = 41
+            Width = 78
+            Caption = 'New private key'
+            ParentColor = False
+          end
+          object lblNewOwnerPublicKey: TLabel
+            Left = 28
+            Height = 13
+            Top = 91
+            Width = 109
+            Caption = 'New owners public key'
+            ParentColor = False
+          end
+          object lblNewOwnerErrors: TLabel
+            Left = 177
+            Height = 13
+            Top = 69
+            Width = 302
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Errors detected'
+            Font.Color = clRed
+            Font.Height = -11
+            Font.Name = 'Tahoma'
+            ParentColor = False
+            ParentFont = False
+          end
+          object lblChangeKeyErrors: TLabel
+            Left = 149
+            Height = 13
+            Top = 16
+            Width = 331
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Errors detected'
+            Font.Color = clRed
+            Font.Height = -11
+            Font.Name = 'Tahoma'
+            ParentColor = False
+            ParentFont = False
+          end
+          object rbChangeKeyWithAnother: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 21
+            Width = 114
+            Caption = 'Change private key'
             TabOrder = 0
             TabOrder = 0
-            object lblNewPrivateKey: TLabel
-              Left = 57
-              Height = 13
-              Top = 33
-              Width = 78
-              Caption = 'New private key'
-              ParentColor = False
-            end
-            object lblNewOwnerPublicKey: TLabel
-              Left = 24
-              Height = 13
-              Top = 83
-              Width = 109
-              Caption = 'New owners public key'
-              ParentColor = False
-            end
-            object lblNewOwnerErrors: TLabel
-              Left = 173
-              Height = 13
-              Top = 61
-              Width = 302
-              Alignment = taRightJustify
-              AutoSize = False
-              Caption = 'Errors detected'
-              Font.Color = clRed
-              Font.Height = -11
-              Font.Name = 'Tahoma'
-              ParentColor = False
-              ParentFont = False
-            end
-            object lblChangeKeyErrors: TLabel
-              Left = 145
-              Height = 13
-              Top = 8
-              Width = 331
-              Alignment = taRightJustify
-              AutoSize = False
-              Caption = 'Errors detected'
-              Font.Color = clRed
-              Font.Height = -11
-              Font.Name = 'Tahoma'
-              ParentColor = False
-              ParentFont = False
-            end
-            object rbChangeKeyWithAnother: TRadioButton
-              Left = 12
-              Height = 19
-              Top = 13
-              Width = 114
-              Caption = 'Change private key'
-              TabOrder = 0
-            end
-            object cbNewPrivateKey: TComboBox
-              Left = 145
-              Height = 21
-              Top = 30
-              Width = 294
-              ItemHeight = 13
-              Sorted = True
-              Style = csDropDownList
-              TabOrder = 1
-            end
-            object ebNewPublicKey: TEdit
-              Left = 144
-              Height = 21
-              Top = 80
-              Width = 331
-              TabOrder = 4
-            end
-            object bbChangePrivateKeyKeys: TBitBtn
-              Left = 445
-              Height = 22
-              Top = 29
-              Width = 31
-              Glyph.Data = {
-                36050000424D3605000000000000360400002800000010000000100000000100
-                0800000000000001000000000000000000000001000000010000FF00FF008C6B
-                6C0087707000AE877C000D7FA9006F7C88006D7C94001F7ECE000E80AA001180
-                A7001081AB00048CB900078DBC000B8DBA000C8DBA00088EBC001285B0007882
-                95006092BD005EA8BE000A91C1000F9DCE002087DE0011A7D10030BCDC001F89
-                E00059A9DC0044BADD004ABFE00057AEF4004DB1F90049B2FF004EB7FF0057B1
-                F60050B6FE0022D7FC0024D7FF0024D8FD0039D7FB0035D8FD004BC6DC0046C6
-                E40048D5EE0075D3E90058E9FD006FE6FF0070E6FF0071F9FE007BFFFF008683
-                88008B8697008F989B00969594009C919000AD858000AD868500AB939500A49E
-                9900B1979400B5A09F008AA5AD00CAA08C00CDAC9300C2A69A00F3BE8000C6AE
-                A000CFB7A100D3BBA200F4C88E00F5CB9A00F5D09C00F8D09800DAC5B700E4CC
-                A900EFD2A900ECD1AC00F6DAAB00F5DEB500F5E1B600F9E1B100FEEAB900FFF2
-                BA00A1C6C8008DE6FA0081F8FE008CFAFD008DFCFE0097FCFD009BFBFD00B8ED
-                F600A7FFFF00AAFFFE00ADFFFE00B6F6FF00B1FCFD00B4FFFF00ECDDCC00E8DD
-                D600FFF7C600FCF5CD00FCF7D100FAF6D600FFFBD500FEFED600F7F2D900FEFF
-                D900FFFEDD00C6F5FF00C6FEFF00D2FFFF00FEF7E000FBFCE100FDFFE100FFFF
-                E400E3FEFF00F9F6F200FFFFF400F1FBFC00F5FFFE00FBFFFF00000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                00000000000000000605000004080408080A000000000011191A000B2A23272D
-                531B080000001116201D0B552C23272E531C1509003207201D000F552C23272B
-                3A3F41030112202000000F552C252938606771684236200000000B5F5D6B3B61
-                74676A67513D000000000B59181735716A676A63474B360000000F282C23396A
-                6A6A6A4C404D360000000B552C2534656A654F455049360000000B552C251343
-                6247446E7336000000000B552C25263C3E4B4E483636000000000F55542F3057
-                523331020000000000000B77766D5F5C5C5C2F08000000000000001476726C5C
-                5A58100000000000000000000F0F0B0F0F0F0000000000000000
-              }
-              OnClick = bbChangePrivateKeyKeysClick
-              TabOrder = 2
-            end
-            object rbChangeKeyTransferAccountToNewOwner: TRadioButton
-              Left = 12
-              Height = 19
-              Top = 60
-              Width = 180
-              Caption = 'Transfer account to a new owner'
-              TabOrder = 3
-            end
+          end
+          object cbNewPrivateKey: TComboBox
+            Left = 149
+            Height = 21
+            Top = 38
+            Width = 294
+            ItemHeight = 13
+            Sorted = True
+            Style = csDropDownList
+            TabOrder = 1
+          end
+          object ebNewPublicKey: TEdit
+            Left = 148
+            Height = 21
+            Top = 88
+            Width = 331
+            TabOrder = 2
+          end
+          object bbChangePrivateKeyKeys: TBitBtn
+            Left = 449
+            Height = 22
+            Top = 37
+            Width = 31
+            Glyph.Data = {
+              36050000424D3605000000000000360400002800000010000000100000000100
+              0800000000000001000000000000000000000001000000010000FF00FF008C6B
+              6C0087707000AE877C000D7FA9006F7C88006D7C94001F7ECE000E80AA001180
+              A7001081AB00048CB900078DBC000B8DBA000C8DBA00088EBC001285B0007882
+              95006092BD005EA8BE000A91C1000F9DCE002087DE0011A7D10030BCDC001F89
+              E00059A9DC0044BADD004ABFE00057AEF4004DB1F90049B2FF004EB7FF0057B1
+              F60050B6FE0022D7FC0024D7FF0024D8FD0039D7FB0035D8FD004BC6DC0046C6
+              E40048D5EE0075D3E90058E9FD006FE6FF0070E6FF0071F9FE007BFFFF008683
+              88008B8697008F989B00969594009C919000AD858000AD868500AB939500A49E
+              9900B1979400B5A09F008AA5AD00CAA08C00CDAC9300C2A69A00F3BE8000C6AE
+              A000CFB7A100D3BBA200F4C88E00F5CB9A00F5D09C00F8D09800DAC5B700E4CC
+              A900EFD2A900ECD1AC00F6DAAB00F5DEB500F5E1B600F9E1B100FEEAB900FFF2
+              BA00A1C6C8008DE6FA0081F8FE008CFAFD008DFCFE0097FCFD009BFBFD00B8ED
+              F600A7FFFF00AAFFFE00ADFFFE00B6F6FF00B1FCFD00B4FFFF00ECDDCC00E8DD
+              D600FFF7C600FCF5CD00FCF7D100FAF6D600FFFBD500FEFED600F7F2D900FEFF
+              D900FFFEDD00C6F5FF00C6FEFF00D2FFFF00FEF7E000FBFCE100FDFFE100FFFF
+              E400E3FEFF00F9F6F200FFFFF400F1FBFC00F5FFFE00FBFFFF00000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              00000000000000000605000004080408080A000000000011191A000B2A23272D
+              531B080000001116201D0B552C23272E531C1509003207201D000F552C23272B
+              3A3F41030112202000000F552C252938606771684236200000000B5F5D6B3B61
+              74676A67513D000000000B59181735716A676A63474B360000000F282C23396A
+              6A6A6A4C404D360000000B552C2534656A654F455049360000000B552C251343
+              6247446E7336000000000B552C25263C3E4B4E483636000000000F55542F3057
+              523331020000000000000B77766D5F5C5C5C2F08000000000000001476726C5C
+              5A58100000000000000000000F0F0B0F0F0F0000000000000000
+            }
+            OnClick = bbChangePrivateKeyKeysClick
+            TabOrder = 3
+          end
+          object rbChangeKeyTransferAccountToNewOwner: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 68
+            Width = 180
+            Caption = 'Transfer account to a new owner'
+            TabOrder = 4
           end
           end
         end
         end
-        object tsListForSale: TTabSheet
-          Caption = 'List account for sale'
+        object tsListAccount: TTabSheet
+          Caption = 'List Account'
           ClientHeight = 141
           ClientHeight = 141
           ClientWidth = 513
           ClientWidth = 513
           ImageIndex = 3
           ImageIndex = 3
-          object gbSaleType: TGroupBox
-            Left = 8
-            Height = 135
-            Top = 5
-            Width = 499
-            Caption = ' Sale type: '
-            ClientHeight = 117
-            ClientWidth = 495
+          object lblPrice: TLabel
+            Left = 38
+            Height = 13
+            Top = 68
+            Width = 50
+            Alignment = taRightJustify
+            Caption = 'Sale price:'
+            ParentColor = False
+          end
+          object lblSeller: TLabel
+            Left = 200
+            Height = 13
+            Top = 68
+            Width = 136
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Seller Account'
+            ParentColor = False
+          end
+          object lblNewKey: TLabel
+            Left = 39
+            Height = 13
+            Top = 91
+            Width = 49
+            Alignment = taRightJustify
+            Caption = 'Buyer Key'
+            ParentColor = False
+          end
+          object lblTimeLock: TLabel
+            Left = 41
+            Height = 13
+            Top = 115
+            Width = 47
+            Alignment = taRightJustify
+            Caption = 'Time-Lock'
+            ParentColor = False
+          end
+          object lblListAccountErrors: TLabel
+            Left = 16
+            Height = 13
+            Top = 8
+            Width = 483
+            AutoSize = False
+            Caption = 'Errors detected'
+            Font.Color = clRed
+            Font.Height = -11
+            Font.Name = 'Tahoma'
+            ParentColor = False
+            ParentFont = False
+          end
+          object rbListAccountForPublicSale: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 26
+            Width = 106
+            Caption = 'List for Public Sale'
             TabOrder = 0
             TabOrder = 0
-            object Label1: TLabel
-              Left = 81
-              Height = 13
-              Top = 44
-              Width = 50
-              Caption = 'Sale price:'
-              ParentColor = False
-            end
-            object Label3: TLabel
-              Left = 229
-              Height = 13
-              Top = 44
-              Width = 142
-              Caption = 'Seller account (where to pay)'
-              ParentColor = False
-            end
-            object lblSaleNewOwnerPublicKey: TLabel
-              Left = 20
-              Height = 13
-              Top = 67
-              Width = 109
-              Caption = 'New owners public key'
-              ParentColor = False
-            end
-            object lblSaleLockedUntilBlock: TLabel
-              Left = 44
-              Height = 13
-              Top = 91
-              Width = 87
-              Caption = 'Locked until block:'
-              ParentColor = False
-            end
-            object lblListAccountErrors: TLabel
-              Left = 145
-              Height = 13
-              Top = 0
-              Width = 331
-              Alignment = taRightJustify
-              AutoSize = False
-              Caption = 'Errors detected'
-              Font.Color = clRed
-              Font.Height = -11
-              Font.Name = 'Tahoma'
-              ParentColor = False
-              ParentFont = False
-            end
-            object rbListAccountForPublicSale: TRadioButton
-              Left = 10
-              Height = 19
-              Top = 2
-              Width = 146
-              Caption = 'List account for public sale'
-              TabOrder = 0
-            end
-            object rbListAccountForPrivateSale: TRadioButton
-              Left = 10
-              Height = 19
-              Top = 22
-              Width = 153
-              Caption = 'List account for private sale'
-              TabOrder = 1
-            end
-            object ebSalePrice: TEdit
-              Left = 137
-              Height = 21
-              Top = 41
-              Width = 86
-              TabOrder = 2
-            end
-            object ebSellerAccount: TEdit
-              Left = 378
-              Height = 21
-              Top = 41
-              Width = 82
-              TabOrder = 3
-            end
-            object ebSaleNewOwnerPublicKey: TEdit
-              Left = 137
-              Height = 21
-              Top = 64
-              Width = 323
-              TabOrder = 4
-            end
-            object ebSaleLockedUntilBlock: TEdit
-              Left = 137
-              Height = 21
-              Top = 88
-              Width = 86
-              TabOrder = 5
-            end
-            object sbSearchListerSellerAccount: TSpeedButton
-              Left = 464
-              Height = 22
-              Top = 41
-              Width = 23
-              Glyph.Data = {
-                36030000424D3803000000000000360000002800000010000000100000000100
-                18000000000000000000120B0000120B00000000000000000000FF00FF4A667C
-                BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
-                FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF
-                00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF
-                2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
-                FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF
-                00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
-                51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00
-                FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF
-                00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
-                FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3
-                B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD
-                EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF
-                FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF
-                FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1
-                C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF
-                FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF
-                E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4
-                C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF
-                FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4
-                C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8
-                DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF
-                FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890
-                86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
-                00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF
-              }
-              OnClick = sbSearchListerSellerAccountClick
-            end
+          end
+          object rbListAccountForPrivateSale: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 46
+            Width = 113
+            Caption = 'List for Private Sale'
+            TabOrder = 1
+          end
+          object ebPrice: TEdit
+            Left = 96
+            Height = 21
+            Top = 65
+            Width = 94
+            TabOrder = 4
+            TextHint = 'PASC Quantity'
+          end
+          object ebSellerAccount: TEdit
+            Left = 344
+            Height = 21
+            Top = 65
+            Width = 122
+            TabOrder = 5
+            TextHint = 'Account Number'
+          end
+          object ebNewKey: TEdit
+            Left = 96
+            Height = 21
+            Top = 88
+            Width = 370
+            TabOrder = 6
+            TextHint = 'BASE58 Encoded Public Key Of New Owner'
+          end
+          object ebTimeLock: TEdit
+            Left = 96
+            Height = 21
+            Top = 112
+            Width = 91
+            TabOrder = 7
+            TextHint = 'Block number'
+          end
+          object sbSearchListerSellerAccount: TSpeedButton
+            Left = 470
+            Height = 22
+            Top = 65
+            Width = 23
+            Glyph.Data = {
+              36030000424D3803000000000000360000002800000010000000100000000100
+              18000000000000000000120B0000120B00000000000000000000FF00FF4A667C
+              BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF
+              2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF
+              00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3
+              B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD
+              EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF
+              FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1
+              C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF
+              FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF
+              E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4
+              C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4
+              C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8
+              DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890
+              86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF
+            }
+            OnClick = sbSearchListerSellerAccountClick
+          end
+          object rbListAccountForAccountSwap: TRadioButton
+            Left = 160
+            Height = 19
+            Top = 26
+            Width = 159
+            Caption = 'List for Atomic Account Swap'
+            TabOrder = 2
+          end
+          object rbListAccountForCoinSwap: TRadioButton
+            Left = 160
+            Height = 19
+            Top = 46
+            Width = 141
+            Caption = 'List for Atomic Coin Swap'
+            TabOrder = 3
+          end
+          object sbTimeLock: TSpeedButton
+            Left = 192
+            Height = 22
+            Top = 112
+            Width = 23
+            Glyph.Data = {
+              36030000424D3603000000000000360000002800000010000000100000000100
+              18000000000000030000120B0000120B00000000000000000000FF00FF53B6F0
+              4A95DFC6CEEDFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FF1B59D3298DEA7AFDFF47A2E6C1C9ECFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF3F9BE4207EE2
+              1C74DE7AFFFF4398E1CFD5F0FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFA3B5E758CDF52B81DD1676DC72FCFF4390DFE5E9F7FF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB385EA
+              57D1F7287CD90F72DB71FFFF4DA9E8ECB4F9FF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FF96A7E258D3F72883D90F5FCF6EFFFF50
+              AFE8B0B7E5FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FF9EADE456D8F8277CD40E56C86DF6FF2DBEF10E85DD1777D7167EDB166D
+              D17C92D8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF97A7E356DAF92781D40D
+              97E30ECFFC0ECCFA0ECFFB0ED0FB0ED5FF12BAF45279D1FF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFB486EA56CCF31ED1FA09BCF411BFF410BFF310BFF310BE
+              F30FD3FD05B2EFA2B4E6FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF6486D72C
+              E3FD09C1F311C4F410C4F40EC8F60ECCF70BC1F30BDCFE3581D6FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF4A95DF1DE4FD0CC5F311C7F40ED1F70FC2F139C6
+              F01FD6F805D1F843BBEBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4491DF1A
+              E8FD00C5F206CEF600C2F27AAAE5FF00FF8CC9ED64F4FF70CFEFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF6390DC44F3FE0ECFF429DAF635D8F5FF00FFFF00
+              FF9DC4EA63FFFF7EBDE8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFBFCEF0AD
+              EBF972F8FF5DE3F65EEAF964D7F384E4F559F1FC5CE8F8B1C6EDFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FF9EB9E99DE5F77DF9FD63F2FC60F7FD66FC
+              FE77EBF9A7C3EBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFCDDAF39CC1EC8AC7EC85C5EB9CC8EDB3C5EDFF00FFFF00FF
+            }
+            OnClick = sbSearchListerSellerAccountClick
+          end
+          object ebHashLock: TEdit
+            Left = 280
+            Height = 21
+            Top = 112
+            Width = 186
+            TabOrder = 8
+          end
+          object btnHashLock: TSpeedButton
+            Left = 470
+            Height = 22
+            Top = 112
+            Width = 23
+            Glyph.Data = {
+              36030000424D3603000000000000360000002800000010000000100000000100
+              18000000000000030000120B0000120B00000000000000000000FF00FF53B6F0
+              4A95DFC6CEEDFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FF1B59D3298DEA7AFDFF47A2E6C1C9ECFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF3F9BE4207EE2
+              1C74DE7AFFFF4398E1CFD5F0FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFA3B5E758CDF52B81DD1676DC72FCFF4390DFE5E9F7FF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB385EA
+              57D1F7287CD90F72DB71FFFF4DA9E8ECB4F9FF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FF96A7E258D3F72883D90F5FCF6EFFFF50
+              AFE8B0B7E5FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FF9EADE456D8F8277CD40E56C86DF6FF2DBEF10E85DD1777D7167EDB166D
+              D17C92D8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF97A7E356DAF92781D40D
+              97E30ECFFC0ECCFA0ECFFB0ED0FB0ED5FF12BAF45279D1FF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFB486EA56CCF31ED1FA09BCF411BFF410BFF310BFF310BE
+              F30FD3FD05B2EFA2B4E6FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF6486D72C
+              E3FD09C1F311C4F410C4F40EC8F60ECCF70BC1F30BDCFE3581D6FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF4A95DF1DE4FD0CC5F311C7F40ED1F70FC2F139C6
+              F01FD6F805D1F843BBEBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4491DF1A
+              E8FD00C5F206CEF600C2F27AAAE5FF00FF8CC9ED64F4FF70CFEFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF6390DC44F3FE0ECFF429DAF635D8F5FF00FFFF00
+              FF9DC4EA63FFFF7EBDE8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFBFCEF0AD
+              EBF972F8FF5DE3F65EEAF964D7F384E4F559F1FC5CE8F8B1C6EDFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FF9EB9E99DE5F77DF9FD63F2FC60F7FD66FC
+              FE77EBF9A7C3EBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFCDDAF39CC1EC8AC7EC85C5EB9CC8EDB3C5EDFF00FFFF00FF
+            }
+            OnClick = sbSearchListerSellerAccountClick
+          end
+          object lblHashLock: TLabel
+            Left = 222
+            Height = 13
+            Top = 115
+            Width = 49
+            Alignment = taRightJustify
+            Caption = 'Hash-Lock'
+            ParentColor = False
           end
           end
         end
         end
         object tsDelist: TTabSheet
         object tsDelist: TTabSheet
@@ -680,8 +778,8 @@ object FRMOperation: TFRMOperation
             Left = 204
             Left = 204
             Height = 13
             Height = 13
             Top = 61
             Top = 61
-            Width = 231
-            Caption = 'excessive amount will remain on bought account'
+            Width = 234
+            Caption = 'any over-payment will remain on bought account'
             Font.Color = clGray
             Font.Color = clGray
             Font.Height = -11
             Font.Height = -11
             Font.Name = 'Tahoma'
             Font.Name = 'Tahoma'
@@ -833,6 +931,7 @@ object FRMOperation: TFRMOperation
             Top = 29
             Top = 29
             Width = 258
             Width = 258
             TabOrder = 0
             TabOrder = 0
+            TextHint = 'Account name (null or 3..64 chars)'
           end
           end
           object lblChangeType: TLabel
           object lblChangeType: TLabel
             Left = 13
             Left = 13
@@ -850,11 +949,30 @@ object FRMOperation: TFRMOperation
             Top = 56
             Top = 56
             Width = 76
             Width = 76
             TabOrder = 1
             TabOrder = 1
+            TextHint = '0..65535'
+          end
+          object lblChangeAccountData: TLabel
+            Left = 13
+            Height = 13
+            Top = 88
+            Width = 100
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Change Data'
+            ParentColor = False
+          end
+          object ebChangeAccountData: TEdit
+            Left = 118
+            Height = 21
+            Top = 84
+            Width = 386
+            TabOrder = 2
+            TextHint = 'Hexadecimal value (0..32 bytes)'
           end
           end
         end
         end
       end
       end
       object lblSignerAccount: TLabel
       object lblSignerAccount: TLabel
-        Left = 176
+        Left = 278
         Height = 13
         Height = 13
         Top = 188
         Top = 188
         Width = 131
         Width = 131
@@ -862,14 +980,14 @@ object FRMOperation: TFRMOperation
         ParentColor = False
         ParentColor = False
       end
       end
       object ebSignerAccount: TEdit
       object ebSignerAccount: TEdit
-        Left = 325
+        Left = 427
         Height = 21
         Height = 21
         Top = 184
         Top = 184
         Width = 82
         Width = 82
         TabOrder = 3
         TabOrder = 3
       end
       end
       object sbSearchSignerAccount: TSpeedButton
       object sbSearchSignerAccount: TSpeedButton
-        Left = 411
+        Left = 513
         Height = 22
         Height = 22
         Top = 184
         Top = 184
         Width = 23
         Width = 23

+ 99 - 26
src/gui-classic/UFRMOperation.pas

@@ -130,6 +130,8 @@ type
     btnHashLock: TSpeedButton;
     btnHashLock: TSpeedButton;
     sbTimeLock: TSpeedButton;
     sbTimeLock: TSpeedButton;
     cbPayloadAsHex: TCheckBox;
     cbPayloadAsHex: TCheckBox;
+    lblChangeAccountData: TLabel;
+    ebChangeAccountData: TEdit;
     procedure ebNewPublicKeyExit(Sender: TObject);
     procedure ebNewPublicKeyExit(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -150,11 +152,12 @@ type
     procedure bbBuyNewKeyClick(Sender: TObject);
     procedure bbBuyNewKeyClick(Sender: TObject);
     procedure ebAccountNumberExit(Sender: TObject);
     procedure ebAccountNumberExit(Sender: TObject);
     procedure ebCurrencyExit(Sender: TObject);
     procedure ebCurrencyExit(Sender: TObject);
+    procedure sbTimeLockClick(Sender: TObject);
   private
   private
     FNode : TNode;
     FNode : TNode;
     FWalletKeys: TWalletKeys;
     FWalletKeys: TWalletKeys;
     FDefaultFee: Int64;
     FDefaultFee: Int64;
-    FEncodedPayload : TRawBytes;
+    FEncodedPayload : TOperationPayload;
     FDisabled : Boolean;
     FDisabled : Boolean;
     FSenderAccounts: TOrderedCardinalList; // TODO: TOrderedCardinalList should be replaced with a "TCardinalList" since signer account should be processed last
     FSenderAccounts: TOrderedCardinalList; // TODO: TOrderedCardinalList should be replaced with a "TCardinalList" since signer account should be processed last
     procedure SetWalletKeys(const Value: TWalletKeys);
     procedure SetWalletKeys(const Value: TWalletKeys);
@@ -169,7 +172,7 @@ type
     Function UpdateOpListAccount(Const TargetAccount : TAccount; var SalePrice : Int64; var SellerAccount,SignerAccount : TAccount; var NewOwnerPublicKey : TAccountKey; var LockedUntilBlock : Cardinal; var HashLock : T32Bytes; var errors : String) : Boolean;
     Function UpdateOpListAccount(Const TargetAccount : TAccount; var SalePrice : Int64; var SellerAccount,SignerAccount : TAccount; var NewOwnerPublicKey : TAccountKey; var LockedUntilBlock : Cardinal; var HashLock : T32Bytes; var errors : String) : Boolean;
     Function UpdateOpDelist(Const TargetAccount : TAccount; var SignerAccount : TAccount; var errors : String) : Boolean;
     Function UpdateOpDelist(Const TargetAccount : TAccount; var SignerAccount : TAccount; var errors : String) : Boolean;
     Function UpdateOpBuyAccount(Const SenderAccount : TAccount; var AccountToBuy : TAccount; var amount : Int64; var NewPublicKey : TAccountKey; var ARecipientSigned : Boolean; var errors : String) : Boolean;
     Function UpdateOpBuyAccount(Const SenderAccount : TAccount; var AccountToBuy : TAccount; var amount : Int64; var NewPublicKey : TAccountKey; var ARecipientSigned : Boolean; var errors : String) : Boolean;
-    Function UpdateOpChangeInfo(Const TargetAccount : TAccount; var SignerAccount : TAccount; var changeName : Boolean; var newName : TRawBytes; var changeType : Boolean; var newType : Word; var errors : String) : Boolean;
+    Function UpdateOpChangeInfo(Const TargetAccount : TAccount; var SignerAccount : TAccount; var changeName : Boolean; var newName : TRawBytes; var changeType : Boolean; var newType : Word; var AChangeData : Boolean; var ANewData : TRawBytes; var errors : String) : Boolean;
     procedure SetDefaultFee(const Value: Int64);
     procedure SetDefaultFee(const Value: Int64);
     Procedure OnSenderAccountsChanged(Sender : TObject);
     Procedure OnSenderAccountsChanged(Sender : TObject);
     procedure OnWalletKeysChanged(Sender : TObject);
     procedure OnWalletKeysChanged(Sender : TObject);
@@ -213,9 +216,9 @@ Var errors : String;
   dooperation : Boolean;
   dooperation : Boolean;
   _newOwnerPublicKey : TECDSA_Public;
   _newOwnerPublicKey : TECDSA_Public;
   LHashLock : T32Bytes;
   LHashLock : T32Bytes;
-  _newName : TRawBytes;
+  _newName, LNewAccountData : TRawBytes;
   _newType : Word;
   _newType : Word;
-  _changeName, _changeType, _V2, _executeSigner, LRecipientSigned : Boolean;
+  _changeName, _changeType, LChangeAccountData, _V2, _executeSigner, LRecipientSigned : Boolean;
   _senderAccounts : TCardinalsArray;
   _senderAccounts : TCardinalsArray;
 label loop_start;
 label loop_start;
 begin
 begin
@@ -241,8 +244,18 @@ loop_start:
       account := FNode.GetMempoolAccount(_senderAccounts[iAcc]);
       account := FNode.GetMempoolAccount(_senderAccounts[iAcc]);
       If Not UpdatePayload(account, errors) then
       If Not UpdatePayload(account, errors) then
         raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
         raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
-      if NOT WalletKeys.TryGetKey(account.accountInfo.accountKey, LKey) then
-        Raise Exception.Create('Sender account private key not found in Wallet');
+      if NOT WalletKeys.TryGetKey(account.accountInfo.accountKey, LKey) then begin
+
+        if  (
+             (TAccountComp.IsAccountForPrivateSale(account.accountInfo)) or
+             (TAccountComp.IsAccountForAccountSwap(account.accountInfo))
+             )
+            and (Not TAccountComp.IsNullAccountKey(account.accountInfo.new_publicKey)) then begin
+
+          if NOT WalletKeys.TryGetKey(account.accountInfo.new_publicKey, LKey) then
+            Raise Exception.Create('New sender account private key not found in Wallet');
+        end else Raise Exception.Create('Sender account private key not found in Wallet');
+      end;
       dooperation := true;
       dooperation := true;
       // Default fee
       // Default fee
       if account.balance > uint64(DefaultFee) then _fee := DefaultFee else _fee := account.balance;
       if account.balance > uint64(DefaultFee) then _fee := DefaultFee else _fee := account.balance;
@@ -299,13 +312,17 @@ loop_start:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         else _fee := signerAccount.balance;
         if (rbListAccountForPublicSale.Checked) then begin
         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
         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
         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
         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,CT_TECDSA_Public_Nul,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end else raise Exception.Create('Select Sale type');
         end else raise Exception.Create('Select Sale type');
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
@@ -326,13 +343,15 @@ loop_start:
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
         {%region Operation: Change Info}
         {%region Operation: Change Info}
-        if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,errors) then begin
+        if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,LChangeAccountData,LNewAccountData,errors) then begin
           If Length(_senderAccounts)=1 then raise Exception.Create(errors);
           If Length(_senderAccounts)=1 then raise Exception.Create(errors);
         end else begin
         end else begin
           if signerAccount.balance>DefaultFee then _fee := DefaultFee
           if signerAccount.balance>DefaultFee then _fee := DefaultFee
           else _fee := signerAccount.balance;
           else _fee := signerAccount.balance;
           op := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1,account.account,LKey.PrivateKey,false,CT_TECDSA_Public_Nul,
           op := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1,account.account,LKey.PrivateKey,false,CT_TECDSA_Public_Nul,
-             _changeName,_newName,_changeType,_newType,_fee,FEncodedPayload);
+             _changeName,_newName,_changeType,_newType,
+             LChangeAccountData,LNewAccountData,
+             _fee,FEncodedPayload);
         end;
         end;
         {%endregion}
         {%endregion}
       end else begin
       end else begin
@@ -576,11 +595,13 @@ begin
   //
   //
   ebChangeName.OnChange:=updateInfoClick;
   ebChangeName.OnChange:=updateInfoClick;
   ebChangeType.OnChange:=updateInfoClick;
   ebChangeType.OnChange:=updateInfoClick;
+  ebChangeAccountData.OnChange:=updateInfoClick;
   //
   //
   sbSearchDestinationAccount.OnClick := sbSearchDestinationAccountClick;
   sbSearchDestinationAccount.OnClick := sbSearchDestinationAccountClick;
   sbSearchListerSellerAccount.OnClick := sbSearchListerSellerAccountClick;
   sbSearchListerSellerAccount.OnClick := sbSearchListerSellerAccountClick;
   btnHashLock.OnClick := sbHashLockClick;
   btnHashLock.OnClick := sbHashLockClick;
   sbSearchBuyAccount.OnClick := sbSearchBuyAccountClick;
   sbSearchBuyAccount.OnClick := sbSearchBuyAccountClick;
+  sbTimeLock.OnClick := sbTimeLockClick;
   //
   //
   ebFee.Text := TAccountComp.FormatMoney(0);
   ebFee.Text := TAccountComp.FormatMoney(0);
   ebFee.OnExit:= ebCurrencyExit;
   ebFee.OnExit:= ebCurrencyExit;
@@ -659,10 +680,12 @@ begin
     ebSignerAccount.text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccounts.Get(0));
     ebSignerAccount.text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccounts.Get(0));
     ebChangeName.Text := FNode.GetMempoolAccount(SenderAccounts.Get(0)).name.ToPrintable;
     ebChangeName.Text := FNode.GetMempoolAccount(SenderAccounts.Get(0)).name.ToPrintable;
     ebChangeType.Text := IntToStr(FNode.GetMempoolAccount(SenderAccounts.Get(0)).account_type);
     ebChangeType.Text := IntToStr(FNode.GetMempoolAccount(SenderAccounts.Get(0)).account_type);
+    ebChangeAccountData.Text := FNode.GetMempoolAccount(SenderAccounts.Get(0)).account_data.ToHexaString;
   end else begin
   end else begin
     ebSignerAccount.text := '';
     ebSignerAccount.text := '';
     ebChangeName.Text := '';
     ebChangeName.Text := '';
     ebChangeType.Text := '';
     ebChangeType.Text := '';
+    ebChangeAccountData.Text := '';
   end;
   end;
   UpdateAccountsInfo;
   UpdateAccountsInfo;
   UpdateOperationOptions(errors);
   UpdateOperationOptions(errors);
@@ -699,6 +722,23 @@ begin
   searchAccount(ebSignerAccount);
   searchAccount(ebSignerAccount);
 end;
 end;
 
 
+procedure TFRMOperation.sbTimeLockClick(Sender: TObject);
+begin
+  Application.MessageBox(PChar(Format('Current block is %d'
+    +#10
+    +#10+'Average seconds per block %d'
+    +#10
+    +#10+'Average Blocks per day %d (Next 24 hours = block %d)'
+    +#10+'Average Blocks per week %d (Next 24*7 hours = block %d)'
+    ,[TNode.Node.Bank.BlocksCount,
+      CT_NewLineSecondsAvg,
+     (86400 DIV CT_NewLineSecondsAvg),
+     ((86400 DIV CT_NewLineSecondsAvg) + TNode.Node.Bank.BlocksCount),
+     ((86400 DIV CT_NewLineSecondsAvg)*7),
+     (((86400 DIV CT_NewLineSecondsAvg)*7) + TNode.Node.Bank.BlocksCount)])),
+    PChar(Application.Title),MB_OK);
+end;
+
 procedure TFRMOperation.sbHashLockClick(Sender: TObject);
 procedure TFRMOperation.sbHashLockClick(Sender: TObject);
 var
 var
   LFRM : TFRMHashLock;
   LFRM : TFRMHashLock;
@@ -834,25 +874,33 @@ begin
       exit;
       exit;
     end;
     end;
     AccountToBuy := FNode.GetMempoolAccount(c);
     AccountToBuy := FNode.GetMempoolAccount(c);
-    ARecipientSigned := TAccountComp.IsOperationRecipientSignable(SenderAccount, AccountToBuy, Amount, FNode.Bank.BlocksCount);
+    ARecipientSigned := TAccountComp.IsOperationRecipientSignable(SenderAccount, AccountToBuy, FNode.Bank.BlocksCount, FNode.Bank.SafeBox.CurrentProtocol);
     if (SenderAccount.account = AccountToBuy.Account) AND (NOT ARecipientSigned) then begin
     if (SenderAccount.account = AccountToBuy.Account) AND (NOT ARecipientSigned) then begin
       errors := 'Not recipient signable';
       errors := 'Not recipient signable';
       exit;
       exit;
     end;
     end;
 
 
-    If not TAccountComp.IsAccountForSaleOrSwap(AccountToBuy.accountInfo, FNode.Bank.BlocksCount) then begin
+    If not TAccountComp.IsAccountForSaleOrSwap(AccountToBuy.accountInfo) then begin
       errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(c)+' is not for sale or swap';
       errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(c)+' is not for sale or swap';
       exit;
       exit;
+    end else if (TAccountComp.IsAccountForCoinSwap(AccountToBuy.accountInfo)) then begin
+      errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(c)+' is for '+TAccountComp.FormatMoney(AccountToBuy.accountInfo.price)+' COIN swap, cannot buy';
+      exit;
     end;
     end;
+
     If Not TAccountComp.TxtToMoney(ebBuyAmount.Text,amount) then begin
     If Not TAccountComp.TxtToMoney(ebBuyAmount.Text,amount) then begin
       errors := 'Invalid amount value';
       errors := 'Invalid amount value';
       exit;
       exit;
     end;
     end;
-     if (AccountToBuy.accountInfo.price>amount) AND (NOT TAccountComp.IsAccountForCoinSwap(AccountToBuy.accountInfo, FNode.Bank.BlocksCount)) then begin
-      errors := 'Account price '+TAccountComp.FormatMoney(AccountToBuy.accountInfo.price);
+    if (AccountToBuy.accountInfo.price>amount) AND (NOT ARecipientSigned) then begin
+      errors := Format('Account price %s < (amount:%s + balance:%s = %s)',[TAccountComp.FormatMoney(AccountToBuy.accountInfo.price),
+        TAccountComp.FormatMoney(amount),
+        TAccountComp.FormatMoney(AccountToBuy.balance),
+        TAccountComp.FormatMoney(amount + AccountToBuy.balance)
+        ]);
       exit;
       exit;
     end;
     end;
-    if TAccountComp.IsAccountForSale(AccountToBuy.accountInfo, FNode.Bank.BlocksCount) AND (amount+DefaultFee > SenderAccount.balance) then begin
+    if TAccountComp.IsAccountForSale(AccountToBuy.accountInfo) AND (amount+DefaultFee > SenderAccount.balance) then begin
       errors := 'Insufficient funds';
       errors := 'Insufficient funds';
       exit;
       exit;
     end;
     end;
@@ -874,7 +922,8 @@ begin
 end;
 end;
 
 
 function TFRMOperation.UpdateOpChangeInfo(const TargetAccount: TAccount; var SignerAccount : TAccount;
 function TFRMOperation.UpdateOpChangeInfo(const TargetAccount: TAccount; var SignerAccount : TAccount;
-   var changeName : Boolean; var newName: TRawBytes; var changeType : Boolean; var newType: Word; var errors: String): Boolean;
+   var changeName : Boolean; var newName: TRawBytes; var changeType : Boolean; var newType: Word;
+   var AChangeData : Boolean; var ANewData : TRawBytes; var errors: String): Boolean;
 var auxC : Cardinal;
 var auxC : Cardinal;
   i : Integer;
   i : Integer;
   errCode : Integer;
   errCode : Integer;
@@ -939,8 +988,21 @@ begin
     end;
     end;
     changeType := TargetAccount.account_type<>newType;
     changeType := TargetAccount.account_type<>newType;
     //
     //
-    If (SenderAccounts.Count=1) And (newName=TargetAccount.name) And (newType=TargetAccount.account_type) then begin
-      errors := 'Account name and type are the same. Not changed';
+    if FNode.Bank.SafeBox.CurrentProtocol>=CT_PROTOCOL_5 then begin
+      // Allow Change Account.Data PIP-0024
+      if Not TCrypto.HexaToRaw(ebChangeAccountData.Text,ANewData) then begin
+        errors := 'Invalid hexadecimal value at Data';
+        Exit;
+      end;
+      AChangeData := Not TBaseType.Equals( TargetAccount.account_data , ANewData);
+      if Length(ANewData)>CT_MaxAccountDataSize then begin
+        errors := Format('Data size (%d) greater than %d',[Length(ANewData),CT_MaxAccountDataSize]);
+        Exit;
+      end;
+    end;
+    If (SenderAccounts.Count=1) And (TBaseType.Equals(newName,TargetAccount.name)) And (newType=TargetAccount.account_type)
+      And (TBaseType.Equals(ANewData,TargetAccount.account_data)) then begin
+      errors := 'No changes on fields. Not changed';
       Exit;
       Exit;
     end;
     end;
   finally
   finally
@@ -1029,7 +1091,7 @@ begin
   Result := false;
   Result := false;
   if not (PageControlOpType.ActivePage=tsDelistAccount) then exit;
   if not (PageControlOpType.ActivePage=tsDelistAccount) then exit;
   try
   try
-    if Not TAccountComp.IsAccountForSaleOrSwap(TargetAccount.accountInfo, FNode.Bank.BlocksCount) then begin
+    if Not TAccountComp.IsAccountForSaleOrSwap(TargetAccount.accountInfo) then begin
       errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(TargetAccount.account)+' is not for sale or swap';
       errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(TargetAccount.account)+' is not for sale or swap';
       exit;
       exit;
     end;
     end;
@@ -1081,8 +1143,8 @@ Var
   LHashLock : T32Bytes;
   LHashLock : T32Bytes;
   salePrice, amount : Int64;
   salePrice, amount : Int64;
   auxC : Cardinal;
   auxC : Cardinal;
-  changeName,changeType, LRecipientSigned : Boolean;
-  newName : TRawBytes;
+  changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
+  newName, LNewAccountData : TRawBytes;
   newType : Word;
   newType : Word;
 begin
 begin
   Result := false;
   Result := false;
@@ -1105,6 +1167,13 @@ begin
       for iAcc := 0 to SenderAccounts.Count - 1 do begin
       for iAcc := 0 to SenderAccounts.Count - 1 do begin
         sender_account := TNode.Node.Bank.SafeBox.Account(SenderAccounts.Get(iAcc));
         sender_account := TNode.Node.Bank.SafeBox.Account(SenderAccounts.Get(iAcc));
         iWallet := WalletKeys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
         iWallet := WalletKeys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
+        if (iWallet<0) and (
+             (TAccountComp.IsAccountForPrivateSale(sender_account.accountInfo)) or
+             (TAccountComp.IsAccountForAccountSwap(sender_account.accountInfo))
+             )
+            and (Not TAccountComp.IsNullAccountKey(sender_account.accountInfo.new_publicKey)) then begin
+          iWallet := WalletKeys.IndexOfAccountKey(sender_account.accountInfo.new_publicKey);
+        end;
         if (iWallet<0) then begin
         if (iWallet<0) then begin
           errors := 'Private key of account '+TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account)+' not found in wallet';
           errors := 'Private key of account '+TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account)+' not found in wallet';
           lblGlobalErrors.Caption := errors;
           lblGlobalErrors.Caption := errors;
@@ -1152,7 +1221,7 @@ begin
   end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
   end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
     Result := UpdateOpBuyAccount(GetDefaultSenderAccount,account_to_buy,amount,publicKey,LRecipientSigned, errors);
     Result := UpdateOpBuyAccount(GetDefaultSenderAccount,account_to_buy,amount,publicKey,LRecipientSigned, errors);
   end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
   end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
-    Result := UpdateOpChangeInfo(GetDefaultSenderAccount,signer_account,changeName,newName,changeType,newType,errors);
+    Result := UpdateOpChangeInfo(GetDefaultSenderAccount,signer_account,changeName,newName,changeType,newType,LChangeAccountData,LNewAccountData,errors);
   end else begin
   end else begin
     errors := 'Must select an operation';
     errors := 'Must select an operation';
   end;
   end;
@@ -1492,7 +1561,11 @@ Var payload_u : AnsiString;
 begin
 begin
   valid := false;
   valid := false;
   payload_encrypted := Nil;
   payload_encrypted := Nil;
-  FEncodedPayload := Nil;
+  FEncodedPayload := CT_TOperationPayload_NUL;
+  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+  // TODO:
+  // Needs to assign FEncodedPayload.payload_type based on PIP-0027
+  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   errors := 'Unknown error';
   errors := 'Unknown error';
   payload_u := memoPayload.Lines.Text;
   payload_u := memoPayload.Lines.Text;
   try
   try
@@ -1603,7 +1676,7 @@ begin
       lblEncryptionErrors.Caption := errors;
       lblEncryptionErrors.Caption := errors;
       lblPayloadLength.Caption := Format('(%db -> ?)',[length(payload_u)]);
       lblPayloadLength.Caption := Format('(%db -> ?)',[length(payload_u)]);
     end;
     end;
-    FEncodedPayload := payload_encrypted;
+    FEncodedPayload.payload_raw := payload_encrypted;
     Result := valid;
     Result := valid;
   end;
   end;
 end;
 end;

+ 1 - 1
src/gui-classic/UFRMOperationsExplorer.pas

@@ -547,7 +547,7 @@ begin
   Try
   Try
     ms.Write(raw[1],Length(raw));
     ms.Write(raw[1],Length(raw));
     ms.Position:=0;
     ms.Position:=0;
-    If Not opht.LoadOperationsHashTreeFromStream(ms,false,0,Nil,errors) then Raise Exception.Create(errors);
+    If Not opht.LoadOperationsHashTreeFromStream(ms,false,TNode.Node.Bank.SafeBox.CurrentProtocol,TNode.Node.Bank.SafeBox.CurrentProtocol,Nil,errors) then Raise Exception.Create(errors);
     For i:=0 to opht.OperationsCount-1 do begin
     For i:=0 to opht.OperationsCount-1 do begin
       FOperationsHashTree.AddOperationToHashTree(opht.GetOperation(i));
       FOperationsHashTree.AddOperationToHashTree(opht.GetOperation(i));
     end;
     end;

+ 3 - 3
src/gui-classic/UFRMPascalCoinWalletConfig.pas

@@ -94,7 +94,7 @@ type
 
 
 implementation
 implementation
 
 
-uses UConst, UAccounts, ULog, UCrypto, UFolderHelper, USettings, UGUIUtils, UNetProtocol;
+uses UConst, UAccounts, ULog, UCrypto, UNode, USettings, UGUIUtils, UNetProtocol;
 
 
 {$IFnDEF FPC}
 {$IFnDEF FPC}
   {$R *.dfm}
   {$R *.dfm}
@@ -148,9 +148,9 @@ end;
 procedure TFRMPascalCoinWalletConfig.bbOpenDataFolderClick(Sender: TObject);
 procedure TFRMPascalCoinWalletConfig.bbOpenDataFolderClick(Sender: TObject);
 begin
 begin
   {$IFDEF FPC}
   {$IFDEF FPC}
-  OpenDocument(pchar(TFolderHelper.GetPascalCoinDataFolder))
+  OpenDocument(pchar(TNode.GetPascalCoinDataFolder))
   {$ELSE}
   {$ELSE}
-  shellexecute(0, 'open', pchar(TFolderHelper.GetPascalCoinDataFolder), nil, nil, SW_SHOW)
+  shellexecute(0, 'open', pchar(TNode.GetPascalCoinDataFolder), nil, nil, SW_SHOW)
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 

+ 2 - 1
src/gui-classic/UFRMPayloadDecoder.dfm

@@ -191,6 +191,8 @@ object FRMPayloadDecoder: TFRMPayloadDecoder
     object tsDecoded: TTabSheet
     object tsDecoded: TTabSheet
       Caption = 'Payload'
       Caption = 'Payload'
       ExplicitLeft = 0
       ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
       ExplicitHeight = 178
       ExplicitHeight = 178
       object Label7: TLabel
       object Label7: TLabel
         Left = 15
         Left = 15
@@ -270,7 +272,6 @@ object FRMPayloadDecoder: TFRMPayloadDecoder
     object tsDecodeMethods: TTabSheet
     object tsDecodeMethods: TTabSheet
       Caption = 'Decode methods'
       Caption = 'Decode methods'
       ImageIndex = 1
       ImageIndex = 1
-      ExplicitHeight = 161
       object lblPasswordsInfo: TLabel
       object lblPasswordsInfo: TLabel
         Left = 235
         Left = 235
         Top = 143
         Top = 143

+ 6 - 2
src/gui-classic/UFRMPayloadDecoder.pas

@@ -338,7 +338,7 @@ begin
     else if Value.Fee=0 then lblFee.Font.Color := clGray
     else if Value.Fee=0 then lblFee.Font.Color := clGray
     else lblFee.Font.Color := clRed;
     else lblFee.Font.Color := clRed;
     ebOpHash.text := TCrypto.ToHexaString(Value.OperationHash);
     ebOpHash.text := TCrypto.ToHexaString(Value.OperationHash);
-    memoOriginalPayloadInHexa.Lines.Text := TCrypto.ToHexaString(Value.OriginalPayload);
+    memoOriginalPayloadInHexa.Lines.Text := TCrypto.ToHexaString(Value.OriginalPayload.payload_raw);
     if Assigned(FWalletKeys) then begin
     if Assigned(FWalletKeys) then begin
       cbMethodPublicPayload.Checked := FAppParams.ParamByName['PayloadDecoder.notencrypted'].GetAsBoolean(true);
       cbMethodPublicPayload.Checked := FAppParams.ParamByName['PayloadDecoder.notencrypted'].GetAsBoolean(true);
       cbUsingPrivateKeys.Checked := FAppParams.ParamByName['PayloadDecoder.usingprivatekeys'].GetAsBoolean(true);
       cbUsingPrivateKeys.Checked := FAppParams.ParamByName['PayloadDecoder.usingprivatekeys'].GetAsBoolean(true);
@@ -401,8 +401,12 @@ Var raw : TRawBytes;
   ok : boolean;
   ok : boolean;
 begin
 begin
   ok := true;
   ok := true;
+  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+  // TODO:
+  // Needs to check valid .payload_type based on PIP-0027
+  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   if Assigned(FWalletKeys) And Assigned(FAppParams) then begin
   if Assigned(FWalletKeys) And Assigned(FAppParams) then begin
-    raw := FOpResume.OriginalPayload;
+    raw := FOpResume.OriginalPayload.payload_raw;
     if Length(raw)>0 then begin
     if Length(raw)>0 then begin
       // First try to a human readable...
       // First try to a human readable...
       if (cbMethodPublicPayload.Checked) and (TCrypto.IsHumanReadable(raw)) then begin
       if (cbMethodPublicPayload.Checked) and (TCrypto.IsHumanReadable(raw)) then begin

+ 2 - 8
src/gui-classic/UFRMRPCCalls.dfm

@@ -128,15 +128,11 @@ object FRMRPCCalls: TFRMRPCCalls
       end
       end
       object tsJSONParams: TTabSheet
       object tsJSONParams: TTabSheet
         Caption = 'Params as JSON'
         Caption = 'Params as JSON'
-        ExplicitLeft = 0
-        ExplicitTop = 0
-        ExplicitWidth = 0
-        ExplicitHeight = 0
         object mJSONParams: TMemo
         object mJSONParams: TMemo
           Left = 0
           Left = 0
           Top = 0
           Top = 0
-          Width = 238
-          Height = 172
+          Width = 281
+          Height = 194
           Align = alClient
           Align = alClient
           ScrollBars = ssVertical
           ScrollBars = ssVertical
           TabOrder = 0
           TabOrder = 0
@@ -151,7 +147,6 @@ object FRMRPCCalls: TFRMRPCCalls
       Anchors = [akLeft, akBottom]
       Anchors = [akLeft, akBottom]
       Caption = '&Send'
       Caption = '&Send'
       Default = True
       Default = True
-      DoubleBuffered = True
       Glyph.Data = {
       Glyph.Data = {
         36040000424D3604000000000000360000002800000010000000100000000100
         36040000424D3604000000000000360000002800000010000000100000000100
         2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
         2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
@@ -187,7 +182,6 @@ object FRMRPCCalls: TFRMRPCCalls
         FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
         FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
         FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
         FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
         FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00}
         FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00}
-      ParentDoubleBuffered = False
       TabOrder = 3
       TabOrder = 3
       OnClick = bbSendCommandClick
       OnClick = bbSendCommandClick
     end
     end

+ 2 - 0
src/gui-classic/UFRMRPCCalls.pas

@@ -153,7 +153,9 @@ end;
 
 
 procedure TFRMRPCCalls.ShowCallInfo(infoType: TInfoType; value: String);
 procedure TFRMRPCCalls.ShowCallInfo(infoType: TInfoType; value: String);
 begin
 begin
+  mCalls.Lines.BeginUpdate;
   mCalls.Lines.Add(Format('%s [%s] %s',[FormatDateTime('hh:nn:ss.zzz',Now),CT_TIntoType_Str[infoType],value]));
   mCalls.Lines.Add(Format('%s [%s] %s',[FormatDateTime('hh:nn:ss.zzz',Now),CT_TIntoType_Str[infoType],value]));
+  mCalls.Lines.EndUpdate;
 end;
 end;
 
 
 procedure TFRMRPCCalls.DoSendJSON(json: TPCJSONObject);
 procedure TFRMRPCCalls.DoSendJSON(json: TPCJSONObject);

+ 44 - 0
src/gui-classic/UFRMRandomHashUtil.dfm

@@ -0,0 +1,44 @@
+object FRMDiagnosticTool: TFRMDiagnosticTool
+  Left = 0
+  Top = 0
+  Caption = 'FRMDiagnosticTool'
+  ClientHeight = 324
+  ClientWidth = 570
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  DesignSize = (
+    570
+    324)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object txtLog: TEdit
+    Left = 8
+    Top = 39
+    Width = 554
+    Height = 21
+    Anchors = [akLeft, akTop, akRight, akBottom]
+    TabOrder = 0
+    Text = 'txtLog'
+  end
+  object btnRH: TButton
+    Left = 8
+    Top = 8
+    Width = 105
+    Height = 25
+    Caption = 'Random Hash'
+    TabOrder = 1
+  end
+  object btnRH2: TButton
+    Left = 119
+    Top = 8
+    Width = 98
+    Height = 25
+    Caption = 'Random Hash 2'
+    TabOrder = 2
+  end
+end

+ 127 - 0
src/gui-classic/UFRMRandomHashUtil.pas

@@ -0,0 +1,127 @@
+unit UFRMRandomHashUtil;
+
+interface
+
+uses
+  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
+  System.TimeSpan, UThread, UMemory, URandomHash, URandomHash2,
+  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
+
+type
+  TFRMDiagnosticTool = class(TForm)
+    txtLog: TEdit;
+    btnRH: TButton;
+    btnRH2: TButton;
+  private
+    { Private declarations }
+    TRHThread : TPCThread;
+    TRH2Thread : TPCThread;
+  public
+    { Public declarations }
+  end;
+
+  TRandomHashNotify  = procedure (ATotalHashes : UInt32; const ATimeSpan : TTimeSpan) of object;
+
+  TAlgorithmThread = class(TPCThread)
+    private
+      FDisposables : TDisposables;
+      FNotifyHashCount : UInt32;
+      FNotifyDuration : TTimeSpan;
+      FNotify : TRandomHashNotify;
+      procedure ThreadSafeNotify;
+    protected
+      FLastHash : TBytes;
+      procedure BCExecute; override;
+      function NextRound : TBytes; virtual; abstract;
+    public
+      constructor Create; virtual;
+  end;
+
+  TRandomHashThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+  TRandomHash2Thread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+implementation
+
+uses UBaseTypes;
+
+{$R *.dfm}
+
+{ TAlgorithmThread }
+
+constructor TAlgorithmThread.Create;
+begin
+  Inherited Create(True);
+end;
+
+procedure TAlgorithmThread.BCExecute;
+var
+ LTC : TTickCount;
+ LStartTime : TDateTime;
+ LTotalHashes : UInt32;
+begin
+  LTotalHashes := 0;
+  LStartTime := Now;
+  LTC := TPlatform.GetTickCount;
+  while True do begin
+   FLastHash := NextRound;
+   inc(LTotalHashes);
+   if TPlatform.GetElapsedMilliseconds(LTC)>1000 then begin
+     FNotifyDuration := TTimeSpan.Subtract(Now, LStartTime);
+     FNotifyHashCount := LTotalHashes;
+     Queue( ThreadSafeNotify );
+     LTotalHashes := 0;
+     LStartTime := Now;
+     LTC := TPlatform.GetTickCount;
+   end;
+  end;
+end;
+
+procedure TAlgorithmThread.ThreadSafeNotify;
+begin
+  FNotify(FNotifyHashCount, FNotifyDuration);
+end;
+
+{ TRandomHashThread }
+
+constructor TRandomHashThread.Create;
+begin
+  FHasher := TRandomHash.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHashThread.NextRound : TBytes;
+begin
+   Result := FHasher.Hash(FLastHash);
+end;
+
+{ TRandomHash2Thread }
+
+constructor TRandomHash2Thread.Create;
+begin
+  FHasher := TRandomHash2.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2Thread.NextRound : TBytes;
+begin
+   Result := FHasher.Hash(FLastHash);
+end;
+
+{ TFRMDiagnosicTool }
+
+end.

+ 9 - 2
src/gui-classic/UFRMRandomOperations.lfm

@@ -1,11 +1,15 @@
 object FRMRandomOperations: TFRMRandomOperations
 object FRMRandomOperations: TFRMRandomOperations
-  Left = 876
+  Left = 783
   Height = 229
   Height = 229
-  Top = 391
+  Top = 423
   Width = 439
   Width = 439
   Caption = 'Random Operations'
   Caption = 'Random Operations'
   ClientHeight = 229
   ClientHeight = 229
   ClientWidth = 439
   ClientWidth = 439
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
   OnClose = FormClose
   OnClose = FormClose
   OnCloseQuery = FormCloseQuery
   OnCloseQuery = FormCloseQuery
   OnCreate = FormCreate
   OnCreate = FormCreate
@@ -31,12 +35,15 @@ object FRMRandomOperations: TFRMRandomOperations
       Align = alClient
       Align = alClient
       AutoSize = False
       AutoSize = False
       Caption = 'WARNING! This form will generate RANDOM operations using your private keys!!!'
       Caption = 'WARNING! This form will generate RANDOM operations using your private keys!!!'
+      Color = clBtnFace
       Font.Color = clRed
       Font.Color = clRed
       Font.Height = -13
       Font.Height = -13
+      Font.Name = 'Tahoma'
       Font.Style = [fsBold]
       Font.Style = [fsBold]
       Layout = tlCenter
       Layout = tlCenter
       ParentColor = False
       ParentColor = False
       ParentFont = False
       ParentFont = False
+      Transparent = False
       WordWrap = True
       WordWrap = True
     end
     end
   end
   end

+ 270 - 12
src/gui-classic/UFRMRandomOperations.pas

@@ -44,6 +44,7 @@ type
     FOnUpdated: TNotifyEvent;
     FOnUpdated: TNotifyEvent;
     FNeedSanitize : Boolean;
     FNeedSanitize : Boolean;
     FAllowExecute: Boolean;
     FAllowExecute: Boolean;
+    FMaxOperationsPerSecond: Integer;
     procedure OnBankNewBlock(Sender : TObject);
     procedure OnBankNewBlock(Sender : TObject);
   protected
   protected
     FBankNotify : TPCBankNotify;
     FBankNotify : TPCBankNotify;
@@ -56,7 +57,7 @@ type
     FnCallsToAddNodeFailed : Int64;
     FnCallsToAddNodeFailed : Int64;
     procedure BCExecute; override;
     procedure BCExecute; override;
   public
   public
-    Constructor Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt);
+    Constructor Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt; AMaxOperationsPerSecond : Integer);
     Destructor Destroy; override;
     Destructor Destroy; override;
     property LastCall_OperationsTotal : Integer read FLastCall_OperationsTotal;
     property LastCall_OperationsTotal : Integer read FLastCall_OperationsTotal;
     property LastCall_OperationsExecuted : Integer read FLastCall_OperationsExecuted;
     property LastCall_OperationsExecuted : Integer read FLastCall_OperationsExecuted;
@@ -64,6 +65,7 @@ type
     property LastCall_Error : String read FLastCall_Error;
     property LastCall_Error : String read FLastCall_Error;
     property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
     property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
     property AllowExecute : Boolean read FAllowExecute write FAllowExecute;
     property AllowExecute : Boolean read FAllowExecute write FAllowExecute;
+    property MaxOperationsPerSecond : Integer read FMaxOperationsPerSecond write FMaxOperationsPerSecond;
   end;
   end;
 
 
   { TFRMRandomOperations }
   { TFRMRandomOperations }
@@ -80,6 +82,7 @@ type
     procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
     procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
+    procedure cbMaxSpeedModeClick(Sender: TObject);
   private
   private
     FSourceNode: TNode;
     FSourceNode: TNode;
     FSourceWalletKeys: TWalletKeysExt;
     FSourceWalletKeys: TWalletKeysExt;
@@ -87,6 +90,7 @@ type
     FCurrOperationsComp : TPCOperationsComp;
     FCurrOperationsComp : TPCOperationsComp;
     FRandomGeneratorThread : TRandomGeneratorThread;
     FRandomGeneratorThread : TRandomGeneratorThread;
     FInternalLog : TLog;
     FInternalLog : TLog;
+    FMaxOperationsPerSecond : Integer;
     procedure SetSourceNode(AValue: TNode);
     procedure SetSourceNode(AValue: TNode);
     procedure SetSourceWalletKeys(AValue: TWalletKeysExt);
     procedure SetSourceWalletKeys(AValue: TWalletKeysExt);
     procedure NewLog(logTxt : String);
     procedure NewLog(logTxt : String);
@@ -104,11 +108,17 @@ type
 
 
   TRandomGenerateOperation = Class
   TRandomGenerateOperation = Class
   private
   private
+    class function GetRandomPayload(Const AStartsWith : String) : TOperationPayload;
   public
   public
     class function GetRandomOwnDestination(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out nAccount : Cardinal) : Boolean;
     class function GetRandomOwnDestination(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out nAccount : Cardinal) : Boolean;
     class function GetRandomSigner(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out iKey : Integer; out nAccount : Cardinal) : Boolean;
     class function GetRandomSigner(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out iKey : Integer; out nAccount : Cardinal) : Boolean;
     class function GenerateOpTransactions(current_protocol : Word; Maxtransaction : Integer; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
     class function GenerateOpTransactions(current_protocol : Word; Maxtransaction : Integer; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
     class function GenerateOpMultiOperation(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
     class function GenerateOpMultiOperation(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
+    class function GenerateOpChangeKey(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
+    class function GenerateOpListAccountForSale(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
+    class function GenerateOpBuyAccount(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
+    class function GetHashLock_Public : T32Bytes;
+    class function GetHashLock_Private : TRawBytes;
   end;
   end;
 
 
 implementation
 implementation
@@ -131,14 +141,22 @@ Var nCounter, nTotalRound, iLastSend, i : Integer;
   operationsComp : TPCOperationsComp;
   operationsComp : TPCOperationsComp;
   ohtToAdd : TOperationsHashTree;
   ohtToAdd : TOperationsHashTree;
   errors : String;
   errors : String;
-  nAddedOperations : Integer;
+  nAddedOperations, nMaxTransactionsValue, nExecutedSinceLastTC : Integer;
+  LLastTC : TTickCount;
+  LProtocol : Word;
 begin
 begin
   operationsComp := TPCOperationsComp.Create(Nil);
   operationsComp := TPCOperationsComp.Create(Nil);
   try
   try
     operationsComp.bank := FSourceNode.Bank;
     operationsComp.bank := FSourceNode.Bank;
     iLastSend := -1;
     iLastSend := -1;
+    LLastTC := TPlatform.GetTickCount;
+    nExecutedSinceLastTC := 0;
     while (Not Terminated) do begin
     while (Not Terminated) do begin
-      nTotalRound := Random(100);
+      if FMaxOperationsPerSecond<=0 then begin
+        nTotalRound := Random(100);
+      end else begin
+        nTotalRound := Random(FMaxOperationsPerSecond)+1;
+      end;
       nCounter := 0;
       nCounter := 0;
       if FNeedSanitize then begin
       if FNeedSanitize then begin
         FNeedSanitize := False;
         FNeedSanitize := False;
@@ -148,17 +166,28 @@ begin
       end;
       end;
       while (nCounter<nTotalRound) And (Not Terminated) And (FAllowExecute) do begin
       while (nCounter<nTotalRound) And (Not Terminated) And (FAllowExecute) do begin
         inc(nCounter);
         inc(nCounter);
+        LProtocol := operationsComp.OperationBlock.protocol_version;
         //
         //
         Case Random(30) of
         Case Random(30) of
-          0..20 : begin
-            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpTransactions(FSourceNode.Bank.SafeBox.CurrentProtocol,100,operationsComp,FSourceWalletKeys));
+          0..10 : begin
+            if FMaxOperationsPerSecond>0 then nMaxTransactionsValue := Random(FMaxOperationsPerSecond)+1
+            else nMaxTransactionsValue := Random(200)+1;
+
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpTransactions(LProtocol,nMaxTransactionsValue,operationsComp,FSourceWalletKeys));
+          end;
+          11..15 : begin
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpChangeKey(LProtocol,operationsComp,FSourceWalletKeys));
           end;
           end;
-          21..25 : begin
-            If TRandomGenerateOperation.GenerateOpMultiOperation(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
+          18..22 : begin
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpListAccountForSale(LProtocol,operationsComp,FSourceWalletKeys));
+          end;
+          25..29 : begin
+            If TRandomGenerateOperation.GenerateOpMultiOperation(LProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
             else inc(FnOperationsCreatedFailed);
             else inc(FnOperationsCreatedFailed);
           end;
           end;
         end;
         end;
       end;
       end;
+
       if (Not Terminated) And (Not FNeedSanitize) And (FAllowExecute) then begin
       if (Not Terminated) And (Not FNeedSanitize) And (FAllowExecute) then begin
         //
         //
         ohtToAdd := TOperationsHashTree.Create;
         ohtToAdd := TOperationsHashTree.Create;
@@ -184,8 +213,18 @@ begin
           ohtToAdd.Free;
           ohtToAdd.Free;
         End;
         End;
         //
         //
-        if Assigned(FOnUpdated) then FOnUpdated(Self);
+        if FLastCall_OperationsTotal>0 then begin
+          if Assigned(FOnUpdated) then FOnUpdated(Self);
+        end;
+
+        inc(nExecutedSinceLastTC,nAddedOperations);
+
+      end;
+      if (FMaxOperationsPerSecond>0) and (nExecutedSinceLastTC>=FMaxOperationsPerSecond) then begin
+        while (Not Terminated) and (FAllowExecute) and (TPlatform.GetElapsedMilliseconds(LLastTC)<1000) do sleep(5);
       end;
       end;
+      LLastTC := TPlatform.GetTickCount;
+      nExecutedSinceLastTC := 0;
       Sleep(1);
       Sleep(1);
     end;
     end;
   finally
   finally
@@ -193,7 +232,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-constructor TRandomGeneratorThread.Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt);
+constructor TRandomGeneratorThread.Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt; AMaxOperationsPerSecond : Integer);
 begin
 begin
   FSourceNode := ASourceNode;
   FSourceNode := ASourceNode;
   FSourceWalletKeys := ASourceWalletKeys;
   FSourceWalletKeys := ASourceWalletKeys;
@@ -213,6 +252,9 @@ begin
   FBankNotify.OnNewBlock:=OnBankNewBlock;
   FBankNotify.OnNewBlock:=OnBankNewBlock;
   FNeedSanitize := True;
   FNeedSanitize := True;
   FAllowExecute := False;
   FAllowExecute := False;
+  if AMaxOperationsPerSecond>=0 then
+    FMaxOperationsPerSecond := AMaxOperationsPerSecond
+  else FMaxOperationsPerSecond := 0;
   inherited Create(False);
   inherited Create(False);
 end;
 end;
 
 
@@ -226,6 +268,16 @@ end;
 
 
 { TRandomGenerateOperation }
 { TRandomGenerateOperation }
 
 
+class function TRandomGenerateOperation.GetHashLock_Private: TRawBytes;
+begin
+  Result.FromString('PRIVATE');
+end;
+
+class function TRandomGenerateOperation.GetHashLock_Public: T32Bytes;
+begin
+  Result := TBaseType.To32Bytes( TCrypto.DoSha256( GetHashLock_Private ) );
+end;
+
 class function TRandomGenerateOperation.GetRandomOwnDestination(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out nAccount: Cardinal): Boolean;
 class function TRandomGenerateOperation.GetRandomOwnDestination(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out nAccount: Cardinal): Boolean;
 var
 var
   nRounds : Integer;
   nRounds : Integer;
@@ -248,6 +300,22 @@ begin
   until (Result) Or (nRounds>0);
   until (Result) Or (nRounds>0);
 end;
 end;
 
 
+class function TRandomGenerateOperation.GetRandomPayload(
+  const AStartsWith: String): TOperationPayload;
+var i,j : Integer;
+begin
+  Result := CT_TOperationPayload_NUL;
+  Result.payload_raw.FromString(AStartsWith);
+  j := Random(255);
+  if j<Length(Result.payload_raw) then j := Length(Result.payload_raw);
+
+  SetLength(Result.payload_raw,j);
+  for i := Length(Result.payload_raw) to j-1 do begin
+    Result.payload_raw[j] := Random(127-32)+32;
+  end;
+
+end;
+
 class function TRandomGenerateOperation.GetRandomSigner(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out iKey: Integer; out nAccount: Cardinal): Boolean;
 class function TRandomGenerateOperation.GetRandomSigner(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out iKey: Integer; out nAccount: Cardinal): Boolean;
 var
 var
   bRoundsIKey, bRoundsNAccount : Boolean;
   bRoundsIKey, bRoundsNAccount : Boolean;
@@ -290,9 +358,10 @@ class function TRandomGenerateOperation.GenerateOpTransactions(current_protocol
 var nAccount, nAccountTarget : Cardinal;
 var nAccount, nAccountTarget : Cardinal;
   iKey, nRounds : Integer;
   iKey, nRounds : Integer;
   opTx : TOpTransaction;
   opTx : TOpTransaction;
-  senderAcc : TAccount;
+  senderAcc, LDestAcc : TAccount;
   amount,fees : Int64;
   amount,fees : Int64;
   errors : String;
   errors : String;
+  LPayload : TOperationPayload;
 begin
 begin
   Result := 0;
   Result := 0;
   If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then Exit;
   If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then Exit;
@@ -301,11 +370,18 @@ begin
   nRounds := 0;
   nRounds := 0;
   while (nRounds<Maxtransaction) do begin
   while (nRounds<Maxtransaction) do begin
     senderAcc := operationsComp.SafeBoxTransaction.Account(nAccount);
     senderAcc := operationsComp.SafeBoxTransaction.Account(nAccount);
+    LDestAcc := operationsComp.SafeBoxTransaction.Account(nAccountTarget);
+    LPayload := CT_TOperationPayload_NUL;
+    if TAccountComp.IsAccountForSwap( LDestAcc.accountInfo ) then begin
+      // Special case, will swap? Will need to provide a HASHLOCK in payload
+      LPayload.payload_raw := GetHashLock_Private;
+    end;
+
     amount := 1; // Minimal amount
     amount := 1; // Minimal amount
     if (Random(500)<1) then fees := 0
     if (Random(500)<1) then fees := 0
     else fees := 1; // Minimal fee
     else fees := 1; // Minimal fee
     if (senderAcc.balance>2) then begin
     if (senderAcc.balance>2) then begin
-      opTx := TOpTransaction.CreateTransaction(current_protocol,senderAcc.account,senderAcc.n_operation+1,nAccountTarget,aWalletKeys.Key[iKey].PrivateKey,amount,fees,Nil);
+      opTx := TOpTransaction.CreateTransaction(current_protocol,senderAcc.account,senderAcc.n_operation+1,nAccountTarget,aWalletKeys.Key[iKey].PrivateKey,amount,fees,LPayload);
       Try
       Try
         if operationsComp.AddOperation(True,opTx,errors) then inc(Result);
         if operationsComp.AddOperation(True,opTx,errors) then inc(Result);
       finally
       finally
@@ -317,6 +393,174 @@ begin
   end;
   end;
 end;
 end;
 
 
+class function TRandomGenerateOperation.GenerateOpBuyAccount(
+  current_protocol: Word; const operationsComp: TPCOperationsComp;
+  const aWalletKeys: TWalletKeysExt): Integer;
+begin
+  raise Exception.Create('XXXXXX TODO TRandomGenerateOperation');
+end;
+
+class function TRandomGenerateOperation.GenerateOpChangeKey(
+  current_protocol: Word;
+  const operationsComp: TPCOperationsComp;
+  const aWalletKeys: TWalletKeysExt): Integer;
+var nAccount, nAccountTarget : Cardinal;
+  iKey, iNewPubKey : Integer;
+  opCk : TOpChangeKey;
+  senderAcc : TAccount;
+  fees : Int64;
+  errors : String;
+  opClass : TPCOperationClass;
+begin
+  Result := 0;
+  If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then Exit;
+  if Random(1)=0 then begin
+    nAccountTarget := Random( aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Count );
+    opClass := TOpChangeKeySigned;
+  end else begin
+    nAccountTarget := nAccount;
+    opClass := TOpChangeKey;
+  end;
+
+  if Not GetRandomOwnDestination(operationsComp,aWalletKeys,nAccountTarget) then Exit;
+  iNewPubKey := Random(aWalletKeys.Count);
+
+  if iKey=iNewPubKey then Exit;
+
+  senderAcc := operationsComp.SafeBoxTransaction.Account(nAccount);
+  if (Random(500)<1) then fees := 0
+  else fees := 1; // Minimal fee
+  if (senderAcc.balance>2) then begin
+    opCk := TOpChangeKey(opClass.NewInstance).Create(current_protocol,senderAcc.account,senderAcc.n_operation+1,nAccountTarget,
+      aWalletKeys.Key[iKey].PrivateKey,
+      aWalletKeys.Key[iNewPubKey].AccountKey,
+      fees,CT_TOperationPayload_NUL);
+    Try
+      if operationsComp.AddOperation(True,opCk,errors) then inc(Result);
+    finally
+      opCk.Free;
+    end;
+  end;
+
+end;
+
+class function TRandomGenerateOperation.GenerateOpListAccountForSale(
+  current_protocol: Word; const operationsComp: TPCOperationsComp;
+  const aWalletKeys: TWalletKeysExt): Integer;
+var nSigner, nTarget : Cardinal;
+  iKey, iNewPubKey, i, j : Integer;
+  opList : TOpListAccountForSaleOrSwap;
+  opDelist : TOpDelistAccountForSale;
+  SignerAccount, AuxAccount : TAccount;
+  fees : Int64;
+  errors : String;
+  DoDelist : Boolean;
+begin
+  Result := 0;
+  If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nSigner) then Exit;
+  SignerAccount := operationsComp.SafeBoxTransaction.Account(nSigner);
+
+  if (Random(500)<1) then fees := 0
+  else fees := 1; // Minimal fee
+
+  if TAccountComp.IsAccountForSaleOrSwap(SignerAccount.accountInfo) then begin
+    // Delist:
+    i := Random( aWalletKeys.AccountsKeyList.AccountKeyList[ iKey ].Count );
+    j := i;
+    DoDelist := False;
+    Repeat
+      AuxAccount := operationsComp.SafeBoxTransaction.Account(j);
+      if TAccountComp.IsAccountForSale(AuxAccount.accountInfo) then begin
+        nTarget := j;
+        DoDelist := True;
+      end;
+
+      //
+      if j<aWalletKeys.AccountsKeyList.AccountKeyList[ iKey ].Count then inc(j)
+      else j:=0;
+    Until (j=i) or (DoDelist);
+    if (DoDelist) then begin
+      try
+        opDelist := TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol,
+          nSigner,SignerAccount.n_operation+1,nTarget,fees,
+          aWalletKeys.Key[iKey].PrivateKey,
+          GetRandomPayload(''));
+        if operationsComp.AddOperation(True,opDelist,errors) then inc(Result);
+      finally
+        opDelist.Free;
+      end;
+    end;
+
+  end else begin
+
+    if Random(1)=0 then begin
+      nTarget := Random( aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Count );
+    end else begin
+      nTarget := nSigner;
+    end;
+
+
+    opList := Nil;
+    try
+      case Random(4) of
+        0 : // Private sale:
+          begin
+            // Private sale:
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForSale,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              aWalletKeys.Key[ Random(aWalletKeys.Count) ].AccountKey,
+              operationsComp.OperationBlock.block + Random(1000),
+              aWalletKeys.Key[iKey].PrivateKey,
+              CT_HashLock_NUL,
+              GetRandomPayload(''));
+          end;
+        1 : // Publis sale:
+          begin
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForSale,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              CT_TECDSA_Public_Nul,
+              0,
+              aWalletKeys.Key[iKey].PrivateKey,
+              CT_HashLock_NUL,
+              GetRandomPayload(''));
+          end;
+        2 : // Atomic Account Swap
+          begin
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForAtomicAccountSwap,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              aWalletKeys.Key[ Random(aWalletKeys.Count) ].AccountKey,
+              operationsComp.OperationBlock.block + Random(1000),
+              aWalletKeys.Key[iKey].PrivateKey,
+              GetHashLock_Public,
+              GetRandomPayload(''));
+          end;
+        3 : // Atomic Coin Swap
+          begin
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForAtomicCoinSwap,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              CT_TECDSA_Public_Nul,
+              operationsComp.OperationBlock.block + Random(1000),
+              aWalletKeys.Key[iKey].PrivateKey,
+              GetHashLock_Public,
+              GetRandomPayload(''));
+          end;
+      end;
+      if operationsComp.AddOperation(True,opList,errors) then inc(Result);
+    finally
+      opList.Free;
+    end;
+
+  end;
+end;
+
 class function TRandomGenerateOperation.GenerateOpMultiOperation(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
 class function TRandomGenerateOperation.GenerateOpMultiOperation(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
    procedure DoSign(opMulti : TOpMultiOperation);
    procedure DoSign(opMulti : TOpMultiOperation);
    var n : Integer;
    var n : Integer;
@@ -432,6 +676,7 @@ begin
   FInternalLog := TLog.Create(Self);
   FInternalLog := TLog.Create(Self);
   FInternalLog.ProcessGlobalLogs := False;
   FInternalLog.ProcessGlobalLogs := False;
   FInternalLog.OnNewLog := OnInternalLog;
   FInternalLog.OnNewLog := OnInternalLog;
+  FMaxOperationsPerSecond := 0;
   mLogs.Clear;
   mLogs.Clear;
 end;
 end;
 
 
@@ -443,6 +688,8 @@ begin
 end;
 end;
 
 
 procedure TFRMRandomOperations.bbRandomOperationsClick(Sender: TObject);
 procedure TFRMRandomOperations.bbRandomOperationsClick(Sender: TObject);
+var sValue : String;
+  n : Integer;
 begin
 begin
   {$IFDEF TESTNET}
   {$IFDEF TESTNET}
   If IsProcessingRandomOperations then begin
   If IsProcessingRandomOperations then begin
@@ -450,6 +697,12 @@ begin
     bbRandomOperations.Caption:='GENERATE RANDOM';
     bbRandomOperations.Caption:='GENERATE RANDOM';
   end else begin
   end else begin
     if Assigned(FRandomGeneratorThread) then begin
     if Assigned(FRandomGeneratorThread) then begin
+      sValue := IntToStr(FMaxOperationsPerSecond);
+      if InputQuery('Max Operations per second','Max operations per second (0=Max)',sValue) then begin
+        n := StrToIntDef(sValue,-1);
+        if (n>=0) then FMaxOperationsPerSecond := n;
+      end;
+      FRandomGeneratorThread.MaxOperationsPerSecond := n;
       FRandomGeneratorThread.AllowExecute := True;
       FRandomGeneratorThread.AllowExecute := True;
       bbRandomOperations.Caption:='STOP RANDOM';
       bbRandomOperations.Caption:='STOP RANDOM';
     end else bbRandomOperations.Caption:='???';
     end else bbRandomOperations.Caption:='???';
@@ -459,6 +712,11 @@ begin
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 
+procedure TFRMRandomOperations.cbMaxSpeedModeClick(Sender: TObject);
+begin
+//
+end;
+
 procedure TFRMRandomOperations.FormClose(Sender: TObject; var CloseAction: TCloseAction);
 procedure TFRMRandomOperations.FormClose(Sender: TObject; var CloseAction: TCloseAction);
 begin
 begin
   CloseAction := caFree;
   CloseAction := caFree;
@@ -516,7 +774,7 @@ begin
     FreeAndNil(FRandomGeneratorThread);
     FreeAndNil(FRandomGeneratorThread);
   end;
   end;
   if (Not DestroyOnly) And Assigned(FSourceNode) And Assigned(FSourceWalletKeys) then begin
   if (Not DestroyOnly) And Assigned(FSourceNode) And Assigned(FSourceWalletKeys) then begin
-    FRandomGeneratorThread := TRandomGeneratorThread.Create(FSourceNode,FSourceWalletKeys);
+    FRandomGeneratorThread := TRandomGeneratorThread.Create(FSourceNode,FSourceWalletKeys,FMaxOperationsPerSecond);
     FRandomGeneratorThread.OnUpdated:=OnRandomGeneratoThreadUpdated;
     FRandomGeneratorThread.OnUpdated:=OnRandomGeneratoThreadUpdated;
   end;
   end;
 end;
 end;

+ 109 - 24
src/gui-classic/UFRMWallet.pas

@@ -228,6 +228,7 @@ type
     procedure MiFindOperationbyOpHashClick(Sender: TObject);
     procedure MiFindOperationbyOpHashClick(Sender: TObject);
     procedure MiAccountInformationClick(Sender: TObject);
     procedure MiAccountInformationClick(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
+    procedure Test_ShowDiagnosticTool(Sender: TObject);
   private
   private
     FLastNodesCacheUpdatedTS : TDateTime;
     FLastNodesCacheUpdatedTS : TDateTime;
     FBackgroundPanel : TPanel;
     FBackgroundPanel : TPanel;
@@ -242,8 +243,8 @@ type
     Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
     Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
     Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
     Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
     Procedure InitMacOSMenu;
     Procedure InitMacOSMenu;
-    {$IFDEF TESTNET}
     Procedure InitMenuForTesting;
     Procedure InitMenuForTesting;
+    {$IFDEF TESTNET}
     Procedure Test_RandomOperations(Sender: TObject);
     Procedure Test_RandomOperations(Sender: TObject);
     Procedure Test_AskForFreeAccount(Sender: TObject);
     Procedure Test_AskForFreeAccount(Sender: TObject);
     {$IFDEF TESTING_NO_POW_CHECK}
     {$IFDEF TESTING_NO_POW_CHECK}
@@ -251,6 +252,7 @@ type
     {$ENDIF}
     {$ENDIF}
     {$ENDIF}
     {$ENDIF}
     Procedure Test_ShowPublicKeys(Sender: TObject);
     Procedure Test_ShowPublicKeys(Sender: TObject);
+    Procedure Test_ShowOperationsInMemory(Sender: TObject);
     procedure OnAccountsGridUpdatedData(Sender : TObject);
     procedure OnAccountsGridUpdatedData(Sender : TObject);
   protected
   protected
     { Private declarations }
     { Private declarations }
@@ -333,6 +335,7 @@ Uses UFolderHelper,
   {$IFDEF TESTNET}
   {$IFDEF TESTNET}
   UFRMRandomOperations,
   UFRMRandomOperations,
   UPCTNetDataExtraMessages,
   UPCTNetDataExtraMessages,
+  UFRMDiagnosticTool,
   {$ENDIF}
   {$ENDIF}
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText,
   USettings, UCommon, UPCOrderedLists;
   USettings, UCommon, UPCOrderedLists;
@@ -382,11 +385,13 @@ begin
   FLastTC := 0;
   FLastTC := 0;
   FLastMsg := '';
   FLastMsg := '';
   //
   //
+  {$IFDEF PRODUCTION}
   OnProgressNotify(Self,'Reading Hardcoded RandomHash file',0,0);
   OnProgressNotify(Self,'Reading Hardcoded RandomHash file',0,0);
   LRaw := TCrypto.HexaToRaw(CT_Hardcoded_RandomHash_Table_HASH);
   LRaw := TCrypto.HexaToRaw(CT_Hardcoded_RandomHash_Table_HASH);
   TPascalCoinProtocol.AllowUseHardcodedRandomHashTable(
   TPascalCoinProtocol.AllowUseHardcodedRandomHashTable(
-    TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data'+PathDelim+CT_Hardcoded_RandomHash_Table_Filename,
+    ExtractFileDir(Application.ExeName)+PathDelim+CT_Hardcoded_RandomHash_Table_Filename,
     LRaw );
     LRaw );
+  {$ENDIF}
   // Read Operations saved from disk
   // Read Operations saved from disk
   TNode.Node.InitSafeboxAndOperations($FFFFFFFF,OnProgressNotify); // New Build 2.1.4 to load pending operations buffer
   TNode.Node.InitSafeboxAndOperations($FFFFFFFF,OnProgressNotify); // New Build 2.1.4 to load pending operations buffer
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
@@ -424,7 +429,7 @@ begin
     TCrypto.InitCrypto;
     TCrypto.InitCrypto;
     // Read Wallet
     // Read Wallet
     Try
     Try
-      FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+      FWalletKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
     Except
     Except
       On E:Exception do begin
       On E:Exception do begin
         E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;
         E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;
@@ -447,7 +452,7 @@ begin
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     // Check Database
     // Check Database
     FNode.Bank.StorageClass := TFileStorage;
     FNode.Bank.StorageClass := TFileStorage;
-    TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+    TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
     TFileStorage(FNode.Bank.Storage).Initialize;
     TFileStorage(FNode.Bank.Storage).Initialize;
     // Init Grid
     // Init Grid
     FSelectedAccountsGrid.Node := FNode;
     FSelectedAccountsGrid.Node := FNode;
@@ -887,6 +892,7 @@ procedure TFRMWallet.FillAccountInformation(const Strings: TStrings;
   const AccountNumber: Cardinal);
   const AccountNumber: Cardinal);
 Var account : TAccount;
 Var account : TAccount;
   s : String;
   s : String;
+  LjsonObj : TPCJSONObject;
 begin
 begin
   if AccountNumber<0 then exit;
   if AccountNumber<0 then exit;
   account := FNode.GetMempoolAccount(AccountNumber);
   account := FNode.GetMempoolAccount(AccountNumber);
@@ -896,15 +902,20 @@ begin
   Strings.Add('');
   Strings.Add('');
   Strings.Add(Format('Current balance: %s',[TAccountComp.FormatMoney(account.balance)]));
   Strings.Add(Format('Current balance: %s',[TAccountComp.FormatMoney(account.balance)]));
   Strings.Add('');
   Strings.Add('');
-  Strings.Add(Format('Updated on block: %d  (%d blocks ago)',[account.updated_block,FNode.Bank.BlocksCount-account.updated_block]));
+  Strings.Add(Format('Updated on block: %d  (%d blocks ago)',[account.updated_on_block,FNode.Bank.BlocksCount-account.updated_on_block]));
+  Strings.Add(Format('Updated on block as active mode: %d  (%d blocks ago)',[account.updated_on_block_active_mode,FNode.Bank.BlocksCount-account.updated_on_block_active_mode]));
   Strings.Add(Format('Public key type: %s',[TAccountComp.GetECInfoTxt(account.accountInfo.accountKey.EC_OpenSSL_NID)]));
   Strings.Add(Format('Public key type: %s',[TAccountComp.GetECInfoTxt(account.accountInfo.accountKey.EC_OpenSSL_NID)]));
   Strings.Add(Format('Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.accountKey)]));
   Strings.Add(Format('Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.accountKey)]));
-  if TAccountComp.IsAccountForSale(account.accountInfo, FNode.Bank.BlocksCount) then begin
+  if Length(account.account_data)>0 then
+    Strings.Add(Format('Account Data: %s',[account.account_data.ToHexaString]))
+  else Strings.Add(Format('Account Data: (No data)',[]));
+  Strings.Add(Format('Account Seal: %s',[account.account_seal.ToHexaString]));
+  if TAccountComp.IsAccountForSale(account.accountInfo) then begin
     Strings.Add('');
     Strings.Add('');
     Strings.Add('** Account is for sale: **');
     Strings.Add('** Account is for sale: **');
     Strings.Add(Format('Price: %s',[TAccountComp.FormatMoney(account.accountInfo.price)]));
     Strings.Add(Format('Price: %s',[TAccountComp.FormatMoney(account.accountInfo.price)]));
     Strings.Add(Format('Seller account (where to pay): %s',[TAccountComp.AccountNumberToAccountTxtNumber(account.accountInfo.account_to_pay)]));
     Strings.Add(Format('Seller account (where to pay): %s',[TAccountComp.AccountNumberToAccountTxtNumber(account.accountInfo.account_to_pay)]));
-    if TAccountComp.IsAccountForPrivateSale(account.accountInfo, FNode.Bank.BlocksCount) then begin
+    if TAccountComp.IsAccountForPrivateSale(account.accountInfo) then begin
       Strings.Add('');
       Strings.Add('');
       Strings.Add('** Private sale **');
       Strings.Add('** Private sale **');
       Strings.Add(Format('New Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.new_publicKey)]));
       Strings.Add(Format('New Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.new_publicKey)]));
@@ -917,7 +928,35 @@ begin
           [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount]));
           [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount]));
       end;
       end;
     end;
     end;
+  end else if TAccountComp.IsAccountForSwap(account.accountInfo) then begin
+    Strings.Add('');
+    if TAccountComp.IsAccountForAccountSwap(account.accountInfo) then begin
+      Strings.Add('** Account is for Atomic Account Swap: **');
+      Strings.Add(Format('New Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.new_publicKey)]));
+    end else if TAccountComp.IsAccountForCoinSwap(account.accountInfo) then begin
+      Strings.Add('** Account is for Atomic Coin Swap: **');
+      Strings.Add(Format('Amount to swap: %s',[TAccountComp.FormatMoney(account.accountInfo.price)]));
+      Strings.Add(Format('Counterparty account: %s',[TAccountComp.AccountNumberToAccountTxtNumber(account.accountInfo.account_to_pay)]));
+    end;
+    Strings.Add(Format('Public secret to find: %s',[account.accountInfo.hashed_secret.ToHexaString]));
+    Strings.Add('');
+    if TAccountComp.IsAccountLocked(account.accountInfo,FNode.Bank.BlocksCount) then begin
+      Strings.Add(Format('SWAP IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
+          [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount,account.accountInfo.locked_until_block-FNode.Bank.BlocksCount]));
+    end else begin
+        Strings.Add(Format('SWAP IS NOT SECURE (Expired on block %d, current %d)',
+          [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount]));
+    end;
+  end;
+  LjsonObj := TPCJSONObject.Create;
+  Try
+    TPascalCoinJSONComp.FillAccountObject(account,LjsonObj);
+    Strings.Add('ACCOUNT JSON:');
+    Strings.Add(LjsonObj.ToJSON(False));
+  Finally
+    LjsonObj.Free;
   end;
   end;
+
 end;
 end;
 
 
 procedure TFRMWallet.FillOperationInformation(const Strings: TStrings;
 procedure TFRMWallet.FillOperationInformation(const Strings: TStrings;
@@ -937,10 +976,10 @@ begin
   If (OperationResume.isMultiOperation) then begin
   If (OperationResume.isMultiOperation) then begin
     Strings.Add('Multioperation:');
     Strings.Add('Multioperation:');
     For i := 0 to High(OperationResume.Senders) do begin
     For i := 0 to High(OperationResume.Senders) do begin
-      Strings.Add(Format('  Sender (%d/%d): %s %s PASC Payload:%s',[i+1,length(OperationResume.Senders),TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.Senders[i].Account),TAccountComp.FormatMoney(OperationResume.Senders[i].Amount),TCrypto.ToHexaString(OperationResume.Senders[i].Payload)]));
+      Strings.Add(Format('  Sender (%d/%d): %s %s PASC Payload(%d):%s',[i+1,length(OperationResume.Senders),TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.Senders[i].Account),TAccountComp.FormatMoney(OperationResume.Senders[i].Amount),OperationResume.Senders[i].Payload.payload_type,OperationResume.Senders[i].Payload.payload_raw.ToHexaString]));
     end;
     end;
     For i := 0 to High(OperationResume.Receivers) do begin
     For i := 0 to High(OperationResume.Receivers) do begin
-      Strings.Add(Format('  Receiver (%d/%d): %s %s PASC Payload:%s',[i+1,length(OperationResume.Receivers),TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.Receivers[i].Account),TAccountComp.FormatMoney(OperationResume.Receivers[i].Amount),TCrypto.ToHexaString(OperationResume.Receivers[i].Payload)]));
+      Strings.Add(Format('  Receiver (%d/%d): %s %s PASC Payload(%d):%s',[i+1,length(OperationResume.Receivers),TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.Receivers[i].Account),TAccountComp.FormatMoney(OperationResume.Receivers[i].Amount),OperationResume.Receivers[i].Payload.payload_type,OperationResume.Receivers[i].Payload.payload_raw.ToHexaString]));
     end;
     end;
     For i := 0 to High(OperationResume.Changers) do begin
     For i := 0 to High(OperationResume.Changers) do begin
       Strings.Add(Format('  Change info (%d/%d): %s [%s]',[i+1,length(OperationResume.Changers),TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.Changers[i].Account),TOpMultiOperation.OpChangeAccountInfoTypesToText(OperationResume.Changers[i].Changes_type)]));
       Strings.Add(Format('  Change info (%d/%d): %s [%s]',[i+1,length(OperationResume.Changers),TAccountComp.AccountNumberToAccountTxtNumber(OperationResume.Changers[i].Account),TOpMultiOperation.OpChangeAccountInfoTypesToText(OperationResume.Changers[i].Changes_type)]));
@@ -952,12 +991,12 @@ begin
   If (Length(OperationResume.OperationHash_OLD)>0) then begin
   If (Length(OperationResume.OperationHash_OLD)>0) then begin
     Strings.Add(Format('Old Operation Hash (old_ophash): %s',[TCrypto.ToHexaString(OperationResume.OperationHash_OLD)]));
     Strings.Add(Format('Old Operation Hash (old_ophash): %s',[TCrypto.ToHexaString(OperationResume.OperationHash_OLD)]));
   end;
   end;
-  if (Length(OperationResume.OriginalPayload)>0) then begin
-    Strings.Add(Format('Payload length:%d',[length(OperationResume.OriginalPayload)]));
+  Strings.Add(Format('Payload type:%d length:%d',[OperationResume.OriginalPayload.payload_type, length(OperationResume.OriginalPayload.payload_raw)]));
+  if (Length(OperationResume.OriginalPayload.payload_raw)>0) then begin
     If OperationResume.PrintablePayload<>'' then begin
     If OperationResume.PrintablePayload<>'' then begin
       Strings.Add(Format('Payload (human): %s',[OperationResume.PrintablePayload]));
       Strings.Add(Format('Payload (human): %s',[OperationResume.PrintablePayload]));
     end;
     end;
-    Strings.Add(Format('Payload (Hexadecimal): %s',[TCrypto.ToHexaString(OperationResume.OriginalPayload)]));
+    Strings.Add(Format('Payload (Hexadecimal): %s',[TCrypto.ToHexaString(OperationResume.OriginalPayload.payload_raw)]));
   end;
   end;
   If OperationResume.Balance>=0 then begin
   If OperationResume.Balance>=0 then begin
     Strings.Add(Format('Final balance: %s',[TAccountComp.FormatMoney(OperationResume.Balance)]));
     Strings.Add(Format('Final balance: %s',[TAccountComp.FormatMoney(OperationResume.Balance)]));
@@ -965,7 +1004,7 @@ begin
   jsonObj := TPCJSONObject.Create;
   jsonObj := TPCJSONObject.Create;
   Try
   Try
     TPascalCoinJSONComp.FillOperationObject(OperationResume,FNode.Bank.BlocksCount,jsonObj);
     TPascalCoinJSONComp.FillOperationObject(OperationResume,FNode.Bank.BlocksCount,jsonObj);
-    Strings.Add('JSON:');
+    Strings.Add('OPERATION JSON:');
     Strings.Add(jsonObj.ToJSON(False));
     Strings.Add(jsonObj.ToJSON(False));
   Finally
   Finally
     jsonObj.Free;
     jsonObj.Free;
@@ -1019,13 +1058,13 @@ begin
 end;
 end;
 
 
 
 
-{$IFDEF TESTNET}
 procedure TFRMWallet.InitMenuForTesting;
 procedure TFRMWallet.InitMenuForTesting;
 var mi : TMenuItem;
 var mi : TMenuItem;
 begin
 begin
   mi := TMenuItem.Create(MainMenu);
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='-';
   mi.Caption:='-';
   miAbout.Add(mi);
   miAbout.Add(mi);
+{$IFDEF TESTNET}
   {$IFDEF TESTING_NO_POW_CHECK}
   {$IFDEF TESTING_NO_POW_CHECK}
   mi := TMenuItem.Create(MainMenu);
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Create a block';
   mi.Caption:='Create a block';
@@ -1033,10 +1072,6 @@ begin
   miAbout.Add(mi);
   miAbout.Add(mi);
   {$ENDIF}
   {$ENDIF}
   mi := TMenuItem.Create(MainMenu);
   mi := TMenuItem.Create(MainMenu);
-  mi.Caption:='Show public keys state';
-  mi.OnClick:=Test_ShowPublicKeys;
-  miAbout.Add(mi);
-  mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Create Random operations';
   mi.Caption:='Create Random operations';
   mi.OnClick:=Test_RandomOperations;
   mi.OnClick:=Test_RandomOperations;
   miAbout.Add(mi);
   miAbout.Add(mi);
@@ -1044,6 +1079,20 @@ begin
   mi.Caption:='Ask for Free Account';
   mi.Caption:='Ask for Free Account';
   mi.OnClick:=Test_AskForFreeAccount;
   mi.OnClick:=Test_AskForFreeAccount;
   miAbout.Add(mi);
   miAbout.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Diagnostic Tool';
+  mi.OnClick:=Test_ShowDiagnosticTool;
+  miAbout.Add(mi);
+{$ENDIF}
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Show public keys state';
+  mi.OnClick:=Test_ShowPublicKeys;
+  miAbout.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Show operations in memory';
+  mi.OnClick:=Test_ShowOperationsInMemory;
+  miAbout.Add(mi);
+
 end;
 end;
 
 
 {$IFDEF TESTING_NO_POW_CHECK}
 {$IFDEF TESTING_NO_POW_CHECK}
@@ -1070,6 +1119,7 @@ begin
 end;
 end;
 {$ENDIF}
 {$ENDIF}
 
 
+{$IFDEF TESTNET}
 procedure TFRMWallet.Test_RandomOperations(Sender: TObject);
 procedure TFRMWallet.Test_RandomOperations(Sender: TObject);
 Var FRM : TFRMRandomOperations;
 Var FRM : TFRMRandomOperations;
 begin
 begin
@@ -1093,6 +1143,27 @@ end;
 
 
 {$ENDIF}
 {$ENDIF}
 
 
+procedure TFRMWallet.Test_ShowOperationsInMemory(Sender: TObject);
+var LFRM : TFRMMemoText;
+  i, nOps : Integer;
+  Lslist : TStrings;
+begin
+  Lslist := TStringList.Create;
+  try
+    TPCOperationsStorage.PCOperationsStorage.GetStats(Lslist);
+    nOps := TPCOperationsStorage.PCOperationsStorage.Count;
+    LFRM := TFRMMemoText.Create(Self);
+    try
+      LFRM.InitData('Operations in Memory '+IntToStr(nOps),Lslist.Text);
+      LFRM.ShowModal;
+    finally
+      LFRM.Free;
+    end;
+  finally
+    Lslist.Free;
+  end;
+end;
+
 procedure TFRMWallet.Test_ShowPublicKeys(Sender: TObject);
 procedure TFRMWallet.Test_ShowPublicKeys(Sender: TObject);
 var F : TFRMMemoText;
 var F : TFRMMemoText;
   i : Integer;
   i : Integer;
@@ -1211,9 +1282,9 @@ begin
   FLog := TLog.Create(Self);
   FLog := TLog.Create(Self);
   FLog.OnNewLog := OnNewLog;
   FLog.OnNewLog := OnNewLog;
   FLog.SaveTypes := [];
   FLog.SaveTypes := [];
-  If Not ForceDirectories(TFolderHelper.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TFolderHelper.GetPascalCoinDataFolder);
+  If Not ForceDirectories(TNode.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TNode.GetPascalCoinDataFolder);
   FAppParams := TAppParams.Create(self);
   FAppParams := TAppParams.Create(self);
-  FAppParams.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+  FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
   FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
@@ -1279,10 +1350,8 @@ begin
   cbHashRateUnits.Items.Add('Th/s');
   cbHashRateUnits.Items.Add('Th/s');
   cbHashRateUnits.Items.Add('Ph/s');
   cbHashRateUnits.Items.Add('Ph/s');
   cbHashRateUnits.Items.Add('Eh/s');
   cbHashRateUnits.Items.Add('Eh/s');
-  {$IFDEF TESTNET}
   // Things for testing purposes only
   // Things for testing purposes only
   InitMenuForTesting;
   InitMenuForTesting;
-  {$ENDIF}
   {$ifdef DARWIN}
   {$ifdef DARWIN}
   // this is macOS specific menu layout
   // this is macOS specific menu layout
   InitMacOSMenu;
   InitMacOSMenu;
@@ -1613,6 +1682,22 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TFRMWallet.Test_ShowDiagnosticTool(Sender: TObject);
+{$IFDEF TESTNET}
+var
+ LDialog : TFRMDiagnosticTool;
+{$ENDIF}
+begin
+{$IFDEF TESTNET}
+  LDialog := TFRMDiagnosticTool.Create(Nil);
+  try
+    LDialog.ShowModal;
+  finally
+    LDialog.Free;
+  end;
+{$ENDIF}
+end;
+
 procedure TFRMWallet.MiFindaccountClick(Sender: TObject);
 procedure TFRMWallet.MiFindaccountClick(Sender: TObject);
 begin
 begin
   PageControl.ActivePage := tsMyAccounts;
   PageControl.ActivePage := tsMyAccounts;
@@ -2262,7 +2347,7 @@ begin
   if FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false) then begin
   if FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false) then begin
     if FAppParams.ParamByName[CT_PARAM_SaveDebugLogs].GetAsBoolean(false) then FLog.SaveTypes := CT_TLogTypes_ALL
     if FAppParams.ParamByName[CT_PARAM_SaveDebugLogs].GetAsBoolean(false) then FLog.SaveTypes := CT_TLogTypes_ALL
     else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
     else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
-    FLog.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
+    FLog.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
   end else begin
   end else begin
     FLog.SaveTypes := [];
     FLog.SaveTypes := [];
     FLog.FileName := '';
     FLog.FileName := '';
@@ -2277,7 +2362,7 @@ begin
     finally
     finally
       FNode.UnlockMempoolWrite;
       FNode.UnlockMempoolWrite;
     end;
     end;
-    FNode.NodeLogFilename := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'blocks.log';
+    FNode.NodeLogFilename := TNode.GetPascalCoinDataFolder+PathDelim+'blocks.log';
   end;
   end;
   if Assigned(FPoolMiningServer) then begin
   if Assigned(FPoolMiningServer) then begin
     if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin
     if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin

+ 34 - 9
src/gui-classic/UGridUtils.pas

@@ -42,6 +42,7 @@ Type
     ColumnType : TAccountColumnType;
     ColumnType : TAccountColumnType;
     width : Integer;
     width : Integer;
   End;
   End;
+  TAccountColumnArray = Array of TAccountColumn;
 
 
   TAccountsGrid = Class;
   TAccountsGrid = Class;
 
 
@@ -73,7 +74,7 @@ Type
   private
   private
     FAccountsBalance : Int64;
     FAccountsBalance : Int64;
     FAccountsList : TOrderedCardinalList;
     FAccountsList : TOrderedCardinalList;
-    FColumns : Array of TAccountColumn;
+    FColumns : TAccountColumnArray;
     FDrawGrid : TDrawGrid;
     FDrawGrid : TDrawGrid;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FOnUpdated: TNotifyEvent;
     FOnUpdated: TNotifyEvent;
@@ -125,6 +126,8 @@ Type
     function IsUpdatingData : Boolean;
     function IsUpdatingData : Boolean;
     property OnAccountsGridUpdatedData : TNotifyEvent read FOnAccountsGridUpdatedData write FOnAccountsGridUpdatedData;
     property OnAccountsGridUpdatedData : TNotifyEvent read FOnAccountsGridUpdatedData write FOnAccountsGridUpdatedData;
     property AccountsGridDatasource : TAccountsGridDatasource read FAccountsGridDatasource write SetAccountsGridDatasource;
     property AccountsGridDatasource : TAccountsGridDatasource read FAccountsGridDatasource write SetAccountsGridDatasource;
+    function GetColumns : TAccountColumnArray;
+    procedure SetColumns(const AColumns : TAccountColumnArray);
   End;
   End;
 
 
   TOperationsGrid = Class;
   TOperationsGrid = Class;
@@ -425,13 +428,13 @@ begin
   FColumns[2].ColumnType := act_balance;
   FColumns[2].ColumnType := act_balance;
   FColumns[2].width := 80;
   FColumns[2].width := 80;
   FColumns[3].ColumnType := act_n_operation;
   FColumns[3].ColumnType := act_n_operation;
-  FColumns[3].width := 40;
+  FColumns[3].width := 35;
   FColumns[4].ColumnType := act_type;
   FColumns[4].ColumnType := act_type;
-  FColumns[4].width := 40;
+  FColumns[4].width := 35;
   FColumns[5].ColumnType := act_saleprice;
   FColumns[5].ColumnType := act_saleprice;
   FColumns[5].width := 45;
   FColumns[5].width := 45;
   FColumns[6].ColumnType := act_updated_state;
   FColumns[6].ColumnType := act_updated_state;
-  FColumns[6].width := 25;
+  FColumns[6].width := 20;
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
   FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
   FAccountsGridUpdateThread := Nil;
   FAccountsGridUpdateThread := Nil;
@@ -460,6 +463,11 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TAccountsGrid.GetColumns: TAccountColumnArray;
+begin
+  Result := FColumns;
+end;
+
 function TAccountsGrid.GetNode: TNode;
 function TAccountsGrid.GetNode: TNode;
 begin
 begin
   Result := FNodeNotifyEvents.Node;
   Result := FNodeNotifyEvents.Node;
@@ -664,7 +672,7 @@ begin
     n_acc := AccountNumber(ARow);
     n_acc := AccountNumber(ARow);
     if (n_acc>=0) then begin
     if (n_acc>=0) then begin
       BufferGetAccount(n_acc,account,LNodeBlocksCount,LNodeAccountsCount);
       BufferGetAccount(n_acc,account,LNodeBlocksCount,LNodeAccountsCount);
-      ndiff := LNodeBlocksCount - account.updated_block;
+      ndiff := LNodeBlocksCount - account.updated_on_block;
       if (gdSelected in State) then
       if (gdSelected in State) then
         If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
         If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
         else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
         else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
@@ -694,7 +702,7 @@ begin
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         End;
         End;
         act_updated : Begin
         act_updated : Begin
-          s := Inttostr(account.updated_block);
+          s := Inttostr(account.updated_on_block);
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         End;
         End;
         act_n_operation : Begin
         act_n_operation : Begin
@@ -722,10 +730,10 @@ begin
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         end;
         end;
         act_saleprice : Begin
         act_saleprice : Begin
-          if TAccountComp.IsAccountForSale(account.accountInfo, LNodeBlocksCount) then begin
+          if TAccountComp.IsAccountForSale(account.accountInfo) then begin
             // Show price for sale
             // Show price for sale
             s := TAccountComp.FormatMoney(account.accountInfo.price);
             s := TAccountComp.FormatMoney(account.accountInfo.price);
-            if TAccountComp.IsAccountForPrivateSale(account.accountInfo, LNodeBlocksCount) then begin
+            if TAccountComp.IsAccountForPrivateSale(account.accountInfo) then begin
               if TAccountComp.IsAccountLocked(account.accountInfo,LNodeBlocksCount) then begin
               if TAccountComp.IsAccountLocked(account.accountInfo,LNodeBlocksCount) then begin
                 DrawGrid.Canvas.Font.Color := clNavy;
                 DrawGrid.Canvas.Font.Color := clNavy;
               end else begin
               end else begin
@@ -734,8 +742,19 @@ begin
             end else begin
             end else begin
               DrawGrid.Canvas.Font.Color := clGrayText
               DrawGrid.Canvas.Font.Color := clGrayText
             end;
             end;
+          end else if TAccountComp.IsAccountForSwap(account.accountInfo) then begin
+            if TAccountComp.IsAccountForAccountSwap(account.accountInfo) then begin
+              s := 'Account SWAP';
+            end else if TAccountComp.IsAccountForCoinSwap(account.accountInfo) then begin
+              s := 'SWAP '+TAccountComp.FormatMoney(account.accountInfo.price);
+            end else s := 'SWAP';
+            if TAccountComp.IsAccountLocked(account.accountInfo,LNodeBlocksCount) then begin
+              DrawGrid.Canvas.Font.Color := clNavy;
+            end else begin
+              DrawGrid.Canvas.Font.Color := clRed;
+            end;
           end else s := '';
           end else s := '';
-          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
+          Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
         end;
         end;
       else
       else
         s := '(???)';
         s := '(???)';
@@ -809,6 +828,12 @@ begin
   InitGrid;
   InitGrid;
 end;
 end;
 
 
+procedure TAccountsGrid.SetColumns(const AColumns: TAccountColumnArray);
+begin
+  FColumns := AColumns;
+  InitGrid;
+end;
+
 procedure TAccountsGrid.SetDrawGrid(const Value: TDrawGrid);
 procedure TAccountsGrid.SetDrawGrid(const Value: TDrawGrid);
 begin
 begin
   if FDrawGrid=Value then exit;
   if FDrawGrid=Value then exit;

+ 273 - 0
src/libraries/cryptolib4pascal/ClpAESPRNGRandom.pas

@@ -0,0 +1,273 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAESPRNGRandom;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SyncObjs,
+  ClpAesEngine,
+  ClpIAesEngine,
+  ClpIBlockCipher,
+  ClpIKeyParameter,
+  ClpKeyParameter,
+  ClpIBufferedCipher,
+  ClpIBufferedBlockCipher,
+  ClpBufferedBlockCipher,
+  ClpArrayUtils,
+  ClpOSRandom,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidAESRNGSeedLength =
+    'AES RNG Seed Length must be either one of these "128/192/256 bits".';
+
+type
+  IAESPRNGRandom = interface(IInterface)
+    ['{DE2F9387-CD63-475F-AD4B-EA0692790FB2}']
+
+    procedure FillBytes(const data: TCryptoLibByteArray);
+    procedure FillNonZeroBytes(const data: TCryptoLibByteArray);
+
+  end;
+
+type
+  TAESPRNGRandom = class sealed(TInterfacedObject, IAESPRNGRandom)
+
+  strict private
+  const
+    COUNTER_SIZE = Int32(16);
+    class var
+
+      FDefaultInstance: IAESPRNGRandom;
+
+  var
+    FLock: TCriticalSection;
+    FCounter: TCryptoLibByteArray;
+    FAESRNGSeedLength, FBytesSinceSeed, FReseedAfterBytes: Int32;
+    FCipher: IBufferedCipher;
+
+    class function GetDefaultInstance: IAESPRNGRandom; static; inline;
+
+    class procedure GetRawEntropy(const AEntropy: TCryptoLibByteArray); inline;
+
+    class procedure Boot(); static;
+    class constructor CreateAESPRNGRandom();
+    class destructor DestroyAESPRNGRandom();
+
+    class procedure ValidateAESRNGSeedLength(ASeedLength: Int32);
+
+    constructor Create(const AAESRNGSeed: TCryptoLibByteArray;
+      AReseedAfterBytes: Int32); overload;
+
+    procedure DoIncrementCounter();
+
+    procedure DoSeed(const AAESRNGSeed: TCryptoLibByteArray);
+
+  public
+    constructor Create(AAESRNGSeedLength: Int32 = 32;
+      AReseedAfterBytes: Int32 = 1024 * 1024); overload;
+
+    destructor Destroy; override;
+
+    procedure FillBytes(const data: TCryptoLibByteArray); overload;
+    procedure FillNonZeroBytes(const data: TCryptoLibByteArray); overload;
+
+    class procedure GetBytes(const data: TCryptoLibByteArray); overload; static;
+    class procedure GetNonZeroBytes(const data: TCryptoLibByteArray);
+      overload; static;
+
+    class property DefaultInstance: IAESPRNGRandom read GetDefaultInstance;
+
+  end;
+
+implementation
+
+{ TAESPRNGRandom }
+
+class function TAESPRNGRandom.GetDefaultInstance: IAESPRNGRandom;
+begin
+  result := FDefaultInstance;
+end;
+
+class procedure TAESPRNGRandom.ValidateAESRNGSeedLength(ASeedLength: Int32);
+begin
+  if ((ASeedLength < 16) or (ASeedLength > 32) or ((ASeedLength and 7) <> 0))
+  then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidAESRNGSeedLength);
+  end;
+end;
+
+class procedure TAESPRNGRandom.GetRawEntropy(const AEntropy
+  : TCryptoLibByteArray);
+begin
+  TOSRandom.GetBytes(AEntropy);
+end;
+
+class procedure TAESPRNGRandom.Boot;
+begin
+  if FDefaultInstance = Nil then
+  begin
+    FDefaultInstance := TAESPRNGRandom.Create();
+  end;
+end;
+
+procedure TAESPRNGRandom.DoIncrementCounter;
+var
+  i: Int32;
+begin
+  for i := System.Low(FCounter) to System.High(FCounter) do
+  begin
+    System.Inc(FCounter[i]);
+    // Check whether we need to loop again to carry the one.
+    if (FCounter[i] <> 0) then
+    begin
+      break;
+    end;
+  end;
+end;
+
+procedure TAESPRNGRandom.DoSeed(const AAESRNGSeed: TCryptoLibByteArray);
+var
+  LKeyParameter: IKeyParameter;
+begin
+  LKeyParameter := TKeyParameter.Create(AAESRNGSeed);
+  FLock.Acquire;
+  try
+    FCipher.Init(True, LKeyParameter);
+    FBytesSinceSeed := 0;
+  finally
+    FLock.Release;
+  end;
+end;
+
+constructor TAESPRNGRandom.Create(const AAESRNGSeed: TCryptoLibByteArray;
+  AReseedAfterBytes: Int32);
+var
+  LAesEngine: IAesEngine;
+  LBlockCipher: IBlockCipher;
+  LAESRNGSeed: TCryptoLibByteArray;
+begin
+  Inherited Create();
+  LAESRNGSeed := System.Copy(AAESRNGSeed);
+  FLock := TCriticalSection.Create;
+  // Set up engine
+  LAesEngine := TAesEngine.Create();
+  LBlockCipher := LAesEngine as IBlockCipher; // ECB no padding
+  FCipher := TBufferedBlockCipher.Create(LBlockCipher) as IBufferedBlockCipher;
+  System.SetLength(FCounter, COUNTER_SIZE);
+  FAESRNGSeedLength := System.Length(LAESRNGSeed);
+  FReseedAfterBytes := AReseedAfterBytes;
+  ValidateAESRNGSeedLength(FAESRNGSeedLength);
+  DoSeed(LAESRNGSeed);
+  TArrayUtils.ZeroFill(LAESRNGSeed); // clear key from memory
+end;
+
+constructor TAESPRNGRandom.Create(AAESRNGSeedLength, AReseedAfterBytes: Int32);
+var
+  LSeed: TCryptoLibByteArray;
+begin
+  System.SetLength(LSeed, AAESRNGSeedLength);
+  GetRawEntropy(LSeed); // pure entropy from OS
+  Create(LSeed, AReseedAfterBytes);
+  TArrayUtils.ZeroFill(LSeed); // clear seed from memory
+end;
+
+destructor TAESPRNGRandom.Destroy;
+begin
+  FLock.Free;
+  inherited Destroy;
+end;
+
+procedure TAESPRNGRandom.FillBytes(const data: TCryptoLibByteArray);
+var
+  LDataLength, LDatum, LResultLength: Int32;
+  LSeed, LResult: TCryptoLibByteArray;
+begin
+  LDataLength := System.Length(data);
+  if LDataLength <= 0 then
+  begin
+    Exit;
+  end;
+
+  if (FBytesSinceSeed > FReseedAfterBytes) then
+  begin
+    System.SetLength(LSeed, FAESRNGSeedLength);
+    GetRawEntropy(LSeed); // pure entropy from OS
+    DoSeed(LSeed);
+    TArrayUtils.ZeroFill(LSeed); // clear seed from memory
+  end;
+
+  LDatum := 0;
+
+  FLock.Acquire;
+  try
+    while (LDataLength shr 4) > 0 do
+    begin
+      DoIncrementCounter;
+      LResultLength := FCipher.DoFinal(FCounter, data, LDatum);
+
+      System.Inc(LDatum, LResultLength);
+      System.Inc(FBytesSinceSeed, LResultLength);
+      System.Dec(LDataLength, LResultLength);
+    end;
+
+    if LDataLength > 0 then
+    begin
+      DoIncrementCounter;
+      LResult := FCipher.DoFinal(FCounter);
+      System.Move(LResult[0], data[LDatum], LDataLength * System.SizeOf(Byte));
+      System.Inc(FBytesSinceSeed, LDataLength);
+    end;
+
+  finally
+    FLock.Release;
+  end;
+end;
+
+procedure TAESPRNGRandom.FillNonZeroBytes(const data: TCryptoLibByteArray);
+begin
+  repeat
+    FillBytes(data);
+  until (TArrayUtils.NoZeroes(data));
+end;
+
+class constructor TAESPRNGRandom.CreateAESPRNGRandom;
+begin
+  TAESPRNGRandom.Boot();
+end;
+
+class destructor TAESPRNGRandom.DestroyAESPRNGRandom;
+begin
+  FDefaultInstance := Nil;
+end;
+
+class procedure TAESPRNGRandom.GetBytes(const data: TCryptoLibByteArray);
+begin
+  DefaultInstance.FillBytes(data);
+end;
+
+class procedure TAESPRNGRandom.GetNonZeroBytes(const data: TCryptoLibByteArray);
+begin
+  DefaultInstance.FillNonZeroBytes(data);
+end;
+
+end.

+ 0 - 101
src/libraries/cryptolib4pascal/ClpAbstractECMultiplier.pas

@@ -1,101 +0,0 @@
-{ *********************************************************************************** }
-{ *                              CryptoLib Library                                  * }
-{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
-{ *                 Github Repository <https://github.com/Xor-el>                   * }
-
-{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
-{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
-
-{ *                              Acknowledgements:                                  * }
-{ *                                                                                 * }
-{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
-{ *                           development of this library                           * }
-
-{ * ******************************************************************************* * }
-
-(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
-
-unit ClpAbstractECMultiplier;
-
-{$I CryptoLib.inc}
-
-interface
-
-uses
-  ClpBigInteger,
-  ClpIECC,
-  ClpIAbstractECMultiplier,
-  ClpECAlgorithms;
-
-type
-  TAbstractECMultiplier = class abstract(TInterfacedObject,
-    IAbstractECMultiplier, IECMultiplier)
-
-  strict protected
-
-    function CheckResult(const p: IECPoint): IECPoint; virtual;
-    function MultiplyPositive(const p: IECPoint; const k: TBigInteger)
-      : IECPoint; virtual; abstract;
-
-  public
-
-    constructor Create();
-    destructor Destroy; override;
-    function Multiply(const p: IECPoint; const k: TBigInteger)
-      : IECPoint; virtual;
-
-  end;
-
-implementation
-
-{ TAbstractECMultiplier }
-
-function TAbstractECMultiplier.CheckResult(const p: IECPoint): IECPoint;
-begin
-  result := TECAlgorithms.ImplCheckResult(p);
-end;
-
-constructor TAbstractECMultiplier.Create;
-begin
-  Inherited Create();
-end;
-
-destructor TAbstractECMultiplier.Destroy;
-begin
-  inherited Destroy;
-end;
-
-function TAbstractECMultiplier.Multiply(const p: IECPoint; const k: TBigInteger)
-  : IECPoint;
-var
-  positive: IECPoint;
-  sign: Int32;
-begin
-
-  sign := k.SignValue;
-  if ((sign = 0) or (p.IsInfinity)) then
-  begin
-    result := p.Curve.Infinity;
-    Exit;
-  end;
-
-  positive := MultiplyPositive(p, k.Abs());
-
-  if sign > 0 then
-  begin
-    result := positive
-  end
-  else
-  begin
-    result := positive.Negate();
-  end;
-
-  // /*
-  // * Although the various multipliers ought not to produce invalid output under normal
-  // * circumstances, a final check here is advised to guard against fault attacks.
-  // */
-  result := CheckResult(result);
-
-end;
-
-end.

+ 4 - 0
src/libraries/cryptolib4pascal/ClpAesEngine.pas

@@ -30,6 +30,7 @@ uses
   ClpCheck,
   ClpCheck,
   ClpBits,
   ClpBits,
   ClpConverters,
   ClpConverters,
+  ClpArrayUtils,
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring
@@ -499,6 +500,7 @@ begin
   keyLen := System.Length(key);
   keyLen := System.Length(key);
   if ((keyLen < 16) or (keyLen > 32) or ((keyLen and 7) <> 0)) then
   if ((keyLen < 16) or (keyLen > 32) or ((keyLen and 7) <> 0)) then
   begin
   begin
+    TArrayUtils.ZeroFill(key);
     raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
     raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
   end;
   end;
 
 
@@ -676,6 +678,7 @@ begin
       end
       end
   else
   else
     begin
     begin
+      TArrayUtils.ZeroFill(key);
       raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidOperation);
       raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidOperation);
     end;
     end;
   end;
   end;
@@ -696,6 +699,7 @@ begin
 
 
   result := bigW;
   result := bigW;
 
 
+  TArrayUtils.ZeroFill(key);
 end;
 end;
 
 
 function TAesEngine.GetAlgorithmName: String;
 function TAesEngine.GetAlgorithmName: String;

+ 4 - 0
src/libraries/cryptolib4pascal/ClpAesLightEngine.pas

@@ -30,6 +30,7 @@ uses
   ClpCheck,
   ClpCheck,
   ClpBits,
   ClpBits,
   ClpConverters,
   ClpConverters,
+  ClpArrayUtils,
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring
@@ -425,6 +426,7 @@ begin
   keyLen := System.Length(key);
   keyLen := System.Length(key);
   if ((keyLen < 16) or (keyLen > 32) or ((keyLen and 7) <> 0)) then
   if ((keyLen < 16) or (keyLen > 32) or ((keyLen and 7) <> 0)) then
   begin
   begin
+    TArrayUtils.ZeroFill(key);
     raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
     raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
   end;
   end;
 
 
@@ -602,6 +604,7 @@ begin
       end
       end
   else
   else
     begin
     begin
+      TArrayUtils.ZeroFill(key);
       raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidOperation);
       raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidOperation);
     end;
     end;
   end;
   end;
@@ -622,6 +625,7 @@ begin
 
 
   result := bigW;
   result := bigW;
 
 
+  TArrayUtils.ZeroFill(key);
 end;
 end;
 
 
 function TAesLightEngine.GetAlgorithmName: String;
 function TAesLightEngine.GetAlgorithmName: String;

+ 8 - 0
src/libraries/cryptolib4pascal/ClpAgreementUtilities.pas

@@ -25,6 +25,8 @@ uses
   SysUtils,
   SysUtils,
   Generics.Collections,
   Generics.Collections,
   ClpIBasicAgreement,
   ClpIBasicAgreement,
+  ClpDHBasicAgreement,
+  ClpIDHBasicAgreement,
   ClpECDHBasicAgreement,
   ClpECDHBasicAgreement,
   ClpIECDHBasicAgreement,
   ClpIECDHBasicAgreement,
   ClpECDHCBasicAgreement,
   ClpECDHCBasicAgreement,
@@ -129,6 +131,12 @@ var
 begin
 begin
   mechanism := GetMechanism(algorithm);
   mechanism := GetMechanism(algorithm);
 
 
+  if ((mechanism = 'DH') or (mechanism = 'DIFFIEHELLMAN')) then
+  begin
+    result := TDHBasicAgreement.Create() as IDHBasicAgreement;
+    Exit;
+  end;
+
   if (mechanism = 'ECDH') then
   if (mechanism = 'ECDH') then
   begin
   begin
     result := TECDHBasicAgreement.Create() as IECDHBasicAgreement;
     result := TECDHBasicAgreement.Create() as IECDHBasicAgreement;

+ 65 - 50
src/libraries/cryptolib4pascal/ClpArgon2ParametersGenerator.pas

@@ -25,17 +25,21 @@ uses
 
 
   HlpIHashInfo,
   HlpIHashInfo,
   HlpHashFactory,
   HlpHashFactory,
+  HlpArgon2TypeAndVersion,
   HlpPBKDF_Argon2NotBuildInAdapter,
   HlpPBKDF_Argon2NotBuildInAdapter,
+  ClpPbeParametersGenerator,
   ClpICipherParameters,
   ClpICipherParameters,
   ClpIArgon2ParametersGenerator,
   ClpIArgon2ParametersGenerator,
   ClpKeyParameter,
   ClpKeyParameter,
   ClpIKeyParameter,
   ClpIKeyParameter,
   ClpParametersWithIV,
   ClpParametersWithIV,
   ClpParameterUtilities,
   ClpParameterUtilities,
+  ClpArrayUtils,
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring
   SArgon2TypeInvalid = 'Selected Argon2Type is Invalid';
   SArgon2TypeInvalid = 'Selected Argon2Type is Invalid';
+  SArgon2VersionInvalid = 'Selected Argon2Version is Invalid';
   SArgon2MemoryCostTypeInvalid = 'Selected Argon2MemoryCostType is Invalid';
   SArgon2MemoryCostTypeInvalid = 'Selected Argon2MemoryCostType is Invalid';
 
 
 type
 type
@@ -45,30 +49,21 @@ type
   /// Argon2 Specification</see>, <see href="https://tools.ietf.org/html/draft-irtf-cfrg-argon2-04">
   /// Argon2 Specification</see>, <see href="https://tools.ietf.org/html/draft-irtf-cfrg-argon2-04">
   /// ietf specs</see>
   /// ietf specs</see>
   /// </summary>
   /// </summary>
-  TArgon2ParametersGenerator = class sealed(TInterfacedObject,
+  TArgon2ParametersGenerator = class sealed(TPbeParametersGenerator,
     IArgon2ParametersGenerator)
     IArgon2ParametersGenerator)
 
 
   strict private
   strict private
   var
   var
     FPassword: TCryptoLibByteArray;
     FPassword: TCryptoLibByteArray;
     FPBKDF_Argon2: IPBKDF_Argon2;
     FPBKDF_Argon2: IPBKDF_Argon2;
-    FArgon2ParametersBuilder: HlpIHashInfo.IArgon2ParametersBuilder;
-
-    /// <returns>
-    /// the password byte array.
-    /// </returns>
-    function GetPassword: TCryptoLibByteArray; inline;
-
-    /// <returns>
-    /// the Argon2 Parameter Builder Instance
-    /// </returns>
-    function GetArgon2ParametersBuilder
-      : HlpIHashInfo.IArgon2ParametersBuilder; inline;
+    FArgon2Parameters: IArgon2Parameters;
 
 
     function GenerateDerivedKey(dkLen: Int32): TCryptoLibByteArray; inline;
     function GenerateDerivedKey(dkLen: Int32): TCryptoLibByteArray; inline;
 
 
   public
   public
 
 
+    procedure Clear(); override;
+
     /// <summary>
     /// <summary>
     /// construct an Argon2 Parameters generator.
     /// construct an Argon2 Parameters generator.
     /// </summary>
     /// </summary>
@@ -77,10 +72,12 @@ type
     /// </param>
     /// </param>
     constructor Create();
     constructor Create();
 
 
-    procedure Init(argon2Type: TArgon2Type; argon2Version: TArgon2Version;
-      const password, salt, secret, additional: TCryptoLibByteArray;
-      iterations, memory, parallelism: Int32;
-      memoryCostType: TArgon2MemoryCostType);
+    destructor Destroy; override;
+
+    procedure Init(argon2Type: TCryptoLibArgon2Type;
+      argon2Version: TCryptoLibArgon2Version; const password, salt, secret,
+      additional: TCryptoLibByteArray; iterations, memory, parallelism: Int32;
+      memoryCostType: TCryptoLibArgon2MemoryCostType);
 
 
     /// <summary>
     /// <summary>
     /// Generate a key parameter derived from the password, salt, and
     /// Generate a key parameter derived from the password, salt, and
@@ -96,7 +93,7 @@ type
     /// a parameters object representing a key.
     /// a parameters object representing a key.
     /// </returns>
     /// </returns>
     function GenerateDerivedParameters(const algorithm: String; keySize: Int32)
     function GenerateDerivedParameters(const algorithm: String; keySize: Int32)
-      : ICipherParameters; overload;
+      : ICipherParameters; overload; override;
 
 
     /// <summary>
     /// <summary>
     /// Generate a key with initialisation vector parameter derived from <br />
     /// Generate a key with initialisation vector parameter derived from <br />
@@ -116,7 +113,7 @@ type
     /// a parameters object representing a key and an iv.
     /// a parameters object representing a key and an iv.
     /// </returns>
     /// </returns>
     function GenerateDerivedParameters(const algorithm: String;
     function GenerateDerivedParameters(const algorithm: String;
-      keySize, ivSize: Int32): ICipherParameters; overload;
+      keySize, ivSize: Int32): ICipherParameters; overload; override;
 
 
     /// <summary>
     /// <summary>
     /// Generate a key parameter for use with a MAC derived from the
     /// Generate a key parameter for use with a MAC derived from the
@@ -130,18 +127,7 @@ type
     /// a parameters object representing a key.
     /// a parameters object representing a key.
     /// </returns>
     /// </returns>
     function GenerateDerivedMacParameters(keySize: Int32)
     function GenerateDerivedMacParameters(keySize: Int32)
-      : ICipherParameters; overload;
-
-    /// <value>
-    /// the password byte array.
-    /// </value>
-    property password: TCryptoLibByteArray read GetPassword;
-
-    /// <returns>
-    /// the Argon2 Parameter Builder Instance
-    /// </returns>
-    property Argon2ParametersBuilder: HlpIHashInfo.IArgon2ParametersBuilder
-      read GetArgon2ParametersBuilder;
+      : ICipherParameters; override;
 
 
   end;
   end;
 
 
@@ -149,15 +135,19 @@ implementation
 
 
 { TArgon2ParametersGenerator }
 { TArgon2ParametersGenerator }
 
 
-function TArgon2ParametersGenerator.GetPassword: TCryptoLibByteArray;
+procedure TArgon2ParametersGenerator.Clear();
 begin
 begin
-  result := System.Copy(FPassword);
-end;
+  TArrayUtils.ZeroFill(FPassword);
 
 
-function TArgon2ParametersGenerator.GetArgon2ParametersBuilder
-  : HlpIHashInfo.IArgon2ParametersBuilder;
-begin
-  result := FArgon2ParametersBuilder;
+  if FArgon2Parameters <> Nil then
+  begin
+    FArgon2Parameters.Clear();
+  end;
+
+  if FPBKDF_Argon2 <> Nil then
+  begin
+    FPBKDF_Argon2.Clear();
+  end;
 end;
 end;
 
 
 constructor TArgon2ParametersGenerator.Create();
 constructor TArgon2ParametersGenerator.Create();
@@ -165,6 +155,12 @@ begin
   Inherited Create();
   Inherited Create();
 end;
 end;
 
 
+destructor TArgon2ParametersGenerator.Destroy();
+begin
+  Clear();
+  inherited Destroy;
+end;
+
 function TArgon2ParametersGenerator.GenerateDerivedKey(dkLen: Int32)
 function TArgon2ParametersGenerator.GenerateDerivedKey(dkLen: Int32)
   : TCryptoLibByteArray;
   : TCryptoLibByteArray;
 begin
 begin
@@ -210,26 +206,27 @@ begin
   result := TParametersWithIV.Create(key, dKey, keySize, ivSize);
   result := TParametersWithIV.Create(key, dKey, keySize, ivSize);
 end;
 end;
 
 
-procedure TArgon2ParametersGenerator.Init(argon2Type: TArgon2Type;
-  argon2Version: TArgon2Version; const password, salt, secret,
+procedure TArgon2ParametersGenerator.Init(argon2Type: TCryptoLibArgon2Type;
+  argon2Version: TCryptoLibArgon2Version; const password, salt, secret,
   additional: TCryptoLibByteArray; iterations, memory, parallelism: Int32;
   additional: TCryptoLibByteArray; iterations, memory, parallelism: Int32;
-  memoryCostType: TArgon2MemoryCostType);
+  memoryCostType: TCryptoLibArgon2MemoryCostType);
 var
 var
   LArgon2ParametersBuilder: IArgon2ParametersBuilder;
   LArgon2ParametersBuilder: IArgon2ParametersBuilder;
+  LArgon2Version: TArgon2Version;
 begin
 begin
-  FPassword := password;
+  FPassword := System.Copy(password);
 
 
   case argon2Type of
   case argon2Type of
-    TArgon2Type.a2tARGON2_d:
+    TCryptoLibArgon2Type.Argon2D:
       begin
       begin
         LArgon2ParametersBuilder := TArgon2dParametersBuilder.Builder();
         LArgon2ParametersBuilder := TArgon2dParametersBuilder.Builder();
       end;
       end;
 
 
-    TArgon2Type.a2tARGON2_i:
+    TCryptoLibArgon2Type.Argon2I:
       begin
       begin
         LArgon2ParametersBuilder := TArgon2iParametersBuilder.Builder();
         LArgon2ParametersBuilder := TArgon2iParametersBuilder.Builder();
       end;
       end;
-    TArgon2Type.a2tARGON2_id:
+    TCryptoLibArgon2Type.Argon2ID:
       begin
       begin
         LArgon2ParametersBuilder := TArgon2idParametersBuilder.Builder();
         LArgon2ParametersBuilder := TArgon2idParametersBuilder.Builder();
       end
       end
@@ -239,18 +236,34 @@ begin
     end;
     end;
   end;
   end;
 
 
+  case argon2Version of
+    TCryptoLibArgon2Version.Argon2Version10:
+      begin
+        LArgon2Version := TArgon2Version.a2vARGON2_VERSION_10;
+      end;
+
+    TCryptoLibArgon2Version.Argon2Version13:
+      begin
+        LArgon2Version := TArgon2Version.a2vARGON2_VERSION_13;
+      end
+  else
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SArgon2VersionInvalid);
+    end;
+  end;
+
   case memoryCostType of
   case memoryCostType of
-    TArgon2MemoryCostType.a2mctMemoryAsKB:
+    TCryptoLibArgon2MemoryCostType.MemoryAsKB:
       begin
       begin
-        LArgon2ParametersBuilder.WithVersion(argon2Version).WithSalt(salt)
+        LArgon2ParametersBuilder.WithVersion(LArgon2Version).WithSalt(salt)
           .WithSecret(secret).WithAdditional(additional)
           .WithSecret(secret).WithAdditional(additional)
           .WithIterations(iterations).WithMemoryAsKB(memory)
           .WithIterations(iterations).WithMemoryAsKB(memory)
           .WithParallelism(parallelism);
           .WithParallelism(parallelism);
       end;
       end;
 
 
-    TArgon2MemoryCostType.a2mctMemoryPowOfTwo:
+    TCryptoLibArgon2MemoryCostType.MemoryPowOfTwo:
       begin
       begin
-        LArgon2ParametersBuilder.WithVersion(argon2Version).WithSalt(salt)
+        LArgon2ParametersBuilder.WithVersion(LArgon2Version).WithSalt(salt)
           .WithSecret(secret).WithAdditional(additional)
           .WithSecret(secret).WithAdditional(additional)
           .WithIterations(iterations).WithMemoryPowOfTwo(memory)
           .WithIterations(iterations).WithMemoryPowOfTwo(memory)
           .WithParallelism(parallelism);
           .WithParallelism(parallelism);
@@ -262,8 +275,10 @@ begin
     end;
     end;
   end;
   end;
 
 
+  FArgon2Parameters := LArgon2ParametersBuilder.Build();
+  LArgon2ParametersBuilder.Clear();
   FPBKDF_Argon2 := TKDF.TPBKDF_Argon2.CreatePBKDF_Argon2(FPassword,
   FPBKDF_Argon2 := TKDF.TPBKDF_Argon2.CreatePBKDF_Argon2(FPassword,
-    LArgon2ParametersBuilder.Build());
+    FArgon2Parameters);
 end;
 end;
 
 
 end.
 end.

+ 45 - 12
src/libraries/cryptolib4pascal/ClpArrayUtils.pas

@@ -95,6 +95,10 @@ type
     class procedure Fill(const buf: TCryptoLibUInt32Array; from, &to: Int32;
     class procedure Fill(const buf: TCryptoLibUInt32Array; from, &to: Int32;
       filler: UInt32); overload; static;
       filler: UInt32); overload; static;
 
 
+    class procedure ZeroFill(const buf: TCryptoLibByteArray); static;
+
+    class function NoZeroes(const data: TCryptoLibByteArray): Boolean; static;
+
   end;
   end;
 
 
 implementation
 implementation
@@ -114,6 +118,21 @@ begin
   Result := newLength;
   Result := newLength;
 end;
 end;
 
 
+class function TArrayUtils.NoZeroes(const data: TCryptoLibByteArray): Boolean;
+var
+  i: Int32;
+begin
+  Result := True;
+  for i := System.Low(data) to System.High(data) do
+  begin
+    if data[i] = 0 then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+end;
+
 class function TArrayUtils.Concatenate(const A, B: TCryptoLibByteArray)
 class function TArrayUtils.Concatenate(const A, B: TCryptoLibByteArray)
   : TCryptoLibByteArray;
   : TCryptoLibByteArray;
 var
 var
@@ -171,7 +190,7 @@ class function TArrayUtils.AreEqual(const A, B: TCryptoLibByteArray): Boolean;
 begin
 begin
   if System.Length(A) <> System.Length(B) then
   if System.Length(A) <> System.Length(B) then
   begin
   begin
-    Result := false;
+    Result := False;
     Exit;
     Exit;
   end;
   end;
 
 
@@ -196,7 +215,7 @@ class function TArrayUtils.AreEqual(const A, B: TCryptoLibInt32Array): Boolean;
 begin
 begin
   if System.Length(A) <> System.Length(B) then
   if System.Length(A) <> System.Length(B) then
   begin
   begin
-    Result := false;
+    Result := False;
     Exit;
     Exit;
   end;
   end;
 
 
@@ -255,31 +274,45 @@ end;
 class procedure TArrayUtils.Fill(const buf: TCryptoLibByteArray;
 class procedure TArrayUtils.Fill(const buf: TCryptoLibByteArray;
   from, &to: Int32; filler: Byte);
   from, &to: Int32; filler: Byte);
 begin
 begin
-  System.FillChar(buf[from], (&to - from) * System.SizeOf(Byte), filler);
+  if buf <> Nil then
+  begin
+    System.FillChar(buf[from], (&to - from) * System.SizeOf(Byte), filler);
+  end;
 end;
 end;
 
 
 class procedure TArrayUtils.Fill(const buf: TCryptoLibInt32Array;
 class procedure TArrayUtils.Fill(const buf: TCryptoLibInt32Array;
   from, &to: Int32; filler: Int32);
   from, &to: Int32; filler: Int32);
 begin
 begin
-  while from < &to do
+  if buf <> Nil then
   begin
   begin
-    buf[from] := filler;
-    System.Inc(from);
+    while from < &to do
+    begin
+      buf[from] := filler;
+      System.Inc(from);
+    end;
   end;
   end;
 end;
 end;
 
 
 class procedure TArrayUtils.Fill(const buf: TCryptoLibUInt32Array;
 class procedure TArrayUtils.Fill(const buf: TCryptoLibUInt32Array;
   from, &to: Int32; filler: UInt32);
   from, &to: Int32; filler: UInt32);
 begin
 begin
+  if buf <> Nil then
+  begin
 {$IFDEF FPC}
 {$IFDEF FPC}
-  System.FillDWord(buf[from], (&to - from), filler);
+    System.FillDWord(buf[from], (&to - from), filler);
 {$ELSE}
 {$ELSE}
-  while from < &to do
-  begin
-    buf[from] := filler;
-    System.Inc(from);
-  end;
+    while from < &to do
+    begin
+      buf[from] := filler;
+      System.Inc(from);
+    end;
 {$ENDIF}
 {$ENDIF}
+  end;
+end;
+
+class procedure TArrayUtils.ZeroFill(const buf: TCryptoLibByteArray);
+begin
+  TArrayUtils.Fill(buf, 0, System.Length(buf), Byte(0));
 end;
 end;
 
 
 class function TArrayUtils.GetArrayHashCode(const data
 class function TArrayUtils.GetArrayHashCode(const data

+ 28 - 46
src/libraries/cryptolib4pascal/ClpAsn1Objects.pas

@@ -784,7 +784,7 @@ type
   strict protected
   strict protected
     function GetCount: Int32; virtual;
     function GetCount: Int32; virtual;
     function GetParser: IAsn1SequenceParser; virtual;
     function GetParser: IAsn1SequenceParser; virtual;
-    function GetSelf(Index: Integer): IAsn1Encodable; virtual;
+    function GetSelf(Index: Int32): IAsn1Encodable; virtual;
     function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
     function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
     function Asn1GetHashCode(): Int32; override;
     function Asn1GetHashCode(): Int32; override;
     function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
     function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
@@ -1179,7 +1179,7 @@ type
   strict protected
   strict protected
     function GetCount: Int32; virtual;
     function GetCount: Int32; virtual;
     function GetParser: IAsn1SetParser; inline;
     function GetParser: IAsn1SetParser; inline;
-    function GetSelf(Index: Integer): IAsn1Encodable; virtual;
+    function GetSelf(Index: Int32): IAsn1Encodable; virtual;
     function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
     function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
     function Asn1GetHashCode(): Int32; override;
     function Asn1GetHashCode(): Int32; override;
     function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
     function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
@@ -4838,51 +4838,48 @@ end;
 class function TDerObjectIdentifier.IsValidBranchID(const branchID: String;
 class function TDerObjectIdentifier.IsValidBranchID(const branchID: String;
   start: Int32): Boolean;
   start: Int32): Boolean;
 var
 var
-  periodAllowed: Boolean;
-  Pos: Int32;
+  digitCount, Pos: Int32;
   ch: Char;
   ch: Char;
 begin
 begin
-  periodAllowed := False;
+  digitCount := 0;
 
 
   Pos := System.length(branchID) + 1;
   Pos := System.length(branchID) + 1;
   System.Dec(Pos);
   System.Dec(Pos);
+
   while (Pos >= start) do
   while (Pos >= start) do
   begin
   begin
     ch := branchID[Pos];
     ch := branchID[Pos];
 
 
-    // TODO Leading zeroes?
-    // if (('0' <= ch) and (ch <= '9')) then
-    // begin
-    // periodAllowed := true;
-    // continue;
-    // end;
-
-    // TODO Leading zeroes?
-    if (CharInSet(ch, ['0' .. '9'])) then
-    begin
-      periodAllowed := True;
-      System.Dec(Pos);
-      continue;
-    end;
-
     if (ch = '.') then
     if (ch = '.') then
     begin
     begin
-      if (not(periodAllowed)) then
+      if ((digitCount = 0) or ((digitCount > 1) and (branchID[Pos + 1] = '0')))
+      then
       begin
       begin
         result := False;
         result := False;
         Exit;
         Exit;
       end;
       end;
 
 
-      periodAllowed := False;
-      System.Dec(Pos);
-      continue;
+      digitCount := 0;
+    end
+    else if (CharInSet(ch, ['0' .. '9'])) then
+    begin
+      System.Inc(digitCount);
+    end
+    else
+    begin
+      result := False;
+      Exit;
     end;
     end;
+    System.Dec(Pos);
+  end;
 
 
+  if ((digitCount = 0) or ((digitCount > 1) and (branchID[Pos + 1] = '0'))) then
+  begin
     result := False;
     result := False;
     Exit;
     Exit;
   end;
   end;
 
 
-  result := periodAllowed;
+  result := True;
 end;
 end;
 
 
 class function TDerObjectIdentifier.IsValidIdentifier(const identifier
 class function TDerObjectIdentifier.IsValidIdentifier(const identifier
@@ -4897,11 +4894,7 @@ begin
   end;
   end;
 
 
   first := identifier[1];
   first := identifier[1];
-  // if ((first < '0') or (first > '2')) then
-  // begin
-  // result := false;
-  // Exit;
-  // end;
+
   if (not CharInSet(first, ['0' .. '2'])) then
   if (not CharInSet(first, ['0' .. '2'])) then
   begin
   begin
     result := False;
     result := False;
@@ -5470,7 +5463,7 @@ begin
   result := TAsn1SequenceParserImpl.Create(Self as IAsn1Sequence);
   result := TAsn1SequenceParserImpl.Create(Self as IAsn1Sequence);
 end;
 end;
 
 
-function TAsn1Sequence.GetSelf(Index: Integer): IAsn1Encodable;
+function TAsn1Sequence.GetSelf(Index: Int32): IAsn1Encodable;
 begin
 begin
   result := FSeq[index];
   result := FSeq[index];
 end;
 end;
@@ -6301,7 +6294,7 @@ begin
   result := TAsn1SetParserImpl.Create(Self as IAsn1Set);
   result := TAsn1SetParserImpl.Create(Self as IAsn1Set);
 end;
 end;
 
 
-function TAsn1Set.GetSelf(Index: Integer): IAsn1Encodable;
+function TAsn1Set.GetSelf(Index: Int32): IAsn1Encodable;
 begin
 begin
   result := F_set[index];
   result := F_set[index];
 end;
 end;
@@ -8134,22 +8127,11 @@ procedure TDerBmpString.Encode(const derOut: TStream);
 var
 var
   c: TCryptoLibCharArray;
   c: TCryptoLibCharArray;
   b: TCryptoLibByteArray;
   b: TCryptoLibByteArray;
-  I, LowPoint, HighPoint: Int32;
+  I: Int32;
 begin
 begin
-  System.SetLength(c, System.length(Str));
 
 
-  // had to use this loop because somehow, StrPLCopy causes memory leak in FPC v3.0.5
-{$IFDEF DELPHIXE3_UP}
-  LowPoint := System.Low(Str);
-  HighPoint := System.High(Str);
-{$ELSE}
-  LowPoint := 1;
-  HighPoint := System.length(Str);
-{$ENDIF DELPHIXE3_UP}
-  for I := LowPoint to HighPoint do
-  begin
-    c[I - 1] := Str[I];
-  end;
+  c := TStringUtils.StringToCharArray(Str);
+
   System.SetLength(b, System.length(c) * 2);
   System.SetLength(b, System.length(c) * 2);
 
 
   I := 0;
   I := 0;

+ 1 - 1
src/libraries/cryptolib4pascal/ClpBigInteger.pas

@@ -2151,7 +2151,7 @@ end;
 
 
 function TBigInteger.IsEqualMagnitude(const x: TBigInteger): Boolean;
 function TBigInteger.IsEqualMagnitude(const x: TBigInteger): Boolean;
 var
 var
-  i: Integer;
+  i: Int32;
   xMag: TCryptoLibInt32Array;
   xMag: TCryptoLibInt32Array;
 begin
 begin
   xMag := x.Fmagnitude;
   xMag := x.Fmagnitude;

+ 48 - 0
src/libraries/cryptolib4pascal/ClpBits.pas

@@ -149,6 +149,7 @@ type
     class function RotateRight64(a_value: UInt64; a_n: Int32): UInt64;
     class function RotateRight64(a_value: UInt64; a_n: Int32): UInt64;
       static; inline;
       static; inline;
 
 
+    class function NumberOfLeadingZeros(a_value: UInt32): Int32; static;
   end;
   end;
 
 
 implementation
 implementation
@@ -372,4 +373,51 @@ begin
 {$ENDIF FPC}
 {$ENDIF FPC}
 end;
 end;
 
 
+class function TBits.NumberOfLeadingZeros(a_value: UInt32): Int32;
+{$IFNDEF FPC}
+var
+  n: UInt32;
+{$ENDIF FPC}
+begin
+
+  if (a_value = 0) then
+  begin
+    Result := ((not a_value) shr (31 - 5)) and (1 shl 5);
+    Exit;
+  end;
+
+{$IFDEF FPC}
+  Result := BsrDWord(a_value) xor ((System.SizeOf(UInt32) * 8) - 1);
+  // this also works
+  // Result := ((System.SizeOf(UInt32) * 8) - 1) - BsrDWord(a_value);
+{$ELSE}
+  n := 1;
+  if ((a_value shr 16) = 0) then
+  begin
+    n := n + 16;
+    a_value := a_value shl 16;
+  end;
+
+  if ((a_value shr 24) = 0) then
+  begin
+    n := n + 8;
+    a_value := a_value shl 8;
+  end;
+
+  if ((a_value shr 28) = 0) then
+  begin
+    n := n + 4;
+    a_value := a_value shl 4;
+  end;
+
+  if ((a_value shr 30) = 0) then
+  begin
+    n := n + 2;
+    a_value := a_value shl 2;
+  end;
+
+  Result := Int32(n) - Int32(a_value shr 31);
+{$ENDIF FPC}
+end;
+
 end.
 end.

+ 2 - 2
src/libraries/cryptolib4pascal/ClpBlockCipherModes.pas

@@ -789,7 +789,7 @@ end;
 procedure TCbcBlockCipher.Reset;
 procedure TCbcBlockCipher.Reset;
 begin
 begin
   System.Move(FIV[0], FcbcV[0], System.Length(FIV));
   System.Move(FIV[0], FcbcV[0], System.Length(FIV));
-  TArrayUtils.Fill(FcbcNextV, 0, System.Length(FcbcNextV), Byte(0));
+  TArrayUtils.ZeroFill(FcbcNextV);
 
 
   Fcipher.Reset();
   Fcipher.Reset();
 end;
 end;
@@ -1179,7 +1179,7 @@ end;
 
 
 procedure TSicBlockCipher.Reset;
 procedure TSicBlockCipher.Reset;
 begin
 begin
-  TArrayUtils.Fill(Fcounter, 0, System.Length(Fcounter), Byte(0));
+  TArrayUtils.ZeroFill(Fcounter);
   System.Move(FIV[0], Fcounter[0], System.Length(FIV) * System.SizeOf(Byte));
   System.Move(FIV[0], Fcounter[0], System.Length(FIV) * System.SizeOf(Byte));
 
 
   Fcipher.Reset();
   Fcipher.Reset();

+ 4 - 0
src/libraries/cryptolib4pascal/ClpBlowfishEngine.pas

@@ -29,6 +29,7 @@ uses
   ClpIBlowfishEngine,
   ClpIBlowfishEngine,
   ClpCheck,
   ClpCheck,
   ClpConverters,
   ClpConverters,
+  ClpArrayUtils,
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring
@@ -326,6 +327,7 @@ begin
   if ((keyLength < 4) or (keyLength > 56) or (((keyLength * 8) and 7) <> 0))
   if ((keyLength < 4) or (keyLength > 56) or (((keyLength * 8) and 7) <> 0))
   then
   then
   begin
   begin
+    TArrayUtils.ZeroFill(key);
     raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
     raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
   end;
   end;
 
 
@@ -410,6 +412,8 @@ begin
   ProcessTable(FS0[SBOX_SK - 2], FS0[SBOX_SK - 1], FS1);
   ProcessTable(FS0[SBOX_SK - 2], FS0[SBOX_SK - 1], FS1);
   ProcessTable(FS1[SBOX_SK - 2], FS1[SBOX_SK - 1], FS2);
   ProcessTable(FS1[SBOX_SK - 2], FS1[SBOX_SK - 1], FS2);
   ProcessTable(FS2[SBOX_SK - 2], FS2[SBOX_SK - 1], FS3);
   ProcessTable(FS2[SBOX_SK - 2], FS2[SBOX_SK - 1], FS3);
+
+  TArrayUtils.ZeroFill(key);
 end;
 end;
 
 
 procedure TBlowfishEngine.EncryptBlock(const src: TCryptoLibByteArray;
 procedure TBlowfishEngine.EncryptBlock(const src: TCryptoLibByteArray;

+ 1 - 1
src/libraries/cryptolib4pascal/ClpBsiObjectIdentifiers.pas

@@ -17,7 +17,7 @@
 
 
 unit ClpBsiObjectIdentifiers;
 unit ClpBsiObjectIdentifiers;
 
 
-{$I CryptoLib.inc}
+{$I ..\..\Include\CryptoLib.inc}
 
 
 interface
 interface
 
 

+ 1 - 1
src/libraries/cryptolib4pascal/ClpBufferedBlockCipher.pas

@@ -540,7 +540,7 @@ end;
 
 
 procedure TBufferedBlockCipher.Reset;
 procedure TBufferedBlockCipher.Reset;
 begin
 begin
-  TArrayUtils.Fill(Fbuf, 0, System.length(Fbuf), Byte(0));
+  TArrayUtils.ZeroFill(Fbuf);
   FbufOff := 0;
   FbufOff := 0;
 
 
   Fcipher.Reset();
   Fcipher.Reset();

+ 6 - 0
src/libraries/cryptolib4pascal/ClpChaChaEngine.pas

@@ -27,6 +27,7 @@ uses
   ClpIChaChaEngine,
   ClpIChaChaEngine,
   ClpSalsa20Engine,
   ClpSalsa20Engine,
   ClpConverters,
   ClpConverters,
+  ClpArrayUtils,
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 type
 type
@@ -242,6 +243,8 @@ begin
   begin
   begin
     if not(Byte(System.Length(keyBytes)) in [16, 32]) then
     if not(Byte(System.Length(keyBytes)) in [16, 32]) then
     begin
     begin
+      TArrayUtils.ZeroFill(keyBytes);
+      TArrayUtils.ZeroFill(ivBytes);
       raise EArgumentCryptoLibException.CreateResFmt(@SInvalidKeySize,
       raise EArgumentCryptoLibException.CreateResFmt(@SInvalidKeySize,
         [AlgorithmName]);
         [AlgorithmName]);
     end;
     end;
@@ -259,6 +262,9 @@ begin
   // IV
   // IV
   TConverters.le32_copy(PByte(ivBytes), 0, PCardinal(FEngineState),
   TConverters.le32_copy(PByte(ivBytes), 0, PCardinal(FEngineState),
     14 * System.SizeOf(UInt32), 2 * System.SizeOf(UInt32));
     14 * System.SizeOf(UInt32), 2 * System.SizeOf(UInt32));
+
+  TArrayUtils.ZeroFill(keyBytes);
+  TArrayUtils.ZeroFill(ivBytes);
 end;
 end;
 
 
 end.
 end.

+ 90 - 0
src/libraries/cryptolib4pascal/ClpCryptLibObjectIdentifiers.pas

@@ -0,0 +1,90 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCryptLibObjectIdentifiers;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpAsn1Objects,
+  ClpIAsn1Objects;
+
+type
+  TCryptLibObjectIdentifiers = class abstract(TObject)
+
+  strict private
+
+  class var
+
+    FIsBooted: Boolean;
+    FCryptlib, FEcc, FCurvey25519: IDerObjectIdentifier;
+
+    class constructor CryptLibObjectIdentifiers();
+  private
+    class function GetCryptlib: IDerObjectIdentifier; static; inline;
+    class function GetEcc: IDerObjectIdentifier; static; inline;
+    class function GetCurvey25519: IDerObjectIdentifier; static; inline;
+
+  public
+
+    class property Cryptlib: IDerObjectIdentifier read GetCryptlib;
+    class property Ecc: IDerObjectIdentifier read GetEcc;
+    class property Curvey25519: IDerObjectIdentifier read GetCurvey25519;
+
+    class procedure Boot(); static;
+  end;
+
+implementation
+
+{ TCryptLibObjectIdentifiers }
+
+class procedure TCryptLibObjectIdentifiers.Boot;
+begin
+  if not FIsBooted then
+  begin
+    FCryptlib := TDerObjectIdentifier.Create('1.3.6.1.4.1.3029');
+
+    FEcc := Cryptlib.branch('1').branch('5');
+    FCurvey25519 := Ecc.branch('1');
+
+    FIsBooted := True;
+  end;
+end;
+
+class constructor TCryptLibObjectIdentifiers.CryptLibObjectIdentifiers;
+begin
+  TCryptLibObjectIdentifiers.Boot();
+end;
+
+class function TCryptLibObjectIdentifiers.GetCryptlib: IDerObjectIdentifier;
+begin
+  result := FCryptlib;
+end;
+
+class function TCryptLibObjectIdentifiers.GetEcc: IDerObjectIdentifier;
+begin
+  result := FEcc;
+end;
+
+class function TCryptLibObjectIdentifiers.GetCurvey25519: IDerObjectIdentifier;
+begin
+  result := FCurvey25519;
+end;
+
+end.

+ 3 - 36
src/libraries/cryptolib4pascal/ClpCryptoApiRandomGenerator.pas

@@ -22,12 +22,11 @@ unit ClpCryptoApiRandomGenerator;
 interface
 interface
 
 
 uses
 uses
-  SyncObjs,
-  ClpCryptoLibTypes,
   ClpIRandomNumberGenerator,
   ClpIRandomNumberGenerator,
   ClpRandomNumberGenerator,
   ClpRandomNumberGenerator,
   ClpICryptoApiRandomGenerator,
   ClpICryptoApiRandomGenerator,
-  ClpIRandomGenerator;
+  ClpIRandomGenerator,
+  ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring
   SNegativeOffset = 'Start Offset Cannot be Negative, "Start"';
   SNegativeOffset = 'Start Offset Cannot be Negative, "Start"';
@@ -44,14 +43,6 @@ type
   var
   var
     FrndProv: IRandomNumberGenerator;
     FrndProv: IRandomNumberGenerator;
 
 
-    class var
-
-      FLock: TCriticalSection;
-
-    class procedure Boot(); static;
-    class constructor CreateCryptoApiRandomGenerator();
-    class destructor DestroyCryptoApiRandomGenerator();
-
   public
   public
     /// <summary>
     /// <summary>
     /// Uses TRandomNumberGenerator.CreateRNG() to Get randomness generator
     /// Uses TRandomNumberGenerator.CreateRNG() to Get randomness generator
@@ -90,14 +81,6 @@ begin
   // We don't care about the seed
   // We don't care about the seed
 end;
 end;
 
 
-class procedure TCryptoApiRandomGenerator.Boot;
-begin
-  if FLock = Nil then
-  begin
-    FLock := TCriticalSection.Create;
-  end;
-end;
-
 procedure TCryptoApiRandomGenerator.AddSeedMaterial
 procedure TCryptoApiRandomGenerator.AddSeedMaterial
   (const seed: TCryptoLibByteArray);
   (const seed: TCryptoLibByteArray);
 begin
 begin
@@ -110,16 +93,6 @@ begin
   FrndProv := rng;
   FrndProv := rng;
 end;
 end;
 
 
-class constructor TCryptoApiRandomGenerator.CreateCryptoApiRandomGenerator;
-begin
-  TCryptoApiRandomGenerator.Boot;
-end;
-
-class destructor TCryptoApiRandomGenerator.DestroyCryptoApiRandomGenerator;
-begin
-  FLock.Free;
-end;
-
 constructor TCryptoApiRandomGenerator.Create;
 constructor TCryptoApiRandomGenerator.Create;
 begin
 begin
   Create(TRandomNumberGenerator.CreateRNG());
   Create(TRandomNumberGenerator.CreateRNG());
@@ -127,13 +100,7 @@ end;
 
 
 procedure TCryptoApiRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray);
 procedure TCryptoApiRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray);
 begin
 begin
-
-  FLock.Acquire;
-  try
-    FrndProv.GetBytes(bytes);
-  finally
-    FLock.Release;
-  end;
+  FrndProv.GetBytes(bytes);
 end;
 end;
 
 
 procedure TCryptoApiRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray;
 procedure TCryptoApiRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray;

+ 1 - 1
src/libraries/cryptolib4pascal/ClpCryptoLibTypes.pas

@@ -53,7 +53,7 @@ type
   EInvalidCipherTextCryptoLibException = class(ECryptoLibException);
   EInvalidCipherTextCryptoLibException = class(ECryptoLibException);
   EStreamCryptoLibException = class(ECryptoLibException);
   EStreamCryptoLibException = class(ECryptoLibException);
   ESecurityUtilityCryptoLibException = class(ECryptoLibException);
   ESecurityUtilityCryptoLibException = class(ECryptoLibException);
-  EAccessCryptoLibException = class(ECryptoLibException);
+  EOSRandomCryptoLibException = class(ECryptoLibException);
   EDataLengthCryptoLibException = class(ECryptoLibException);
   EDataLengthCryptoLibException = class(ECryptoLibException);
   EMaxBytesExceededCryptoLibException = class(ECryptoLibException);
   EMaxBytesExceededCryptoLibException = class(ECryptoLibException);
   EOutputLengthCryptoLibException = class(ECryptoLibException);
   EOutputLengthCryptoLibException = class(ECryptoLibException);

+ 55 - 25
src/libraries/cryptolib4pascal/ClpCurve25519Custom.pas

@@ -237,8 +237,8 @@ type
   strict private
   strict private
 
 
   type
   type
-    TCurve25519LookupTable = class sealed(TInterfacedObject,
-      ICurve25519LookupTable, IECLookupTable)
+    TCurve25519LookupTable = class sealed(TAbstractECLookupTable,
+      ICurve25519LookupTable)
 
 
     strict private
     strict private
     var
     var
@@ -246,16 +246,19 @@ type
       Fm_table: TCryptoLibUInt32Array;
       Fm_table: TCryptoLibUInt32Array;
       Fm_size: Int32;
       Fm_size: Int32;
 
 
-      function GetSize: Int32; virtual;
+      function CreatePoint(const x, y: TCryptoLibUInt32Array): IECPoint;
+
+    strict protected
+
+      function GetSize: Int32; override;
 
 
     public
     public
 
 
       constructor Create(const outer: ICurve25519;
       constructor Create(const outer: ICurve25519;
         const table: TCryptoLibUInt32Array; size: Int32);
         const table: TCryptoLibUInt32Array; size: Int32);
 
 
-      function Lookup(index: Int32): IECPoint; virtual;
-
-      property size: Int32 read GetSize;
+      function Lookup(index: Int32): IECPoint; override;
+      function LookupVar(index: Int32): IECPoint; override;
 
 
     end;
     end;
 
 
@@ -267,8 +270,6 @@ type
   var
   var
     Fq: TBigInteger;
     Fq: TBigInteger;
 
 
-    class function GetCurve25519_Q: TBigInteger; static; inline;
-
   strict protected
   strict protected
   var
   var
     Fm_infinity: ICurve25519Point;
     Fm_infinity: ICurve25519Point;
@@ -300,8 +301,6 @@ type
     property Infinity: IECPoint read GetInfinity;
     property Infinity: IECPoint read GetInfinity;
     property FieldSize: Int32 read GetFieldSize;
     property FieldSize: Int32 read GetFieldSize;
 
 
-    class property Curve25519_Q: TBigInteger read GetCurve25519_Q;
-
   end;
   end;
 
 
 implementation
 implementation
@@ -601,7 +600,7 @@ end;
 
 
 class function TCurve25519FieldElement.GetQ: TBigInteger;
 class function TCurve25519FieldElement.GetQ: TBigInteger;
 begin
 begin
-  result := TCurve25519.Curve25519_Q;
+  result := TNat256.ToBigInteger(TCurve25519Field.P);
 end;
 end;
 
 
 class procedure TCurve25519FieldElement.Boot;
 class procedure TCurve25519FieldElement.Boot;
@@ -708,10 +707,10 @@ begin
     * Q == 8m + 5, so we use Pocklington's method for this case.
     * Q == 8m + 5, so we use Pocklington's method for this case.
     *
     *
     * First, raise this element to the exponent 2^252 - 2^1 (i.e. m + 1)
     * First, raise this element to the exponent 2^252 - 2^1 (i.e. m + 1)
-    * 
+    *
     * Breaking up the exponent's binary representation into "repunits", we get:
     * Breaking up the exponent's binary representation into "repunits", we get:
     * { 251 1s } { 1 0s }
     * { 251 1s } { 1 0s }
-    * 
+    *
     * Therefore we need an addition chain containing 251 (the lengths of the repunits)
     * Therefore we need an addition chain containing 251 (the lengths of the repunits)
     * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251]
     * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251]
   *)
   *)
@@ -1236,25 +1235,20 @@ end;
 
 
 { TCurve25519 }
 { TCurve25519 }
 
 
-class function TCurve25519.GetCurve25519_Q: TBigInteger;
-begin
-  result := TNat256.ToBigInteger(TCurve25519Field.P);
-end;
-
 constructor TCurve25519.Create;
 constructor TCurve25519.Create;
 begin
 begin
-  Fq := Curve25519_Q;
+  Fq := TCurve25519FieldElement.Q;
   Inherited Create(Fq);
   Inherited Create(Fq);
   Fm_infinity := TCurve25519Point.Create(Self as IECCurve, Nil, Nil);
   Fm_infinity := TCurve25519Point.Create(Self as IECCurve, Nil, Nil);
 
 
   Fm_a := FromBigInteger(TBigInteger.Create(1,
   Fm_a := FromBigInteger(TBigInteger.Create(1,
-    THex.Decode
+    THex.decode
     ('2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144')));
     ('2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144')));
   Fm_b := FromBigInteger(TBigInteger.Create(1,
   Fm_b := FromBigInteger(TBigInteger.Create(1,
-    THex.Decode
+    THex.decode
     ('7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864')));
     ('7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864')));
   Fm_order := TBigInteger.Create(1,
   Fm_order := TBigInteger.Create(1,
-    THex.Decode
+    THex.decode
     ('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED'));
     ('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED'));
   Fm_cofactor := TBigInteger.ValueOf(8);
   Fm_cofactor := TBigInteger.ValueOf(8);
   Fm_coord := Curve25519_DEFAULT_COORDS;
   Fm_coord := Curve25519_DEFAULT_COORDS;
@@ -1342,6 +1336,26 @@ begin
   Fm_size := size;
   Fm_size := size;
 end;
 end;
 
 
+function TCurve25519.TCurve25519LookupTable.CreatePoint(const x,
+  y: TCryptoLibUInt32Array): IECPoint;
+var
+  XFieldElement, YFieldElement: ICurve25519FieldElement;
+  CURVE25519_AFFINE_ZS: TCryptoLibGenericArray<IECFieldElement>;
+  C_a: TBigInteger;
+begin
+  C_a := TBigInteger.Create(1,
+    THex.decode
+    ('2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144'));
+  CURVE25519_AFFINE_ZS := TCryptoLibGenericArray<IECFieldElement>.Create
+    (TCurve25519FieldElement.Create(TBigInteger.One) as ICurve25519FieldElement,
+    TCurve25519FieldElement.Create(C_a) as ICurve25519FieldElement);
+
+  XFieldElement := TCurve25519FieldElement.Create(x);
+  YFieldElement := TCurve25519FieldElement.Create(y);
+  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement,
+    CURVE25519_AFFINE_ZS, false);
+end;
+
 function TCurve25519.TCurve25519LookupTable.GetSize: Int32;
 function TCurve25519.TCurve25519LookupTable.GetSize: Int32;
 begin
 begin
   result := Fm_size;
   result := Fm_size;
@@ -1370,9 +1384,25 @@ begin
     pos := pos + (CURVE25519_FE_INTS * 2);
     pos := pos + (CURVE25519_FE_INTS * 2);
   end;
   end;
 
 
-  result := Fm_outer.CreateRawPoint(TCurve25519FieldElement.Create(x)
-    as ICurve25519FieldElement, TCurve25519FieldElement.Create(y)
-    as ICurve25519FieldElement, false);
+  result := CreatePoint(x, y)
+end;
+
+function TCurve25519.TCurve25519LookupTable.LookupVar(index: Int32): IECPoint;
+var
+  x, y: TCryptoLibUInt32Array;
+  pos, J: Int32;
+begin
+  x := TNat256.Create();
+  y := TNat256.Create();
+  pos := index * CURVE25519_FE_INTS * 2;
+
+  for J := 0 to System.Pred(CURVE25519_FE_INTS) do
+  begin
+    x[J] := Fm_table[pos + J];
+    y[J] := Fm_table[pos + CURVE25519_FE_INTS + J];
+  end;
+
+  result := CreatePoint(x, y)
 end;
 end;
 
 
 end.
 end.

+ 48 - 42
src/libraries/cryptolib4pascal/ClpCustomNamedCurves.pas

@@ -28,9 +28,11 @@ uses
   ClpGlvTypeBParameters,
   ClpGlvTypeBParameters,
   ClpIGlvTypeBEndomorphism,
   ClpIGlvTypeBEndomorphism,
   ClpSecObjectIdentifiers,
   ClpSecObjectIdentifiers,
+  ClpCryptLibObjectIdentifiers,
   ClpCryptoLibTypes,
   ClpCryptoLibTypes,
   ClpBigInteger,
   ClpBigInteger,
   ClpECC,
   ClpECC,
+  ClpECCompUtilities,
   ClpSecP256K1Custom,
   ClpSecP256K1Custom,
   ClpISecP256K1Custom,
   ClpISecP256K1Custom,
   ClpSecP256R1Custom,
   ClpSecP256R1Custom,
@@ -47,6 +49,8 @@ uses
   ClpX9ECC,
   ClpX9ECC,
   ClpIX9ECC,
   ClpIX9ECC,
   ClpIAsn1Objects,
   ClpIAsn1Objects,
+  ClpScalarSplitParameters,
+  ClpIScalarSplitParameters,
   ClpGlvTypeBEndomorphism,
   ClpGlvTypeBEndomorphism,
   ClpX9ECParameters,
   ClpX9ECParameters,
   ClpIX9ECParameters,
   ClpIX9ECParameters,
@@ -69,8 +73,8 @@ type
 
 
     class function GetNames: TCryptoLibStringArray; static; inline;
     class function GetNames: TCryptoLibStringArray; static; inline;
 
 
-    class procedure DefineCurve(const name: String;
-      const holder: IX9ECParametersHolder); static; inline;
+    // class procedure DefineCurve(const name: String;
+    // const holder: IX9ECParametersHolder); static; inline;
 
 
     class procedure DefineCurveWithOid(const name: String;
     class procedure DefineCurveWithOid(const name: String;
       const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
       const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
@@ -84,6 +88,9 @@ type
     class function ConfigureCurveGlv(const c: IECCurve;
     class function ConfigureCurveGlv(const c: IECCurve;
       const p: IGlvTypeBParameters): IECCurve; static; inline;
       const p: IGlvTypeBParameters): IECCurve; static; inline;
 
 
+    class function ConfigureBasepoint(const curve: IECCurve;
+      const encoding: String): IX9ECPoint; static;
+
     class procedure Boot(); static;
     class procedure Boot(); static;
     class constructor CreateCustomNamedCurves();
     class constructor CreateCustomNamedCurves();
     class destructor DestroyCustomNamedCurves();
     class destructor DestroyCustomNamedCurves();
@@ -220,17 +227,24 @@ implementation
 
 
 { TCustomNamedCurves }
 { TCustomNamedCurves }
 
 
-class procedure TCustomNamedCurves.DefineCurve(const name: String;
-  const holder: IX9ECParametersHolder);
-var
-  LName: string;
+class function TCustomNamedCurves.ConfigureBasepoint(const curve: IECCurve;
+  const encoding: String): IX9ECPoint;
 begin
 begin
-  LName := name;
-  Fnames.Add(LName);
-  LName := UpperCase(LName);
-  FnameToCurve.Add(LName, holder);
+  result := TX9ECPoint.Create(curve, THex.Decode(encoding));
+  TWnafUtilities.ConfigureBasepoint(result.Point);
 end;
 end;
 
 
+// class procedure TCustomNamedCurves.DefineCurve(const name: String;
+// const holder: IX9ECParametersHolder);
+// var
+// LName: string;
+// begin
+// LName := name;
+// Fnames.Add(LName);
+// LName := UpperCase(LName);
+// FnameToCurve.Add(LName, holder);
+// end;
+
 class procedure TCustomNamedCurves.DefineCurveWithOid(const name: String;
 class procedure TCustomNamedCurves.DefineCurveWithOid(const name: String;
   const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
   const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
 var
 var
@@ -353,7 +367,8 @@ begin
 
 
   Fnames := TList<String>.Create();
   Fnames := TList<String>.Create();
 
 
-  DefineCurve('curve25519', TCurve25519Holder.Instance);
+  DefineCurveWithOid('curve25519', TCryptlibObjectIdentifiers.Curvey25519,
+    TCurve25519Holder.Instance);
 
 
   DefineCurveWithOid('secp256k1', TSecObjectIdentifiers.SecP256k1,
   DefineCurveWithOid('secp256k1', TSecObjectIdentifiers.SecP256k1,
     TSecP256K1Holder.Instance);
     TSecP256K1Holder.Instance);
@@ -397,10 +412,8 @@ begin
     *
     *
     * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14)
     * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14)
     * }
     * }
-  G := TX9ECPoint.Create(curve,
-    THex.Decode('04' +
-    '2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A' +
-    '20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9'));
+  G := ConfigureBasepoint(curve,
+    '042AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9');
 
 
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
 end;
 end;
@@ -426,19 +439,19 @@ begin
     ('7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', 16),
     ('7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', 16),
     TBigInteger.Create
     TBigInteger.Create
     ('5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', 16),
     ('5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', 16),
-    TCryptoLibGenericArray<TBigInteger>.Create
+
+    TScalarSplitParameters.Create(TCryptoLibGenericArray<TBigInteger>.Create
     (TBigInteger.Create('3086d221a7d46bcde86c90e49284eb15', 16),
     (TBigInteger.Create('3086d221a7d46bcde86c90e49284eb15', 16),
     TBigInteger.Create('-e4437ed6010e88286f547fa90abfe4c3', 16)),
     TBigInteger.Create('-e4437ed6010e88286f547fa90abfe4c3', 16)),
-    TCryptoLibGenericArray<TBigInteger>.Create
-    (TBigInteger.Create('114ca50f7a8e2f3f657c1108d9d44cfd8', 16),
+    TCryptoLibGenericArray<TBigInteger>.Create(TBigInteger.Create
+    ('114ca50f7a8e2f3f657c1108d9d44cfd8', 16),
     TBigInteger.Create('3086d221a7d46bcde86c90e49284eb15', 16)),
     TBigInteger.Create('3086d221a7d46bcde86c90e49284eb15', 16)),
     TBigInteger.Create('3086d221a7d46bcde86c90e49284eb153dab', 16),
     TBigInteger.Create('3086d221a7d46bcde86c90e49284eb153dab', 16),
-    TBigInteger.Create('e4437ed6010e88286f547fa90abfe4c42212', 16), 272);
+    TBigInteger.Create('e4437ed6010e88286f547fa90abfe4c42212', 16), 272)
+    as IScalarSplitParameters);
   curve := ConfigureCurveGlv(TSecP256K1Curve.Create() as ISecP256K1Curve, glv);
   curve := ConfigureCurveGlv(TSecP256K1Curve.Create() as ISecP256K1Curve, glv);
-  G := TX9ECPoint.Create(curve,
-    THex.Decode('04' +
-    '79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' +
-    '483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8'));
+  G := ConfigureBasepoint(curve,
+    '0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8');
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
 end;
 end;
 
 
@@ -458,11 +471,9 @@ var
 begin
 begin
   S := THex.Decode('A335926AA319A27A1D00896A6773A4827ACDAC73');
   S := THex.Decode('A335926AA319A27A1D00896A6773A4827ACDAC73');
   curve := ConfigureCurve(TSecP384R1Curve.Create() as ISecP384R1Curve);
   curve := ConfigureCurve(TSecP384R1Curve.Create() as ISecP384R1Curve);
-  G := TX9ECPoint.Create(curve,
-    THex.Decode('04' +
-    'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7'
-    + '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F')
-    );
+  G := ConfigureBasepoint(curve,
+    '04' + 'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7'
+    + '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F');
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
 end;
 end;
 
 
@@ -482,11 +493,9 @@ var
 begin
 begin
   S := THex.Decode('D09E8800291CB85396CC6717393284AAA0DA64BA');
   S := THex.Decode('D09E8800291CB85396CC6717393284AAA0DA64BA');
   curve := ConfigureCurve(TSecP521R1Curve.Create() as ISecP521R1Curve);
   curve := ConfigureCurve(TSecP521R1Curve.Create() as ISecP521R1Curve);
-  G := TX9ECPoint.Create(curve,
-    THex.Decode('04' +
-    '00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66'
-    + '011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650')
-    );
+  G := ConfigureBasepoint(curve,
+    '04' + '00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66'
+    + '011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650');
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
 end;
 end;
 
 
@@ -506,11 +515,9 @@ var
 begin
 begin
   S := Nil;
   S := Nil;
   curve := ConfigureCurve(TSecT283K1Curve.Create() as ISecT283K1Curve);
   curve := ConfigureCurve(TSecT283K1Curve.Create() as ISecT283K1Curve);
-  G := TX9ECPoint.Create(curve,
-    THex.Decode('04' +
-    '0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836' +
-    '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259')
-    );
+  G := ConfigureBasepoint(curve,
+    '04' + '0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836'
+    + '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259');
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
 end;
 end;
 
 
@@ -530,10 +537,9 @@ var
 begin
 begin
   S := THex.Decode('C49D360886E704936A6678E1139D26B7819F7E90');
   S := THex.Decode('C49D360886E704936A6678E1139D26B7819F7E90');
   curve := ConfigureCurve(TSecP256R1Curve.Create() as ISecP256R1Curve);
   curve := ConfigureCurve(TSecP256R1Curve.Create() as ISecP256R1Curve);
-  G := TX9ECPoint.Create(curve,
-    THex.Decode('04' +
-    '6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296' +
-    '4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5'));
+  G := ConfigureBasepoint(curve,
+    '046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5');
+
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
   result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
 end;
 end;
 
 

+ 187 - 0
src/libraries/cryptolib4pascal/ClpDHAgreement.pas

@@ -0,0 +1,187 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHAgreement;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpISecureRandom,
+  ClpSecureRandom,
+  ClpICipherParameters,
+  ClpIDHAgreement,
+  ClpIDHParameters,
+  ClpDHKeyPairGenerator,
+  ClpIDHKeyPairGenerator,
+  ClpIDHPrivateKeyParameters,
+  ClpIDHPublicKeyParameters,
+  ClpDHKeyGenerationParameters,
+  ClpIDHKeyGenerationParameters,
+  ClpIAsymmetricKeyParameter,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIParametersWithRandom,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SDHPublicKeyWrongParameter =
+    'Diffie-Hellman Public Key has Wrong Parameters.';
+  SNotDHPrivateKeyParameters = 'DHEngine Expects DHPrivateKeyParameters';
+  SMessageNotInitialized = 'Message not Initialised';
+  SSharedKeyInvalid = 'Shared Key Can''t be 1';
+  SDHPublicKeyWeak = 'Diffie-Hellman Public Key is Weak';
+  SDHPublicKeyNil = 'DH Public Key Parameter Cannot be Nil';
+
+type
+  /// <summary>
+  /// <para>
+  /// a Diffie-Hellman key exchange engine.
+  /// </para>
+  /// <para>
+  /// note: This uses MTI/A0 key agreement in order to make the key
+  /// agreement secure against passive attacks. If you're doing
+  /// Diffie-Hellman and both parties have long term public keys you
+  /// should look at using this. For further information have a look at
+  /// RFC 2631.
+  /// </para>
+  /// <para>
+  /// It's possible to extend this to more than two parties as well, for
+  /// the moment that is left as an exercise for the reader.
+  /// </para>
+  /// </summary>
+  TDHAgreement = class sealed(TInterfacedObject, IDHAgreement)
+
+  strict private
+  var
+    Fkey: IDHPrivateKeyParameters;
+    FdhParams: IDHParameters;
+    FprivateValue: TBigInteger;
+    Frandom: ISecureRandom;
+
+  public
+    /// <summary>
+    /// initialise the agreement engine.
+    /// </summary>
+    procedure Init(const parameters: ICipherParameters);
+
+    /// <summary>
+    /// calculate our initial message.
+    /// </summary>
+    function CalculateMessage(): TBigInteger;
+
+    /// <summary>
+    /// given a message from a given party and the corresponding public key
+    /// calculate the next message in the agreement sequence. In this case
+    /// this will represent the shared secret.
+    /// </summary>
+    function CalculateAgreement(const pub: IDHPublicKeyParameters;
+      const &message: TBigInteger): TBigInteger;
+
+  end;
+
+implementation
+
+{ TDHAgreement }
+
+function TDHAgreement.CalculateAgreement(const pub: IDHPublicKeyParameters;
+  const &message: TBigInteger): TBigInteger;
+var
+  p, peerY: TBigInteger;
+begin
+
+  if (pub = Nil) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SDHPublicKeyNil);
+  end;
+
+  if (not &message.IsInitialized) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SMessageNotInitialized);
+  end;
+
+  if (not(pub.parameters.Equals(FdhParams))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SDHPublicKeyWrongParameter);
+  end;
+
+  p := FdhParams.p;
+
+  peerY := pub.Y;
+
+  if ((not peerY.IsInitialized) or (peerY.CompareTo(TBigInteger.One) <= 0) or
+    (peerY.CompareTo(p.Subtract(TBigInteger.One)) >= 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SDHPublicKeyWeak);
+  end;
+
+  result := peerY.ModPow(FprivateValue, p);
+
+  if (result.Equals(TBigInteger.One)) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SSharedKeyInvalid);
+  end;
+
+  result := &message.ModPow(Fkey.X, p).Multiply(result).&Mod(p);
+end;
+
+function TDHAgreement.CalculateMessage: TBigInteger;
+var
+  dhGen: IDHKeyPairGenerator;
+  dhPair: IAsymmetricCipherKeyPair;
+begin
+  dhGen := TDHKeyPairGenerator.Create();
+
+  dhGen.Init(TDHKeyGenerationParameters.Create(Frandom, FdhParams)
+    as IDHKeyGenerationParameters);
+
+  dhPair := dhGen.GenerateKeyPair();
+
+  FprivateValue := (dhPair.Private as IDHPrivateKeyParameters).X;
+
+  result := (dhPair.Public as IDHPublicKeyParameters).Y;
+end;
+
+procedure TDHAgreement.Init(const parameters: ICipherParameters);
+var
+  kParam: IAsymmetricKeyParameter;
+  rParam: IParametersWithRandom;
+begin
+  if Supports(parameters, IParametersWithRandom, rParam) then
+  begin
+    Frandom := rParam.Random;
+    kParam := rParam.parameters as IAsymmetricKeyParameter;
+  end
+  else
+  begin
+    Frandom := TSecureRandom.Create();
+    kParam := parameters as IAsymmetricKeyParameter;
+  end;
+
+  if (not Supports(kParam, IDHPrivateKeyParameters)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNotDHPrivateKeyParameters);
+  end;
+
+  Fkey := kParam as IDHPrivateKeyParameters;
+  FdhParams := Fkey.parameters;
+end;
+
+end.

+ 148 - 0
src/libraries/cryptolib4pascal/ClpDHBasicAgreement.pas

@@ -0,0 +1,148 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHBasicAgreement;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpICipherParameters,
+  ClpIBasicAgreement,
+  ClpIDHBasicAgreement,
+  ClpIDHParameters,
+  ClpIDHPrivateKeyParameters,
+  ClpIDHPublicKeyParameters,
+  ClpIParametersWithRandom,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SDHPublicKeyWrongParameter =
+    'Diffie-Hellman Public Key has Wrong Parameters.';
+  SNotDHPrivateKeyParameters = 'DHEngine Expects DHPrivateKeyParameters';
+  SAlgorithmNotInitialized = 'Agreement Algorithm not Initialised';
+  SSharedKeyInvalid = 'Shared Key Can''t be 1';
+  SDHPublicKeyWeak = 'Diffie-Hellman Public Key is Weak';
+
+type
+  /// <summary>
+  /// <para>
+  /// a Diffie-Hellman key agreement class.
+  /// </para>
+  /// <para>
+  /// note: This is only the basic algorithm, it doesn't take advantage
+  /// of long term public keys if they are available. See the DHAgreement
+  /// class for a "better" implementation.
+  /// </para>
+  /// </summary>
+  TDHBasicAgreement = class sealed(TInterfacedObject, IDHBasicAgreement,
+    IBasicAgreement)
+
+  strict private
+  var
+    Fkey: IDHPrivateKeyParameters;
+    FdhParams: IDHParameters;
+
+  public
+    /// <summary>
+    /// initialise the agreement engine.
+    /// </summary>
+    procedure Init(const parameters: ICipherParameters);
+
+    /// <summary>
+    /// return the field size for the agreement algorithm in bytes.
+    /// </summary>
+    function GetFieldSize(): Int32;
+
+    /// <summary>
+    /// given a short term public key from a given party calculate the next
+    /// message in the agreement sequence.
+    /// </summary>
+    function CalculateAgreement(const pubKey: ICipherParameters): TBigInteger;
+
+  end;
+
+implementation
+
+{ TDHBasicAgreement }
+
+function TDHBasicAgreement.CalculateAgreement(const pubKey: ICipherParameters)
+  : TBigInteger;
+var
+  pub: IDHPublicKeyParameters;
+  p, peerY: TBigInteger;
+begin
+
+  if (Fkey = Nil) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SAlgorithmNotInitialized);
+  end;
+
+  pub := pubKey as IDHPublicKeyParameters;
+
+  if (not(pub.parameters.Equals(FdhParams))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SDHPublicKeyWrongParameter);
+  end;
+
+  p := FdhParams.p;
+
+  peerY := pub.Y;
+
+  if ((not peerY.IsInitialized) or (peerY.CompareTo(TBigInteger.One) <= 0) or
+    (peerY.CompareTo(p.Subtract(TBigInteger.One)) >= 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SDHPublicKeyWeak);
+  end;
+
+  result := peerY.ModPow(Fkey.X, p);
+
+  if (result.Equals(TBigInteger.One)) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SSharedKeyInvalid);
+  end;
+end;
+
+function TDHBasicAgreement.GetFieldSize: Int32;
+begin
+  result := (Fkey.parameters.p.BitLength + 7) div 8;
+end;
+
+procedure TDHBasicAgreement.Init(const parameters: ICipherParameters);
+var
+  Lparameters: ICipherParameters;
+begin
+  Lparameters := parameters;
+  if Supports(Lparameters, IParametersWithRandom) then
+  begin
+    Lparameters := (Lparameters as IParametersWithRandom).parameters;
+  end;
+
+  if (not Supports(Lparameters, IDHPrivateKeyParameters)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNotDHPrivateKeyParameters);
+  end;
+
+  Fkey := Lparameters as IDHPrivateKeyParameters;
+  FdhParams := Fkey.parameters;
+end;
+
+end.

+ 104 - 0
src/libraries/cryptolib4pascal/ClpDHBasicKeyPairGenerator.pas

@@ -0,0 +1,104 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHBasicKeyPairGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHParameters,
+  ClpIDHBasicKeyPairGenerator,
+  ClpDHPublicKeyParameters,
+  ClpIDHPublicKeyParameters,
+  ClpDHPrivateKeyParameters,
+  ClpIDHPrivateKeyParameters,
+  ClpAsymmetricCipherKeyPair,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIKeyGenerationParameters,
+  ClpIDHKeyGenerationParameters,
+  ClpDHKeyGeneratorHelper,
+  ClpIDHKeyGeneratorHelper,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SParametersCannotBeNil = '"parameters" Cannot Be Nil';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// a basic Diffie-Hellman key pair generator.
+  /// </para>
+  /// <para>
+  /// This generates keys consistent for use with the basic algorithm for
+  /// Diffie-Hellman.
+  /// </para>
+  /// </summary>
+  TDHBasicKeyPairGenerator = class sealed(TInterfacedObject,
+    IAsymmetricCipherKeyPairGenerator, IDHBasicKeyPairGenerator)
+
+  strict private
+
+  var
+    Fparam: IDHKeyGenerationParameters;
+
+  public
+
+    procedure Init(const parameters: IKeyGenerationParameters);
+
+    function GenerateKeyPair(): IAsymmetricCipherKeyPair;
+
+  end;
+
+implementation
+
+{ TDHBasicKeyPairGenerator }
+
+function TDHBasicKeyPairGenerator.GenerateKeyPair: IAsymmetricCipherKeyPair;
+var
+  dhp: IDHParameters;
+  helper: IDHKeyGeneratorHelper;
+  x, y: TBigInteger;
+begin
+
+  helper := TDHKeyGeneratorHelper.Instance;
+  dhp := Fparam.parameters;
+
+  x := helper.CalculatePrivate(dhp, Fparam.Random);
+  y := helper.CalculatePublic(dhp, x);
+
+  result := TAsymmetricCipherKeyPair.Create(TDHPublicKeyParameters.Create(y,
+    dhp) as IDHPublicKeyParameters, TDHPrivateKeyParameters.Create(x, dhp)
+    as IDHPrivateKeyParameters);
+end;
+
+procedure TDHBasicKeyPairGenerator.Init(const parameters
+  : IKeyGenerationParameters);
+begin
+  if (parameters = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SParametersCannotBeNil);
+  end;
+
+  Fparam := parameters as IDHKeyGenerationParameters;
+end;
+
+end.

+ 242 - 0
src/libraries/cryptolib4pascal/ClpDHDomainParameters.pas

@@ -0,0 +1,242 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHDomainParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHDomainParameters,
+  ClpIDHValidationParams,
+  ClpDHValidationParams,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SPNil = 'P Cannot be Nil';
+  SGNil = 'G Cannot be Nil';
+  SQNil = 'Q Cannot be Nil';
+  SJNil = 'J Cannot be Nil';
+  SBadSequenceSize = 'Bad Sequence Size "seq": %d';
+  SInvalidDHDomainParameters = 'Invalid DHDomainParameters: %s';
+
+type
+  TDHDomainParameters = class(TAsn1Encodable, IDHDomainParameters)
+
+  strict private
+  var
+    Fp, Fg, Fq, Fj: IDerInteger;
+    FvalidationParams: IDHValidationParams;
+
+    function GetP: IDerInteger; inline;
+    function GetG: IDerInteger; inline;
+    function GetQ: IDerInteger; inline;
+    function GetJ: IDerInteger; inline;
+    function GetValidationParams: IDHValidationParams; inline;
+
+    constructor Create(const seq: IAsn1Sequence); overload;
+
+    class function GetNext(const e: TCryptoLibGenericArray<IAsn1Encodable>;
+      var Idx: Int32): IAsn1Encodable; static; inline;
+
+  public
+    constructor Create(const p, g, q, j: IDerInteger;
+      const validationParams: IDHValidationParams); overload;
+
+    function ToAsn1Object(): IAsn1Object; override;
+
+    property p: IDerInteger read GetP;
+
+    property g: IDerInteger read GetG;
+
+    property q: IDerInteger read GetQ;
+
+    property j: IDerInteger read GetJ;
+
+    property validationParams: IDHValidationParams read GetValidationParams;
+
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDHDomainParameters; overload; static; inline;
+
+    class function GetInstance(obj: TObject): IDHDomainParameters; overload;
+      static; inline;
+
+  end;
+
+implementation
+
+{ TDHDomainParameters }
+
+function TDHDomainParameters.GetP: IDerInteger;
+begin
+  result := Fp;
+end;
+
+function TDHDomainParameters.GetG: IDerInteger;
+begin
+  result := Fg;
+end;
+
+function TDHDomainParameters.GetJ: IDerInteger;
+begin
+  result := Fj;
+end;
+
+function TDHDomainParameters.GetQ: IDerInteger;
+begin
+  result := Fq;
+end;
+
+function TDHDomainParameters.GetValidationParams: IDHValidationParams;
+begin
+  result := FvalidationParams;
+end;
+
+class function TDHDomainParameters.GetNext
+  (const e: TCryptoLibGenericArray<IAsn1Encodable>; var Idx: Int32)
+  : IAsn1Encodable;
+begin
+  if Idx <= (System.Length(e) - 1) then
+  begin
+    result := e[Idx];
+    System.Inc(Idx);
+  end
+  else
+  begin
+    result := Nil;
+  end;
+end;
+
+constructor TDHDomainParameters.Create(const seq: IAsn1Sequence);
+var
+  e: TCryptoLibGenericArray<IAsn1Encodable>;
+  next: IAsn1Encodable;
+  Idx: Int32;
+begin
+  Inherited Create();
+  if (seq.Count < 3) or (seq.Count > 5) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize,
+      [seq.Count]);
+  end;
+
+  Idx := 0;
+  e := seq.GetEnumerable();
+
+  Fp := TDerInteger.GetInstance(GetNext(e, Idx) as TAsn1Encodable);
+
+  Fg := TDerInteger.GetInstance(GetNext(e, Idx) as TAsn1Encodable);
+
+  Fq := TDerInteger.GetInstance(GetNext(e, Idx) as TAsn1Encodable);
+
+  next := GetNext(e, Idx);
+
+  if ((next <> Nil) and ((next as TAsn1Encodable) is TDerInteger)) then
+  begin
+    Fj := TDerInteger.GetInstance(next as TAsn1Encodable);
+    next := GetNext(e, Idx);
+  end;
+
+  if (next <> Nil) then
+  begin
+    FvalidationParams := TDHValidationParams.GetInstance
+      (next.ToAsn1Object() as TAsn1Object);
+  end;
+
+end;
+
+constructor TDHDomainParameters.Create(const p, g, q, j: IDerInteger;
+  const validationParams: IDHValidationParams);
+begin
+  Inherited Create();
+
+  if (p = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SPNil);
+  end;
+
+  if (g = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SGNil);
+  end;
+
+  if (q = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SQNil);
+  end;
+
+  if (j = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SJNil);
+  end;
+
+  Fp := p;
+  Fg := g;
+  Fq := q;
+  Fj := j;
+  FvalidationParams := validationParams;
+end;
+
+class function TDHDomainParameters.GetInstance(obj: TObject)
+  : IDHDomainParameters;
+begin
+  if ((obj = Nil) or (obj is TDHDomainParameters)) then
+  begin
+    result := obj as TDHDomainParameters;
+    Exit;
+  end;
+
+  if (obj is TAsn1Sequence) then
+  begin
+    result := TDHDomainParameters.Create(obj as TAsn1Sequence);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SInvalidDHDomainParameters,
+    [obj.ToString]);
+end;
+
+class function TDHDomainParameters.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDHDomainParameters;
+begin
+  result := GetInstance(TAsn1Sequence.GetInstance(obj, isExplicit)
+    as TAsn1Sequence);
+end;
+
+function TDHDomainParameters.ToAsn1Object: IAsn1Object;
+var
+  v: IAsn1EncodableVector;
+begin
+  v := TAsn1EncodableVector.Create([p, g, q]);
+
+  if (Fj <> Nil) then
+  begin
+    v.Add([Fj]);
+  end;
+
+  if (FvalidationParams <> Nil) then
+  begin
+    v.Add([FvalidationParams]);
+  end;
+
+  result := TDerSequence.Create(v);
+end;
+
+end.

+ 78 - 0
src/libraries/cryptolib4pascal/ClpDHKeyGenerationParameters.pas

@@ -0,0 +1,78 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHKeyGenerationParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISecureRandom,
+  ClpIDHParameters,
+  ClpIDHKeyGenerationParameters,
+  ClpKeyGenerationParameters;
+
+type
+  TDHKeyGenerationParameters = class sealed(TKeyGenerationParameters,
+    IDHKeyGenerationParameters)
+  strict private
+  var
+    Fparameters: IDHParameters;
+
+    function GetParameters: IDHParameters; inline;
+
+    class function GetStrengthLocal(const parameters: IDHParameters): Int32;
+      static; inline;
+
+  public
+    constructor Create(const random: ISecureRandom;
+      const parameters: IDHParameters);
+
+    property parameters: IDHParameters read GetParameters;
+  end;
+
+implementation
+
+{ TDHKeyGenerationParameters }
+
+function TDHKeyGenerationParameters.GetParameters: IDHParameters;
+begin
+  result := Fparameters;
+end;
+
+class function TDHKeyGenerationParameters.GetStrengthLocal(const parameters
+  : IDHParameters): Int32;
+begin
+  if parameters.L <> 0 then
+  begin
+    result := parameters.L;
+  end
+  else
+  begin
+    result := parameters.P.BitLength;
+  end;
+end;
+
+constructor TDHKeyGenerationParameters.Create(const random: ISecureRandom;
+  const parameters: IDHParameters);
+begin
+  Inherited Create(random, GetStrengthLocal(parameters));
+  Fparameters := parameters;
+end;
+
+end.

+ 140 - 0
src/libraries/cryptolib4pascal/ClpDHKeyGeneratorHelper.pas

@@ -0,0 +1,140 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHKeyGeneratorHelper;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+
+  ClpISecureRandom,
+  ClpBits,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpECCompUtilities,
+  ClpIDHParameters,
+  ClpIDHKeyGeneratorHelper;
+
+type
+  TDHKeyGeneratorHelper = class sealed(TInterfacedObject, IDHKeyGeneratorHelper)
+
+  strict private
+  class var
+
+    FIsBooted: Boolean;
+    FInstance: IDHKeyGeneratorHelper;
+
+    class function GetInstance: IDHKeyGeneratorHelper; static; inline;
+
+    class procedure Boot(); static;
+    class constructor DHKeyGeneratorHelper();
+  public
+
+    function CalculatePrivate(const dhParams: IDHParameters;
+      const random: ISecureRandom): TBigInteger;
+
+    function CalculatePublic(const dhParams: IDHParameters;
+      const x: TBigInteger): TBigInteger;
+
+    class property Instance: IDHKeyGeneratorHelper read GetInstance;
+
+  end;
+
+implementation
+
+{ TDHKeyGeneratorHelper }
+
+class procedure TDHKeyGeneratorHelper.Boot;
+begin
+  if not FIsBooted then
+  begin
+    FInstance := TDHKeyGeneratorHelper.Create();
+
+    FIsBooted := True;
+  end;
+end;
+
+function TDHKeyGeneratorHelper.CalculatePrivate(const dhParams: IDHParameters;
+  const random: ISecureRandom): TBigInteger;
+var
+  limit, minWeight, m: Int32;
+  x, min, q, max: TBigInteger;
+begin
+  Result := Default (TBigInteger);
+  limit := dhParams.L;
+
+  if (limit <> 0) then
+  begin
+    minWeight := TBits.Asr32(limit, 2);
+
+    while True do
+    begin
+      x := TBigInteger.Create(limit, random).SetBit(limit - 1);
+      if (TWNafUtilities.GetNafWeight(x) >= minWeight) then
+      begin
+        Result := x;
+        Exit;
+      end;
+    end;
+  end;
+
+  min := TBigInteger.Two;
+  m := dhParams.m;
+  if (m <> 0) then
+  begin
+    min := TBigInteger.One.ShiftLeft(m - 1);
+  end;
+
+  q := dhParams.q;
+  if (not(q.IsInitialized)) then
+  begin
+    q := dhParams.P;
+  end;
+  max := q.Subtract(TBigInteger.Two);
+
+  minWeight := TBits.Asr32(max.BitLength, 2);
+
+  while True do
+  begin
+    x := TBigIntegers.CreateRandomInRange(min, max, random);
+    if (TWNafUtilities.GetNafWeight(x) >= minWeight) then
+    begin
+      Result := x;
+      Exit;
+    end;
+  end;
+end;
+
+function TDHKeyGeneratorHelper.CalculatePublic(const dhParams: IDHParameters;
+  const x: TBigInteger): TBigInteger;
+begin
+  Result := dhParams.G.ModPow(x, dhParams.P);
+end;
+
+class constructor TDHKeyGeneratorHelper.DHKeyGeneratorHelper;
+begin
+  TDHKeyGeneratorHelper.Boot();
+end;
+
+class function TDHKeyGeneratorHelper.GetInstance: IDHKeyGeneratorHelper;
+begin
+  Result := FInstance;
+end;
+
+end.

+ 104 - 0
src/libraries/cryptolib4pascal/ClpDHKeyPairGenerator.pas

@@ -0,0 +1,104 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHKeyPairGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHParameters,
+  ClpIDHKeyPairGenerator,
+  ClpDHPublicKeyParameters,
+  ClpIDHPublicKeyParameters,
+  ClpDHPrivateKeyParameters,
+  ClpIDHPrivateKeyParameters,
+  ClpAsymmetricCipherKeyPair,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIKeyGenerationParameters,
+  ClpIDHKeyGenerationParameters,
+  ClpDHKeyGeneratorHelper,
+  ClpIDHKeyGeneratorHelper,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SParametersCannotBeNil = '"parameters" Cannot Be Nil';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// a Diffie-Hellman key pair generator.
+  /// </para>
+  /// <para>
+  /// This generates keys consistent for use in the MTI/A0 key agreement
+  /// protocol as described in "Handbook of Applied Cryptography", Pages
+  /// 516-519.
+  /// </para>
+  /// </summary>
+  TDHKeyPairGenerator = class sealed(TInterfacedObject,
+    IAsymmetricCipherKeyPairGenerator, IDHKeyPairGenerator)
+
+  strict private
+
+  var
+    Fparam: IDHKeyGenerationParameters;
+
+  public
+
+    procedure Init(const parameters: IKeyGenerationParameters);
+
+    function GenerateKeyPair(): IAsymmetricCipherKeyPair;
+
+  end;
+
+implementation
+
+{ TDHKeyPairGenerator }
+
+function TDHKeyPairGenerator.GenerateKeyPair: IAsymmetricCipherKeyPair;
+var
+  dhp: IDHParameters;
+  helper: IDHKeyGeneratorHelper;
+  x, y: TBigInteger;
+begin
+
+  helper := TDHKeyGeneratorHelper.Instance;
+  dhp := Fparam.parameters;
+
+  x := helper.CalculatePrivate(dhp, Fparam.Random);
+  y := helper.CalculatePublic(dhp, x);
+
+  result := TAsymmetricCipherKeyPair.Create(TDHPublicKeyParameters.Create(y,
+    dhp) as IDHPublicKeyParameters, TDHPrivateKeyParameters.Create(x, dhp)
+    as IDHPrivateKeyParameters);
+end;
+
+procedure TDHKeyPairGenerator.Init(const parameters: IKeyGenerationParameters);
+begin
+  if (parameters = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SParametersCannotBeNil);
+  end;
+
+  Fparam := parameters as IDHKeyGenerationParameters;
+end;
+
+end.

+ 115 - 0
src/libraries/cryptolib4pascal/ClpDHKeyParameters.pas

@@ -0,0 +1,115 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHParameters,
+  ClpIDHKeyParameters,
+  ClpPkcsObjectIdentifiers,
+  ClpIAsn1Objects,
+  ClpAsymmetricKeyParameter;
+
+type
+  TDHKeyParameters = class abstract(TAsymmetricKeyParameter, IDHKeyParameters)
+
+  strict private
+  var
+    Fparameters: IDHParameters;
+    FalgorithmOid: IDerObjectIdentifier;
+  strict protected
+    function GetParameters: IDHParameters;
+    function GetAlgorithmOid: IDerObjectIdentifier;
+
+    constructor Create(isPrivate: Boolean;
+      const parameters: IDHParameters); overload;
+
+    constructor Create(isPrivate: Boolean; const parameters: IDHParameters;
+      const algorithmOid: IDerObjectIdentifier); overload;
+
+  public
+    function Equals(const other: IDHKeyParameters): Boolean;
+      reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property parameters: IDHParameters read GetParameters;
+    property algorithmOid: IDerObjectIdentifier read GetAlgorithmOid;
+
+  end;
+
+implementation
+
+{ TDHKeyParameters }
+
+function TDHKeyParameters.GetParameters: IDHParameters;
+begin
+  result := Fparameters;
+end;
+
+function TDHKeyParameters.GetAlgorithmOid: IDerObjectIdentifier;
+begin
+  result := FalgorithmOid;
+end;
+
+constructor TDHKeyParameters.Create(isPrivate: Boolean;
+  const parameters: IDHParameters);
+begin
+  Create(isPrivate, parameters, TPkcsObjectIdentifiers.DhKeyAgreement);
+end;
+
+constructor TDHKeyParameters.Create(isPrivate: Boolean;
+  const parameters: IDHParameters; const algorithmOid: IDerObjectIdentifier);
+begin
+  Inherited Create(isPrivate);
+  // TODO Should we allow parameters to be null?
+  Fparameters := parameters;
+  FalgorithmOid := algorithmOid;
+end;
+
+function TDHKeyParameters.Equals(const other: IDHKeyParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDHKeyParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+
+  result := parameters.Equals(other.parameters) and (Inherited Equals(other));
+end;
+
+function TDHKeyParameters.GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := Inherited GetHashCode();
+
+  if (parameters <> Nil) then
+  begin
+    result := result xor parameters.GetHashCode();
+  end;
+end;
+
+end.

+ 266 - 0
src/libraries/cryptolib4pascal/ClpDHParameters.pas

@@ -0,0 +1,266 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  ClpICipherParameters,
+  ClpIDHParameters,
+  ClpIDHValidationParameters,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SPUnInitialized = '"P" Cannot Be Uninitialized';
+  SGUnInitialized = '"G" Cannot Be Uninitialized';
+  SMustBeOddPrime = 'Field must be an Odd Prime, "P"';
+  SInvalidGeneratorRange = 'Generator must in the Range [2, p - 2], "G"';
+  SQTooBigToBeAFactor = 'Q too Big to be a Factor of (P - 1), "Q"';
+  SMTooBig = 'M value must be < BitLength of P, "M"';
+  SLErrorOne = 'when L value specified, it must be less than bitlength(P), "L"';
+  SLErrorTwo = 'when L value specified, it may not be less than m value, "L"';
+  SInvalidSubGroupFactor = 'Subgroup factor must be >= 2, "j"';
+
+type
+  TDHParameters = class(TInterfacedObject, ICipherParameters, IDHParameters)
+
+  strict private
+
+  const
+    DefaultMinimumLength = Int32(160);
+
+  var
+    Fp, Fq, Fg, Fj: TBigInteger;
+    Fm, Fl: Int32;
+    Fvalidation: IDHValidationParameters;
+
+    function GetG: TBigInteger; inline;
+    function GetP: TBigInteger; inline;
+    function GetQ: TBigInteger; inline;
+    function GetJ: TBigInteger; inline;
+    function GetM: Int32; inline;
+    function GetL: Int32; inline;
+    function GetValidationParameters: IDHValidationParameters; inline;
+
+    class function GetDefaultMParam(lParam: Int32): Int32; static; inline;
+
+  public
+
+    constructor Create(const p, g: TBigInteger); overload;
+
+    constructor Create(const p, g, q: TBigInteger); overload;
+
+    constructor Create(const p, g, q: TBigInteger; l: Int32); overload;
+
+    constructor Create(const p, g, q: TBigInteger; m, l: Int32); overload;
+
+    constructor Create(const p, g, q, j: TBigInteger;
+      const validation: IDHValidationParameters); overload;
+
+    constructor Create(const p, g, q: TBigInteger; m, l: Int32;
+      const j: TBigInteger; const validation: IDHValidationParameters);
+      overload;
+
+    function Equals(const other: IDHParameters): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property p: TBigInteger read GetP;
+    property q: TBigInteger read GetQ;
+    property g: TBigInteger read GetG;
+    property j: TBigInteger read GetJ;
+    /// <summary>The minimum bitlength of the private value.</summary>
+    property m: Int32 read GetM;
+    /// <summary>The bitlength of the private value.</summary>
+    property l: Int32 read GetL;
+    property ValidationParameters: IDHValidationParameters
+      read GetValidationParameters;
+
+  end;
+
+implementation
+
+{ TDHParameters }
+
+function TDHParameters.GetL: Int32;
+begin
+  result := Fl;
+end;
+
+function TDHParameters.GetM: Int32;
+begin
+  result := Fm;
+end;
+
+function TDHParameters.GetJ: TBigInteger;
+begin
+  result := Fj;
+end;
+
+function TDHParameters.GetP: TBigInteger;
+begin
+  result := Fp;
+end;
+
+function TDHParameters.GetQ: TBigInteger;
+begin
+  result := Fq;
+end;
+
+function TDHParameters.GetG: TBigInteger;
+begin
+  result := Fg;
+end;
+
+class function TDHParameters.GetDefaultMParam(lParam: Int32): Int32;
+begin
+  if (lParam = 0) then
+  begin
+    result := DefaultMinimumLength;
+    Exit;
+  end;
+
+  result := Min(lParam, DefaultMinimumLength);
+end;
+
+constructor TDHParameters.Create(const p, g: TBigInteger);
+begin
+  Create(p, g, Default (TBigInteger), 0);
+end;
+
+constructor TDHParameters.Create(const p, g, q: TBigInteger);
+begin
+  Create(p, g, q, 0);
+end;
+
+constructor TDHParameters.Create(const p, g, q: TBigInteger; l: Int32);
+begin
+  Create(p, g, q, GetDefaultMParam(l), l, Default (TBigInteger), Nil);
+end;
+
+constructor TDHParameters.Create(const p, g, q: TBigInteger; m, l: Int32);
+begin
+  Create(p, g, q, m, l, Default (TBigInteger), Nil);
+end;
+
+constructor TDHParameters.Create(const p, g, q, j: TBigInteger;
+  const validation: IDHValidationParameters);
+begin
+  Create(p, g, q, DefaultMinimumLength, 0, j, validation)
+end;
+
+constructor TDHParameters.Create(const p, g, q: TBigInteger; m, l: Int32;
+  const j: TBigInteger; const validation: IDHValidationParameters);
+begin
+  Inherited Create();
+  if (not(p.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SPUnInitialized);
+  end;
+
+  if (not(g.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SGUnInitialized);
+  end;
+
+  if (not p.TestBit(0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SMustBeOddPrime);
+  end;
+
+  if ((g.CompareTo(TBigInteger.Two) < 0) or
+    (g.CompareTo(p.Subtract(TBigInteger.Two)) > 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidGeneratorRange);
+  end;
+
+  if ((q.IsInitialized) and (q.BitLength >= p.BitLength)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SQTooBigToBeAFactor);
+  end;
+
+  if (m >= p.BitLength) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SMTooBig);
+  end;
+
+  if (l <> 0) then
+  begin
+
+    if (l >= p.BitLength) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SLErrorOne);
+    end;
+    if (l < m) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SLErrorTwo);
+    end;
+  end;
+
+  if ((j.IsInitialized) and (j.CompareTo(TBigInteger.Two) < 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidSubGroupFactor);
+  end;
+
+  // TODO If q, j both provided, validate p = jq + 1 ?
+
+  Fp := p;
+  Fg := g;
+  Fq := q;
+  Fm := m;
+  Fl := l;
+  Fj := j;
+  Fvalidation := validation;
+end;
+
+function TDHParameters.Equals(const other: IDHParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDHParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := p.Equals(other.p) and q.Equals(other.q) and g.Equals(other.g);
+end;
+
+function TDHParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := p.GetHashCode() xor g.GetHashCode();
+
+  if Fq.IsInitialized then
+  begin
+    result := result xor q.GetHashCode();
+  end;
+end;
+
+function TDHParameters.GetValidationParameters: IDHValidationParameters;
+begin
+  result := Fvalidation;
+end;
+
+end.

+ 88 - 0
src/libraries/cryptolib4pascal/ClpDHParametersGenerator.pas

@@ -0,0 +1,88 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHParametersGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISecureRandom,
+  ClpDHParameters,
+  ClpIDHParameters,
+  ClpIDHParametersGenerator,
+  ClpDHParametersHelper,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+type
+  TDHParametersGenerator = class(TInterfacedObject, IDHParametersGenerator)
+
+  strict private
+  var
+    Fcertainty, Fsize: Int32;
+    Frandom: ISecureRandom;
+
+  public
+
+    procedure Init(size, certainty: Int32; const random: ISecureRandom);
+
+    /// <summary>
+    /// <para>
+    /// which Generates the p and g values from the given parameters,
+    /// returning the DHParameters object.
+    /// </para>
+    /// <para>
+    /// Note: can take a while...
+    /// </para>
+    /// </summary>
+    function GenerateParameters(): IDHParameters; virtual;
+
+  end;
+
+implementation
+
+{ TDHParametersGenerator }
+
+function TDHParametersGenerator.GenerateParameters: IDHParameters;
+var
+  safePrimes: TCryptoLibGenericArray<TBigInteger>;
+  p, q, g: TBigInteger;
+begin
+  //
+  // find a safe prime p where p = 2*q + 1, where p and q are prime.
+  //
+  safePrimes := TDHParametersHelper.GenerateSafePrimes(Fsize,
+    Fcertainty, Frandom);
+
+  p := safePrimes[0];
+  q := safePrimes[1];
+  g := TDHParametersHelper.SelectGenerator(p, q, Frandom);
+
+  result := TDHParameters.Create(p, g, q, TBigInteger.Two, Nil);
+end;
+
+procedure TDHParametersGenerator.Init(size, certainty: Int32;
+  const random: ISecureRandom);
+begin
+  Fsize := size;
+  Fcertainty := certainty;
+  Frandom := random;
+end;
+
+end.

+ 270 - 0
src/libraries/cryptolib4pascal/ClpDHParametersHelper.pas

@@ -0,0 +1,270 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHParametersHelper;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISecureRandom,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpECCompUtilities,
+  ClpBits,
+  ClpCryptoLibTypes;
+
+type
+  TDHParametersHelper = class sealed(TObject)
+
+  strict private
+  class var
+
+    FSix: TBigInteger;
+    FPrimeProducts: TCryptoLibInt32Array;
+    FPrimeLists: TCryptoLibMatrixInt32Array;
+    FBigPrimeProducts: TCryptoLibGenericArray<TBigInteger>;
+    FIsBooted: Boolean;
+
+    class function ConstructBigPrimeProducts(const primeProducts
+      : TCryptoLibInt32Array): TCryptoLibGenericArray<TBigInteger>; static;
+
+    class procedure Boot(); static;
+
+    class constructor DHParametersHelper();
+
+  public
+
+    /// <summary>
+    /// <para>
+    /// Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
+    /// </para>
+    /// <para>
+    /// (see: Handbook of Applied Cryptography 4.86)
+    /// </para>
+    /// </summary>
+    class function GenerateSafePrimes(size, certainty: Int32;
+      const random: ISecureRandom): TCryptoLibGenericArray<TBigInteger>; static;
+
+{$IFNDEF _FIXINSIGHT_}
+    /// <summary>
+    /// <para>
+    /// Select a high order element of the multiplicative group Zp*
+    /// </para>
+    /// <para>
+    /// p and q must be s.t. p = 2*q + 1, where p and q are prime (see
+    /// generateSafePrimes)
+    /// </para>
+    /// </summary>
+    class function SelectGenerator(const p, q: TBigInteger;
+      const random: ISecureRandom): TBigInteger; static;
+{$ENDIF}
+  end;
+
+implementation
+
+{ TDHParametersHelper }
+
+class procedure TDHParametersHelper.Boot;
+begin
+  if not FIsBooted then
+  begin
+    FSix := TBigInteger.ValueOf(6);
+
+    FPrimeLists := TBigInteger.primeLists;
+    FPrimeProducts := TBigInteger.primeProducts;
+    FBigPrimeProducts := ConstructBigPrimeProducts(FPrimeProducts);
+
+    FIsBooted := True;
+  end;
+end;
+
+class constructor TDHParametersHelper.DHParametersHelper;
+begin
+  TDHParametersHelper.Boot;
+end;
+
+class function TDHParametersHelper.ConstructBigPrimeProducts(const primeProducts
+  : TCryptoLibInt32Array): TCryptoLibGenericArray<TBigInteger>;
+var
+  bpp: TCryptoLibGenericArray<TBigInteger>;
+  i: Int32;
+begin
+  System.SetLength(bpp, System.Length(FPrimeProducts));
+
+  for i := 0 to System.Pred(System.Length(bpp)) do
+  begin
+    bpp[i] := TBigInteger.ValueOf(primeProducts[i]);
+  end;
+
+  result := bpp;
+end;
+
+class function TDHParametersHelper.GenerateSafePrimes(size, certainty: Int32;
+  const random: ISecureRandom): TCryptoLibGenericArray<TBigInteger>;
+var
+  p, q: TBigInteger;
+  qLength, minWeight, i, test, rem3, diff, j, prime, qRem: Int32;
+  retryFlag: Boolean;
+  LPrimeList: TCryptoLibInt32Array;
+begin
+  retryFlag := False;
+  qLength := size - 1;
+  minWeight := TBits.Asr32(size, 2);
+
+  if (size <= 32) then
+  begin
+    while True do
+
+    begin
+      q := TBigInteger.Create(qLength, 2, random);
+
+      p := q.ShiftLeft(1).Add(TBigInteger.One);
+
+      if (not p.IsProbablePrime(certainty, True)) then
+      begin
+        continue;
+      end;
+
+      if ((certainty > 2) and (not(q.IsProbablePrime(certainty, True)))) then
+      begin
+        continue;
+      end;
+
+      break;
+    end;
+  end
+  else
+  begin
+    while True do
+    begin
+      q := TBigInteger.Create(qLength, 0, random);
+
+      i := 0;
+      while i < System.Length(FPrimeLists) do
+      begin
+        test := q.Remainder(FBigPrimeProducts[i]).Int32Value;
+
+        if (i = 0) then
+        begin
+          rem3 := test mod 3;
+          if (rem3 <> 2) then
+          begin
+            diff := (2 * rem3) + 2;
+            q := q.Add(TBigInteger.ValueOf(diff));
+            test := (test + diff) mod FPrimeProducts[i];
+          end;
+        end;
+
+        LPrimeList := FPrimeLists[i];
+        for j := 0 to System.Pred(System.Length(LPrimeList)) do
+        begin
+          prime := LPrimeList[j];
+          qRem := test mod prime;
+          if ((qRem = 0) or (qRem = TBits.Asr32(prime, 1))) then
+          begin
+            q := q.Add(FSix);
+            retryFlag := True;
+            break;
+          end;
+        end;
+
+        if retryFlag then
+        begin
+          i := 0;
+          retryFlag := False;
+        end
+        else
+        begin
+          System.Inc(i);
+        end;
+
+      end;
+
+      if (q.BitLength <> qLength) then
+      begin
+        continue;
+      end;
+
+      if (not(q.RabinMillerTest(2, random, True))) then
+      begin
+        continue;
+      end;
+
+      p := q.ShiftLeft(1).Add(TBigInteger.One);
+
+      if (not(p.RabinMillerTest(certainty, random, True))) then
+      begin
+        continue;
+      end;
+
+      if ((certainty > 2) and (not q.RabinMillerTest(certainty - 2, random,
+        True))) then
+      begin
+        continue;
+      end;
+
+      (*
+        * Require a minimum weight of the NAF representation, since low-weight primes may be
+        * weak against a version of the number-field-sieve for the discrete-logarithm-problem.
+        *
+        * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+      *)
+      if (TWNafUtilities.GetNafWeight(p) < minWeight) then
+      begin
+        continue;
+      end;
+
+      break;
+    end;
+  end;
+
+  result := TCryptoLibGenericArray<TBigInteger>.Create(p, q);
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+class function TDHParametersHelper.SelectGenerator(const p, q: TBigInteger;
+  const random: ISecureRandom): TBigInteger;
+var
+  g, h, pMinusTwo: TBigInteger;
+  // CompareResOne, CompareResTwo: Boolean;
+begin
+  pMinusTwo := p.Subtract(TBigInteger.Two);
+
+
+  // (see: Handbook of Applied Cryptography 4.80)
+  //
+  // repeat
+  // g := TBigIntegers.CreateRandomInRange(TBigInteger.Two, pMinusTwo, random);
+  // CompareResOne := g.ModPow(TBigInteger.Two, p).Equals(TBigInteger.One);
+  // CompareResTwo := g.ModPow(q, p).Equals(TBigInteger.One);
+  // until ((not CompareResOne) and (not CompareResTwo));
+
+  // RFC 2631 2.2.1.2 (and see: Handbook of Applied Cryptography 4.81)
+
+  repeat
+    h := TBigIntegers.CreateRandomInRange(TBigInteger.Two, pMinusTwo, random);
+    g := h.ModPow(TBigInteger.Two, p);
+  until ((not g.Equals(TBigInteger.One)));
+
+  result := g;
+end;
+{$ENDIF}
+
+end.

+ 117 - 0
src/libraries/cryptolib4pascal/ClpDHPrivateKeyParameters.pas

@@ -0,0 +1,117 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHPrivateKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHParameters,
+  ClpIDHPrivateKeyParameters,
+  ClpDHKeyParameters,
+  ClpIAsn1Objects,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SXUnInitialized = '"X" Cannot Be Uninitialized';
+
+type
+  TDHPrivateKeyParameters = class sealed(TDHKeyParameters,
+    IDHPrivateKeyParameters)
+
+  strict private
+  var
+    Fx: TBigInteger;
+
+    function GetX: TBigInteger; inline;
+
+    class function Validate(const x: TBigInteger): TBigInteger; static; inline;
+
+  public
+    constructor Create(const x: TBigInteger;
+      const parameters: IDHParameters); overload;
+
+    constructor Create(const x: TBigInteger; const parameters: IDHParameters;
+      const algorithmOid: IDerObjectIdentifier); overload;
+
+    function Equals(const other: IDHPrivateKeyParameters): Boolean;
+      reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property x: TBigInteger read GetX;
+  end;
+
+implementation
+
+{ TDHPrivateKeyParameters }
+
+function TDHPrivateKeyParameters.GetX: TBigInteger;
+begin
+  result := Fx;
+end;
+
+class function TDHPrivateKeyParameters.Validate(const x: TBigInteger)
+  : TBigInteger;
+begin
+  if (not(x.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SXUnInitialized);
+  end;
+  result := x;
+end;
+
+constructor TDHPrivateKeyParameters.Create(const x: TBigInteger;
+  const parameters: IDHParameters);
+begin
+  Inherited Create(true, parameters);
+  Fx := Validate(x);
+end;
+
+constructor TDHPrivateKeyParameters.Create(const x: TBigInteger;
+  const parameters: IDHParameters; const algorithmOid: IDerObjectIdentifier);
+begin
+  Inherited Create(true, parameters, algorithmOid);
+  Fx := Validate(x);
+end;
+
+function TDHPrivateKeyParameters.Equals(const other
+  : IDHPrivateKeyParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDHPrivateKeyParameters) = other) then
+  begin
+    result := true;
+    Exit;
+  end;
+  result := (x.Equals(other.x)) and (Inherited Equals(other));
+end;
+
+function TDHPrivateKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := x.GetHashCode() xor (Inherited GetHashCode());
+end;
+
+end.

+ 135 - 0
src/libraries/cryptolib4pascal/ClpDHPublicKeyParameters.pas

@@ -0,0 +1,135 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHPublicKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHParameters,
+  ClpIDHPublicKeyParameters,
+  ClpIAsn1Objects,
+  ClpDHKeyParameters,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SYUnInitialized = '"Y" Cannot Be Uninitialized';
+  SInvalidDHPublicKey = 'Invalid DH public key "Y"';
+  SInvalidYInCorrectGroup = '"Y" Value Does Not Appear To Be In Correct Group';
+
+type
+  TDHPublicKeyParameters = class sealed(TDHKeyParameters,
+    IDHPublicKeyParameters)
+
+  strict private
+  var
+    Fy: TBigInteger;
+
+    class function Validate(const y: TBigInteger; const dhParams: IDHParameters)
+      : TBigInteger; static; inline;
+
+    function GetY: TBigInteger; inline;
+
+  public
+    constructor Create(const y: TBigInteger;
+      const parameters: IDHParameters); overload;
+
+    constructor Create(const y: TBigInteger; const parameters: IDHParameters;
+      const algorithmOid: IDerObjectIdentifier); overload;
+
+    function Equals(const other: IDHPublicKeyParameters): Boolean;
+      reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property y: TBigInteger read GetY;
+  end;
+
+implementation
+
+{ TDHPublicKeyParameters }
+
+function TDHPublicKeyParameters.GetY: TBigInteger;
+begin
+  result := Fy;
+end;
+
+class function TDHPublicKeyParameters.Validate(const y: TBigInteger;
+  const dhParams: IDHParameters): TBigInteger;
+begin
+  if (not(y.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SYUnInitialized);
+  end;
+
+  // TLS check
+  if ((y.CompareTo(TBigInteger.Two) < 0) or
+    (y.CompareTo(dhParams.P.Subtract(TBigInteger.Two)) > 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidDHPublicKey);
+  end;
+
+  // we can't validate without Q.
+  if ((dhParams.Q.IsInitialized) and
+    (not(y.ModPow(dhParams.Q, dhParams.P).Equals(TBigInteger.One)))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidYInCorrectGroup);
+  end;
+
+  result := y;
+end;
+
+constructor TDHPublicKeyParameters.Create(const y: TBigInteger;
+  const parameters: IDHParameters);
+begin
+  Inherited Create(false, parameters);
+  Fy := Validate(y, parameters);
+end;
+
+constructor TDHPublicKeyParameters.Create(const y: TBigInteger;
+  const parameters: IDHParameters; const algorithmOid: IDerObjectIdentifier);
+begin
+  Inherited Create(false, parameters, algorithmOid);
+  Fy := Validate(y, parameters);
+end;
+
+function TDHPublicKeyParameters.Equals(const other
+  : IDHPublicKeyParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := false;
+    Exit;
+  end;
+  if ((Self as IDHPublicKeyParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := (y.Equals(other.y)) and (Inherited Equals(other));
+end;
+
+function TDHPublicKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := y.GetHashCode() xor (Inherited GetHashCode());
+end;
+
+end.

+ 103 - 0
src/libraries/cryptolib4pascal/ClpDHValidationParameters.pas

@@ -0,0 +1,103 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHValidationParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHValidationParameters,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SSeedNil = '"Seed" Cannot Be Nil';
+
+type
+  TDHValidationParameters = class(TInterfacedObject, IDHValidationParameters)
+  strict private
+  var
+    Fseed: TCryptoLibByteArray;
+    Fcounter: Int32;
+
+    function GetCounter: Int32; virtual;
+    function GetSeed: TCryptoLibByteArray; virtual;
+
+  public
+    constructor Create(const seed: TCryptoLibByteArray; counter: Int32);
+
+    function Equals(const other: IDHValidationParameters): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property counter: Int32 read GetCounter;
+    property seed: TCryptoLibByteArray read GetSeed;
+  end;
+
+implementation
+
+{ TDHValidationParameters }
+
+constructor TDHValidationParameters.Create(const seed: TCryptoLibByteArray;
+  counter: Int32);
+begin
+  Inherited Create();
+  if (seed = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SSeedNil);
+  end;
+
+  Fseed := System.Copy(seed);
+  Fcounter := counter;
+end;
+
+function TDHValidationParameters.Equals(const other
+  : IDHValidationParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDHValidationParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := (counter = other.counter) and TArrayUtils.AreEqual(seed,
+    other.seed);
+end;
+
+function TDHValidationParameters.GetCounter: Int32;
+begin
+  result := Fcounter;
+end;
+
+function TDHValidationParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := counter xor TArrayUtils.GetArrayHashCode(seed);
+end;
+
+function TDHValidationParameters.GetSeed: TCryptoLibByteArray;
+begin
+  result := System.Copy(Fseed);
+end;
+
+end.

+ 145 - 0
src/libraries/cryptolib4pascal/ClpDHValidationParams.pas

@@ -0,0 +1,145 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDHValidationParams;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDHValidationParams,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SSeedNil = 'Seed Cannot be Nil';
+  SPGenCounterNil = 'PGenCounter Cannot be Nil';
+  SBadSequenceSize = 'Bad Sequence Size "seq": %d';
+  SInvalidDHValidationParams = 'Invalid DHValidationParams: %s';
+
+type
+  TDHValidationParams = class(TAsn1Encodable, IDHValidationParams)
+
+  strict private
+  var
+    Fseed: IDerBitString;
+    FpgenCounter: IDerInteger;
+
+    function GetSeed: IDerBitString; inline;
+    function GetPGenCounter: IDerInteger; inline;
+
+    constructor Create(const seq: IAsn1Sequence); overload;
+
+  public
+    constructor Create(const seed: IDerBitString;
+      const pgenCounter: IDerInteger); overload;
+
+    function ToAsn1Object(): IAsn1Object; override;
+
+    property seed: IDerBitString read GetSeed;
+
+    property pgenCounter: IDerInteger read GetPGenCounter;
+
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDHValidationParams; overload; static; inline;
+
+    class function GetInstance(obj: TObject): IDHValidationParams; overload;
+      static; inline;
+
+  end;
+
+implementation
+
+{ TDHValidationParams }
+
+function TDHValidationParams.GetPGenCounter: IDerInteger;
+begin
+  result := FpgenCounter;
+end;
+
+function TDHValidationParams.GetSeed: IDerBitString;
+begin
+  result := Fseed;
+end;
+
+function TDHValidationParams.ToAsn1Object: IAsn1Object;
+begin
+  result := TDerSequence.Create([Fseed, FpgenCounter]);
+end;
+
+constructor TDHValidationParams.Create(const seq: IAsn1Sequence);
+begin
+  Inherited Create();
+  if (seq.Count <> 2) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize,
+      [seq.Count]);
+  end;
+
+  Fseed := TDerBitString.GetInstance(seq[0] as TAsn1Encodable);
+  FpgenCounter := TDerInteger.GetInstance(seq[1] as TAsn1Encodable);
+end;
+
+constructor TDHValidationParams.Create(const seed: IDerBitString;
+  const pgenCounter: IDerInteger);
+begin
+  Inherited Create();
+
+  if (seed = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SSeedNil);
+  end;
+
+  if (pgenCounter = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SPGenCounterNil);
+  end;
+
+  Fseed := seed;
+  FpgenCounter := pgenCounter;
+end;
+
+class function TDHValidationParams.GetInstance(obj: TObject)
+  : IDHValidationParams;
+begin
+  if ((obj = Nil) or (obj is TDHValidationParams)) then
+  begin
+    result := obj as TDHValidationParams;
+    Exit;
+  end;
+
+  if (obj is TAsn1Sequence) then
+  begin
+    result := TDHValidationParams.Create(obj as TAsn1Sequence);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SInvalidDHValidationParams,
+    [obj.ToString]);
+end;
+
+class function TDHValidationParams.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDHValidationParams;
+begin
+  result := GetInstance(TAsn1Sequence.GetInstance(obj, isExplicit)
+    as TAsn1Sequence);
+end;
+
+end.

+ 6 - 26
src/libraries/cryptolib4pascal/ClpDigestRandomGenerator.pas

@@ -45,6 +45,7 @@ type
     CYCLE_COUNT = Int64(10);
     CYCLE_COUNT = Int64(10);
 
 
   var
   var
+    FLock: TCriticalSection;
     FstateCounter, FseedCounter: Int64;
     FstateCounter, FseedCounter: Int64;
     Fdigest: IDigest;
     Fdigest: IDigest;
     Fstate, Fseed: TCryptoLibByteArray;
     Fstate, Fseed: TCryptoLibByteArray;
@@ -55,17 +56,10 @@ type
     procedure DigestUpdate(const inSeed: TCryptoLibByteArray); inline;
     procedure DigestUpdate(const inSeed: TCryptoLibByteArray); inline;
     procedure DigestDoFinal(const result: TCryptoLibByteArray); inline;
     procedure DigestDoFinal(const result: TCryptoLibByteArray); inline;
 
 
-    class var
-
-      FLock: TCriticalSection;
-
-    class procedure Boot(); static;
-    class constructor CreateDigestRandomGenerator();
-    class destructor DestroyDigestRandomGenerator();
-
   public
   public
 
 
     constructor Create(const digest: IDigest);
     constructor Create(const digest: IDigest);
+    destructor Destroy; override;
     procedure AddSeedMaterial(const inSeed: TCryptoLibByteArray);
     procedure AddSeedMaterial(const inSeed: TCryptoLibByteArray);
       overload; inline;
       overload; inline;
     procedure AddSeedMaterial(rSeed: Int64); overload; inline;
     procedure AddSeedMaterial(rSeed: Int64); overload; inline;
@@ -102,7 +96,6 @@ end;
 
 
 procedure TDigestRandomGenerator.AddSeedMaterial(rSeed: Int64);
 procedure TDigestRandomGenerator.AddSeedMaterial(rSeed: Int64);
 begin
 begin
-
   FLock.Acquire;
   FLock.Acquire;
   try
   try
     DigestAddCounter(rSeed);
     DigestAddCounter(rSeed);
@@ -113,18 +106,9 @@ begin
   end;
   end;
 end;
 end;
 
 
-class procedure TDigestRandomGenerator.Boot;
-begin
-  if FLock = Nil then
-  begin
-    FLock := TCriticalSection.Create;
-  end;
-end;
-
 procedure TDigestRandomGenerator.AddSeedMaterial(const inSeed
 procedure TDigestRandomGenerator.AddSeedMaterial(const inSeed
   : TCryptoLibByteArray);
   : TCryptoLibByteArray);
 begin
 begin
-
   FLock.Acquire;
   FLock.Acquire;
   try
   try
     DigestUpdate(inSeed);
     DigestUpdate(inSeed);
@@ -138,6 +122,7 @@ end;
 constructor TDigestRandomGenerator.Create(const digest: IDigest);
 constructor TDigestRandomGenerator.Create(const digest: IDigest);
 begin
 begin
   Inherited Create();
   Inherited Create();
+  FLock := TCriticalSection.Create;
   Fdigest := digest;
   Fdigest := digest;
   System.SetLength(Fseed, digest.GetDigestSize);
   System.SetLength(Fseed, digest.GetDigestSize);
   FseedCounter := 1;
   FseedCounter := 1;
@@ -145,11 +130,6 @@ begin
   FstateCounter := 1;
   FstateCounter := 1;
 end;
 end;
 
 
-class constructor TDigestRandomGenerator.CreateDigestRandomGenerator;
-begin
-  TDigestRandomGenerator.Boot;
-end;
-
 procedure TDigestRandomGenerator.CycleSeed;
 procedure TDigestRandomGenerator.CycleSeed;
 begin
 begin
   DigestUpdate(Fseed);
   DigestUpdate(Fseed);
@@ -158,9 +138,10 @@ begin
   DigestDoFinal(Fseed);
   DigestDoFinal(Fseed);
 end;
 end;
 
 
-class destructor TDigestRandomGenerator.DestroyDigestRandomGenerator;
+destructor TDigestRandomGenerator.Destroy;
 begin
 begin
   FLock.Free;
   FLock.Free;
+  inherited Destroy;
 end;
 end;
 
 
 procedure TDigestRandomGenerator.GenerateState;
 procedure TDigestRandomGenerator.GenerateState;
@@ -186,9 +167,8 @@ procedure TDigestRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray;
   start, len: Int32);
   start, len: Int32);
 var
 var
   stateOff, endPoint: Int32;
   stateOff, endPoint: Int32;
-  I: Integer;
+  I: Int32;
 begin
 begin
-
   FLock.Acquire;
   FLock.Acquire;
   try
   try
     stateOff := 0;
     stateOff := 0;

+ 1 - 1
src/libraries/cryptolib4pascal/ClpDsaKeyPairGenerator.pas

@@ -37,7 +37,7 @@ uses
   ClpBits,
   ClpBits,
   ClpBigInteger,
   ClpBigInteger,
   ClpBigIntegers,
   ClpBigIntegers,
-  ClpECAlgorithms,
+  ClpECCompUtilities,
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring

+ 7 - 6
src/libraries/cryptolib4pascal/ClpDsaKeyParameters.pas

@@ -33,11 +33,12 @@ type
   var
   var
     Fparameters: IDsaParameters;
     Fparameters: IDsaParameters;
   strict protected
   strict protected
-  function GetParameters: IDsaParameters;
-    constructor Create(isPrivate: Boolean; parameters: IDsaParameters);
+    function GetParameters: IDsaParameters;
+    constructor Create(isPrivate: Boolean; const parameters: IDsaParameters);
 
 
   public
   public
-    function Equals(const other: IDsaKeyParameters): Boolean; reintroduce; overload;
+    function Equals(const other: IDsaKeyParameters): Boolean;
+      reintroduce; overload;
     function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
     function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
 {$ENDIF DELPHI}override;
 {$ENDIF DELPHI}override;
 
 
@@ -50,7 +51,7 @@ implementation
 { TDsaKeyParameters }
 { TDsaKeyParameters }
 
 
 constructor TDsaKeyParameters.Create(isPrivate: Boolean;
 constructor TDsaKeyParameters.Create(isPrivate: Boolean;
-  parameters: IDsaParameters);
+  const parameters: IDsaParameters);
 begin
 begin
   Inherited Create(isPrivate);
   Inherited Create(isPrivate);
   // Note: parameters may be Nil
   // Note: parameters may be Nil
@@ -69,8 +70,8 @@ begin
     result := True;
     result := True;
     Exit;
     Exit;
   end;
   end;
-  result := (parameters as TObject).Equals(other.parameters as TObject) and
-    (Inherited Equals(other));
+
+  result := parameters.Equals(other.parameters) and (Inherited Equals(other));
 end;
 end;
 
 
 function TDsaKeyParameters.GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
 function TDsaKeyParameters.GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;

+ 1 - 1
src/libraries/cryptolib4pascal/ClpDsaParametersGenerator.pas

@@ -160,7 +160,7 @@ type
     /// <returns>
     /// <returns>
     /// a generated DSA parameters object.
     /// a generated DSA parameters object.
     /// </returns>
     /// </returns>
-    function GenerateParameters(): IDsaParameters; inline;
+    function GenerateParameters(): IDsaParameters; virtual;
 
 
   end;
   end;
 
 

+ 11 - 4
src/libraries/cryptolib4pascal/ClpDsaPrivateKeyParameters.pas

@@ -41,6 +41,8 @@ type
 
 
     function GetX: TBigInteger; inline;
     function GetX: TBigInteger; inline;
 
 
+    class function Validate(const x: TBigInteger): TBigInteger; static; inline;
+
   public
   public
     constructor Create(const x: TBigInteger; const parameters: IDsaParameters);
     constructor Create(const x: TBigInteger; const parameters: IDsaParameters);
 
 
@@ -61,16 +63,21 @@ begin
   result := Fx;
   result := Fx;
 end;
 end;
 
 
-constructor TDsaPrivateKeyParameters.Create(const x: TBigInteger;
-  const parameters: IDsaParameters);
+class function TDsaPrivateKeyParameters.Validate(const x: TBigInteger)
+  : TBigInteger;
 begin
 begin
-  Inherited Create(true, parameters);
   if (not(x.IsInitialized)) then
   if (not(x.IsInitialized)) then
   begin
   begin
     raise EArgumentNilCryptoLibException.CreateRes(@SXUnInitialized);
     raise EArgumentNilCryptoLibException.CreateRes(@SXUnInitialized);
   end;
   end;
+  result := x;
+end;
 
 
-  Fx := x;
+constructor TDsaPrivateKeyParameters.Create(const x: TBigInteger;
+  const parameters: IDsaParameters);
+begin
+  Inherited Create(true, parameters);
+  Fx := Validate(x);
 end;
 end;
 
 
 function TDsaPrivateKeyParameters.Equals(const other
 function TDsaPrivateKeyParameters.Equals(const other

+ 6 - 6
src/libraries/cryptolib4pascal/ClpDsaPublicKeyParameters.pas

@@ -48,7 +48,8 @@ type
   public
   public
     constructor Create(const y: TBigInteger; const parameters: IDsaParameters);
     constructor Create(const y: TBigInteger; const parameters: IDsaParameters);
 
 
-    function Equals(const other: IDsaPublicKeyParameters): Boolean; reintroduce; overload;
+    function Equals(const other: IDsaPublicKeyParameters): Boolean;
+      reintroduce; overload;
     function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
     function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
 {$ENDIF DELPHI}override;
 {$ENDIF DELPHI}override;
 
 
@@ -67,6 +68,10 @@ end;
 class function TDsaPublicKeyParameters.Validate(const y: TBigInteger;
 class function TDsaPublicKeyParameters.Validate(const y: TBigInteger;
   const parameters: IDsaParameters): TBigInteger;
   const parameters: IDsaParameters): TBigInteger;
 begin
 begin
+  if (not(y.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SYUnInitialized);
+  end;
   // we can't validate without params, fortunately we can't use the key either...
   // we can't validate without params, fortunately we can't use the key either...
   if (parameters <> Nil) then
   if (parameters <> Nil) then
   begin
   begin
@@ -85,11 +90,6 @@ constructor TDsaPublicKeyParameters.Create(const y: TBigInteger;
   const parameters: IDsaParameters);
   const parameters: IDsaParameters);
 begin
 begin
   Inherited Create(false, parameters);
   Inherited Create(false, parameters);
-  if (not(y.IsInitialized)) then
-  begin
-    raise EArgumentNilCryptoLibException.CreateRes(@SYUnInitialized);
-  end;
-
   Fy := Validate(y, parameters);
   Fy := Validate(y, parameters);
 end;
 end;
 
 

+ 243 - 974
src/libraries/cryptolib4pascal/ClpECAlgorithms.pas

@@ -25,17 +25,18 @@ uses
   SysUtils,
   SysUtils,
   Math,
   Math,
   ClpCryptoLibTypes,
   ClpCryptoLibTypes,
-  ClpBits,
   ClpBigInteger,
   ClpBigInteger,
-  ClpWNafPreCompInfo,
-  ClpIPolynomialExtensionField,
-  ClpIGlvEndomorphism,
-  ClpIWNafPreCompInfo,
-  ClpIPreCompInfo,
-  ClpIPreCompCallBack,
+  ClpBits,
+  ClpNat,
   ClpIECC,
   ClpIECC,
-  ClpECCurveConstants,
-  ClpIFiniteField;
+  ClpECCompUtilities,
+  ClpIWNafPreCompInfo,
+  ClpIFiniteField,
+  ClpIFixedPointPreCompInfo,
+  ClpIGlvEndomorphism,
+  ClpIMultipliers,
+  ClpMultipliers,
+  ClpIPolynomialExtensionField;
 
 
 resourcestring
 resourcestring
   SInvalidArray =
   SInvalidArray =
@@ -43,160 +44,8 @@ resourcestring
   SInvalidPointLocation = 'Point Must be on the Same Curve';
   SInvalidPointLocation = 'Point Must be on the Same Curve';
   SInvalidPoint = 'Invalid Point, "P"';
   SInvalidPoint = 'Invalid Point, "P"';
   SInvalidResult = 'Invalid Result';
   SInvalidResult = 'Invalid Result';
-  SInvalidRange = 'Must be in the Range [2, 16], "width"';
-  SInvalidRange2 = 'Must be in the Range [2, 8], "width"';
-
-type
-  TWNafUtilities = class abstract(TObject)
-
-  strict private
-  const
-    FDEFAULT_WINDOW_SIZE_CUTOFFS: array [0 .. 5] of Int32 = (13, 41, 121, 337,
-      897, 2305);
-
-  class var
-    FEMPTY_BYTES: TCryptoLibByteArray;
-    FEMPTY_INTS: TCryptoLibInt32Array;
-
-  type
-    IMapPointCallback = interface(IPreCompCallback)
-      ['{730BF27F-D5C3-4DF4-AC77-B8653C457C10}']
-
-    end;
-
-  type
-    TMapPointCallback = class(TInterfacedObject, IPreCompCallback,
-      IMapPointCallback)
-
-    strict private
-    var
-      Fm_wnafPreCompP: IWNafPreCompInfo;
-      Fm_includeNegated: Boolean;
-      Fm_pointMap: IECPointMap;
-
-    public
-      constructor Create(const wnafPreCompP: IWNafPreCompInfo;
-        includeNegated: Boolean; const pointMap: IECPointMap);
-
-      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
-
-    end;
-
-  type
-    IWNafCallback = interface(IPreCompCallback)
-      ['{A439A606-7899-4720-937E-C2F3D94D4811}']
-
-    end;
-
-  type
-    TWNafCallback = class(TInterfacedObject, IPreCompCallback, IWNafCallback)
-
-    strict private
-
-    var
-      Fm_p: IECPoint;
-      Fm_width: Int32;
-      Fm_includeNegated: Boolean;
-
-    public
-      constructor Create(const p: IECPoint; width: Int32;
-        includeNegated: Boolean);
-
-      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
-
-    end;
-
-  class function CheckExisting(const existingWNaf: IWNafPreCompInfo;
-    reqPreCompLen: Int32; includeNegated: Boolean): Boolean; static; inline;
-
-  class function CheckTable(const table: TCryptoLibGenericArray<IECPoint>;
-    reqLen: Int32): Boolean; static; inline;
-
-  class function Trim(const a: TCryptoLibByteArray; length: Int32)
-    : TCryptoLibByteArray; overload; static; inline;
-
-  class function Trim(const a: TCryptoLibInt32Array; length: Int32)
-    : TCryptoLibInt32Array; overload; static; inline;
-
-  class function ResizeTable(const a: TCryptoLibGenericArray<IECPoint>;
-    length: Int32): TCryptoLibGenericArray<IECPoint>; static; inline;
-
-  class procedure Boot(); static;
-  class constructor CreateWNafUtilities();
-
-  public
-
-    const
-    PRECOMP_NAME: String = 'bc_wnaf';
-
-    class function GenerateCompactNaf(const k: TBigInteger)
-      : TCryptoLibInt32Array; static;
-    class function GenerateCompactWindowNaf(width: Int32; const k: TBigInteger)
-      : TCryptoLibInt32Array; static;
-
-    class function GenerateJsf(const g, h: TBigInteger)
-      : TCryptoLibByteArray; static;
-    class function GenerateNaf(const k: TBigInteger)
-      : TCryptoLibByteArray; static;
-    // /**
-    // * Computes the Window NAF (non-adjacent Form) of an integer.
-    // * @param width The width <code>w</code> of the Window NAF. The width is
-    // * defined as the minimal number <code>w</code>, such that for any
-    // * <code>w</code> consecutive digits in the resulting representation, at
-    // * most one is non-zero.
-    // * @param k The integer of which the Window NAF is computed.
-    // * @return The Window NAF of the given width, such that the following holds:
-    // * <code>k = &amp;sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
-    // * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
-    // * returned <code>byte[]</code>.
-    // */
-    class function GenerateWindowNaf(width: Int32; const k: TBigInteger)
-      : TCryptoLibByteArray; static;
-
-    class function GetNafWeight(const k: TBigInteger): Int32; static; inline;
-
-    class function GetWNafPreCompInfo(const p: IECPoint): IWNafPreCompInfo;
-      overload; static; inline;
-
-    class function GetWNafPreCompInfo(const preCompInfo: IPreCompInfo)
-      : IWNafPreCompInfo; overload; static; inline;
-
-    /// <summary>
-    /// Determine window width to use for a scalar multiplication of the
-    /// given size.
-    /// </summary>
-    /// <param name="bits">
-    /// the bit-length of the scalar to multiply by
-    /// </param>
-    /// <returns>
-    /// the window size to use
-    /// </returns>
-    class function GetWindowSize(bits: Int32): Int32; overload; static; inline;
-
-    /// <summary>
-    /// Determine window width to use for a scalar multiplication of the
-    /// given size.
-    /// </summary>
-    /// <param name="bits">
-    /// the bit-length of the scalar to multiply by
-    /// </param>
-    /// <param name="windowSizeCutoffs">
-    /// a monotonically increasing list of bit sizes at which to increment
-    /// the window width
-    /// </param>
-    /// <returns>
-    /// the window size to use
-    /// </returns>
-    class function GetWindowSize(bits: Int32;
-      const windowSizeCutoffs: array of Int32): Int32; overload; static;
-
-    class function MapPointWithPrecomp(const p: IECPoint; width: Int32;
-      includeNegated: Boolean; const pointMap: IECPointMap): IECPoint; static;
-
-    class function Precompute(const p: IECPoint; width: Int32;
-      includeNegated: Boolean): IWNafPreCompInfo; static;
-
-  end;
+  SInvalidComputation =
+    'Fixed-Point Comb Doesn''t Support Scalars Larger Than The Curve Order';
 
 
 type
 type
   TECAlgorithms = class sealed(TObject)
   TECAlgorithms = class sealed(TObject)
@@ -212,6 +61,10 @@ type
       const infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
       const infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
       const wnafs: TCryptoLibMatrixByteArray): IECPoint; overload; static;
       const wnafs: TCryptoLibMatrixByteArray): IECPoint; overload; static;
 
 
+    class function ImplShamirsTrickFixedPoint(const p: IECPoint;
+      const k: TBigInteger; const q: IECPoint; const l: TBigInteger)
+      : IECPoint; static;
+
   public
   public
     class function IsF2mCurve(const c: IECCurve): Boolean; static;
     class function IsF2mCurve(const c: IECCurve): Boolean; static;
     class function IsF2mField(const field: IFiniteField): Boolean; static;
     class function IsF2mField(const field: IFiniteField): Boolean; static;
@@ -222,7 +75,7 @@ type
       const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint; static;
       const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint; static;
 
 
     class function SumOfTwoMultiplies(const p: IECPoint; const a: TBigInteger;
     class function SumOfTwoMultiplies(const p: IECPoint; const a: TBigInteger;
-      const Q: IECPoint; const b: TBigInteger): IECPoint; static;
+      const q: IECPoint; const b: TBigInteger): IECPoint; static;
 
 
     // /*
     // /*
     // * "Shamir's Trick", originally due to E. G. Straus
     // * "Shamir's Trick", originally due to E. G. Straus
@@ -243,7 +96,7 @@ type
     // * 9: return R
     // * 9: return R
     // */
     // */
     class function ShamirsTrick(const p: IECPoint; const k: TBigInteger;
     class function ShamirsTrick(const p: IECPoint; const k: TBigInteger;
-      const Q: IECPoint; const l: TBigInteger): IECPoint; static;
+      const q: IECPoint; const l: TBigInteger): IECPoint; static;
 
 
     class function ImportPoint(const c: IECCurve; const p: IECPoint)
     class function ImportPoint(const c: IECCurve; const p: IECPoint)
       : IECPoint; static;
       : IECPoint; static;
@@ -277,14 +130,13 @@ type
       : IECPoint; static;
       : IECPoint; static;
 
 
     class function ImplShamirsTrickJsf(const p: IECPoint; const k: TBigInteger;
     class function ImplShamirsTrickJsf(const p: IECPoint; const k: TBigInteger;
-      const Q: IECPoint; const l: TBigInteger): IECPoint; static;
+      const q: IECPoint; const l: TBigInteger): IECPoint; static;
 
 
     class function ImplShamirsTrickWNaf(const p: IECPoint; const k: TBigInteger;
     class function ImplShamirsTrickWNaf(const p: IECPoint; const k: TBigInteger;
-      const Q: IECPoint; const l: TBigInteger): IECPoint; overload; static;
+      const q: IECPoint; const l: TBigInteger): IECPoint; overload; static;
 
 
-    class function ImplShamirsTrickWNaf(const p: IECPoint; const k: TBigInteger;
-      const pointMapQ: IECPointMap; const l: TBigInteger): IECPoint;
-      overload; static;
+    class function ImplShamirsTrickWNaf(const endomorphism: IECEndomorphism;
+      const p: IECPoint; const k, l: TBigInteger): IECPoint; overload; static;
 
 
     class function ImplSumOfMultiplies
     class function ImplSumOfMultiplies
       (const ps: TCryptoLibGenericArray<IECPoint>;
       (const ps: TCryptoLibGenericArray<IECPoint>;
@@ -296,8 +148,8 @@ type
       const ks: TCryptoLibGenericArray<TBigInteger>;
       const ks: TCryptoLibGenericArray<TBigInteger>;
       const glvEndomorphism: IGlvEndomorphism): IECPoint; static;
       const glvEndomorphism: IGlvEndomorphism): IECPoint; static;
 
 
-    class function ImplSumOfMultiplies
-      (const ps: TCryptoLibGenericArray<IECPoint>; const pointMap: IECPointMap;
+    class function ImplSumOfMultiplies(const endomorphism: IECEndomorphism;
+      const ps: TCryptoLibGenericArray<IECPoint>;
       const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
       const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
       overload; static;
       overload; static;
 
 
@@ -341,33 +193,124 @@ begin
   result := p;
   result := p;
 end;
 end;
 
 
+class function TECAlgorithms.ImplShamirsTrickFixedPoint(const p: IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
+var
+  c: IECCurve;
+  combSize, widthP, widthQ, width, d, fullComb, i, top, j: Int32;
+  infoP, infoQ: IFixedPointPreCompInfo;
+  lookupTableP, lookupTableQ: IECLookupTable;
+  m: IFixedPointCombMultiplier;
+  r1, r2, R, addP, addQ, t: IECPoint;
+  BigK, BigL: TCryptoLibUInt32Array;
+  secretBitK, secretBitL, secretIndexK, secretIndexL: UInt32;
+begin
+  c := p.Curve;
+  combSize := TFixedPointUtilities.GetCombSize(c);
+
+  if (((k.BitLength) > combSize) or (l.BitLength > combSize)) then
+  begin
+    (*
+      * TODO The comb works best when the scalars are less than the (possibly unknown) order.
+      * Still, if we want to handle larger scalars, we could allow customization of the comb
+      * size, or alternatively we could deal with the 'extra' bits either by running the comb
+      * multiple times as necessary, or by using an alternative multiplier as prelude.
+    *)
+    raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidComputation);
+  end;
+
+  infoP := TFixedPointUtilities.Precompute(p);
+  infoQ := TFixedPointUtilities.Precompute(q);
+
+  lookupTableP := infoP.LookupTable;
+  lookupTableQ := infoQ.LookupTable;
+
+  widthP := infoP.width;
+  widthQ := infoQ.width;
+
+  // TODO This shouldn't normally happen, but a better "solution" is desirable anyway
+  if (widthP <> widthQ) then
+  begin
+    m := TFixedPointCombMultiplier.Create();
+    r1 := m.Multiply(p, k);
+    r2 := m.Multiply(q, l);
+    result := r1.Add(r2);
+    Exit;
+  end;
+
+  width := widthP;
+
+  d := ((combSize + width) - 1) div width;
+
+  R := c.Infinity;
+
+  fullComb := d * width;
+  BigK := TNat.FromBigInteger(fullComb, k);
+  BigL := TNat.FromBigInteger(fullComb, l);
+
+  top := fullComb - 1;
+
+  for i := 0 to System.Pred(d) do
+  begin
+    secretIndexK := 0;
+    secretIndexL := 0;
+
+    j := top - i;
+
+    while j >= 0 do
+    begin
+
+      secretBitK := BigK[TBits.Asr32(j, 5)] shr (j and $1F);
+      secretIndexK := secretIndexK xor (secretBitK shr 1);
+      secretIndexK := secretIndexK shl 1;
+      secretIndexK := secretIndexK xor secretBitK;
+
+      secretBitL := BigL[TBits.Asr32(j, 5)] shr (j and $1F);
+      secretIndexL := secretIndexL xor (secretBitL shr 1);
+      secretIndexL := secretIndexL shl 1;
+      secretIndexL := secretIndexL xor secretBitL;
+
+      System.Dec(j, d);
+    end;
+
+    addP := lookupTableP.LookupVar(Int32(secretIndexK));
+    addQ := lookupTableQ.LookupVar(Int32(secretIndexL));
+
+    t := addP.Add(addQ);
+
+    R := R.TwicePlus(t);
+  end;
+
+  result := R.Add(infoP.Offset).Add(infoQ.Offset);
+end;
+
 class function TECAlgorithms.ImplShamirsTrickJsf(const p: IECPoint;
 class function TECAlgorithms.ImplShamirsTrickJsf(const p: IECPoint;
-  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
 var
 var
   Curve: IECCurve;
   Curve: IECCurve;
-  infinity, R: IECPoint;
+  Infinity, R: IECPoint;
   PaddQ, PsubQ: IECPoint;
   PaddQ, PsubQ: IECPoint;
   points, table: TCryptoLibGenericArray<IECPoint>;
   points, table: TCryptoLibGenericArray<IECPoint>;
   jsf: TCryptoLibByteArray;
   jsf: TCryptoLibByteArray;
   i, jsfi, kDigit, lDigit, index: Int32;
   i, jsfi, kDigit, lDigit, index: Int32;
 begin
 begin
   Curve := p.Curve;
   Curve := p.Curve;
-  infinity := Curve.infinity;
+  Infinity := Curve.Infinity;
 
 
   // TODO conjugate co-Z addition (ZADDC) can return both of these
   // TODO conjugate co-Z addition (ZADDC) can return both of these
-  PaddQ := p.Add(Q);
-  PsubQ := p.Subtract(Q);
+  PaddQ := p.Add(q);
+  PsubQ := p.Subtract(q);
 
 
-  points := TCryptoLibGenericArray<IECPoint>.Create(Q, PsubQ, p, PaddQ);
+  points := TCryptoLibGenericArray<IECPoint>.Create(q, PsubQ, p, PaddQ);
   Curve.NormalizeAll(points);
   Curve.NormalizeAll(points);
 
 
   table := TCryptoLibGenericArray<IECPoint>.Create(points[3].Negate(),
   table := TCryptoLibGenericArray<IECPoint>.Create(points[3].Negate(),
-    points[2].Negate(), points[1].Negate(), points[0].Negate(), infinity,
+    points[2].Negate(), points[1].Negate(), points[0].Negate(), Infinity,
     points[0], points[1], points[2], points[3]);
     points[0], points[1], points[2], points[3]);
 
 
   jsf := TWNafUtilities.GenerateJsf(k, l);
   jsf := TWNafUtilities.GenerateJsf(k, l);
 
 
-  R := infinity;
+  R := Infinity;
 
 
   i := System.length(jsf);
   i := System.length(jsf);
   System.Dec(i);
   System.Dec(i);
@@ -387,13 +330,12 @@ begin
   result := R;
   result := R;
 end;
 end;
 
 
-class function TECAlgorithms.ImplShamirsTrickWNaf(const p: IECPoint;
-  const k: TBigInteger; const pointMapQ: IECPointMap; const l: TBigInteger)
-  : IECPoint;
+class function TECAlgorithms.ImplShamirsTrickWNaf(const endomorphism
+  : IECEndomorphism; const p: IECPoint; const k, l: TBigInteger): IECPoint;
 var
 var
   negK, negL: Boolean;
   negK, negL: Boolean;
-  width: Int32;
-  Q: IECPoint;
+  minWidth, widthP, widthQ: Int32;
+  q: IECPoint;
   infoP, infoQ: IWNafPreCompInfo;
   infoP, infoQ: IWNafPreCompInfo;
   preCompP, preCompQ, preCompNegP, preCompNegQ
   preCompP, preCompQ, preCompNegP, preCompNegQ
     : TCryptoLibGenericArray<IECPoint>;
     : TCryptoLibGenericArray<IECPoint>;
@@ -408,12 +350,15 @@ begin
   LK := LK.Abs();
   LK := LK.Abs();
   LL := LL.Abs();
   LL := LL.Abs();
 
 
-  width := Max(2, Min(16, TWNafUtilities.GetWindowSize(Max(LK.BitLength,
-    LL.BitLength))));
+  minWidth := TWNafUtilities.GetWindowSize(Max(k.BitLength, l.BitLength), 8);
 
 
-  Q := TWNafUtilities.MapPointWithPrecomp(p, width, true, pointMapQ);
-  infoP := TWNafUtilities.GetWNafPreCompInfo(p);
-  infoQ := TWNafUtilities.GetWNafPreCompInfo(Q);
+  infoP := TWNafUtilities.Precompute(p, minWidth, true);
+  q := TEndoUtilities.MapPoint(endomorphism, p);
+  infoQ := TWNafUtilities.PrecomputeWithPointMap(q, endomorphism.pointMap,
+    infoP, true);
+
+  widthP := Min(8, infoP.width);
+  widthQ := Min(8, infoQ.width);
 
 
   case negK of
   case negK of
     true:
     true:
@@ -443,8 +388,8 @@ begin
       preCompNegQ := infoQ.PreCompNeg
       preCompNegQ := infoQ.PreCompNeg
   end;
   end;
 
 
-  wnafP := TWNafUtilities.GenerateWindowNaf(width, LK);
-  wnafQ := TWNafUtilities.GenerateWindowNaf(width, LL);
+  wnafP := TWNafUtilities.GenerateWindowNaf(widthP, LK);
+  wnafQ := TWNafUtilities.GenerateWindowNaf(widthQ, LL);
 
 
   result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
   result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
     preCompNegQ, wnafQ);
     preCompNegQ, wnafQ);
@@ -457,29 +402,48 @@ begin
 end;
 end;
 
 
 class function TECAlgorithms.ImplShamirsTrickWNaf(const p: IECPoint;
 class function TECAlgorithms.ImplShamirsTrickWNaf(const p: IECPoint;
-  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
 var
 var
   negK, negL: Boolean;
   negK, negL: Boolean;
-  widthP, widthQ: Int32;
+  minWidthP, minWidthQ, widthP, widthQ, combSize: Int32;
   infoP, infoQ: IWNafPreCompInfo;
   infoP, infoQ: IWNafPreCompInfo;
   preCompP, preCompQ, preCompNegP, preCompNegQ
   preCompP, preCompQ, preCompNegP, preCompNegQ
     : TCryptoLibGenericArray<IECPoint>;
     : TCryptoLibGenericArray<IECPoint>;
   wnafP, wnafQ: TCryptoLibByteArray;
   wnafP, wnafQ: TCryptoLibByteArray;
-  LK, LL: TBigInteger;
+  kAbs, lAbs: TBigInteger;
+  c: IECCurve;
 begin
 begin
-  LK := k;
-  LL := l;
-  negK := LK.SignValue < 0;
-  negL := LL.SignValue < 0;
 
 
-  LK := LK.Abs();
-  LL := LL.Abs();
+  negK := k.SignValue < 0;
+  negL := l.SignValue < 0;
+
+  kAbs := k.Abs();
+  lAbs := l.Abs();
 
 
-  widthP := Max(2, Min(16, TWNafUtilities.GetWindowSize(LK.BitLength)));
-  widthQ := Max(2, Min(16, TWNafUtilities.GetWindowSize(LL.BitLength)));
+  minWidthP := TWNafUtilities.GetWindowSize(kAbs.BitLength, 8);
+  minWidthQ := TWNafUtilities.GetWindowSize(lAbs.BitLength, 8);
 
 
-  infoP := TWNafUtilities.Precompute(p, widthP, true);
-  infoQ := TWNafUtilities.Precompute(Q, widthQ, true);
+  infoP := TWNafUtilities.Precompute(p, minWidthP, true);
+  infoQ := TWNafUtilities.Precompute(q, minWidthQ, true);
+
+  // When P, Q are 'promoted' (i.e. reused several times), switch to fixed-point algorithm
+
+  c := p.Curve;
+  combSize := TFixedPointUtilities.GetCombSize(c);
+  if ((not negK) and (not negL) and (k.BitLength <= combSize) and
+    (l.BitLength <= combSize) and (infoP.IsPromoted) and (infoQ.IsPromoted))
+  then
+  begin
+    result := ImplShamirsTrickFixedPoint(p, k, q, l);
+    infoP.PreComp := Nil; // Review
+    infoP.PreCompNeg := Nil; // Review
+    infoQ.PreComp := Nil; // Review
+    infoQ.PreCompNeg := Nil; // Review
+    Exit;
+  end;
+
+  widthP := Min(8, infoP.width);
+  widthQ := Min(8, infoQ.width);
 
 
   if negK then
   if negK then
   begin
   begin
@@ -517,8 +481,8 @@ begin
     preCompNegQ := infoQ.PreCompNeg
     preCompNegQ := infoQ.PreCompNeg
   end;
   end;
 
 
-  wnafP := TWNafUtilities.GenerateWindowNaf(widthP, LK);
-  wnafQ := TWNafUtilities.GenerateWindowNaf(widthQ, LL);
+  wnafP := TWNafUtilities.GenerateWindowNaf(widthP, kAbs);
+  wnafQ := TWNafUtilities.GenerateWindowNaf(widthQ, lAbs);
 
 
   result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
   result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
     preCompNegQ, wnafQ);
     preCompNegQ, wnafQ);
@@ -536,15 +500,15 @@ class function TECAlgorithms.ImplShamirsTrickWNaf(const preCompP,
 var
 var
   len, zeroes, i, wiP, wiQ, nP, nQ: Int32;
   len, zeroes, i, wiP, wiQ, nP, nQ: Int32;
   Curve: IECCurve;
   Curve: IECCurve;
-  infinity, R, point: IECPoint;
+  Infinity, R, point: IECPoint;
   tableP, tableQ: TCryptoLibGenericArray<IECPoint>;
   tableP, tableQ: TCryptoLibGenericArray<IECPoint>;
 begin
 begin
-  len := Math.Max(System.length(wnafP), System.length(wnafQ));
+  len := Max(System.length(wnafP), System.length(wnafQ));
 
 
   Curve := preCompP[0].Curve;
   Curve := preCompP[0].Curve;
-  infinity := Curve.infinity;
+  Infinity := Curve.Infinity;
 
 
-  R := infinity;
+  R := Infinity;
   zeroes := 0;
   zeroes := 0;
 
 
   i := len - 1;
   i := len - 1;
@@ -576,7 +540,7 @@ begin
       continue;
       continue;
     end;
     end;
 
 
-    point := infinity;
+    point := Infinity;
     if (wiP <> 0) then
     if (wiP <> 0) then
     begin
     begin
       nP := System.Abs(wiP);
       nP := System.Abs(wiP);
@@ -626,17 +590,19 @@ begin
   result := R;
   result := R;
 end;
 end;
 
 
-class function TECAlgorithms.ImplSumOfMultiplies
-  (const ps: TCryptoLibGenericArray<IECPoint>; const pointMap: IECPointMap;
+class function TECAlgorithms.ImplSumOfMultiplies(const endomorphism
+  : IECEndomorphism; const ps: TCryptoLibGenericArray<IECPoint>;
   const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
   const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
 var
 var
   halfCount, fullCount: Int32;
   halfCount, fullCount: Int32;
   negs: TCryptoLibBooleanArray;
   negs: TCryptoLibBooleanArray;
   infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
   infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
+  infoP, infoQ: IWNafPreCompInfo;
   wnafs: TCryptoLibMatrixByteArray;
   wnafs: TCryptoLibMatrixByteArray;
-  i, j0, j1, width: Int32;
+  i, j0, j1, minWidth, widthP, widthQ: Int32;
   kj0, kj1: TBigInteger;
   kj0, kj1: TBigInteger;
-  p, Q: IECPoint;
+  p, q: IECPoint;
+  pointMap: IECPointMap;
 begin
 begin
   halfCount := System.length(ps);
   halfCount := System.length(ps);
   fullCount := halfCount shl 1;
   fullCount := halfCount shl 1;
@@ -644,6 +610,8 @@ begin
   System.SetLength(infos, fullCount);
   System.SetLength(infos, fullCount);
   System.SetLength(wnafs, fullCount);
   System.SetLength(wnafs, fullCount);
 
 
+  pointMap := endomorphism.pointMap;
+
   for i := 0 to System.Pred(halfCount) do
   for i := 0 to System.Pred(halfCount) do
   begin
   begin
     j0 := i shl 1;
     j0 := i shl 1;
@@ -656,15 +624,21 @@ begin
     negs[j1] := kj1.SignValue < 0;
     negs[j1] := kj1.SignValue < 0;
     kj1 := kj1.Abs();
     kj1 := kj1.Abs();
 
 
-    width := Max(2, Min(16, TWNafUtilities.GetWindowSize(Max(kj0.BitLength,
-      kj1.BitLength))));
+    minWidth := TWNafUtilities.GetWindowSize
+      (Max(kj0.BitLength, kj1.BitLength), 8);
 
 
     p := ps[i];
     p := ps[i];
-    Q := TWNafUtilities.MapPointWithPrecomp(p, width, true, pointMap);
-    infos[j0] := TWNafUtilities.GetWNafPreCompInfo(p);
-    infos[j1] := TWNafUtilities.GetWNafPreCompInfo(Q);
-    wnafs[j0] := TWNafUtilities.GenerateWindowNaf(width, kj0);
-    wnafs[j1] := TWNafUtilities.GenerateWindowNaf(width, kj1);
+    infoP := TWNafUtilities.Precompute(p, minWidth, true);
+    q := TEndoUtilities.MapPoint(endomorphism, p);
+    infoQ := TWNafUtilities.PrecomputeWithPointMap(q, pointMap, infoP, true);
+
+    widthP := Min(8, infoP.width);
+    widthQ := Min(8, infoQ.width);
+
+    infos[j0] := infoP;
+    infos[j1] := infoQ;
+    wnafs[j0] := TWNafUtilities.GenerateWindowNaf(widthP, kj0);
+    wnafs[j1] := TWNafUtilities.GenerateWindowNaf(widthQ, kj1);
   end;
   end;
 
 
   result := ImplSumOfMultiplies(negs, infos, wnafs);
   result := ImplSumOfMultiplies(negs, infos, wnafs);
@@ -681,8 +655,9 @@ class function TECAlgorithms.ImplSumOfMultiplies
   (const ps: TCryptoLibGenericArray<IECPoint>;
   (const ps: TCryptoLibGenericArray<IECPoint>;
   const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
   const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
 var
 var
-  count, i, width: Int32;
+  count, i, width, minWidth: Int32;
   negs: TCryptoLibBooleanArray;
   negs: TCryptoLibBooleanArray;
+  info: IWNafPreCompInfo;
   infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
   infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
   wnafs: TCryptoLibMatrixByteArray;
   wnafs: TCryptoLibMatrixByteArray;
   ki: TBigInteger;
   ki: TBigInteger;
@@ -700,8 +675,11 @@ begin
     negs[i] := ki.SignValue < 0;
     negs[i] := ki.SignValue < 0;
     ki := ki.Abs();
     ki := ki.Abs();
 
 
-    width := Max(2, Min(16, TWNafUtilities.GetWindowSize(ki.BitLength)));
-    infos[i] := TWNafUtilities.Precompute(ps[i], width, true);
+    minWidth := TWNafUtilities.GetWindowSize(ki.BitLength, 8);
+    info := TWNafUtilities.Precompute(ps[i], minWidth, true);
+    width := Min(8, info.width);
+
+    infos[i] := info;
     wnafs[i] := TWNafUtilities.GenerateWindowNaf(width, ki);
     wnafs[i] := TWNafUtilities.GenerateWindowNaf(width, ki);
   end;
   end;
 
 
@@ -721,9 +699,9 @@ class function TECAlgorithms.ImplSumOfMultiplies
   const wnafs: TCryptoLibMatrixByteArray): IECPoint;
   const wnafs: TCryptoLibMatrixByteArray): IECPoint;
 var
 var
   len, count, zeroes: Int32;
   len, count, zeroes: Int32;
-  i, J, wi, n: Int32;
+  i, j, wi, n: Int32;
   Curve: IECCurve;
   Curve: IECCurve;
-  infinity, R, point: IECPoint;
+  Infinity, R, point: IECPoint;
   wnaf: TCryptoLibByteArray;
   wnaf: TCryptoLibByteArray;
   info: IWNafPreCompInfo;
   info: IWNafPreCompInfo;
   table: TCryptoLibGenericArray<IECPoint>;
   table: TCryptoLibGenericArray<IECPoint>;
@@ -737,19 +715,19 @@ begin
   end;
   end;
 
 
   Curve := infos[0].PreComp[0].Curve;
   Curve := infos[0].PreComp[0].Curve;
-  infinity := Curve.infinity;
+  Infinity := Curve.Infinity;
 
 
-  R := infinity;
+  R := Infinity;
   zeroes := 0;
   zeroes := 0;
 
 
   i := len - 1;
   i := len - 1;
   while (i >= 0) do
   while (i >= 0) do
   begin
   begin
-    point := infinity;
+    point := Infinity;
 
 
-    for J := 0 to System.Pred(count) do
+    for j := 0 to System.Pred(count) do
     begin
     begin
-      wnaf := wnafs[J];
+      wnaf := wnafs[j];
       if i < System.length(wnaf) then
       if i < System.length(wnaf) then
       begin
       begin
         wi := Int32(ShortInt(wnaf[i]));
         wi := Int32(ShortInt(wnaf[i]));
@@ -762,8 +740,8 @@ begin
       if (wi <> 0) then
       if (wi <> 0) then
       begin
       begin
         n := System.Abs(wi);
         n := System.Abs(wi);
-        info := infos[J];
-        if (wi < 0 = negs[J]) then
+        info := infos[j];
+        if (wi < 0 = negs[j]) then
         begin
         begin
           table := info.PreComp;
           table := info.PreComp;
         end
         end
@@ -776,7 +754,7 @@ begin
       end;
       end;
     end;
     end;
 
 
-    if (point = infinity) then
+    if (point = Infinity) then
     begin
     begin
       System.Inc(zeroes);
       System.Inc(zeroes);
       System.Dec(i);
       System.Dec(i);
@@ -809,11 +787,10 @@ class function TECAlgorithms.ImplSumOfMultipliesGlv
   const glvEndomorphism: IGlvEndomorphism): IECPoint;
   const glvEndomorphism: IGlvEndomorphism): IECPoint;
 var
 var
   n: TBigInteger;
   n: TBigInteger;
-  len, i, J: Int32;
+  len, i, j: Int32;
   &abs, ab: TCryptoLibGenericArray<TBigInteger>;
   &abs, ab: TCryptoLibGenericArray<TBigInteger>;
-  pointMap: IECPointMap;
   pqs: TCryptoLibGenericArray<IECPoint>;
   pqs: TCryptoLibGenericArray<IECPoint>;
-  p, Q: IECPoint;
+  p, q: IECPoint;
 begin
 begin
   n := ps[0].Curve.Order;
   n := ps[0].Curve.Order;
 
 
@@ -822,44 +799,43 @@ begin
   System.SetLength(Abs, len shl 1);
   System.SetLength(Abs, len shl 1);
 
 
   i := 0;
   i := 0;
-  J := 0;
+  j := 0;
 
 
   while (i < len) do
   while (i < len) do
   begin
   begin
     ab := glvEndomorphism.DecomposeScalar(ks[i].&Mod(n));
     ab := glvEndomorphism.DecomposeScalar(ks[i].&Mod(n));
 
 
-    Abs[J] := ab[0];
-    System.Inc(J);
-    Abs[J] := ab[1];
-    System.Inc(J);
+    Abs[j] := ab[0];
+    System.Inc(j);
+    Abs[j] := ab[1];
+    System.Inc(j);
     System.Inc(i);
     System.Inc(i);
   end;
   end;
 
 
-  pointMap := glvEndomorphism.pointMap;
   if (glvEndomorphism.HasEfficientPointMap) then
   if (glvEndomorphism.HasEfficientPointMap) then
   begin
   begin
-    result := TECAlgorithms.ImplSumOfMultiplies(ps, pointMap, Abs);
+    result := ImplSumOfMultiplies(glvEndomorphism, ps, Abs);
     Exit;
     Exit;
   end;
   end;
 
 
   System.SetLength(pqs, len shl 1);
   System.SetLength(pqs, len shl 1);
 
 
   i := 0;
   i := 0;
-  J := 0;
+  j := 0;
 
 
   while (i < len) do
   while (i < len) do
   begin
   begin
     p := ps[i];
     p := ps[i];
-    Q := pointMap.Map(p);
+    q := TEndoUtilities.MapPoint(glvEndomorphism, p);
 
 
-    pqs[J] := p;
-    System.Inc(J);
-    pqs[J] := Q;
-    System.Inc(J);
+    pqs[j] := p;
+    System.Inc(j);
+    pqs[j] := q;
+    System.Inc(j);
     System.Inc(i);
     System.Inc(i);
   end;
   end;
 
 
-  result := TECAlgorithms.ImplSumOfMultiplies(pqs, Abs);
+  result := ImplSumOfMultiplies(pqs, Abs);
 end;
 end;
 
 
 class function TECAlgorithms.ImportPoint(const c: IECCurve; const p: IECPoint)
 class function TECAlgorithms.ImportPoint(const c: IECCurve; const p: IECPoint)
@@ -903,7 +879,7 @@ class procedure TECAlgorithms.MontgomeryTrick
   const scale: IECFieldElement);
   const scale: IECFieldElement);
 var
 var
   c: TCryptoLibGenericArray<IECFieldElement>;
   c: TCryptoLibGenericArray<IECFieldElement>;
-  i, J: Int32;
+  i, j: Int32;
   u, tmp: IECFieldElement;
   u, tmp: IECFieldElement;
 begin
 begin
   // /*
   // /*
@@ -935,10 +911,10 @@ begin
 
 
   while (i > 0) do
   while (i > 0) do
   begin
   begin
-    J := off + i;
+    j := off + i;
     System.Dec(i);
     System.Dec(i);
-    tmp := zs[J];
-    zs[J] := c[i].Multiply(u);
+    tmp := zs[j];
+    zs[j] := c[i].Multiply(u);
     u := u.Multiply(tmp);
     u := u.Multiply(tmp);
   end;
   end;
 
 
@@ -955,18 +931,18 @@ class function TECAlgorithms.ReferenceMultiply(const p: IECPoint;
   const k: TBigInteger): IECPoint;
   const k: TBigInteger): IECPoint;
 var
 var
   x: TBigInteger;
   x: TBigInteger;
-  Q, LP: IECPoint;
+  q, LP: IECPoint;
   t, i: Int32;
   t, i: Int32;
 begin
 begin
   LP := p;
   LP := p;
   x := k.Abs();
   x := k.Abs();
-  Q := LP.Curve.infinity;
+  q := LP.Curve.Infinity;
   t := x.BitLength;
   t := x.BitLength;
   if (t > 0) then
   if (t > 0) then
   begin
   begin
     if (x.TestBit(0)) then
     if (x.TestBit(0)) then
     begin
     begin
-      Q := LP;
+      q := LP;
     end;
     end;
     i := 1;
     i := 1;
     while (i < t) do
     while (i < t) do
@@ -974,7 +950,7 @@ begin
       LP := LP.Twice();
       LP := LP.Twice();
       if (x.TestBit(i)) then
       if (x.TestBit(i)) then
       begin
       begin
-        Q := Q.Add(LP);
+        q := q.Add(LP);
       end;
       end;
       System.Inc(i);
       System.Inc(i);
     end;
     end;
@@ -983,23 +959,23 @@ begin
 
 
   if k.SignValue < 0 then
   if k.SignValue < 0 then
   begin
   begin
-    result := Q.Negate();
+    result := q.Negate();
   end
   end
   else
   else
   begin
   begin
-    result := Q;
+    result := q;
   end;
   end;
 
 
 end;
 end;
 
 
 class function TECAlgorithms.ShamirsTrick(const p: IECPoint;
 class function TECAlgorithms.ShamirsTrick(const p: IECPoint;
-  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
 var
 var
   cp: IECCurve;
   cp: IECCurve;
   LQ: IECPoint;
   LQ: IECPoint;
 begin
 begin
   cp := p.Curve;
   cp := p.Curve;
-  LQ := Q;
+  LQ := q;
   LQ := ImportPoint(cp, LQ);
   LQ := ImportPoint(cp, LQ);
 
 
   result := ImplCheckResult(ImplShamirsTrickJsf(p, k, LQ, l));
   result := ImplCheckResult(ImplShamirsTrickJsf(p, k, LQ, l));
@@ -1060,7 +1036,7 @@ begin
 end;
 end;
 
 
 class function TECAlgorithms.SumOfTwoMultiplies(const p: IECPoint;
 class function TECAlgorithms.SumOfTwoMultiplies(const p: IECPoint;
-  const a: TBigInteger; const Q: IECPoint; const b: TBigInteger): IECPoint;
+  const a: TBigInteger; const q: IECPoint; const b: TBigInteger): IECPoint;
 var
 var
   cp: IECCurve;
   cp: IECCurve;
   f2mCurve: IAbstractF2mCurve;
   f2mCurve: IAbstractF2mCurve;
@@ -1068,7 +1044,7 @@ var
   LQ: IECPoint;
   LQ: IECPoint;
 begin
 begin
   cp := p.Curve;
   cp := p.Curve;
-  LQ := Q;
+  LQ := q;
   LQ := ImportPoint(cp, LQ);
   LQ := ImportPoint(cp, LQ);
 
 
   // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
   // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
@@ -1090,711 +1066,4 @@ begin
   result := ImplCheckResult(ImplShamirsTrickWNaf(p, a, LQ, b));
   result := ImplCheckResult(ImplShamirsTrickWNaf(p, a, LQ, b));
 end;
 end;
 
 
-{ TWNafUtilities }
-
-class function TWNafUtilities.ResizeTable
-  (const a: TCryptoLibGenericArray<IECPoint>; length: Int32)
-  : TCryptoLibGenericArray<IECPoint>;
-begin
-  result := System.Copy(a);
-  System.SetLength(result, length);
-end;
-
-class function TWNafUtilities.Trim(const a: TCryptoLibInt32Array; length: Int32)
-  : TCryptoLibInt32Array;
-begin
-  result := System.Copy(a, 0, length);
-end;
-
-class function TWNafUtilities.Trim(const a: TCryptoLibByteArray; length: Int32)
-  : TCryptoLibByteArray;
-begin
-  result := System.Copy(a, 0, length);
-end;
-
-class function TWNafUtilities.CheckTable(const table
-  : TCryptoLibGenericArray<IECPoint>; reqLen: Int32): Boolean;
-begin
-  result := (table <> Nil) and (System.length(table) >= reqLen);
-end;
-
-class procedure TWNafUtilities.Boot;
-begin
-  FEMPTY_BYTES := Nil;
-  FEMPTY_INTS := Nil;
-end;
-
-class constructor TWNafUtilities.CreateWNafUtilities;
-begin
-  TWNafUtilities.Boot;
-end;
-
-class function TWNafUtilities.CheckExisting(const existingWNaf
-  : IWNafPreCompInfo; reqPreCompLen: Int32; includeNegated: Boolean): Boolean;
-begin
-  result := (existingWNaf <> Nil) and CheckTable(existingWNaf.PreComp,
-    reqPreCompLen) and ((not includeNegated) or
-    CheckTable(existingWNaf.PreCompNeg, reqPreCompLen));
-end;
-
-class function TWNafUtilities.GenerateCompactNaf(const k: TBigInteger)
-  : TCryptoLibInt32Array;
-var
-  _3k, diff: TBigInteger;
-  bits, highBit, &length, zeroes, i, digit: Int32;
-  naf: TCryptoLibInt32Array;
-begin
-  if ((TBits.Asr32(k.BitLength, 16)) <> 0) then
-  begin
-    raise EArgumentCryptoLibException.CreateRes(@SInvalidBitLength);
-  end;
-  if (k.SignValue = 0) then
-  begin
-    result := FEMPTY_INTS;
-    Exit;
-  end;
-
-  _3k := k.ShiftLeft(1).Add(k);
-
-  bits := _3k.BitLength;
-  System.SetLength(naf, TBits.Asr32(bits, 1));
-
-  diff := _3k.&Xor(k);
-
-  highBit := bits - 1;
-  &length := 0;
-  zeroes := 0;
-
-  i := 1;
-
-  while (i < highBit) do
-  begin
-    if (not diff.TestBit(i)) then
-    begin
-      System.Inc(zeroes);
-      System.Inc(i);
-      continue;
-    end;
-
-    if k.TestBit(i) then
-    begin
-      digit := -1;
-    end
-    else
-    begin
-      digit := 1;
-    end;
-
-    naf[length] := (digit shl 16) or zeroes;
-    System.Inc(length);
-    zeroes := 1;
-
-    System.Inc(i, 2);
-
-  end;
-
-  naf[length] := (1 shl 16) or zeroes;
-  System.Inc(length);
-
-  if (System.length(naf) > length) then
-  begin
-    naf := Trim(naf, length);
-  end;
-
-  result := naf;
-end;
-
-class function TWNafUtilities.GenerateCompactWindowNaf(width: Int32;
-  const k: TBigInteger): TCryptoLibInt32Array;
-var
-  wnaf: TCryptoLibInt32Array;
-  pow2, mask, sign, &length, &pos, digit, zeroes: Int32;
-  carry: Boolean;
-  LK: TBigInteger;
-begin
-  LK := k;
-  if (width = 2) then
-  begin
-    result := GenerateCompactNaf(LK);
-    Exit;
-  end;
-
-  if ((width < 2) or (width > 16)) then
-  begin
-    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange);
-  end;
-  if ((TBits.Asr32(LK.BitLength, 16)) <> 0) then
-  begin
-    raise EArgumentCryptoLibException.CreateRes(@SInvalidBitLength);
-  end;
-  if (LK.SignValue = 0) then
-  begin
-    result := FEMPTY_INTS;
-    Exit;
-  end;
-
-  System.SetLength(wnaf, (LK.BitLength div width) + 1);
-
-  // 2^width and a mask and sign bit set accordingly
-  pow2 := 1 shl width;
-  mask := pow2 - 1;
-  sign := TBits.Asr32(pow2, 1);
-
-  carry := false;
-  length := 0;
-  pos := 0;
-
-  while (pos <= LK.BitLength) do
-  begin
-    if (LK.TestBit(pos) = carry) then
-    begin
-      System.Inc(pos);
-      continue;
-    end;
-
-    LK := LK.ShiftRight(pos);
-
-    digit := LK.Int32Value and mask;
-    if (carry) then
-    begin
-      System.Inc(digit);
-    end;
-
-    carry := (digit and sign) <> 0;
-    if (carry) then
-    begin
-      digit := digit - pow2;
-    end;
-
-    if length > 0 then
-    begin
-      zeroes := pos - 1;
-    end
-    else
-    begin
-      zeroes := pos;
-    end;
-
-    wnaf[length] := (digit shl 16) or zeroes;
-    System.Inc(length);
-    pos := width;
-  end;
-
-  // Reduce the WNAF array to its actual length
-  if (System.length(wnaf) > length) then
-  begin
-    wnaf := Trim(wnaf, length);
-  end;
-
-  result := wnaf;
-end;
-
-class function TWNafUtilities.GenerateJsf(const g, h: TBigInteger)
-  : TCryptoLibByteArray;
-var
-  digits, J, d0, d1, offset, n0, n1, u0, u1: Int32;
-  jsf: TCryptoLibByteArray;
-  k0, k1: TBigInteger;
-begin
-  digits := Max(g.BitLength, h.BitLength) + 1;
-
-  System.SetLength(jsf, digits);
-
-  k0 := g;
-  k1 := h;
-  J := 0;
-  d0 := 0;
-  d1 := 0;
-
-  offset := 0;
-
-  while (((d0 or d1) <> 0) or (k0.BitLength > offset) or
-    (k1.BitLength > offset)) do
-  begin
-    n0 := (Int32(UInt32(k0.Int32Value) shr offset) + d0) and 7;
-    n1 := (Int32(UInt32(k1.Int32Value) shr offset) + d1) and 7;
-
-    u0 := n0 and 1;
-    if (u0 <> 0) then
-    begin
-      u0 := u0 - (n0 and 2);
-      if (((n0 + u0) = 4) and ((n1 and 3) = 2)) then
-      begin
-        u0 := -u0;
-      end;
-    end;
-
-    u1 := n1 and 1;
-    if (u1 <> 0) then
-    begin
-      u1 := u1 - (n1 and 2);
-      if (((n1 + u1) = 4) and ((n0 and 3) = 2)) then
-      begin
-        u1 := -u1;
-      end;
-    end;
-
-    if ((d0 shl 1) = (1 + u0)) then
-    begin
-      d0 := d0 xor 1;
-    end;
-    if ((d1 shl 1) = (1 + u1)) then
-    begin
-      d1 := d1 xor 1;
-    end;
-
-    System.Inc(offset);
-    if (offset = 30) then
-    begin
-      offset := 0;
-      k0 := k0.ShiftRight(30);
-      k1 := k1.ShiftRight(30);
-    end;
-
-    jsf[J] := Byte((u0 shl 4) or (u1 and $F));
-    System.Inc(J);
-  end;
-
-  // Reduce the JSF array to its actual length
-  if (System.length(jsf) > J) then
-  begin
-    jsf := Trim(jsf, J);
-  end;
-
-  result := jsf;
-end;
-
-class function TWNafUtilities.GenerateNaf(const k: TBigInteger)
-  : TCryptoLibByteArray;
-var
-  _3k, diff: TBigInteger;
-  digits, i: Int32;
-  naf: TCryptoLibByteArray;
-begin
-  if (k.SignValue = 0) then
-  begin
-    result := FEMPTY_BYTES;
-    Exit;
-  end;
-
-  _3k := k.ShiftLeft(1).Add(k);
-
-  digits := _3k.BitLength - 1;
-  System.SetLength(naf, digits);
-
-  diff := _3k.&Xor(k);
-
-  i := 1;
-
-  while i < digits do
-  begin
-    if (diff.TestBit(i)) then
-    begin
-      if k.TestBit(i) then
-      begin
-        naf[i - 1] := Byte(-1);
-      end
-      else
-      begin
-        naf[i - 1] := Byte(1);
-      end;
-
-      System.Inc(i);
-    end;
-    System.Inc(i);
-  end;
-
-  naf[digits - 1] := 1;
-
-  result := naf;
-end;
-
-class function TWNafUtilities.GenerateWindowNaf(width: Int32;
-  const k: TBigInteger): TCryptoLibByteArray;
-var
-  wnaf: TCryptoLibByteArray;
-  pow2, mask, sign, &length, &pos, digit: Int32;
-  carry: Boolean;
-  LK: TBigInteger;
-begin
-  LK := k;
-  if (width = 2) then
-  begin
-    result := GenerateNaf(LK);
-    Exit;
-  end;
-
-  if ((width < 2) or (width > 8)) then
-  begin
-    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange2);
-  end;
-  if (LK.SignValue = 0) then
-  begin
-    result := FEMPTY_BYTES;
-    Exit;
-  end;
-
-  System.SetLength(wnaf, LK.BitLength + 1);
-
-  // 2^width and a mask and sign bit set accordingly
-  pow2 := 1 shl width;
-  mask := pow2 - 1;
-  sign := TBits.Asr32(pow2, 1);
-
-  carry := false;
-  length := 0;
-  pos := 0;
-
-  while (pos <= LK.BitLength) do
-  begin
-    if (LK.TestBit(pos) = carry) then
-    begin
-      System.Inc(pos);
-      continue;
-    end;
-
-    LK := LK.ShiftRight(pos);
-
-    digit := LK.Int32Value and mask;
-    if (carry) then
-    begin
-      System.Inc(digit);
-    end;
-
-    carry := (digit and sign) <> 0;
-    if (carry) then
-    begin
-      digit := digit - pow2;
-    end;
-
-    if length > 0 then
-    begin
-      length := length + (pos - 1);
-    end
-    else
-    begin
-      length := length + (pos);
-    end;
-
-    wnaf[length] := Byte(digit);
-    System.Inc(length);
-    pos := width;
-  end;
-
-  // Reduce the WNAF array to its actual length
-  if (System.length(wnaf) > length) then
-  begin
-    wnaf := Trim(wnaf, length);
-  end;
-
-  result := wnaf;
-end;
-
-class function TWNafUtilities.GetNafWeight(const k: TBigInteger): Int32;
-var
-  _3k, diff: TBigInteger;
-begin
-  if (k.SignValue = 0) then
-  begin
-    result := 0;
-    Exit;
-  end;
-
-  _3k := k.ShiftLeft(1).Add(k);
-  diff := _3k.&Xor(k);
-
-  result := diff.BitCount;
-end;
-
-class function TWNafUtilities.GetWindowSize(bits: Int32;
-  const windowSizeCutoffs: array of Int32): Int32;
-var
-  w: Int32;
-begin
-  w := 0;
-  while (w < System.length(windowSizeCutoffs)) do
-  begin
-    if (bits < windowSizeCutoffs[w]) then
-    begin
-      break;
-    end;
-    System.Inc(w);
-  end;
-
-  result := w + 2;
-end;
-
-class function TWNafUtilities.GetWindowSize(bits: Int32): Int32;
-begin
-  result := GetWindowSize(bits, FDEFAULT_WINDOW_SIZE_CUTOFFS);
-end;
-
-class function TWNafUtilities.GetWNafPreCompInfo(const preCompInfo
-  : IPreCompInfo): IWNafPreCompInfo;
-begin
-  result := preCompInfo as IWNafPreCompInfo;
-end;
-
-class function TWNafUtilities.GetWNafPreCompInfo(const p: IECPoint)
-  : IWNafPreCompInfo;
-var
-  preCompInfo: IPreCompInfo;
-begin
-  preCompInfo := p.Curve.GetPreCompInfo(p, PRECOMP_NAME);
-  result := GetWNafPreCompInfo(preCompInfo);
-end;
-
-class function TWNafUtilities.MapPointWithPrecomp(const p: IECPoint;
-  width: Int32; includeNegated: Boolean; const pointMap: IECPointMap): IECPoint;
-var
-  c: IECCurve;
-  wnafPreCompP: IWNafPreCompInfo;
-  Q: IECPoint;
-begin
-  c := p.Curve;
-
-  wnafPreCompP := Precompute(p, width, includeNegated);
-
-  Q := pointMap.Map(p);
-
-  c.Precompute(Q, PRECOMP_NAME, TMapPointCallback.Create(wnafPreCompP,
-    includeNegated, pointMap) as IMapPointCallback);
-
-  result := Q;
-
-end;
-
-class function TWNafUtilities.Precompute(const p: IECPoint; width: Int32;
-  includeNegated: Boolean): IWNafPreCompInfo;
-begin
-  result := p.Curve.Precompute(p, PRECOMP_NAME, TWNafCallback.Create(p, width,
-    includeNegated) as IWNafCallback) as IWNafPreCompInfo;
-end;
-
-{ TWNafUtilities.TMapPointCallback }
-
-constructor TWNafUtilities.TMapPointCallback.Create(const wnafPreCompP
-  : IWNafPreCompInfo; includeNegated: Boolean; const pointMap: IECPointMap);
-begin
-  Inherited Create();
-  Fm_wnafPreCompP := wnafPreCompP;
-  Fm_includeNegated := includeNegated;
-  Fm_pointMap := pointMap;
-end;
-
-function TWNafUtilities.TMapPointCallback.Precompute(const existing
-  : IPreCompInfo): IPreCompInfo;
-var
-  tempResult: IWNafPreCompInfo;
-  twiceP, twiceQ: IECPoint;
-  preCompP, preCompQ, preCompNegQ: TCryptoLibGenericArray<IECPoint>;
-  i: Int32;
-begin
-  tempResult := TWNafPreCompInfo.Create();
-
-  twiceP := Fm_wnafPreCompP.Twice;
-  if (twiceP <> Nil) then
-  begin
-    twiceQ := Fm_pointMap.Map(twiceP);
-    tempResult.Twice := twiceQ;
-  end;
-
-  preCompP := Fm_wnafPreCompP.PreComp;
-
-  System.SetLength(preCompQ, System.length(preCompP));
-  for i := 0 to System.Pred(System.length(preCompP)) do
-  begin
-    preCompQ[i] := Fm_pointMap.Map(preCompP[i]);
-  end;
-
-  tempResult.PreComp := preCompQ;
-
-  if (Fm_includeNegated) then
-  begin
-
-    System.SetLength(preCompNegQ, System.length(preCompQ));
-
-    for i := 0 to System.Pred(System.length(preCompNegQ)) do
-    begin
-      preCompNegQ[i] := preCompQ[i].Negate();
-    end;
-
-    tempResult.PreCompNeg := preCompNegQ;
-  end;
-
-  result := tempResult;
-end;
-
-{ TWNafUtilities.TWNafCallback }
-
-constructor TWNafUtilities.TWNafCallback.Create(const p: IECPoint; width: Int32;
-  includeNegated: Boolean);
-begin
-  Inherited Create();
-  Fm_p := p;
-  Fm_width := width;
-  Fm_includeNegated := includeNegated;
-end;
-
-function TWNafUtilities.TWNafCallback.Precompute(const existing: IPreCompInfo)
-  : IPreCompInfo;
-var
-  twiceP, isoTwiceP, last: IECPoint;
-  c: IECCurve;
-  PreComp, PreCompNeg, EMPTY_POINTS: TCryptoLibGenericArray<IECPoint>;
-  tempRes, existingWNaf: IWNafPreCompInfo;
-  reqPreCompLen, iniPreCompLen, curPreCompLen, pos: Int32;
-  iso, iso2, iso3: IECFieldElement;
-begin
-  EMPTY_POINTS := Nil;
-  existingWNaf := existing as IWNafPreCompInfo;
-
-  reqPreCompLen := 1 shl Max(0, Fm_width - 2);
-
-  if (CheckExisting(existingWNaf, reqPreCompLen, Fm_includeNegated)) then
-  begin
-    result := existingWNaf;
-    Exit;
-  end;
-
-  c := Fm_p.Curve;
-
-  if (existingWNaf <> Nil) then
-  begin
-    PreComp := existingWNaf.PreComp;
-    PreCompNeg := existingWNaf.PreCompNeg;
-    twiceP := existingWNaf.Twice;
-  end;
-
-  iniPreCompLen := 0;
-  if (PreComp = Nil) then
-  begin
-    PreComp := EMPTY_POINTS;
-  end
-  else
-  begin
-    iniPreCompLen := System.length(PreComp);
-  end;
-
-  if (iniPreCompLen < reqPreCompLen) then
-  begin
-    PreComp := TWNafUtilities.ResizeTable(PreComp, reqPreCompLen);
-
-    if (reqPreCompLen = 1) then
-    begin
-      PreComp[0] := Fm_p.Normalize();
-    end
-    else
-    begin
-      curPreCompLen := iniPreCompLen;
-      if (curPreCompLen = 0) then
-      begin
-        PreComp[0] := Fm_p;
-        curPreCompLen := 1;
-      end;
-
-      if (reqPreCompLen = 2) then
-      begin
-        PreComp[1] := Fm_p.threeTimes();
-      end
-      else
-      begin
-        isoTwiceP := twiceP;
-        last := PreComp[curPreCompLen - 1];
-        if (isoTwiceP = Nil) then
-        begin
-          isoTwiceP := PreComp[0].Twice();
-          twiceP := isoTwiceP;
-          //
-          // /*
-          // * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
-          // * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
-          // * also requires scaling the initial point's X, Y coordinates, and reversing the
-          // * isomorphism as part of the subsequent normalization.
-          // *
-          // *  NOTE: The correctness of this optimization depends on:
-          // *      1) additions do not use the curve's A, B coefficients.
-          // *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
-          // */
-          if ((not(twiceP.IsInfinity)) and (TECAlgorithms.IsFpCurve(c)) and
-            (c.FieldSize >= 64)) then
-          begin
-            case (c.CoordinateSystem) of
-              TECCurveConstants.COORD_JACOBIAN,
-                TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
-                TECCurveConstants.COORD_JACOBIAN_MODIFIED:
-
-                begin
-                  iso := twiceP.GetZCoord(0);
-                  isoTwiceP := c.CreatePoint(twiceP.XCoord.ToBigInteger,
-                    twiceP.YCoord.ToBigInteger());
-
-                  iso2 := iso.square();
-                  iso3 := iso2.Multiply(iso);
-                  last := last.scaleX(iso2).scaleY(iso3);
-
-                  if (iniPreCompLen = 0) then
-                  begin
-                    PreComp[0] := last;
-                  end;
-                end;
-
-            end;
-
-          end;
-        end;
-
-        while (curPreCompLen < reqPreCompLen) do
-        begin
-          // /*
-          // * Compute the new ECPoints for the precomputation array. The values 1, 3,
-          // * 5, ..., 2^(width-1)-1 times p are computed
-          // */
-          last := last.Add(isoTwiceP);
-          PreComp[curPreCompLen] := last;
-          System.Inc(curPreCompLen);
-        end;
-      end;
-      //
-      // /*
-      // * Having oft-used operands in affine form makes operations faster.
-      // */
-      c.NormalizeAll(PreComp, iniPreCompLen,
-        reqPreCompLen - iniPreCompLen, iso);
-    end;
-  end;
-
-  if (Fm_includeNegated) then
-  begin
-
-    if (PreCompNeg = Nil) then
-    begin
-      pos := 0;
-      System.SetLength(PreCompNeg, reqPreCompLen);
-
-    end
-    else
-    begin
-      pos := System.length(PreCompNeg);
-      if (pos < reqPreCompLen) then
-      begin
-        PreCompNeg := TWNafUtilities.ResizeTable(PreCompNeg, reqPreCompLen);
-      end;
-    end;
-
-    while (pos < reqPreCompLen) do
-    begin
-      PreCompNeg[pos] := PreComp[pos].Negate();
-      System.Inc(pos);
-    end;
-  end;
-
-  tempRes := TWNafPreCompInfo.Create();
-  tempRes.PreComp := PreComp;
-  tempRes.PreCompNeg := PreCompNeg;
-  tempRes.Twice := twiceP;
-
-  result := tempRes;
-end;
-
 end.
 end.

+ 319 - 105
src/libraries/cryptolib4pascal/ClpECC.pas

@@ -37,9 +37,7 @@ uses
   ClpIGlvEndomorphism,
   ClpIGlvEndomorphism,
   ClpECAlgorithms,
   ClpECAlgorithms,
   ClpLongArray,
   ClpLongArray,
-  ClpGlvMultiplier,
-  ClpWNafL2RMultiplier,
-  ClpWTauNafMultiplier,
+  ClpMultipliers,
   ClpFiniteFields,
   ClpFiniteFields,
   ClpSetWeakRef,
   ClpSetWeakRef,
   ClpECCurveConstants,
   ClpECCurveConstants,
@@ -95,6 +93,7 @@ resourcestring
   SNotProjectiveCoordSystem = 'Not a Projective Coordinate System';
   SNotProjectiveCoordSystem = 'Not a Projective Coordinate System';
   SCannotBeNegative = 'Cannot be Negative, "e"';
   SCannotBeNegative = 'Cannot be Negative, "e"';
   SNilFieldElement = 'Exactly one of the Field Elements is Nil';
   SNilFieldElement = 'Exactly one of the Field Elements is Nil';
+  SUnsupportedOperation = 'Constant-time Lookup not Supported';
 
 
 type
 type
   TECFieldElement = class abstract(TInterfacedObject, IECFieldElement)
   TECFieldElement = class abstract(TInterfacedObject, IECFieldElement)
@@ -260,6 +259,7 @@ type
   public
   public
     function Trace(): Int32; virtual;
     function Trace(): Int32; virtual;
     function HalfTrace(): IECFieldElement; virtual;
     function HalfTrace(): IECFieldElement; virtual;
+    function HasFastTrace(): Boolean; virtual;
 
 
   end;
   end;
 
 
@@ -474,19 +474,10 @@ type
   /// </summary>
   /// </summary>
   TECCurve = class abstract(TInterfacedObject, IECCurve)
   TECCurve = class abstract(TInterfacedObject, IECCurve)
 
 
-  strict private
-
-    class procedure Boot(); static;
-    class constructor CreateECCurve();
-    class destructor DestroyECCurve();
-
   strict protected
   strict protected
 
 
-    class var
-
-      FLock: TCriticalSection;
-
   var
   var
+    FLock: TCriticalSection;
     Fm_field: IFiniteField;
     Fm_field: IFiniteField;
     Fm_order, Fm_cofactor: TBigInteger;
     Fm_order, Fm_cofactor: TBigInteger;
 
 
@@ -700,20 +691,61 @@ type
   end;
   end;
 
 
 type
 type
-  TDefaultLookupTable = class(TInterfacedObject, IDefaultLookupTable,
-    IECLookupTable)
+  TAbstractECLookupTable = class abstract(TInterfacedObject,
+    IAbstractECLookupTable, IECLookupTable)
+
+  strict protected
+    function GetSize: Int32; virtual; abstract;
+
+  public
+    function Lookup(index: Int32): IECPoint; virtual; abstract;
+    function LookupVar(index: Int32): IECPoint; virtual; abstract;
+    property Size: Int32 read GetSize;
+
+  end;
+
+type
+  TSimpleLookupTable = class abstract(TAbstractECLookupTable,
+    ISimpleLookupTable)
+
+  strict private
+  var
+    FPoints: TCryptoLibGenericArray<IECPoint>;
+
+    class function Copy(const points: TCryptoLibGenericArray<IECPoint>;
+      off, len: Int32): TCryptoLibGenericArray<IECPoint>; static;
+
+  strict protected
+    function GetSize: Int32; override;
+
+  public
+    constructor Create(const points: TCryptoLibGenericArray<IECPoint>;
+      off, len: Int32);
+
+    function Lookup(index: Int32): IECPoint; override;
+    function LookupVar(index: Int32): IECPoint; override;
+
+  end;
+
+type
+  TDefaultLookupTable = class(TAbstractECLookupTable, IDefaultLookupTable)
   strict private
   strict private
   var
   var
     Fm_outer: IECCurve;
     Fm_outer: IECCurve;
     Fm_table: TCryptoLibByteArray;
     Fm_table: TCryptoLibByteArray;
     Fm_size: Int32;
     Fm_size: Int32;
 
 
+    function CreatePoint(const x, y: TCryptoLibByteArray): IECPoint;
+
+  strict protected
+    function GetSize: Int32; override;
+
   public
   public
     constructor Create(const outer: IECCurve; const table: TCryptoLibByteArray;
     constructor Create(const outer: IECCurve; const table: TCryptoLibByteArray;
-      size: Int32);
-    function GetSize: Int32; virtual;
-    function Lookup(index: Int32): IECPoint; virtual;
-    property size: Int32 read GetSize;
+      Size: Int32);
+
+    function Lookup(index: Int32): IECPoint; override;
+    function LookupVar(index: Int32): IECPoint; override;
 
 
   end;
   end;
 
 
@@ -733,20 +765,24 @@ type
   end;
   end;
 
 
 type
 type
-  TDefaultF2mLookupTable = class(TInterfacedObject, IDefaultF2mLookupTable,
-    IECLookupTable)
+  TDefaultF2mLookupTable = class(TAbstractECLookupTable, IDefaultF2mLookupTable)
   strict private
   strict private
   var
   var
     Fm_outer: IF2mCurve;
     Fm_outer: IF2mCurve;
     Fm_table: TCryptoLibInt64Array;
     Fm_table: TCryptoLibInt64Array;
     Fm_size: Int32;
     Fm_size: Int32;
 
 
+    function CreatePoint(const x, y: TCryptoLibInt64Array): IECPoint;
+
+  strict protected
+    function GetSize: Int32; override;
+
   public
   public
     constructor Create(const outer: IF2mCurve;
     constructor Create(const outer: IF2mCurve;
-      const table: TCryptoLibInt64Array; size: Int32);
-    function GetSize: Int32; virtual;
-    function Lookup(index: Int32): IECPoint; virtual;
-    property size: Int32 read GetSize;
+      const table: TCryptoLibInt64Array; Size: Int32);
+
+    function Lookup(index: Int32): IECPoint; override;
+    function LookupVar(index: Int32): IECPoint; override;
 
 
   end;
   end;
 
 
@@ -1167,6 +1203,9 @@ type
     function ScaleX(const scale: IECFieldElement): IECPoint; virtual;
     function ScaleX(const scale: IECFieldElement): IECPoint; virtual;
     function ScaleY(const scale: IECFieldElement): IECPoint; virtual;
     function ScaleY(const scale: IECFieldElement): IECPoint; virtual;
 
 
+    function ScaleXNegateY(const scale: IECFieldElement): IECPoint; virtual;
+    function ScaleYNegateX(const scale: IECFieldElement): IECPoint; virtual;
+
     function GetEncoded(): TCryptoLibByteArray; overload; virtual;
     function GetEncoded(): TCryptoLibByteArray; overload; virtual;
     function GetEncoded(compressed: Boolean): TCryptoLibByteArray; overload;
     function GetEncoded(compressed: Boolean): TCryptoLibByteArray; overload;
       virtual; abstract;
       virtual; abstract;
@@ -1409,6 +1448,9 @@ type
     function ScaleX(const scale: IECFieldElement): IECPoint; override;
     function ScaleX(const scale: IECFieldElement): IECPoint; override;
     function ScaleY(const scale: IECFieldElement): IECPoint; override;
     function ScaleY(const scale: IECFieldElement): IECPoint; override;
 
 
+    function ScaleXNegateY(const scale: IECFieldElement): IECPoint; override;
+    function ScaleYNegateX(const scale: IECFieldElement): IECPoint; override;
+
     function Subtract(const b: IECPoint): IECPoint; override;
     function Subtract(const b: IECPoint): IECPoint; override;
 
 
     function Tau(): IAbstractF2mPoint; virtual;
     function Tau(): IAbstractF2mPoint; virtual;
@@ -2442,8 +2484,8 @@ end;
 
 
 function TAbstractF2mFieldElement.HalfTrace: IECFieldElement;
 function TAbstractF2mFieldElement.HalfTrace: IECFieldElement;
 var
 var
-  m, i: Int32;
-  fe, ht: IECFieldElement;
+  m, n, K, nk: Int32;
+  ht: IECFieldElement;
 begin
 begin
   m := FieldSize;
   m := FieldSize;
   if ((m and 1) = 0) then
   if ((m and 1) = 0) then
@@ -2451,34 +2493,53 @@ begin
     raise EArgumentCryptoLibException.CreateRes(@SHalfTraceUndefinedForM);
     raise EArgumentCryptoLibException.CreateRes(@SHalfTraceUndefinedForM);
   end;
   end;
 
 
-  fe := Self as IECFieldElement;
-  ht := fe;
-  i := 2;
-  while i < m do
+  n := TBits.Asr32((m + 1), 1);
+  K := 31 - TBits.NumberOfLeadingZeros(n);
+  nk := 1;
+
+  ht := Self as IECFieldElement;
+  while (K > 0) do
   begin
   begin
-    fe := fe.SquarePow(2);
-    ht := ht.Add(fe);
-    System.Inc(i, 2);
-  end;
+    ht := ht.SquarePow(nk shl 1).Add(ht);
+    System.Dec(K);
+    nk := TBits.Asr32(n, K);
 
 
+    if ((nk and 1) <> 0) then
+    begin
+      ht := ht.SquarePow(2).Add(Self as IECFieldElement);
+    end;
+  end;
   result := ht;
   result := ht;
 end;
 end;
 
 
+function TAbstractF2mFieldElement.HasFastTrace: Boolean;
+begin
+  result := false;
+
+end;
+
 function TAbstractF2mFieldElement.Trace: Int32;
 function TAbstractF2mFieldElement.Trace: Int32;
 var
 var
-  m, i: Int32;
-  fe, tr: IECFieldElement;
+  m, K, mk: Int32;
+  tr: IECFieldElement;
 begin
 begin
   m := FieldSize;
   m := FieldSize;
-  fe := Self as IECFieldElement;
-  tr := fe;
 
 
-  i := 1;
-  while i < m do
+  K := 31 - TBits.NumberOfLeadingZeros(m);
+  mk := 1;
+
+  tr := Self as IECFieldElement;
+  while (K > 0) do
   begin
   begin
-    fe := fe.Square();
-    tr := tr.Add(fe);
-    System.Inc(i);
+    tr := tr.SquarePow(mk).Add(tr);
+
+    System.Dec(K);
+    mk := TBits.Asr32(m, K);
+
+    if ((mk and 1) <> 0) then
+    begin
+      tr := tr.Square().Add(Self as IECFieldElement);
+    end;
   end;
   end;
 
 
   if (tr.IsZero) then
   if (tr.IsZero) then
@@ -2496,14 +2557,6 @@ end;
 
 
 { TECCurve }
 { TECCurve }
 
 
-class procedure TECCurve.Boot;
-begin
-  if FLock = Nil then
-  begin
-    FLock := TCriticalSection.Create;
-  end;
-end;
-
 procedure TECCurve.CheckPoint(const point: IECPoint);
 procedure TECCurve.CheckPoint(const point: IECPoint);
 begin
 begin
   if ((point = Nil) or ((Self as IECCurve) <> point.curve)) then
   if ((point = Nil) or ((Self as IECCurve) <> point.curve)) then
@@ -2552,9 +2605,16 @@ end;
 constructor TECCurve.Create(const field: IFiniteField);
 constructor TECCurve.Create(const field: IFiniteField);
 begin
 begin
   inherited Create();
   inherited Create();
+  FLock := TCriticalSection.Create;
   Fm_field := field;
   Fm_field := field;
 end;
 end;
 
 
+destructor TECCurve.Destroy;
+begin
+  FLock.Free;
+  inherited Destroy;
+end;
+
 function TECCurve.CreateCacheSafeLookupTable(const points
 function TECCurve.CreateCacheSafeLookupTable(const points
   : TCryptoLibGenericArray<IECPoint>; off, len: Int32): IECLookupTable;
   : TCryptoLibGenericArray<IECPoint>; off, len: Int32): IECLookupTable;
 var
 var
@@ -2618,11 +2678,6 @@ begin
   result := TWNafL2RMultiplier.Create();
   result := TWNafL2RMultiplier.Create();
 end;
 end;
 
 
-class constructor TECCurve.CreateECCurve;
-begin
-  TECCurve.Boot;
-end;
-
 function TECCurve.CreatePoint(const x, y: TBigInteger): IECPoint;
 function TECCurve.CreatePoint(const x, y: TBigInteger): IECPoint;
 begin
 begin
   result := CreatePoint(x, y, false);
   result := CreatePoint(x, y, false);
@@ -2728,16 +2783,6 @@ begin
   result := P;
   result := P;
 end;
 end;
 
 
-destructor TECCurve.Destroy;
-begin
-  inherited Destroy;
-end;
-
-class destructor TECCurve.DestroyECCurve;
-begin
-  FLock.Free;
-end;
-
 function TECCurve.Equals(const other: IECCurve): Boolean;
 function TECCurve.Equals(const other: IECCurve): Boolean;
 begin
 begin
   if ((Self as IECCurve) = other) then
   if ((Self as IECCurve) = other) then
@@ -3397,9 +3442,36 @@ end;
 function TAbstractF2mCurve.SolveQuadraticEquation(const beta: IECFieldElement)
 function TAbstractF2mCurve.SolveQuadraticEquation(const beta: IECFieldElement)
   : IECFieldElement;
   : IECFieldElement;
 var
 var
-  gamma, z, zeroElement, t, w, w2: IECFieldElement;
+  gamma, z, zeroElement, t, w, w2, r: IECFieldElement;
+  betaF2m: IAbstractF2mFieldElement;
   m, i: Int32;
   m, i: Int32;
+  fastTrace: Boolean;
 begin
 begin
+
+  betaF2m := beta as IAbstractF2mFieldElement;
+
+  fastTrace := betaF2m.HasFastTrace();
+  if ((fastTrace) and (betaF2m.Trace() <> 0)) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+
+  m := FieldSize;
+
+  // For odd m, use the half-trace
+  if ((m and 1) <> 0) then
+  begin
+    r := betaF2m.HalfTrace();
+    if ((fastTrace) or (r.Square().Add(r).Add(beta).IsZero)) then
+    begin
+      result := r;
+      Exit;
+    end;
+    result := Nil;
+    Exit;
+  end;
+
   if (beta.IsZero) then
   if (beta.IsZero) then
   begin
   begin
     result := beta;
     result := beta;
@@ -3628,12 +3700,22 @@ end;
 { TDefaultLookupTable }
 { TDefaultLookupTable }
 
 
 constructor TDefaultLookupTable.Create(const outer: IECCurve;
 constructor TDefaultLookupTable.Create(const outer: IECCurve;
-  const table: TCryptoLibByteArray; size: Int32);
+  const table: TCryptoLibByteArray; Size: Int32);
 begin
 begin
   Inherited Create();
   Inherited Create();
   Fm_outer := outer;
   Fm_outer := outer;
   Fm_table := table;
   Fm_table := table;
-  Fm_size := size;
+  Fm_size := Size;
+end;
+
+function TDefaultLookupTable.CreatePoint(const x, y: TCryptoLibByteArray)
+  : IECPoint;
+var
+  XFieldElement, YFieldElement: IECFieldElement;
+begin
+  XFieldElement := Fm_outer.FromBigInteger(TBigInteger.Create(1, x));
+  YFieldElement := Fm_outer.FromBigInteger(TBigInteger.Create(1, y));
+  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement, false);
 end;
 end;
 
 
 function TDefaultLookupTable.GetSize: Int32;
 function TDefaultLookupTable.GetSize: Int32;
@@ -3646,7 +3728,6 @@ var
   FE_BYTES, position, i, j: Int32;
   FE_BYTES, position, i, j: Int32;
   x, y: TCryptoLibByteArray;
   x, y: TCryptoLibByteArray;
   MASK: Byte;
   MASK: Byte;
-  XFieldElement, YFieldElement: IECFieldElement;
 begin
 begin
   FE_BYTES := (Fm_outer.FieldSize + 7) div 8;
   FE_BYTES := (Fm_outer.FieldSize + 7) div 8;
   System.SetLength(x, FE_BYTES);
   System.SetLength(x, FE_BYTES);
@@ -3661,41 +3742,52 @@ begin
 
 
     for j := 0 to System.Pred(FE_BYTES) do
     for j := 0 to System.Pred(FE_BYTES) do
     begin
     begin
-
       x[j] := x[j] xor Byte(Fm_table[position + j] and MASK);
       x[j] := x[j] xor Byte(Fm_table[position + j] and MASK);
       y[j] := y[j] xor Byte(Fm_table[position + FE_BYTES + j] and MASK);
       y[j] := y[j] xor Byte(Fm_table[position + FE_BYTES + j] and MASK);
     end;
     end;
     position := position + (FE_BYTES * 2);
     position := position + (FE_BYTES * 2);
   end;
   end;
 
 
-  XFieldElement := Fm_outer.FromBigInteger(TBigInteger.Create(1, x));
-  YFieldElement := Fm_outer.FromBigInteger(TBigInteger.Create(1, y));
-  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement, false);
+  result := CreatePoint(x, y);
+end;
+
+function TDefaultLookupTable.LookupVar(index: Int32): IECPoint;
+var
+  FE_BYTES, position, j: Int32;
+  x, y: TCryptoLibByteArray;
+begin
+  FE_BYTES := (Fm_outer.FieldSize + 7) div 8;
+  System.SetLength(x, FE_BYTES);
+  System.SetLength(y, FE_BYTES);
+
+  position := index * FE_BYTES * 2;
+
+  for j := 0 to System.Pred(FE_BYTES) do
+  begin
+    x[j] := Fm_table[position + j];
+    y[j] := Fm_table[position + FE_BYTES + j];
+  end;
+
+  result := CreatePoint(x, y);
 end;
 end;
 
 
 { TDefaultF2mLookupTable }
 { TDefaultF2mLookupTable }
 
 
 constructor TDefaultF2mLookupTable.Create(const outer: IF2mCurve;
 constructor TDefaultF2mLookupTable.Create(const outer: IF2mCurve;
-  const table: TCryptoLibInt64Array; size: Int32);
+  const table: TCryptoLibInt64Array; Size: Int32);
 begin
 begin
   Inherited Create();
   Inherited Create();
   Fm_outer := outer;
   Fm_outer := outer;
   Fm_table := table;
   Fm_table := table;
-  Fm_size := size;
-end;
-
-function TDefaultF2mLookupTable.GetSize: Int32;
-begin
-  result := Fm_size;
+  Fm_size := Size;
 end;
 end;
 
 
-function TDefaultF2mLookupTable.Lookup(index: Int32): IECPoint;
+function TDefaultF2mLookupTable.CreatePoint(const x, y: TCryptoLibInt64Array)
+  : IECPoint;
 var
 var
-  FE_LONGS, position, m, i, j: Int32;
-  ks: TCryptoLibInt32Array;
-  x, y: TCryptoLibInt64Array;
-  MASK: Int64;
   XFieldElement, YFieldElement: IECFieldElement;
   XFieldElement, YFieldElement: IECFieldElement;
+  m: Int32;
+  ks: TCryptoLibInt32Array;
 begin
 begin
   m := Fm_outer.m;
   m := Fm_outer.m;
   if Fm_outer.IsTrinomial() then
   if Fm_outer.IsTrinomial() then
@@ -3707,6 +3799,22 @@ begin
     ks := TCryptoLibInt32Array.Create(Fm_outer.k1, Fm_outer.k2, Fm_outer.k3);
     ks := TCryptoLibInt32Array.Create(Fm_outer.k1, Fm_outer.k2, Fm_outer.k3);
   end;
   end;
 
 
+  XFieldElement := TF2mFieldElement.Create(m, ks, TLongArray.Create(x));
+  YFieldElement := TF2mFieldElement.Create(m, ks, TLongArray.Create(y));
+  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement, false);
+end;
+
+function TDefaultF2mLookupTable.GetSize: Int32;
+begin
+  result := Fm_size;
+end;
+
+function TDefaultF2mLookupTable.Lookup(index: Int32): IECPoint;
+var
+  FE_LONGS, position, i, j: Int32;
+  x, y: TCryptoLibInt64Array;
+  MASK: Int64;
+begin
   FE_LONGS := (Fm_outer.m + 63) div 64;
   FE_LONGS := (Fm_outer.m + 63) div 64;
   System.SetLength(x, FE_LONGS);
   System.SetLength(x, FE_LONGS);
   System.SetLength(y, FE_LONGS);
   System.SetLength(y, FE_LONGS);
@@ -3720,16 +3828,33 @@ begin
 
 
     for j := 0 to System.Pred(FE_LONGS) do
     for j := 0 to System.Pred(FE_LONGS) do
     begin
     begin
-
       x[j] := x[j] xor (Fm_table[position + j] and MASK);
       x[j] := x[j] xor (Fm_table[position + j] and MASK);
       y[j] := y[j] xor (Fm_table[position + FE_LONGS + j] and MASK);
       y[j] := y[j] xor (Fm_table[position + FE_LONGS + j] and MASK);
     end;
     end;
     position := position + (FE_LONGS * 2);
     position := position + (FE_LONGS * 2);
   end;
   end;
 
 
-  XFieldElement := TF2mFieldElement.Create(m, ks, TLongArray.Create(x));
-  YFieldElement := TF2mFieldElement.Create(m, ks, TLongArray.Create(y));
-  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement, false);
+  result := CreatePoint(x, y);
+end;
+
+function TDefaultF2mLookupTable.LookupVar(index: Int32): IECPoint;
+var
+  FE_LONGS, position, j: Int32;
+  x, y: TCryptoLibInt64Array;
+begin
+  FE_LONGS := (Fm_outer.m + 63) div 64;
+  System.SetLength(x, FE_LONGS);
+  System.SetLength(y, FE_LONGS);
+
+  position := index * FE_LONGS * 2;
+
+  for j := 0 to System.Pred(FE_LONGS) do
+  begin
+    x[j] := Fm_table[position + j];
+    y[j] := Fm_table[position + FE_LONGS + j];
+  end;
+
+  result := CreatePoint(x, y);
 end;
 end;
 
 
 { TECPoint }
 { TECPoint }
@@ -3823,6 +3948,19 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TECPoint.ScaleXNegateY(const scale: IECFieldElement): IECPoint;
+begin
+  if IsInfinity then
+  begin
+    result := Self;
+  end
+  else
+  begin
+    result := curve.CreateRawPoint(RawXCoord.Multiply(scale), RawYCoord.Negate,
+      RawZCoords, IsCompressed);
+  end;
+end;
+
 function TECPoint.ScaleY(const scale: IECFieldElement): IECPoint;
 function TECPoint.ScaleY(const scale: IECFieldElement): IECPoint;
 begin
 begin
   if IsInfinity then
   if IsInfinity then
@@ -3836,6 +3974,19 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TECPoint.ScaleYNegateX(const scale: IECFieldElement): IECPoint;
+begin
+  if IsInfinity then
+  begin
+    result := Self;
+  end
+  else
+  begin
+    result := curve.CreateRawPoint(RawXCoord.Negate, RawYCoord.Multiply(scale),
+      RawZCoords, IsCompressed);
+  end;
+end;
+
 procedure TECPoint.SetpreCompTable(const Value
 procedure TECPoint.SetpreCompTable(const Value
   : TDictionary<String, IPreCompInfo>);
   : TDictionary<String, IPreCompInfo>);
 begin
 begin
@@ -6226,21 +6377,23 @@ function TAbstractF2mPoint.SatisfiesOrder: Boolean;
 var
 var
   Cofactor: TBigInteger;
   Cofactor: TBigInteger;
   n: IECPoint;
   n: IECPoint;
-  x, rhs, lambda, w, t: IECFieldElement;
+  x, rhs, L, t, y: IECFieldElement;
   Lcurve: IECCurve;
   Lcurve: IECCurve;
 begin
 begin
   Lcurve := curve;
   Lcurve := curve;
   Cofactor := Lcurve.GetCofactor();
   Cofactor := Lcurve.GetCofactor();
   if (TBigInteger.Two.Equals(Cofactor)) then
   if (TBigInteger.Two.Equals(Cofactor)) then
   begin
   begin
-    // /*
-    // *  Check that the trace of (X + A) is 0, then there exists a solution to L^2 + L = X + A,
-    // *  and so a halving is possible, so this point is the double of another.
-    // */
+    (*
+      * Check that 0 == Tr(X + A); then there exists a solution to L^2 + L = X + A, and
+      * so a halving is possible, so this point is the double of another.
+      *
+      * Note: Tr(A) == 1 for cofactor 2 curves.
+    *)
     n := Normalize();
     n := Normalize();
     x := n.AffineXCoord;
     x := n.AffineXCoord;
     rhs := x.Add(Lcurve.a);
     rhs := x.Add(Lcurve.a);
-    result := (rhs as IAbstractF2mFieldElement).Trace() = 0;
+    result := (x as IAbstractF2mFieldElement).Trace() <> 0;
     Exit;
     Exit;
   end;
   end;
   if (TBigInteger.Four.Equals(Cofactor)) then
   if (TBigInteger.Four.Equals(Cofactor)) then
@@ -6251,21 +6404,33 @@ begin
     // * and check if Tr(w + A) == 0 for at least one; then a second halving is possible
     // * and check if Tr(w + A) == 0 for at least one; then a second halving is possible
     // * (see comments for cofactor 2 above), so this point is four times another.
     // * (see comments for cofactor 2 above), so this point is four times another.
     // *
     // *
-    // * Note: Tr(x^2) == Tr(x).
+    // * Note: Tr(A) == 0 for cofactor 4 curves.
     // */
     // */
     n := Normalize();
     n := Normalize();
     x := n.AffineXCoord;
     x := n.AffineXCoord;
-    lambda := (Lcurve as IAbstractF2mCurve).SolveQuadraticEquation
-      (x.Add(curve.a));
-    if (lambda = Nil) then
+    L := (Lcurve as IAbstractF2mCurve).SolveQuadraticEquation(x.Add(curve.a));
+    if (L = Nil) then
     begin
     begin
       result := false;
       result := false;
       Exit;
       Exit;
     end;
     end;
-    w := x.Multiply(lambda).Add(n.AffineYCoord);
-    t := w.Add(Lcurve.a);
-    result := ((t as IAbstractF2mFieldElement).Trace() = 0) or
-      ((t.Add(x) as IAbstractF2mFieldElement).Trace() = 0);
+
+    (*
+      * A solution exists, therefore 0 == Tr(X + A) == Tr(X).
+    *)
+    y := n.AffineYCoord;
+    t := x.Multiply(L).Add(y);
+
+    (*
+      * Either T or (T + X) is the square of a half-point's x coordinate (hx). In either
+      * case, the half-point can be halved again when 0 == Tr(hx + A).
+      *
+      * Note: Tr(hx + A) == Tr(hx) == Tr(hx^2) == Tr(T) == Tr(T + X)
+      *
+      * Check that 0 == Tr(T); then there exists a solution to L^2 + L = hx + A, and so a
+      * second halving is possible and this point is four times some other.
+    *)
+    result := (t as IAbstractF2mFieldElement).Trace() = 0;
     Exit;
     Exit;
   end;
   end;
 
 
@@ -6321,6 +6486,12 @@ begin
 
 
 end;
 end;
 
 
+function TAbstractF2mPoint.ScaleXNegateY(const scale: IECFieldElement)
+  : IECPoint;
+begin
+  result := ScaleX(scale);
+end;
+
 function TAbstractF2mPoint.ScaleY(const scale: IECFieldElement): IECPoint;
 function TAbstractF2mPoint.ScaleY(const scale: IECFieldElement): IECPoint;
 var
 var
   Lx, L, L2: IECFieldElement;
   Lx, L, L2: IECFieldElement;
@@ -6352,6 +6523,12 @@ begin
 
 
 end;
 end;
 
 
+function TAbstractF2mPoint.ScaleYNegateX(const scale: IECFieldElement)
+  : IECPoint;
+begin
+  result := ScaleY(scale);
+end;
+
 function TAbstractF2mPoint.Subtract(const b: IECPoint): IECPoint;
 function TAbstractF2mPoint.Subtract(const b: IECPoint): IECPoint;
 begin
 begin
   if (b.IsInfinity) then
   if (b.IsInfinity) then
@@ -6505,4 +6682,41 @@ begin
   result := info;
   result := info;
 end;
 end;
 
 
+{ TSimpleLookupTable }
+
+class function TSimpleLookupTable.Copy(const points
+  : TCryptoLibGenericArray<IECPoint>; off, len: Int32)
+  : TCryptoLibGenericArray<IECPoint>;
+var
+  i: Int32;
+begin
+  System.SetLength(result, len);
+  for i := 0 to System.Pred(len) do
+  begin
+    result[i] := points[off + i];
+  end;
+end;
+
+constructor TSimpleLookupTable.Create(const points
+  : TCryptoLibGenericArray<IECPoint>; off, len: Int32);
+begin
+  inherited Create();
+  FPoints := Copy(points, off, len);
+end;
+
+function TSimpleLookupTable.GetSize: Int32;
+begin
+  result := System.Length(FPoints);
+end;
+
+function TSimpleLookupTable.Lookup(index: Int32): IECPoint;
+begin
+  raise EInvalidOperationCryptoLibException.CreateRes(@SUnsupportedOperation);
+end;
+
+function TSimpleLookupTable.LookupVar(index: Int32): IECPoint;
+begin
+  result := FPoints[index];
+end;
+
 end.
 end.

+ 1534 - 0
src/libraries/cryptolib4pascal/ClpECCompUtilities.pas

@@ -0,0 +1,1534 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECCompUtilities;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Math,
+  ClpBits,
+  ClpIECC,
+  ClpIPreCompInfo,
+  ClpIWNafPreCompInfo,
+  ClpWNafPreCompInfo,
+  ClpEndoPreCompInfo,
+  ClpIFixedPointPreCompInfo,
+  ClpIPreCompCallBack,
+  ClpBigInteger,
+  ClpIEndoPreCompInfo,
+  ClpIScalarSplitParameters,
+  ClpFixedPointPreCompInfo,
+  ClpCryptoLibTypes,
+  ClpECCurveConstants;
+
+resourcestring
+  SInvalidRange = 'Must be in the Range [2, 16], "width"';
+  SInvalidRange2 = 'Must be in the Range [2, 8], "width"';
+
+type
+  TWNafUtilities = class abstract(TObject)
+
+  strict private
+  const
+    DEFAULT_WINDOW_SIZE_CUTOFFS: array [0 .. 5] of Int32 = (13, 41, 121, 337,
+      897, 2305);
+
+    MAX_WIDTH = Int32(16);
+
+  class var
+    FEMPTY_BYTES: TCryptoLibByteArray;
+    FEMPTY_INTS: TCryptoLibInt32Array;
+
+  type
+    IMapPointCallback = interface(IPreCompCallback)
+      ['{730BF27F-D5C3-4DF4-AC77-B8653C457C10}']
+
+    end;
+
+  type
+    TMapPointCallback = class(TInterfacedObject, IPreCompCallback,
+      IMapPointCallback)
+
+    strict private
+    var
+      Fm_wnafPreCompP: IWNafPreCompInfo;
+      Fm_includeNegated: Boolean;
+      Fm_pointMap: IECPointMap;
+
+    public
+      constructor Create(const wnafPreCompP: IWNafPreCompInfo;
+        includeNegated: Boolean; const pointMap: IECPointMap);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  type
+    IWNafCallback = interface(IPreCompCallback)
+      ['{A439A606-7899-4720-937E-C2F3D94D4811}']
+
+    end;
+
+  type
+    TWNafCallback = class(TInterfacedObject, IPreCompCallback, IWNafCallback)
+
+    strict private
+
+    var
+      Fm_p: IECPoint;
+      FminWidth: Int32;
+      Fm_includeNegated: Boolean;
+
+      class function CheckExisting(const existingWNaf: IWNafPreCompInfo;
+        width, reqPreCompLen: Int32; includeNegated: Boolean): Boolean;
+        static; inline;
+
+      class function CheckTable(const table: TCryptoLibGenericArray<IECPoint>;
+        reqLen: Int32): Boolean; static; inline;
+
+    public
+      constructor Create(const p: IECPoint; minWidth: Int32;
+        includeNegated: Boolean);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  type
+    IPointMapCallback = interface(IPreCompCallback)
+      ['{00A66D4E-7D61-4A47-AE36-E8D89DEE8D9F}']
+
+    end;
+
+  type
+    TPointMapCallback = class(TInterfacedObject, IPreCompCallback,
+      IPointMapCallback)
+
+    strict private
+
+    var
+      Fm_p: IECPoint;
+      FpointMap: IECPointMap;
+      FfromWNaf: IWNafPreCompInfo;
+      FIncludeNegated: Boolean;
+
+      class function CheckExisting(const existingWNaf: IWNafPreCompInfo;
+        width, reqPreCompLen: Int32; includeNegated: Boolean): Boolean;
+        static; inline;
+
+      class function CheckTable(const table: TCryptoLibGenericArray<IECPoint>;
+        reqLen: Int32): Boolean; static; inline;
+
+    public
+      constructor Create(const p: IECPoint; const pointMap: IECPointMap;
+        const fromWNaf: IWNafPreCompInfo; includeNegated: Boolean);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  type
+    IBasePointCallback = interface(IPreCompCallback)
+      ['{5A81CB00-3CB4-474D-A2A7-E949F7E71AEC}']
+
+    end;
+
+  type
+    TBasePointCallback = class(TInterfacedObject, IPreCompCallback,
+      IBasePointCallback)
+
+    strict private
+    var
+      FConfWidth: Int32;
+
+    public
+      constructor Create(ConfWidth: Int32);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  class function Trim(const a: TCryptoLibByteArray; length: Int32)
+    : TCryptoLibByteArray; overload; static; inline;
+
+  class function Trim(const a: TCryptoLibInt32Array; length: Int32)
+    : TCryptoLibInt32Array; overload; static; inline;
+
+  class function ResizeTable(const a: TCryptoLibGenericArray<IECPoint>;
+    length: Int32): TCryptoLibGenericArray<IECPoint>; static; inline;
+
+  class procedure Boot(); static;
+  class constructor CreateWNafUtilities();
+
+  public
+
+    const
+    PRECOMP_NAME: String = 'bc_wnaf';
+
+    class function GenerateCompactNaf(const k: TBigInteger)
+      : TCryptoLibInt32Array; static;
+    class function GenerateCompactWindowNaf(width: Int32; const k: TBigInteger)
+      : TCryptoLibInt32Array; static;
+
+    class function GenerateJsf(const g, h: TBigInteger)
+      : TCryptoLibByteArray; static;
+    class function GenerateNaf(const k: TBigInteger)
+      : TCryptoLibByteArray; static;
+    // /**
+    // * Computes the Window NAF (non-adjacent Form) of an integer.
+    // * @param width The width <code>w</code> of the Window NAF. The width is
+    // * defined as the minimal number <code>w</code>, such that for any
+    // * <code>w</code> consecutive digits in the resulting representation, at
+    // * most one is non-zero.
+    // * @param k The integer of which the Window NAF is computed.
+    // * @return The Window NAF of the given width, such that the following holds:
+    // * <code>k = &amp;sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
+    // * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
+    // * returned <code>byte[]</code>.
+    // */
+    class function GenerateWindowNaf(width: Int32; const k: TBigInteger)
+      : TCryptoLibByteArray; static;
+
+    class function GetNafWeight(const k: TBigInteger): Int32; static; inline;
+
+    class function GetWNafPreCompInfo(const p: IECPoint): IWNafPreCompInfo;
+      overload; static; inline;
+
+    class function GetWNafPreCompInfo(const preCompInfo: IPreCompInfo)
+      : IWNafPreCompInfo; overload; static; inline;
+
+    /// <summary>
+    /// Determine window width to use for a scalar multiplication of the
+    /// given size.
+    /// </summary>
+    /// <param name="bits">
+    /// the bit-length of the scalar to multiply by
+    /// </param>
+    /// <returns>
+    /// the window size to use
+    /// </returns>
+    class function GetWindowSize(bits: Int32): Int32; overload; static; inline;
+
+    /// <summary>
+    /// Determine window width to use for a scalar multiplication of the
+    /// given size.
+    /// </summary>
+    /// <param name="bits">
+    /// the bit-length of the scalar to multiply by
+    /// </param>
+    /// <param name="maxWidth">
+    /// the maximum window width to return
+    /// </param>
+    /// <returns>
+    /// the window size to use
+    /// </returns>
+    class function GetWindowSize(bits, maxWidth: Int32): Int32; overload;
+      static; inline;
+
+    /// <summary>
+    /// Determine window width to use for a scalar multiplication of the
+    /// given size.
+    /// </summary>
+    /// <param name="bits">
+    /// the bit-length of the scalar to multiply by
+    /// </param>
+    /// <param name="windowSizeCutoffs">
+    /// a monotonically increasing list of bit sizes at which to increment
+    /// the window width
+    /// </param>
+    /// <returns>
+    /// the window size to use
+    /// </returns>
+    class function GetWindowSize(bits: Int32;
+      const windowSizeCutoffs: array of Int32): Int32; overload; static;
+
+    /// <summary>
+    /// Determine window width to use for a scalar multiplication of the
+    /// given size.
+    /// </summary>
+    /// <param name="bits">
+    /// the bit-length of the scalar to multiply by
+    /// </param>
+    /// <param name="windowSizeCutoffs">
+    /// a monotonically increasing list of bit sizes at which to increment
+    /// the window width
+    /// </param>
+    /// /// <param name="maxWidth">
+    /// the maximum window width to return
+    /// </param>
+    /// <returns>
+    /// the window size to use
+    /// </returns>
+    class function GetWindowSize(bits: Int32;
+      const windowSizeCutoffs: array of Int32; maxWidth: Int32): Int32;
+      overload; static;
+
+    class function Precompute(const p: IECPoint; minWidth: Int32;
+      includeNegated: Boolean): IWNafPreCompInfo; static;
+
+    class procedure ConfigureBasepoint(const p: IECPoint); static;
+
+    class function PrecomputeWithPointMap(const p: IECPoint;
+      const pointMap: IECPointMap; const fromWNaf: IWNafPreCompInfo;
+      includeNegated: Boolean): IWNafPreCompInfo;
+
+  end;
+
+type
+  TEndoUtilities = class abstract(TObject)
+
+  strict private
+  type
+    IEndoCallback = interface(IPreCompCallback)
+      ['{80C0B850-A97A-4603-A42F-A476ABAF2026}']
+
+    end;
+
+  type
+    TEndoCallback = class(TInterfacedObject, IPreCompCallback, IEndoCallback)
+
+    strict private
+    var
+      Fendomorphism: IECEndomorphism;
+      Fp: IECPoint;
+
+      class function CheckExisting(const existingEndo: IEndoPreCompInfo;
+        const endomorphism: IECEndomorphism): Boolean; static; inline;
+
+    public
+      constructor Create(const endomorphism: IECEndomorphism;
+        const p: IECPoint);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  class function CalculateB(const k, g: TBigInteger; t: Int32)
+    : TBigInteger; static;
+
+  public
+
+    const
+    PRECOMP_NAME: String = 'bc_endo';
+
+  public
+    class function MapPoint(const endomorphism: IECEndomorphism;
+      const p: IECPoint): IECPoint; static;
+
+    class function DecomposeScalar(const p: IScalarSplitParameters;
+      const k: TBigInteger): TCryptoLibGenericArray<TBigInteger>; static;
+
+  end;
+
+type
+  TFixedPointUtilities = class sealed(TObject)
+  strict private
+
+  type
+    IFixedPointCallback = interface(IPreCompCallback)
+      ['{E6DFE8D3-A890-4568-AA4A-3D8BC6AF16E9}']
+
+    end;
+
+  type
+    TFixedPointCallback = class(TInterfacedObject, IPreCompCallback,
+      IFixedPointCallback)
+
+    strict private
+    var
+      Fm_p: IECPoint;
+
+      class function CheckExisting(const existingFP: IFixedPointPreCompInfo;
+        n: Int32): Boolean; static; inline;
+
+      class function CheckTable(const table: IECLookupTable; n: Int32): Boolean;
+        static; inline;
+
+    public
+      constructor Create(const p: IECPoint);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  const
+    PRECOMP_NAME: String = 'bc_fixed_point';
+
+  public
+
+    class function GetFixedPointPreCompInfo(const preCompInfo: IPreCompInfo)
+      : IFixedPointPreCompInfo; static; inline;
+
+    class function GetCombSize(const c: IECCurve): Int32; static; inline;
+
+    class function Precompute(const p: IECPoint)
+      : IFixedPointPreCompInfo; static;
+  end;
+
+implementation
+
+{ TWNafUtilities }
+
+uses
+  ClpECAlgorithms; // included here to avoid circular dependency :)
+
+class function TWNafUtilities.ResizeTable
+  (const a: TCryptoLibGenericArray<IECPoint>; length: Int32)
+  : TCryptoLibGenericArray<IECPoint>;
+begin
+  result := System.Copy(a);
+  System.SetLength(result, length);
+end;
+
+class function TWNafUtilities.Trim(const a: TCryptoLibInt32Array; length: Int32)
+  : TCryptoLibInt32Array;
+begin
+  result := System.Copy(a, 0, length);
+end;
+
+class function TWNafUtilities.Trim(const a: TCryptoLibByteArray; length: Int32)
+  : TCryptoLibByteArray;
+begin
+  result := System.Copy(a, 0, length);
+end;
+
+class procedure TWNafUtilities.Boot;
+begin
+  FEMPTY_BYTES := Nil;
+  FEMPTY_INTS := Nil;
+end;
+
+class constructor TWNafUtilities.CreateWNafUtilities;
+begin
+  TWNafUtilities.Boot;
+end;
+
+class function TWNafUtilities.GetWindowSize(bits: Int32;
+  const windowSizeCutoffs: array of Int32; maxWidth: Int32): Int32;
+var
+  w: Int32;
+begin
+  w := 0;
+  while (w < System.length(windowSizeCutoffs)) do
+  begin
+    if (bits < windowSizeCutoffs[w]) then
+    begin
+      break;
+    end;
+    System.Inc(w);
+  end;
+
+  result := Max(2, Min(maxWidth, w + 2));
+end;
+
+class function TWNafUtilities.GetWindowSize(bits: Int32;
+  const windowSizeCutoffs: array of Int32): Int32;
+begin
+  result := GetWindowSize(bits, windowSizeCutoffs, MAX_WIDTH);
+end;
+
+class function TWNafUtilities.GetWindowSize(bits, maxWidth: Int32): Int32;
+begin
+  result := GetWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS, maxWidth);
+end;
+
+class function TWNafUtilities.GetWindowSize(bits: Int32): Int32;
+begin
+  result := GetWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS, MAX_WIDTH);
+end;
+
+class procedure TWNafUtilities.ConfigureBasepoint(const p: IECPoint);
+var
+  c: IECCurve;
+  n: TBigInteger;
+  bits, ConfWidth: Int32;
+begin
+  c := p.Curve;
+  if (c = Nil) then
+  begin
+    Exit;
+  end;
+
+  n := c.Order;
+  if (not n.IsInitialized) then
+  begin
+    bits := c.FieldSize + 1;
+  end
+  else
+  begin
+    bits := n.BitLength;
+  end;
+
+  ConfWidth := Min(MAX_WIDTH, GetWindowSize(bits) + 3);
+
+  c.Precompute(p, PRECOMP_NAME, TBasePointCallback.Create(ConfWidth)
+    as IBasePointCallback);
+end;
+
+class function TWNafUtilities.GenerateCompactNaf(const k: TBigInteger)
+  : TCryptoLibInt32Array;
+var
+  _3k, diff: TBigInteger;
+  bits, highBit, &length, zeroes, i, digit: Int32;
+  naf: TCryptoLibInt32Array;
+begin
+  if ((TBits.Asr32(k.BitLength, 16)) <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBitLength);
+  end;
+  if (k.SignValue = 0) then
+  begin
+    result := FEMPTY_INTS;
+    Exit;
+  end;
+
+  _3k := k.ShiftLeft(1).Add(k);
+
+  bits := _3k.BitLength;
+  System.SetLength(naf, TBits.Asr32(bits, 1));
+
+  diff := _3k.&Xor(k);
+
+  highBit := bits - 1;
+  &length := 0;
+  zeroes := 0;
+
+  i := 1;
+
+  while (i < highBit) do
+  begin
+    if (not diff.TestBit(i)) then
+    begin
+      System.Inc(zeroes);
+      System.Inc(i);
+      continue;
+    end;
+
+    if k.TestBit(i) then
+    begin
+      digit := -1;
+    end
+    else
+    begin
+      digit := 1;
+    end;
+
+    naf[length] := (digit shl 16) or zeroes;
+    System.Inc(length);
+    zeroes := 1;
+
+    System.Inc(i, 2);
+
+  end;
+
+  naf[length] := (1 shl 16) or zeroes;
+  System.Inc(length);
+
+  if (System.length(naf) > length) then
+  begin
+    naf := Trim(naf, length);
+  end;
+
+  result := naf;
+end;
+
+class function TWNafUtilities.GenerateCompactWindowNaf(width: Int32;
+  const k: TBigInteger): TCryptoLibInt32Array;
+var
+  wnaf: TCryptoLibInt32Array;
+  pow2, mask, sign, &length, &pos, digit, zeroes: Int32;
+  carry: Boolean;
+  LK: TBigInteger;
+begin
+  LK := k;
+  if (width = 2) then
+  begin
+    result := GenerateCompactNaf(LK);
+    Exit;
+  end;
+
+  if ((width < 2) or (width > 16)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange);
+  end;
+  if ((TBits.Asr32(LK.BitLength, 16)) <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBitLength);
+  end;
+  if (LK.SignValue = 0) then
+  begin
+    result := FEMPTY_INTS;
+    Exit;
+  end;
+
+  System.SetLength(wnaf, (LK.BitLength div width) + 1);
+
+  // 2^width and a mask and sign bit set accordingly
+  pow2 := 1 shl width;
+  mask := pow2 - 1;
+  sign := TBits.Asr32(pow2, 1);
+
+  carry := false;
+  length := 0;
+  pos := 0;
+
+  while (pos <= LK.BitLength) do
+  begin
+    if (LK.TestBit(pos) = carry) then
+    begin
+      System.Inc(pos);
+      continue;
+    end;
+
+    LK := LK.ShiftRight(pos);
+
+    digit := LK.Int32Value and mask;
+    if (carry) then
+    begin
+      System.Inc(digit);
+    end;
+
+    carry := (digit and sign) <> 0;
+    if (carry) then
+    begin
+      digit := digit - pow2;
+    end;
+
+    if length > 0 then
+    begin
+      zeroes := pos - 1;
+    end
+    else
+    begin
+      zeroes := pos;
+    end;
+
+    wnaf[length] := (digit shl 16) or zeroes;
+    System.Inc(length);
+    pos := width;
+  end;
+
+  // Reduce the WNAF array to its actual length
+  if (System.length(wnaf) > length) then
+  begin
+    wnaf := Trim(wnaf, length);
+  end;
+
+  result := wnaf;
+end;
+
+class function TWNafUtilities.GenerateJsf(const g, h: TBigInteger)
+  : TCryptoLibByteArray;
+var
+  digits, j, d0, d1, Offset, n0, n1, u0, u1: Int32;
+  jsf: TCryptoLibByteArray;
+  k0, k1: TBigInteger;
+begin
+  digits := Max(g.BitLength, h.BitLength) + 1;
+
+  System.SetLength(jsf, digits);
+
+  k0 := g;
+  k1 := h;
+  j := 0;
+  d0 := 0;
+  d1 := 0;
+
+  Offset := 0;
+
+  while (((d0 or d1) <> 0) or (k0.BitLength > Offset) or
+    (k1.BitLength > Offset)) do
+  begin
+    n0 := (Int32(UInt32(k0.Int32Value) shr Offset) + d0) and 7;
+    n1 := (Int32(UInt32(k1.Int32Value) shr Offset) + d1) and 7;
+
+    u0 := n0 and 1;
+    if (u0 <> 0) then
+    begin
+      u0 := u0 - (n0 and 2);
+      if (((n0 + u0) = 4) and ((n1 and 3) = 2)) then
+      begin
+        u0 := -u0;
+      end;
+    end;
+
+    u1 := n1 and 1;
+    if (u1 <> 0) then
+    begin
+      u1 := u1 - (n1 and 2);
+      if (((n1 + u1) = 4) and ((n0 and 3) = 2)) then
+      begin
+        u1 := -u1;
+      end;
+    end;
+
+    if ((d0 shl 1) = (1 + u0)) then
+    begin
+      d0 := d0 xor 1;
+    end;
+    if ((d1 shl 1) = (1 + u1)) then
+    begin
+      d1 := d1 xor 1;
+    end;
+
+    System.Inc(Offset);
+    if (Offset = 30) then
+    begin
+      Offset := 0;
+      k0 := k0.ShiftRight(30);
+      k1 := k1.ShiftRight(30);
+    end;
+
+    jsf[j] := Byte((u0 shl 4) or (u1 and $F));
+    System.Inc(j);
+  end;
+
+  // Reduce the JSF array to its actual length
+  if (System.length(jsf) > j) then
+  begin
+    jsf := Trim(jsf, j);
+  end;
+
+  result := jsf;
+end;
+
+class function TWNafUtilities.GenerateNaf(const k: TBigInteger)
+  : TCryptoLibByteArray;
+var
+  _3k, diff: TBigInteger;
+  digits, i: Int32;
+  naf: TCryptoLibByteArray;
+begin
+  if (k.SignValue = 0) then
+  begin
+    result := FEMPTY_BYTES;
+    Exit;
+  end;
+
+  _3k := k.ShiftLeft(1).Add(k);
+
+  digits := _3k.BitLength - 1;
+  System.SetLength(naf, digits);
+
+  diff := _3k.&Xor(k);
+
+  i := 1;
+
+  while i < digits do
+  begin
+    if (diff.TestBit(i)) then
+    begin
+      if k.TestBit(i) then
+      begin
+        naf[i - 1] := Byte(-1);
+      end
+      else
+      begin
+        naf[i - 1] := Byte(1);
+      end;
+
+      System.Inc(i);
+    end;
+    System.Inc(i);
+  end;
+
+  naf[digits - 1] := 1;
+
+  result := naf;
+end;
+
+class function TWNafUtilities.GenerateWindowNaf(width: Int32;
+  const k: TBigInteger): TCryptoLibByteArray;
+var
+  wnaf: TCryptoLibByteArray;
+  pow2, mask, sign, &length, &pos, digit: Int32;
+  carry: Boolean;
+  LK: TBigInteger;
+begin
+  LK := k;
+  if (width = 2) then
+  begin
+    result := GenerateNaf(LK);
+    Exit;
+  end;
+
+  if ((width < 2) or (width > 8)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange2);
+  end;
+  if (LK.SignValue = 0) then
+  begin
+    result := FEMPTY_BYTES;
+    Exit;
+  end;
+
+  System.SetLength(wnaf, LK.BitLength + 1);
+
+  // 2^width and a mask and sign bit set accordingly
+  pow2 := 1 shl width;
+  mask := pow2 - 1;
+  sign := TBits.Asr32(pow2, 1);
+
+  carry := false;
+  length := 0;
+  pos := 0;
+
+  while (pos <= LK.BitLength) do
+  begin
+    if (LK.TestBit(pos) = carry) then
+    begin
+      System.Inc(pos);
+      continue;
+    end;
+
+    LK := LK.ShiftRight(pos);
+
+    digit := LK.Int32Value and mask;
+    if (carry) then
+    begin
+      System.Inc(digit);
+    end;
+
+    carry := (digit and sign) <> 0;
+    if (carry) then
+    begin
+      digit := digit - pow2;
+    end;
+
+    if length > 0 then
+    begin
+      length := length + (pos - 1);
+    end
+    else
+    begin
+      length := length + (pos);
+    end;
+
+    wnaf[length] := Byte(digit);
+    System.Inc(length);
+    pos := width;
+  end;
+
+  // Reduce the WNAF array to its actual length
+  if (System.length(wnaf) > length) then
+  begin
+    wnaf := Trim(wnaf, length);
+  end;
+
+  result := wnaf;
+end;
+
+class function TWNafUtilities.GetNafWeight(const k: TBigInteger): Int32;
+var
+  _3k, diff: TBigInteger;
+begin
+  if (k.SignValue = 0) then
+  begin
+    result := 0;
+    Exit;
+  end;
+
+  _3k := k.ShiftLeft(1).Add(k);
+  diff := _3k.&Xor(k);
+
+  result := diff.BitCount;
+end;
+
+class function TWNafUtilities.GetWNafPreCompInfo(const preCompInfo
+  : IPreCompInfo): IWNafPreCompInfo;
+begin
+  result := preCompInfo as IWNafPreCompInfo;
+end;
+
+class function TWNafUtilities.GetWNafPreCompInfo(const p: IECPoint)
+  : IWNafPreCompInfo;
+var
+  preCompInfo: IPreCompInfo;
+begin
+  preCompInfo := p.Curve.GetPreCompInfo(p, PRECOMP_NAME);
+  result := GetWNafPreCompInfo(preCompInfo);
+end;
+
+class function TWNafUtilities.Precompute(const p: IECPoint; minWidth: Int32;
+  includeNegated: Boolean): IWNafPreCompInfo;
+begin
+  result := p.Curve.Precompute(p, PRECOMP_NAME,
+    TWNafCallback.Create(p, minWidth, includeNegated) as IWNafCallback)
+    as IWNafPreCompInfo;
+end;
+
+class function TWNafUtilities.PrecomputeWithPointMap(const p: IECPoint;
+  const pointMap: IECPointMap; const fromWNaf: IWNafPreCompInfo;
+  includeNegated: Boolean): IWNafPreCompInfo;
+var
+  c: IECCurve;
+begin
+  c := p.Curve;
+  result := c.Precompute(p, PRECOMP_NAME, TPointMapCallback.Create(p, pointMap,
+    fromWNaf, includeNegated) as IPointMapCallback) as IWNafPreCompInfo;
+end;
+
+{ TWNafUtilities.TMapPointCallback }
+
+constructor TWNafUtilities.TMapPointCallback.Create(const wnafPreCompP
+  : IWNafPreCompInfo; includeNegated: Boolean; const pointMap: IECPointMap);
+begin
+  Inherited Create();
+  Fm_wnafPreCompP := wnafPreCompP;
+  Fm_includeNegated := includeNegated;
+  Fm_pointMap := pointMap;
+end;
+
+function TWNafUtilities.TMapPointCallback.Precompute(const existing
+  : IPreCompInfo): IPreCompInfo;
+var
+  tempResult: IWNafPreCompInfo;
+  twiceP, twiceQ: IECPoint;
+  preCompP, preCompQ, preCompNegQ: TCryptoLibGenericArray<IECPoint>;
+  i: Int32;
+begin
+  tempResult := TWNafPreCompInfo.Create();
+
+  tempResult.ConfWidth := Fm_wnafPreCompP.ConfWidth;
+
+  twiceP := Fm_wnafPreCompP.Twice;
+  if (twiceP <> Nil) then
+  begin
+    twiceQ := Fm_pointMap.Map(twiceP);
+    tempResult.Twice := twiceQ;
+  end;
+
+  preCompP := Fm_wnafPreCompP.PreComp;
+
+  System.SetLength(preCompQ, System.length(preCompP));
+  for i := 0 to System.Pred(System.length(preCompP)) do
+  begin
+    preCompQ[i] := Fm_pointMap.Map(preCompP[i]);
+  end;
+
+  tempResult.PreComp := preCompQ;
+  tempResult.width := Fm_wnafPreCompP.width;
+
+  if (Fm_includeNegated) then
+  begin
+
+    System.SetLength(preCompNegQ, System.length(preCompQ));
+
+    for i := 0 to System.Pred(System.length(preCompNegQ)) do
+    begin
+      preCompNegQ[i] := preCompQ[i].Negate();
+    end;
+
+    tempResult.PreCompNeg := preCompNegQ;
+  end;
+
+  result := tempResult;
+end;
+
+{ TWNafUtilities.TWNafCallback }
+
+class function TWNafUtilities.TWNafCallback.CheckTable
+  (const table: TCryptoLibGenericArray<IECPoint>; reqLen: Int32): Boolean;
+begin
+  result := (table <> Nil) and (System.length(table) >= reqLen);
+end;
+
+class function TWNafUtilities.TWNafCallback.CheckExisting(const existingWNaf
+  : IWNafPreCompInfo; width, reqPreCompLen: Int32;
+  includeNegated: Boolean): Boolean;
+begin
+  result := (existingWNaf <> Nil) and
+    (existingWNaf.width >= Max(existingWNaf.ConfWidth, width))
+
+    and CheckTable(existingWNaf.PreComp, reqPreCompLen) and
+    ((not includeNegated) or CheckTable(existingWNaf.PreCompNeg,
+    reqPreCompLen));
+end;
+
+constructor TWNafUtilities.TWNafCallback.Create(const p: IECPoint;
+  minWidth: Int32; includeNegated: Boolean);
+begin
+  Inherited Create();
+  Fm_p := p;
+  FminWidth := minWidth;
+  Fm_includeNegated := includeNegated;
+end;
+
+function TWNafUtilities.TWNafCallback.Precompute(const existing: IPreCompInfo)
+  : IPreCompInfo;
+var
+  twiceP, isoTwiceP, last: IECPoint;
+  c: IECCurve;
+  PreComp, PreCompNeg, EMPTY_POINTS: TCryptoLibGenericArray<IECPoint>;
+  tempRes, existingWNaf: IWNafPreCompInfo;
+  reqPreCompLen, iniPreCompLen, curPreCompLen, pos, width, PromotionCountdown,
+    ConfWidth: Int32;
+  iso, iso2, iso3: IECFieldElement;
+begin
+  c := Fm_p.Curve;
+  EMPTY_POINTS := Nil;
+  existingWNaf := existing as IWNafPreCompInfo;
+
+  width := Max(2, Min(MAX_WIDTH, FminWidth));
+  reqPreCompLen := 1 shl (width - 2);
+
+  if (CheckExisting(existingWNaf, width, reqPreCompLen, Fm_includeNegated)) then
+  begin
+    existingWNaf.DecrementPromotionCountdown;
+    result := existingWNaf;
+    Exit;
+  end;
+
+  tempRes := TWNafPreCompInfo.Create();
+
+  if (existingWNaf <> Nil) then
+  begin
+
+    PromotionCountdown := existingWNaf.DecrementPromotionCountdown;
+    tempRes.PromotionCountdown := PromotionCountdown;
+
+    ConfWidth := existingWNaf.ConfWidth;
+    tempRes.ConfWidth := ConfWidth;
+
+    PreComp := existingWNaf.PreComp;
+    PreCompNeg := existingWNaf.PreCompNeg;
+    twiceP := existingWNaf.Twice;
+  end;
+
+  width := Min(MAX_WIDTH, Max(tempRes.ConfWidth, width));
+  reqPreCompLen := 1 shl (width - 2);
+
+  iniPreCompLen := 0;
+  if (PreComp = Nil) then
+  begin
+    PreComp := EMPTY_POINTS;
+  end
+  else
+  begin
+    iniPreCompLen := System.length(PreComp);
+  end;
+
+  if (iniPreCompLen < reqPreCompLen) then
+  begin
+    PreComp := TWNafUtilities.ResizeTable(PreComp, reqPreCompLen);
+
+    if (reqPreCompLen = 1) then
+    begin
+      PreComp[0] := Fm_p.Normalize();
+    end
+    else
+    begin
+      curPreCompLen := iniPreCompLen;
+      if (curPreCompLen = 0) then
+      begin
+        PreComp[0] := Fm_p;
+        curPreCompLen := 1;
+      end;
+
+      if (reqPreCompLen = 2) then
+      begin
+        PreComp[1] := Fm_p.threeTimes();
+      end
+      else
+      begin
+        isoTwiceP := twiceP;
+        last := PreComp[curPreCompLen - 1];
+        if (isoTwiceP = Nil) then
+        begin
+          isoTwiceP := PreComp[0].Twice();
+          twiceP := isoTwiceP;
+          //
+          // /*
+          // * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
+          // * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
+          // * also requires scaling the initial point's X, Y coordinates, and reversing the
+          // * isomorphism as part of the subsequent normalization.
+          // *
+          // *  NOTE: The correctness of this optimization depends on:
+          // *      1) additions do not use the curve's A, B coefficients.
+          // *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
+          // */
+          if ((not(twiceP.IsInfinity)) and (TECAlgorithms.IsFpCurve(c)) and
+            (c.FieldSize >= 64)) then
+          begin
+            case (c.CoordinateSystem) of
+              TECCurveConstants.COORD_JACOBIAN,
+                TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+                TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+
+                begin
+                  iso := twiceP.GetZCoord(0);
+                  isoTwiceP := c.CreatePoint(twiceP.XCoord.ToBigInteger,
+                    twiceP.YCoord.ToBigInteger());
+
+                  iso2 := iso.square();
+                  iso3 := iso2.Multiply(iso);
+                  last := last.scaleX(iso2).scaleY(iso3);
+
+                  if (iniPreCompLen = 0) then
+                  begin
+                    PreComp[0] := last;
+                  end;
+                end;
+
+            end;
+
+          end;
+        end;
+
+        while (curPreCompLen < reqPreCompLen) do
+        begin
+          // /*
+          // * Compute the new ECPoints for the precomputation array. The values 1, 3,
+          // * 5, ..., 2^(width-1)-1 times p are computed
+          // */
+          last := last.Add(isoTwiceP);
+          PreComp[curPreCompLen] := last;
+          System.Inc(curPreCompLen);
+        end;
+      end;
+      //
+      // /*
+      // * Having oft-used operands in affine form makes operations faster.
+      // */
+      c.NormalizeAll(PreComp, iniPreCompLen,
+        reqPreCompLen - iniPreCompLen, iso);
+    end;
+  end;
+
+  if (Fm_includeNegated) then
+  begin
+
+    if (PreCompNeg = Nil) then
+    begin
+      pos := 0;
+      System.SetLength(PreCompNeg, reqPreCompLen);
+
+    end
+    else
+    begin
+      pos := System.length(PreCompNeg);
+      if (pos < reqPreCompLen) then
+      begin
+        PreCompNeg := TWNafUtilities.ResizeTable(PreCompNeg, reqPreCompLen);
+      end;
+    end;
+
+    while (pos < reqPreCompLen) do
+    begin
+      PreCompNeg[pos] := PreComp[pos].Negate();
+      System.Inc(pos);
+    end;
+  end;
+
+  tempRes.PreComp := PreComp;
+  tempRes.PreCompNeg := PreCompNeg;
+  tempRes.Twice := twiceP;
+  tempRes.width := width;
+
+  result := tempRes;
+end;
+
+{ TWNafUtilities.TBasePointCallback }
+
+constructor TWNafUtilities.TBasePointCallback.Create(ConfWidth: Int32);
+begin
+  Inherited Create();
+  FConfWidth := ConfWidth;
+end;
+
+function TWNafUtilities.TBasePointCallback.Precompute(const existing
+  : IPreCompInfo): IPreCompInfo;
+var
+  existingWNaf, tempResult: IWNafPreCompInfo;
+begin
+
+  if Supports(existing, IWNafPreCompInfo) then
+  begin
+    existingWNaf := existing as IWNafPreCompInfo;
+  end
+  else
+  begin
+    existingWNaf := Nil;
+  end;
+
+  if ((existingWNaf <> Nil) and (existingWNaf.ConfWidth = FConfWidth)) then
+  begin
+    existingWNaf.PromotionCountdown := 0;
+    result := existingWNaf;
+    Exit;
+  end;
+
+  tempResult := TWNafPreCompInfo.Create();
+
+  tempResult.PromotionCountdown := 0;
+  tempResult.ConfWidth := FConfWidth;
+
+  if (existingWNaf <> Nil) then
+  begin
+    tempResult.PreComp := existingWNaf.PreComp;
+    tempResult.PreCompNeg := existingWNaf.PreCompNeg;
+    tempResult.Twice := existingWNaf.Twice;
+    tempResult.width := existingWNaf.width;
+  end;
+  result := tempResult;
+end;
+
+{ TWNafUtilities.TPointMapCallback }
+
+class function TWNafUtilities.TPointMapCallback.CheckTable
+  (const table: TCryptoLibGenericArray<IECPoint>; reqLen: Int32): Boolean;
+begin
+  result := ((table <> Nil) and (System.length(table) >= reqLen));
+end;
+
+class function TWNafUtilities.TPointMapCallback.CheckExisting(const existingWNaf
+  : IWNafPreCompInfo; width, reqPreCompLen: Int32;
+  includeNegated: Boolean): Boolean;
+begin
+  result := ((existingWNaf <> Nil) and (existingWNaf.width >= width) and
+    (CheckTable(existingWNaf.PreComp, reqPreCompLen)) and
+    ((not includeNegated) or (CheckTable(existingWNaf.PreCompNeg,
+    reqPreCompLen))));
+end;
+
+constructor TWNafUtilities.TPointMapCallback.Create(const p: IECPoint;
+  const pointMap: IECPointMap; const fromWNaf: IWNafPreCompInfo;
+  includeNegated: Boolean);
+begin
+  Inherited Create();
+  Fm_p := p;
+  FpointMap := pointMap;
+  FfromWNaf := fromWNaf;
+  FIncludeNegated := includeNegated;
+end;
+
+function TWNafUtilities.TPointMapCallback.Precompute(const existing
+  : IPreCompInfo): IPreCompInfo;
+var
+  existingWNaf: IWNafPreCompInfo;
+  width, reqPreCompLen, i: Int32;
+  tempResult: IWNafPreCompInfo;
+  twiceFrom, Ltwice: IECPoint;
+  LpreCompFrom, LpreComp, LpreCompNeg: TCryptoLibGenericArray<IECPoint>;
+begin
+  if Supports(existing, IWNafPreCompInfo) then
+  begin
+    existingWNaf := existing as IWNafPreCompInfo;
+  end
+  else
+  begin
+    existingWNaf := Nil;
+  end;
+  width := FfromWNaf.width;
+  reqPreCompLen := System.length(FfromWNaf.PreComp);
+
+  if (CheckExisting(existingWNaf, width, reqPreCompLen, FIncludeNegated)) then
+  begin
+    existingWNaf.DecrementPromotionCountdown;
+    result := existingWNaf;
+    Exit;
+  end;
+
+  (*
+    * TODO Ideally this method would support incremental calculation, but given the
+    * existing use-cases it would be of little-to-no benefit.
+  *)
+  tempResult := TWNafPreCompInfo.Create();
+
+  tempResult.PromotionCountdown := FfromWNaf.PromotionCountdown;
+
+  twiceFrom := FfromWNaf.Twice;
+  if (twiceFrom <> Nil) then
+  begin
+    Ltwice := FpointMap.Map(twiceFrom);
+    tempResult.Twice := Ltwice;
+  end;
+
+  LpreCompFrom := FfromWNaf.PreComp;
+  System.SetLength(LpreComp, System.length(LpreCompFrom));
+
+  for i := 0 to System.Pred(System.length(LpreCompFrom)) do
+  begin
+    LpreComp[i] := FpointMap.Map(LpreCompFrom[i]);
+  end;
+  tempResult.PreComp := LpreComp;
+  tempResult.width := width;
+
+  if (FIncludeNegated) then
+  begin
+    System.SetLength(LpreCompNeg, System.length(LpreComp));
+
+    for i := 0 to System.Pred(System.length(LpreCompNeg)) do
+    begin
+      LpreCompNeg[i] := LpreComp[i].Negate();
+    end;
+    tempResult.PreCompNeg := LpreCompNeg;
+  end;
+
+  result := tempResult;
+end;
+
+{ TEndoUtilities }
+
+class function TEndoUtilities.CalculateB(const k, g: TBigInteger; t: Int32)
+  : TBigInteger;
+var
+  negative, extra: Boolean;
+  b: TBigInteger;
+begin
+  negative := (g.SignValue < 0);
+  b := k.Multiply(g.Abs());
+  extra := b.TestBit(t - 1);
+  b := b.ShiftRight(t);
+  if (extra) then
+  begin
+    b := b.Add(TBigInteger.One);
+  end;
+
+  if negative then
+  begin
+    result := b.Negate();
+  end
+  else
+  begin
+    result := b;
+  end;
+end;
+
+class function TEndoUtilities.DecomposeScalar(const p: IScalarSplitParameters;
+  const k: TBigInteger): TCryptoLibGenericArray<TBigInteger>;
+var
+  bits: Int32;
+  b1, b2, a, b: TBigInteger;
+begin
+
+  bits := p.bits;
+  b1 := CalculateB(k, p.G1, bits);
+  b2 := CalculateB(k, p.G2, bits);
+
+  a := k.Subtract((b1.Multiply(p.V1A)).Add(b2.Multiply(p.V2A)));
+  b := (b1.Multiply(p.V1B)).Add(b2.Multiply(p.V2B)).Negate();
+
+  result := TCryptoLibGenericArray<TBigInteger>.Create(a, b);
+end;
+
+class function TEndoUtilities.MapPoint(const endomorphism: IECEndomorphism;
+  const p: IECPoint): IECPoint;
+var
+  c: IECCurve;
+  PreComp: IEndoPreCompInfo;
+begin
+  c := p.Curve;
+  PreComp := c.Precompute(p, PRECOMP_NAME, TEndoCallback.Create(endomorphism, p)
+    as IEndoCallback) as IEndoPreCompInfo;
+
+  result := PreComp.MappedPoint;
+end;
+
+{ TEndoUtilities.TEndoCallback }
+
+class function TEndoUtilities.TEndoCallback.CheckExisting(const existingEndo
+  : IEndoPreCompInfo; const endomorphism: IECEndomorphism): Boolean;
+begin
+  result := ((existingEndo <> Nil) and
+    (existingEndo.endomorphism = endomorphism) and
+    (existingEndo.MappedPoint <> Nil));
+end;
+
+constructor TEndoUtilities.TEndoCallback.Create(const endomorphism
+  : IECEndomorphism; const p: IECPoint);
+begin
+  Inherited Create();
+  Fendomorphism := endomorphism;
+  Fp := p;
+end;
+
+function TEndoUtilities.TEndoCallback.Precompute(const existing: IPreCompInfo)
+  : IPreCompInfo;
+var
+  existingEndo: IEndoPreCompInfo;
+  MappedPoint: IECPoint;
+  tempResult: IEndoPreCompInfo;
+begin
+
+  if Supports(existing, IEndoPreCompInfo) then
+  begin
+    existingEndo := existing as IEndoPreCompInfo;
+  end
+  else
+  begin
+    existingEndo := Nil;
+  end;
+
+  if (CheckExisting(existingEndo, Fendomorphism)) then
+  begin
+    result := existingEndo;
+    Exit;
+  end;
+
+  MappedPoint := Fendomorphism.pointMap.Map(Fp);
+
+  tempResult := TEndoPreCompInfo.Create() as IEndoPreCompInfo;
+  tempResult.endomorphism := Fendomorphism;
+  tempResult.MappedPoint := MappedPoint;
+  result := tempResult as IPreCompInfo;
+end;
+
+{ TFixedPointUtilities }
+
+class function TFixedPointUtilities.TFixedPointCallback.CheckTable
+  (const table: IECLookupTable; n: Int32): Boolean;
+begin
+  result := (table <> Nil) and (table.Size >= n);
+end;
+
+class function TFixedPointUtilities.TFixedPointCallback.CheckExisting
+  (const existingFP: IFixedPointPreCompInfo; n: Int32): Boolean;
+begin
+  result := (existingFP <> Nil) and CheckTable(existingFP.LookupTable, n);
+end;
+
+class function TFixedPointUtilities.GetCombSize(const c: IECCurve): Int32;
+var
+  Order: TBigInteger;
+begin
+  Order := c.Order;
+  if (not(Order.IsInitialized)) then
+  begin
+    result := c.FieldSize + 1;
+  end
+  else
+  begin
+    result := Order.BitLength;
+  end;
+end;
+
+class function TFixedPointUtilities.GetFixedPointPreCompInfo(const preCompInfo
+  : IPreCompInfo): IFixedPointPreCompInfo;
+begin
+  result := preCompInfo as IFixedPointPreCompInfo;
+end;
+
+class function TFixedPointUtilities.Precompute(const p: IECPoint)
+  : IFixedPointPreCompInfo;
+var
+  c: IECCurve;
+begin
+  c := p.Curve;
+
+  result := c.Precompute(p, PRECOMP_NAME, TFixedPointCallback.Create(p)
+    as IFixedPointCallback) as IFixedPointPreCompInfo;
+end;
+
+{ TFixedPointUtilities.TFixedPointCallback }
+
+constructor TFixedPointUtilities.TFixedPointCallback.Create(const p: IECPoint);
+begin
+  Inherited Create();
+  Fm_p := p;
+end;
+
+function TFixedPointUtilities.TFixedPointCallback.Precompute(const existing
+  : IPreCompInfo): IPreCompInfo;
+var
+  bit, bits, minWidth, n, d, i, step: Int32;
+  existingFP: IFixedPointPreCompInfo;
+  pow2Table, LookupTable: TCryptoLibGenericArray<IECPoint>;
+  pow2: IECPoint;
+  c: IECCurve;
+  tempResult: IFixedPointPreCompInfo;
+begin
+  if Supports(existing, IFixedPointPreCompInfo) then
+  begin
+    existingFP := existing as IFixedPointPreCompInfo;
+  end
+  else
+  begin
+    existingFP := Nil;
+  end;
+
+  c := Fm_p.Curve;
+  bits := TFixedPointUtilities.GetCombSize(c);
+  if bits > 250 then
+  begin
+    minWidth := 6
+  end
+  else
+  begin
+    minWidth := 5
+  end;
+  n := 1 shl minWidth;
+
+  if (CheckExisting(existingFP, n)) then
+  begin
+    result := existingFP;
+    Exit;
+  end;
+
+  d := (bits + minWidth - 1) div minWidth;
+
+  System.SetLength(pow2Table, minWidth + 1);
+
+  pow2Table[0] := Fm_p;
+  for i := 1 to System.Pred(minWidth) do
+  begin
+    pow2Table[i] := pow2Table[i - 1].TimesPow2(d);
+  end;
+
+  // This will be the 'offset' value
+  pow2Table[minWidth] := pow2Table[0].Subtract(pow2Table[1]);
+
+  c.NormalizeAll(pow2Table);
+
+  System.SetLength(LookupTable, n);
+  LookupTable[0] := pow2Table[0];
+
+  bit := minWidth - 1;
+  while bit >= 0 do
+  begin
+    pow2 := pow2Table[bit];
+
+    step := 1 shl bit;
+
+    i := step;
+
+    while i < n do
+    begin
+      LookupTable[i] := LookupTable[i - step].Add(pow2);
+
+      System.Inc(i, step shl 1);
+    end;
+
+    System.Dec(bit);
+  end;
+
+  c.NormalizeAll(LookupTable);
+
+  tempResult := TFixedPointPreCompInfo.Create();
+  tempResult.LookupTable := c.CreateCacheSafeLookupTable(LookupTable, 0,
+    System.length(LookupTable));
+  tempResult.Offset := pow2Table[minWidth];
+  tempResult.width := minWidth;
+  result := tempResult;
+end;
+
+end.

+ 5 - 6
src/libraries/cryptolib4pascal/ClpECDHBasicAgreement.pas

@@ -53,10 +53,10 @@ type
   /// used with the schemes ECKAS-DH1 and <br />DL/ECKAS-DH2. It assumes that
   /// used with the schemes ECKAS-DH1 and <br />DL/ECKAS-DH2. It assumes that
   /// the input keys are valid (see also <br />Section 7.2.2). <br />
   /// the input keys are valid (see also <br />Section 7.2.2). <br />
   /// </summary>
   /// </summary>
-  TECDHBasicAgreement = class(TInterfacedObject, IECDHBasicAgreement,
+  TECDHBasicAgreement = class sealed(TInterfacedObject, IECDHBasicAgreement,
     IBasicAgreement)
     IBasicAgreement)
 
 
-  strict protected
+  strict private
   var
   var
     FprivKey: IECPrivateKeyParameters;
     FprivKey: IECPrivateKeyParameters;
 
 
@@ -64,19 +64,18 @@ type
     /// <summary>
     /// <summary>
     /// initialise the agreement engine.
     /// initialise the agreement engine.
     /// </summary>
     /// </summary>
-    procedure Init(const parameters: ICipherParameters); virtual;
+    procedure Init(const parameters: ICipherParameters);
 
 
     /// <summary>
     /// <summary>
     /// return the field size for the agreement algorithm in bytes.
     /// return the field size for the agreement algorithm in bytes.
     /// </summary>
     /// </summary>
-    function GetFieldSize(): Int32; virtual;
+    function GetFieldSize(): Int32;
 
 
     /// <summary>
     /// <summary>
     /// given a public key from a given party calculate the next message
     /// given a public key from a given party calculate the next message
     /// in the agreement sequence.
     /// in the agreement sequence.
     /// </summary>
     /// </summary>
-    function CalculateAgreement(const pubKey: ICipherParameters)
-      : TBigInteger; virtual;
+    function CalculateAgreement(const pubKey: ICipherParameters): TBigInteger;
 
 
   end;
   end;
 
 

Some files were not shown because too many files changed in this diff