Trade tokens between different blockchains without using an intermediary party in the process.
Alice and Bob want to exchange 10 alice.tokens for 10 bob.tokens. The problem is that they are not in the same blockchain: alice.token is defined in Symbol’s public chain, whereas bob.token is only present in a private chain using Symbol technology.
One non-atomic solution could be:
However, they do not trust each other that much. As you may have noticed, Bob could keep Alice’s tokens if he opts not to perform 3.
This guide will show you how to define a secure way to exchange tokens between different participants and networks.
const alicePublicChainAccount = Account.createFromPrivateKey('', NetworkType.MAIN_NET);
const alicePrivateChainAccount = Account.createFromPrivateKey('', NetworkType.MIJIN);
const bobPublicChainAccount = Account.createFromPrivateKey('', NetworkType.MAIN_NET);
const bobPrivateChainAccount = Account.createFromPrivateKey('', NetworkType.MIJIN);
const privateChainTransactionHttp = new TransactionHttp('http://localhost:3000');
const publicChainTransactionHttp = new TransactionHttp('http://localhost:3000');
const publicChainGenerationHash = process.env.NETWORK_GENERATION_HASH as string;
const privateChainGenerationHash = process.env.NETWORK_GENERATION_HASH as string;
const alicePublicChainAccount = symbol_sdk_1.Account.createFromPrivateKey('', symbol_sdk_1.NetworkType.MAIN_NET);
const alicePrivateChainAccount = symbol_sdk_1.Account.createFromPrivateKey('', symbol_sdk_1.NetworkType.MIJIN);
const bobPublicChainAccount = symbol_sdk_1.Account.createFromPrivateKey('', symbol_sdk_1.NetworkType.MAIN_NET);
const bobPrivateChainAccount = symbol_sdk_1.Account.createFromPrivateKey('', symbol_sdk_1.NetworkType.MIJIN);
const privateChainTransactionHttp = new symbol_sdk_1.TransactionHttp('http://localhost:3000');
const publicChainTransactionHttp = new symbol_sdk_1.TransactionHttp('http://localhost:3000');
const publicChainGenerationHash = process.env.NETWORK_GENERATION_HASH;
const privateChainGenerationHash = process.env.NETWORK_GENERATION_HASH;
1. Alice generates a random set of bytes called proof
. The proof should have a size between 10
and 1000
bytes.
Then, applies a SHA3-256 algorithm to it, obtaining the secret
.
secret
.const random = crypto.randomBytes(10);
const proof = random.toString('hex');
console.log('Proof:', proof);
const hash = sha3_256.create();
const secret = hash.update(random).hex().toUpperCase();
console.log('Secret:', secret);
const random = crypto.randomBytes(10);
const proof = random.toString('hex');
console.log('Proof:', proof);
const hash = js_sha3_1.sha3_256.create();
const secret = hash.update(random).hex().toUpperCase();
console.log('Secret:', secret);
TX1 Property | Value |
---|---|
Type | SecretLockTransaction |
Mosaic | 10 00D3378709746FC4 (alice token) |
Recipient | Bob’s address (Private Chain) |
Algorithm | SHA3-256 |
Duration | 96 h |
Secret | SHA3-256(proof) |
Network | Private Chain |
const tx1 = SecretLockTransaction.create(
Deadline.create(),
new Mosaic(new MosaicId('00D3378709746FC4'), UInt64.fromUint(10)),
UInt64.fromUint(96 * 3600 / 15), // assuming one block every 15 seconds
LockHashAlgorithm.Op_Sha3_256,
secret,
bobPrivateChainAccount.address,
NetworkType.MIJIN);
const tx1 = symbol_sdk_1.SecretLockTransaction.create(symbol_sdk_1.Deadline.create(), new symbol_sdk_1.Mosaic(new symbol_sdk_1.MosaicId('00D3378709746FC4'), symbol_sdk_1.UInt64.fromUint(10)), symbol_sdk_1.UInt64.fromUint(96 * 3600 / 15), // assuming one block every 15 seconds
symbol_sdk_1.LockHashAlgorithm.Op_Sha3_256, secret, bobPrivateChainAccount.address, symbol_sdk_1.NetworkType.MIJIN);
Once announced, this transaction will remain locked until someone discovers the proof that matches the secret. If no one unlocks it before the duration set is reached, the locked funds will be returned back to Alice.
Note
Bob should retrieve the secret from the chain. It is Bob’s responsibility to verify the secret correctness.
const tx1Signed = alicePrivateChainAccount.sign(tx1, privateChainGenerationHash);
privateChainTransactionHttp
.announce(tx1Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
const tx1Signed = alicePrivateChainAccount.sign(tx1, privateChainGenerationHash);
privateChainTransactionHttp
.announce(tx1Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
TX2 Property | Value |
---|---|
Type | SecretLockTransaction |
Mosaic | 10 10293DE77C684F71 (bob token) |
Recipient | Alice’s address (Public Chain) |
Algorithm | SHA3-256 |
Duration | 84 h |
Secret | SHA3-256(proof) |
Network | Public Chain |
const tx2 = SecretLockTransaction.create(
Deadline.create(),
new Mosaic(new MosaicId('10293DE77C684F71'), UInt64.fromUint(10)),
UInt64.fromUint(84 * 3600 / 15), // assuming one block every 15 seconds
LockHashAlgorithm.Op_Sha3_256,
secret,
alicePublicChainAccount.address,
NetworkType.MAIN_NET,
UInt64.fromUint(2000000));
const tx2 = symbol_sdk_1.SecretLockTransaction.create(symbol_sdk_1.Deadline.create(), new symbol_sdk_1.Mosaic(new symbol_sdk_1.MosaicId('10293DE77C684F71'), symbol_sdk_1.UInt64.fromUint(10)), symbol_sdk_1.UInt64.fromUint(84 * 3600 / 15), // assuming one block every 15 seconds
symbol_sdk_1.LockHashAlgorithm.Op_Sha3_256, secret, alicePublicChainAccount.address, symbol_sdk_1.NetworkType.MAIN_NET, symbol_sdk_1.UInt64.fromUint(2000000));
Note
The duration which funds can be unlocked should be a smaller time frame than TX1’s. Alice knows the secret, so Bob must make sure he will have some time left after Alice releases the secret.
Note
To guarantee that TX1 cannot be rolled back, Bob must wait until TX1 receives at least maxRollBackBlocks
confirmations before announcing TX2.
const tx2Signed = bobPublicChainAccount.sign(tx2, publicChainGenerationHash);
publicChainTransactionHttp
.announce(tx2Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
const tx2Signed = bobPublicChainAccount.sign(tx2, publicChainGenerationHash);
publicChainTransactionHttp
.announce(tx2Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
TX3 Property | Value |
---|---|
Type | SecretProofTransaction |
Recipient | Alice’s address (Public Chain) |
Algorithm | SHA3-256 |
Secret | SHA3-256(proof) |
Proof | proof |
Network | Public Chain |
Note
To guarantee that TX2 cannot be rolled back, Alice must wait until TX2 receives at least maxRollBackBlocks
confirmations before announcing TX3.
const tx3 = SecretProofTransaction.create(
Deadline.create(),
LockHashAlgorithm.Op_Sha3_256,
secret,
alicePublicChainAccount.address,
proof,
NetworkType.MAIN_NET,
UInt64.fromUint(2000000));
const tx3Signed = alicePublicChainAccount.sign(tx3, publicChainGenerationHash);
publicChainTransactionHttp
.announce(tx3Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
const tx3 = symbol_sdk_1.SecretProofTransaction.create(symbol_sdk_1.Deadline.create(), symbol_sdk_1.LockHashAlgorithm.Op_Sha3_256, secret, alicePublicChainAccount.address, proof, symbol_sdk_1.NetworkType.MAIN_NET, symbol_sdk_1.UInt64.fromUint(2000000));
const tx3Signed = alicePublicChainAccount.sign(tx3, publicChainGenerationHash);
publicChainTransactionHttp
.announce(tx3Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
Note
To guarantee that TX3 cannot be rolled back, Bob must wait until TX3 receives at least maxRollBackBlocks
before announcing TX4.
TX4 Property | Value |
---|---|
Type | SecretProofTransaction |
Recipient | Bob’s address (Private Chain) |
Algorithm | SHA3-256 |
Secret | SHA3-256(proof) |
Proof | proof |
Network | Private Chain |
const tx4 = SecretProofTransaction.create(
Deadline.create(),
LockHashAlgorithm.Op_Sha3_256,
secret,
bobPrivateChainAccount.address,
proof,
NetworkType.MIJIN);
const tx4Signed = bobPrivateChainAccount.sign(tx4, privateChainGenerationHash);
privateChainTransactionHttp
.announce(tx4Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
const tx4 = symbol_sdk_1.SecretProofTransaction.create(symbol_sdk_1.Deadline.create(), symbol_sdk_1.LockHashAlgorithm.Op_Sha3_256, secret, bobPrivateChainAccount.address, proof, symbol_sdk_1.NetworkType.MIJIN);
const tx4Signed = bobPrivateChainAccount.sign(tx4, privateChainGenerationHash);
privateChainTransactionHttp
.announce(tx4Signed)
.subscribe((x) => console.log(x), (err) => console.error(err));
The process is atomic, but should be completed with lots of time before the deadlines:
maxRollBackBlocks
between each transaction confirmation.Did you find what you were looking for? Give us your feedback.