qQQIDfactory: qQQIDfactory

Description Usage Arguments Details Value Usage Properties of the produced QQIDs qQQIDs vs. rngQQIDs Caching QQIDs to avoid latency Warning - parallelization Disclaimer and caution Author(s) See Also Examples

View source: R/qQQIDfactory.R


qQQIDfactory returns a closure (a function with an associated environment) that retrieves, caches, and returns quantum-random QQIDs.


qQQIDfactory(nBatch = 1023)



(numeric) The batch size requested from the ANU server. The default 1023 does not normally need to be changed. (Larger batches take more time to process, smaller batches offer no speed benefit). It might be increased e.g. for storing a large cache in case an interruption to Internet connectivity is anticipated. Values < 1 and > 1e05 will cause an error in qrandom::qUUID().


In what follows we will call the closure that is produced by qQQIDfactory "qQQID()" (even though you could assign it to a different name). The function factory qQQIDfactory and the qQQID() closure address a latency problem that arises when we retrieve true random numbers from the ANU Quantum Random Number Generator at the Australian National University. qQQIDfactory produced closures maintain a local cache of true random QQIDs, which can be accessed without latency. In a design use case, qQQID() could be produced in an R session startup script. From that point on, latency in generating true random QQIDs is only experienced occasionally when the cache needs to be replenished.


qQQIDfactory returns a closure that takes the following arguments:

If no connection to ANU can be established and the initial cache cannot be filled, qQQIDfactory returns NULL invisibly.


qQQIDfactoryproduces a closure which is meant to be assigned to a variable and called by that variable name (see examples). While any legal variable name can be used, assigning to "qQQID" is recommended. Producing the closure requires an Internet connection to first fill the closure's cache of QQID values. Once the cache is filled, no Internet connection is required until the cache is depleted and the closure attempts to replenish it. If replenishing the cache fails because of a failure to open a connection to the ANU server, an informative message is printed and the closure returns NULL invisibly. If there are remaining QQIDs in the cache, these can be retrieved by requesting no more than the number that currently exist in the cache. The closure either returns the requested number of QQIDs, or NULL, but it will never return fewer than the requested number of QQIDs, since implicit recycling of shorter vectors would be a critical failure mode for a function that is expected to return unique identifiers.

Properties of the produced QQIDs

Internally, qQQID converts true random UUIDs obtained from qrandom::qUUID. These UUIDs are RFC 4122 compliant and contain a six-bit version code, and 122 random bits. Such RFC compliant QQIDs are drawn from 2^122 =~ 5.3e+36 possibilities, whereas totally random 128-bit numbers are drawn from a =~ 3.4e+38 number space. The 50% collision probability of random 122-bit numbers is =~ 2.7e+18 numbers, while for 128-bit numbers it is =~ 2.2e+19.

qQQIDs vs. rngQQIDs

Whether to use true random or pseudo-random QQIDs is a tradeoff between speed and safety. The ANU quantum random number server can have considerable latency (a problem that qQQIDfactory addresses through caching), but pseudo-random numbers may not be sufficiently collision-safe for use cases that depend on the uniqueness of the resulting numbers: while the RNGs provided by R are very good, all RNGs potentially suffer from the possibility of an initialization collision, i.e. when two runs of the RNG are accidentally initialized with the same seed, due to an improper and/or unrecognized use of set.seed() in another function, script or package, or due to the limited randomness of time- and machine-state based seeds. This is not a problem for long runs of key-generation on a single machine, but it may be an issue for the decentralized generation of random unique keys, which is the design use case of qqid. The only way to prevent this with certainty is to use true random keys (as provided with this function). True random qQQIDs have a 50% collision probability in =~ 2.7e+18 keys, and this is the same at all times, regardless of the state of the requesting machine. Thus unless throughput of keys is a critical concern, it is advisable to use true random QQIDs from a qQQIDfactory closure over those returned by a rngQQID() process, or at least to initialize the RNG with a true random seed (the default option for rngQQID()).

Caching QQIDs to avoid latency

The ANU server produces true random numbers from quantum fluctuations of the vacuum. The high latency of requests for quantum random numbers from the ANU server - between 6 to 10 seconds per request - is practically independent of the size of the request's payload. This suggests a strategy to serve keys from a local cache. R provides an elegant way of doing this by defining functions as closures - i.e. along with their own environment. This function environment allows to maintain state between function calls. qQQIDfactory retrieves 1023 qQQIDs and caches them in the environment of qQQID. The qQQID closure replenishes the cache if it does not contain at least the requested number of QQIDs, then it unshifts the QQIDs from the cache, and returns them. The user experience is that qQQID is responsive, and latency arises only occasionally when the cache is replenished.

Warning - parallelization

If you are executing code in parallel on separate processors, you must make sure that every task uses its own, independent copy of qQQID() and not a copy of an instance of the closure - such copies would all contain the same cache! Given the relatively high latency of cache replenishment, it is likely that an approach that uses rngQQID(N, method = "q"), is more promising.

Disclaimer and caution

Although this function has been written and tested with care, no suitability for any particular purpose, in particular no suitability for high-value transactions, for applications whose failure could endanger life or property, or for cryptography is claimed. The source code is published in full and it is up to the user to audit and adapt the code for their own purposes and needs.


(c) 2019 Boris Steipe, licensed under MIT (see file LICENSE in this package).

See Also

rngQQID() to generate QQIDs via the inbuilt RNG.


## Not run: 
# prepare a qQQID function and use it to retrieve three true random  QQIDs
qQQID <- qQQIDfactory()

# Use the function to return 10 UUIDs from the same cache

# inspect the first 5 QQIDs on the cache ...
qQQID(5, inspectOnly = TRUE)

# ... retrieve four of the QQIDs (they are identical
# to the first four we just inspected) ...

# ... show that the four keys are gone from the cache which now
# begins with the fifth QQID we saw before.
qQQID(5, inspectOnly = TRUE)

## End(Not run)

qqid documentation built on May 2, 2019, 12:19 p.m.