Skip to content

Instantly share code, notes, and snippets.

@coranos
Last active February 5, 2019 03:55
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save coranos/4b3becee345c479caec04cbf6fdbe66e to your computer and use it in GitHub Desktop.
Camo Banano Using Representatives

Camo Banano Using Representatives

Private Coins on Banano using ECDH key exchange.

by Coranos

camo banano consists of two layers of technology:

  1. storing ecdh public key in the blockchain as a junk representative.
  2. private reversible transactions, using shared seeds.

storing ecdh public key in the blockchain

prologue

Because banano hashes the private key (using blake hashing) before creating the public key, you cannot use simple scalar multiplication to create the shared secret.

In addition, tweetnacl seems to have two different scalar multiplication algorithms ("scalarbase" and "scalarMult.base") which return different values.

The tweetnacl function "sign.keyPair.fromSecretKey" uses "scalarbase" whereas computing a shared secret only succeeds when using "scalarMult.base".

What all this means is that the banano public key cannot be used as-is in ECDH, and instead another value must be stored in the blockchain. a second public key, which i will call the "camo public key" or "ECDH public key" as it's the public key iused by camo in ECDH shared secret generation.

camo public key implementation

The ECDH public key is computed using "nacl.scalarMult.base". The value is then stored in the blockchain by having an account change their representative to that value.

For example:

const expectedCamoPublicKey = '8DBF4E1DA79918DFBE4E8AA2B4755B70535FD18E9F1CA535D0FF09EDE437D22F';
const camoPrivateKey = '0000000000000000000000000000000000000000000000000000000000000000';
const camoPublicKey = nacl.scalarMult.base(camoPrivateKey);
expect( camoPublicKey ).to.deep.equal( expectedCamoPublicKey );

camo shared seed implementation

To create a shared seed, you first create a shared secret using ecdh.
You then concatenate that secret with a number (starting at zero), and blake hash the combined value.
The result of the blake has is your shared seed.
If the seed has already been used in the blockchain, increment the number, and recalculate the shared seed using the new number and the same secret.

for example:

const alicePrivateKey = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const bobCamoPublicKey = '8DBF4E1DA79918DFBE4E8AA2B4755B70535FD18E9F1CA535D0FF09EDE437D22F';
const secret = nacl.scalarMult( alicePrivateKey, bobCamoPublicKey );
let sharedSecretIx = 0;
let foundEmptySeed = false;
while(!foundEmptySeed) {
  const seed = blake2b(secret + sharedSecretIx);
  const privateKey = bananoUtil.getPrivateKey(seed, 0);
  const address = bananoUtil.getAddress(privateKey);
  const transactionHistory = getTransactionHistory(publicKey);
  if(transactionHistory.length = 0) {
    foundEmptySeed = true;
  }
}
const seed = blake2b(secret + sharedSecretIx);

camo shared seed usage

After computing the shared seed, the amount in the transaction should be broken up into powers of two.
I.E. a transacaction of 5 banano should be broken up into a transaction of 1 banano (2^0) and 4 banano (2^2).
Each transaction should then be sent to a different account under the shared seed.
The reason for this is to be able to send outgoing transactions from several accounts, combining their value.
If the transaction is from a camo account to a camo account, it will appear that there are several power-of-two transactions going between unrelated accounts.

example:

const alicePrivateKey = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const bobCamoPublicKey = '8DBF4E1DA79918DFBE4E8AA2B4755B70535FD18E9F1CA535D0FF09EDE437D22F';
const amountBananos = [1,4];
const secret = nacl.scalarMult( alicePrivateKey, bobCamoPublicKey );
const sharedSecretIx = 0;
const seed = blake2b(secret + sharedSecretIx);
amountBananos.forEach((amountBanano, ix) => {
  const privateKey = bananoUtil.getPrivateKey(seed, ix);
  const address = bananoUtil.getAddress(privateKey);
  bananoUtil.send(alicePrivateKey,address0,amountBanano);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment