Nyzo version 603 (commit on GitHub) adds Micropay support to the client and documentation server.
This version primarily affects the client and documentation server. Some changes may affect other run modes in minor ways. This version is part of the delivery to fulfill NTTP-5.
To make support of Micropay as easy as possible for web servers, the client was updated to provide more substantial feedback on the /api/forwardTransaction endpoint. Also, basic Micropay support was added, as a demonstration, to the documentation server.
In BalanceListManager, the frozenEdgeBalanceMap was added to eliminate the need to derive a balance map from the frozen-edge balance list on demand.
In BalanceManager.makeBalanceMap(), the balance map is now a ConcurrentHashMap.
In Transaction.performInitialValidation(), the pre-calculated frozenEdgeBalanceMap is now used.
In TransactionForwardCommand, a map now stores transactions that have recently been forwarded. Some additional fields are used for maintenance of this map.
The TransactionForwardCommand now accepts a supplementalTransaction parameter. This parameter, only necessary for Micropay, allows an old transaction to be forwarded while proving ownership of the key used to sign the old transaction. This is necessary to allow reuse of old transactions for reauthorizing Micropay purchases while protecting against theft of Micropay content by those who watch the blockchain to find transactions that authorize content they want to acquire.
The TransactionForwardCommand.run() method now performs periodic maintenance, cleaning the recently forwarded transactions map every 10 executions.
The result of TransactionForwardCommand.run() returns several new fields to assist a web server in determining whether to deliver Micropay content.
The core logic of the TransactionForwardCommand.run() has changed substantially. The behavior with a single transaction is basically the same as it was before, but considerations for recently forwarded transactions and handling of supplemental transactions are now intermingled in this logic.
The new logic begins by decoding the transaction. If the transaction is under the frozen edge, it is removed from the recently forwarded transactions map, because this is no longer relevant information for a block that has been frozen. A check is then performed to see if the transaction was already forwarded, and another check is performed to see if the transaction is already in the blockchain.
If the transaction needs to be forwarded to the cycle, it is validated and forwarded.
If the supplemental transaction is present, it is validated, also. The supplemental transaction is only valid if it is within the replay interval and if it is from the same sender as the transaction. Performing this validation on the client substantially reduces the complexity of the web server.
The result now includes the new fields. An error is now returned in the JSON response if a transaction is not provided. This would have previously caused an exception.
The old result is shown as completely removed due to indentation changes.
The TransactionForwardCommand.performMaintenance() method first removes all transactions behind the frozen edge. If the map is still too large, it then removes arbitrary transactions. The only potential ill effect of premature transaction removal would be an inability to revalidate a Micropay transaction in the time between its initial submission and its incorporation in the blockchain.
DocumentationController.buildEndpointMap() now processes files with the htm and txt extensions.
The DocumentationEndpoint class contains 3 new fields to demonstrate Micropay functionality: micropayPrice, micropayReceiverIdentifier, and micropaySenderData.
In the DocumentationEndpoint constructor, the loadMicropayParameters() method is now called to load the new parameters.
To specify Micropay content, a Micropay file is placed alongside the content file, with the same filename as the content followed by the .micropay extension. This is a standard configuration file. Price is specified in nyzos with the key price. Receiver identifier is specified as a Nyzo string with the key receiver_identifier. Sender data, optional but recommended, is specified either as a normalized sender-data string or a text string. The key for sender data is sender_data.
The DocumentationEndpointType enumeration now includes types for HtmlFragment and Text. The HtmlFragment type is used for files that should be delivered without modification, unlike files of Html type, which undergo modification by the documentation server. The DocumentationEndpoint.determineType() method now handles these new types.
In DocumentationEndpoint.getResponse(), Micropay authorization is now considered.
The DocumentationEndpoint.micropayAuthorized() method demonstrates the basic logic that is required of a web server to authorize Micropay content. The transaction and supplemental transaction are sent to the /api/forwardTransaction endpoint on a Nyzo client, and the response is retrieved.
The client response is checked to determine whether the Micropay content should be delivered to the user. The conditions for authorization are explained in the comments. For endpoints that do not require payment, authorization always succeeds.
The DocumentationEndpoint.getResponseForInvalidMicropay() method assembles a helpful response to let the requester know how to access the content.
The DocumentationEndpointType enumeration contains the new HtmlFragment and Text values previously explained.
The Json class is a simple, purpose-built JSON parser. This is not a good, robust JSON parser. This is a minimal implementation that just barely works to support structuring the client response for the Micropay demonstration for the documentation server. The Json class stores internal parser state. This class will not parse JSON strings longer than 10,000 characters.
Some helper methods are used to improve readability of the parser.
The Json.parse() method will parse either a dictionary (JsonObject), array (JsonArray), or string. Escaped quotes are replaced with the null character to eliminate the need to handle them in the parsing logic. These null characters are then replaced with quotes at the end of the process.
The Json.parseJsonObject() method starts by handling quoted strings.
For characters outside quoted strings, the various JSON markers ({, }, [, ], and ,) are used to determine structure. Recursion is used to process nested values.
The Json.parseJsonArray() method also starts with handling of quoted strings.
The structure of an array is simpler, only containing a list of values instead of key/value pairs. This method also utilizes recursion to handle nesting.
The JsonArray class structures lists, providing the accessors needed by the DocumentationEndpoint.
The JsonObject class structures dictionaries, also providing the accessors needed by the DocumentationEndpoint.
The HttpStatusCode enumeration now includes a value for 402: Payment Required.
A small styling change was made to the hover-button CSS class.