As explained in Chapter 2 of the Technical Reference, an account is an Ed25519 cryptographic keypair associated to a mutable state stored on the NEM blockchain. The state of the account is modified when transactions involving it are accepted by the network.
Nem is using the Ed25519 publick key signature system with the
SHA3 hashing algorithm.
Ed25519 was introduced when sha3 was not yet
standardised, and it uses the SHA512 hashing algorithm (the paper names it Ed25519-SHA-512).
However the paper clearly states that we will not hesitate to recommend Ed25519-SHA-3 after SHA-3 is standardized
.
Most Ed25519 implementations use the SHA-512 method described in the original paper. To generate NEM keypairs, you might need to patch standard implementations, as was done for the python implementation by the NEM authors..
The private key uniquely identifies a NEM account. The public key is derived from the private key, and the address of the account is derived from the public key.
The address of an account is a base-32 encoded triplet consisting of: * network byte: is it an address on the testnet or the mainnet? * 160-bit hash of the public key * 4 byte checksum, for detection of mistyped addresses
From this description we see that we can generate a new account offline, there’s no need to be connected to the network to create an account.
Here is the code generating an address from the public key, extracted from nem.code:
private static String generateEncoded(final byte version, final byte[] publicKey) {
// step 1: sha3 hash of the public key
final byte[] sha3PublicKeyHash = Hashes.sha3_256(publicKey);
// step 2: ripemd160 hash of (1)
final byte[] ripemd160StepOneHash = Hashes.ripemd160(sha3PublicKeyHash);
// step 3: add version byte in front of (2)
final byte[] versionPrefixedRipemd160Hash = ArrayUtils.concat(new byte[] { version }, ripemd160StepOneHash);
// step 4: get the checksum of (3)
final byte[] stepThreeChecksum = generateChecksum(versionPrefixedRipemd160Hash);
// step 5: concatenate (3) and (4)
final byte[] concatStepThreeAndStepSix = ArrayUtils.concat(versionPrefixedRipemd160Hash, stepThreeChecksum);
// step 6: base32 encode (5)
return Base32Encoder.getString(concatStepThreeAndStepSix);
}
We see that the steps are:
Nem account can be converted to multisig account (from multi signatures). A multisig account has co-signatories accounts, and it cannot initiate transactions itself. Only the co-signatories can initiate transactions.
When an account is converted to multisig, the transaction adds the co-signatories and indicates how many of them need to accept a transaction for it to go through. That’s why you’ll hear that NEM supports N-of-M multisig accounts. A multisig account can have M co-signatories, of which N need to validate the transactions. As long as N is smaller or equal to M, it is a supported configuration. You can have a 1-of-2, 3-of-4, 7-of-20, etc…
From the explanation above, we see that we can create an account without ever interacting the with blockchain. Does it mean that all possible accounts are pre-defined on the blockchain? No. It means that only accounts that have had activity are tracked on the blockchain. Creating your account is done by defining your private key. With that private key you have the information needed to access the related account. Be before it had any activity, it isn’t tracked on the blockchain and the amount it stores is 0.
So what is an activity? It is simply a transaction involving the account. That can be an incoming transaction (the first transaction obviously can’t be an outgoing transaction) or a multi-sig conversion transaction.
The private key is 256 bits long, which might seem small when you hear advices to use 2048 bits keys for RSA. But key length is not the only parameter defining your security. Nem uses Ed25519, which is an elliptic curve cryptography, requiring smaller key than non elliptic curve cryptography methods to provide the same security level. It appears that in general, to break an n bit elliptic curve public key, the effort is 2^(n/2), or about 3.4*10^38, basic operations.
The probability to randomly generate a secret key that is linked to an account already existing is also very small. The key being 256 bits long, there are 10^77 possibilities. We thus see that collision probability is very small, and in cryptographic term, negligible.
What’s more, you can increase the security of your account by using multi-sig, with which multiple accounts have to validate transactions from one account.
The blockchain stores the following data about accounts:
The public key of an account will be stored on the blockchain with the first transaction issued the account. An account which hasn’t issued any transaction will have its public key field empty.
The easiest way to generate a key-pair is currently the nem-sdk, which is a javascript implementation usable in the browser and on nodejs. All this is setup in the docker container accompanying this guide.
The first step is to generate a random private key. When printed as an hexadecimal value, it can be used to create an account with the NanoWallet.
Here is how you generate a key-pair:
//import the nem-sdk
// This is not needed if you use the repl.js script available in the container
var nem = require("nem-sdk").default;
// generate 32 random bytes.
// You could write the 32 bytes of your choice if you prefer, but that might be dangerous as
// it would be less random.
//
var rBytes = nem.crypto.nacl.randomBytes(32);
// convert the random bytes to an hex string
// the result, rHex, can be printed out to the console for taking a backup with console.log(rBytes).
// Take a backup copy of that value as it lets you recreate the keypair to give
// you access to your account.
// This value is also usable with the NEM NanoWallet.
var rHex = nem.utils.convert.ua2hex(rBytes);
// generate the keypair
var keyPair = nem.crypto.keyPair.create(rHex);
The public key can be printed out easily with:
keyPair.publicKey.toString()
'4fe5efd97360bc8a32ec105d419222eeb714e6d06fd8b895a5eedda2b0edf931'
As described above, the address has a prefix for each network supported (mainnet, testnet, mijin), so the nem-sdk helpers to generate an address take as argument the public key and the network id for which to generate the address.
The network ids are stored under nem.model.network.data.testnet.id
,
nem.model.network.data.mainnet.id
, nem.model.network.data.mijin.id
.
With this info, we can generate the address:
nem.model.address.toAddress(keyPair.publicKey.toString(), nem.model.network.data.testnet.id)
'TA6XFSJYZYAIYP7FL7X2RL63647FRMB65YC6CO3G'