Nyzo version 530 (commit on GitHub) adds the Micropay server and client.
This version adds two new run modes: the Micropay server and the Micropay client. It also affects the command-line client.
This is a large commit, but the changes are all in systems that interact externally with the verifiers. The internal workings of the verifier and the sentinel are unaffected, so this version does not introduce any new concerns regarding the security or efficiency of blockchain processing.
A new configuration file was added for the Micropay server. This follows the same pattern as the files provided for the verifier and sentinel.
In Message.fetchFromRandomNode(), the new MicropayServer run mode has been added as a consumer of ClientNodeManager.randomNode(). As will be seen later in these notes, the MicropayServer run mode currently shares the client's blockchain-tracking functionality. They have similar relationships to the mesh: neither services incoming Nyzo messages and neither will ever produce a block.
In RunMode, MicropayServer and MicropayClient have been added. The Micropay server is a simple but fully functioning web server that collects Nyzo payments before allowing access to premium resources. The Micropay client is a lightweight local application that allows a user to interact with Micropay servers.
In ClientTransactionUtil, the createAndSendTransaction() methods have been refactored for readability and to expose intermediate calculations. The suggestedTransactionTimestamp() method provides a timestamp that will allow for a new transaction to be created that will be approved in a reasonable amount of time even if blockchain processing has fallen behind. This calculation was previously performed in the createAndSendTransaction() method, and it is now also used by the Micropay server to produce suggested timestamps for Micropay strings.
Refactoring of the createAndSendTransaction() method did not change its behavior. However, when an IP address and port are not provided, sendTransactionToCycle() has been replaced with sendTransactionToExpectedVerifier(). So, instead of sending the transaction to multiple verifiers, and continuing to send the transaction until the block that is supposed to contain the transaction is received, the transaction is only sent to the one verifier. This significantly reduces the network traffic required for each transaction while negligibly reducing the chance that the transaction will be incorporated into a block. This process should continue to be refined in the future to account for new verifiers and verifiers dropping from the cycle, but such refinements are small relative to the functionality already provided.
The sendTransactionToExpectedBlockVerifier() method is used by both the client and the Micropay server. The waitForBlock argument is used by the client to wait for the block that is supposed to contain the transaction. When this block is received, the user is informed whether the block contains the transaction, and the command completes. In the case of the Micropay server, such user feedback is an unnecessary overhead, so it is bypassed.
This method uses the frozen edge, along with the ordered list of verifiers in the current cycle, to determine which verifier should process the block for the transaction. Note that the calculation works properly even when the ordered list of verifiers was generated for a different block than the frozen edge that was retrieved. While this is a rare occurrence, it can happen due to the multithreaded nature of the system.
MicropayCreateCommand and MicropaySendCommand have been eliminated from the client. They were useful for exploring the mechanics of the Micropay system, but they are no longer appropriate due to changes in the Micropay string structure. To simplify design of the Micropay client and to reduce overall burden on the mesh, Micropay clients do not track the blockchain. This requires the Micropay server to provide a previous-block hash and suggested timestamp to Micropay clients. Those fields have such a limited lifetime that manual creation and use of Micropay strings is an unlikely use case.
The MicropayAuthorization class is a simple container for tracking which users are allowed to access which endpoints on the Micropay server. As the comment explains, the class will evolve to allow more authorization-control options. Also, loading of previous authorizations from the blockchain will be added so that authorizations are not lost on server restart.
The MicropayClient run mode has the simplest of all run mode initialization processes. The run mode is set, and the web listener is started. The Micropay client does not track the blockchain.
MicropayController provides endpoints for both the Micropay server and the Micropay client. The Micropay client has two endpoints: the page for authorizing payments and a ping endpoint that is used for automatic port detection.
The endpoints for the Micropay server are built dynamically based on the contents of the directory specified as micropay_data_root in the preferences file. The process() method recursively builds the server paths based on the directory structure.
The Micropay client's ping page provides a plain text, known response to allow easy identification of a Micropay client. The page also displays the IP address of the requester, though this information is unused by the port-detection process.
The Micropay client's authorization page is presented to ask whether the user wants to approve a Micropay transaction. If the Micropay information is valid, the client signs the transaction and sends it back to the Micropay server at the provided callback URL.
The client authorization page will only create a transaction if the Micropay client has been configured correctly and if the transaction amount is less than the user-specified maximum. This protects a user from accidentally sending large transactions with Micropay.
This is an example Micropay authorization page. While this is a web page, it is served by the locally running Micropay client, not a remote server.
The MicropayEndpoint class encapsulates all of the Micropay server's functionality for authorizing accesses and delivering content. The Micropay server automatically builds endpoints based on a directory structure, much like a typical web server. Content for an example Micropay site is available on Github.
By default, a page on a Micropay site does not require payment. To require payment for a page, add the following tag to the head element of the page. The following tag would require a payment of ∩0.1 to access the page.
<meta name="nyzo-micropay-amount" content="0.1">
The getMicropayAmount() method extracts the Micropay value from the page. The standard Java libraries do not offer an HTML parser, and Nyzo seeks to limit dependencies as much as possible, so this method uses a regular expression and basic string manipulation to find the value.
The renderByteArray() method provides the page content to the web listener. If a page is free, if the user is previously authorized, or if the request contains a valid transaction, the page content is delivered. Otherwise, an authorization page is delivered.
The server authorization page is displayed when a user has not yet paid to access a piece of content. This page creates the appropriate Micropay string that the user must approve to access the content.
A script scans the most common ports on which a user might run a Micropay client: 80 and 8080 for http, 443 and 8443 for https. The authorization link is pointed to the appropriate port.
The processTransactionParameter() method checks the transaction provided by the user. If the receiver, sender data, and amount are correct, and the transaction appears likely to be accepted into the blockchain, then the Micropay server forwards the transaction to the appropriate verifier and immediately provides access to the requested content.
This method demonstrates what the Micropay system is all about: because of the way the Nyzo blockchain is structured, anyone tracking the blockchain can easily determine whether a transaction is likely to be incorporated into the blockchain, and anyone in possession of a transaction can take the appropriate steps to try to get that transaction into the blockchain. While some transactions will be lost, the small values of these transactions make periodic losses acceptable, and the system can be improved over time to lower transaction loss rate.
The senderDataForIp() method provides sender data for the transaction. The IP address and path are signed by the verifier, and the first 32 bytes of the signature are used for the sender data. As the comment notes, this calculation has some nice properties, but many any calculation that suits the needs of the Micropay server operator can be used.
The MicropayServer class is the entry point for the MicropayServer run mode. The Micropay server shares the client's data manager for mesh and blockchain tracking, and a new class, TransactionTracker, watches incoming blocks to report transactions for which this verifier is the recipient.
The TransactionTracker class provides minimal reporting on transactions in incoming blocks. This class will provide more substantial reporting and help in the authorization process in future versions, potentially allowing exclusion of transactions from senders that are known to abuse the system.
The NyzoStringEncoder class now has the ability to decode transaction strings.
In Micropay strings, the receiver IP address and receiver port have been removed, and timestamp and previous-block information added, to support the updated use of these strings. The new Micropay format is not compatible with the old format, and the old format and use case will no longer be supported.
NyzoStringTransaction allows signed transactions to be represented as Nyzo strings. These are used in the Micropay process, and they will also be useful in many other situations.
In NyzoStringType, the prefix tx__ has been registered for transaction strings.
In NyzoStringTest, a test has been added for transaction strings.
The test for Micropay strings has been updated for the new Micropay string structure.
In PreferencesUtil, methods have been added for retrieving byte arrays and long values.
In CycleController, the signatures of the endpoint methods have changed to the new definition of EndpointMethod. Query parameters are now passed into the method, along with request headers, and the EndpointResponse return type contains the response headers in addition to the content.
The EndpointMethod interface, as noted above, is evolving to handle more of the necessary complexities of web requests and responses.
The EndpointResponse class contains response headers in addition to the response content.
SentinelController has also been changed for the revised EndpointMethod interface. Additionally, a small formatting issue has been corrected.
In WebListener, the constant for port has been replaced with a method for determining the port, which is now dependent on the run mode.
The endpoint map is no longer a constant (final) variable. This change was made so that the loading process could be written to allow the map to be reloaded, allowing update of endpoints in the Micropay server without restart of the server. Note that this is not yet fully implemented: the Micropay server will pick up page content changes while running, but Nyzo Micropay pricing metadata and the endpoint map are only currently read on server initialization, and the Micropay server must be restarted for these changes to be updated.
The new port method is used in place of the constant, and the port is logged so the operator of the server can ensure the correct port was used.
Endpoint methods are now provided with the query parameters and source IP address, and the headers returned in the EndpointResponse are passed to the HttpExchange.
The path() method has been updated to provide some path normalization. A method has been added to extract query parameters into a map.
The buildEndpointMap() now includes endpoints for the Micropay server and Micropay client. The process has been built to allow atomic swaps of the endpoint map. Dynamic endpoint map updates for the Micropay server will be implemented in a future version.
The getPort() method provides more options than the previous port constant. The web_port constant is treated exactly as before, but run-mode-specific settings are now also available. For instance, to run the Micropay client on port 8081 and all other run modes on port 8080, you would specify web_port_micropay=8081 and web_port=8080 in /var/lib/nyzo/production/preferences.
In WebUtil, convenience constants have been added for inline button styles.
Classes have been added to structure links, paragraphs, and titles in HTML.
To run a Micropay server, follow the same installation instructions as the Nyzo verifier, using the nyzoMicropayServer.conf file in place of the nyzoVerifier.conf file. Also, be sure to include some web content and specify the web root as micropay_data_root in /var/lib/nyzo/production/preferences. Content for an example Micropay site is available on Github.
The Micropay server uses the private key specified in /var/lib/nyzo/production/verifier_private_seed as the recipient of all Micropay transactions. Be sure that this is an existing account, as new accounts are unable to receive small transactions due to balance-list spam protections.
The Micropay client follows the same build process as all other run modes. It does not require the trusted_entry_points file. It does require specification of micropay_sender_key and micropay_max_amount_nyzos in /var/lib/nyzo/production/verifier_private_seed. The entry point for the client is co.nyzo.verifier.micropay.MicropayClient. Micropay clients for mobile systems are currently under consideration for development.