Intercambio atómico entre cadenas de bloques públicas y privadas

Intercambia tokens entre diferentes cadenas de bloques sin usar una parte intermediaria en el proceso.

Caso de uso

Alice y Bob desean intercambiar 10 tokens de Alice por 10 tokens de Bob. El problema es que no están en la misma red: el token de Alice está definido en una cadena privada utilizando la tecnología |nombre_de_codigo|, mientras que el token de Bob solo está presente en la cadena pública de |nombre_de_codigo|.

Una solución no atómica podría ser:

  1. Alice envía 10 tokens de Alice a Bob (cadena privada).

  2. Bob recibe la transacción.

  3. Bob envía 10 tokens de Bob a Alice (cadena pública).

  4. Alice recibe la transacción.

Sin embargo, no se tienen tanta confianza el uno al otro. Como habrás notado, Bob podría quedarse con los tokens de Alice si decide no realizar el paso 3.

Esta guía te mostrará cómo definir una forma segura de intercambiar tokens entre diferentes participantes y redes.

Prerrequisitos

  • Completa la sección de introducción.

  • Todos los participantes involucrados en el intercambio deben poseer al menos una cuenta en cada cadena de bloques:

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 = bitxor_sdk_1.Account.createFromPrivateKey(
  '',
  bitxor_sdk_1.NetworkType.MAIN_NET,
);
const alicePrivateChainAccount = bitxor_sdk_1.Account.createFromPrivateKey(
  '',
  bitxor_sdk_1.NetworkType.MIJIN,
);
const bobPublicChainAccount = bitxor_sdk_1.Account.createFromPrivateKey(
  '',
  bitxor_sdk_1.NetworkType.MAIN_NET,
);
const bobPrivateChainAccount = bitxor_sdk_1.Account.createFromPrivateKey(
  '',
  bitxor_sdk_1.NetworkType.MIJIN,
);
const privateChainTransactionHttp = new bitxor_sdk_1.TransactionHttp(
  'http://localhost:3000',
);
const publicChainTransactionHttp = new bitxor_sdk_1.TransactionHttp(
  'http://localhost:3000',
);
const publicChainGenerationHash = process.env.NETWORK_GENERATION_HASH;
const privateChainGenerationHash = process.env.NETWORK_GENERATION_HASH;
  • La cuenta de Alice en la cadena privada debe tener al menos 10 tokens de Alice.

  • La cuenta de Bob en la cadena pública debe tener al menos 10 tokens de Bob.

  • Ambas cuentas deben tener suficiente moneda de la red para pagar las tarifas de las transacciones.

Método #01: Usando el SDK

1. Alice genera un conjunto aleatorio de bytes llamado “prueba” (proof). La prueba debe tener un tamaño entre 10 y 1000 bytes. Luego, aplica el algoritmo SHA3-256 a ella, obteniendo el “secreto” (secret).

  1. Alice hace un hash de la prueba obtenida con uno de los algoritmos disponibles para generar el “secreto” (secret).

const random = crypto.randomBytes(20);
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(20);
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);
  1. Alice define la transacción TX1 de SecretLockTransaction:

Propiedad de TX1

Valor

Tipo

SecretLockTransaction

Token

10 00D3378709746FC4 (token de Alice)

Destinatario

Dirección de Bob (Cadena privada)

Algoritmo

SHA3-256

Duración

96 horas

Secreto

SHA3-256(prueba)

Red

Cadena privada

const tx1 = SecretLockTransaction.create(
  Deadline.create(epochAdjustment),
  new Token(new TokenId('00D3378709746FC4'), UInt64.fromUint(10)),
  UInt64.fromUint((96 * 3600) / 30), // assuming one block every 30 seconds
  LockHashAlgorithm.Op_Sha3_256,
  secret,
  bobPrivateChainAccount.address,
  NetworkType.MIJIN,
);
const tx1 = bitxor_sdk_1.SecretLockTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  new bitxor_sdk_1.Token(
    new bitxor_sdk_1.TokenId('00D3378709746FC4'),
    bitxor_sdk_1.UInt64.fromUint(10),
  ),
  bitxor_sdk_1.UInt64.fromUint((96 * 3600) / 30), // assuming one block every 30 seconds
  bitxor_sdk_1.LockHashAlgorithm.Op_Sha3_256,
  secret,
  bobPrivateChainAccount.address,
  bitxor_sdk_1.NetworkType.MIJIN,
);

Una vez anunciada, esta transacción permanecerá bloqueada hasta que alguien descubra la prueba que coincide con el secreto. Si nadie lo desbloquea antes de que se alcance la duración establecida, los fondos bloqueados se devolverán a Alice.

  1. Alice anuncia la TX1 en la red privada y comparte el secreto con Bob.

Note

Bob debe obtener el secreto de la cadena. Es responsabilidad de Bob verificar la corrección del secreto.

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),
);
  1. Bob anuncia la siguiente SecretLockTransaction TX2 en la red pública

Propiedad de TX2

Valor

Tipo

SecretLockTransaction

Token

10 10293DE77C684F71 (token de Bob)

Destinatario

Dirección de Alice (Cadena pública)

Algoritmo

SHA3-256

Duración

84 horas

Secreto

SHA3-256(prueba)

Red

Cadena pública

const tx2 = SecretLockTransaction.create(
  Deadline.create(epochAdjustment),
  new Token(new TokenId('10293DE77C684F71'), UInt64.fromUint(10)),
  UInt64.fromUint((84 * 3600) / 30), // assuming one block every 30 seconds
  LockHashAlgorithm.Op_Sha3_256,
  secret,
  alicePublicChainAccount.address,
  NetworkType.MAIN_NET,
  UInt64.fromUint(2000000),
);
const tx2 = bitxor_sdk_1.SecretLockTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  new bitxor_sdk_1.Token(
    new bitxor_sdk_1.TokenId('10293DE77C684F71'),
    bitxor_sdk_1.UInt64.fromUint(10),
  ),
  bitxor_sdk_1.UInt64.fromUint((84 * 3600) / 30), // assuming one block every 30 seconds
  bitxor_sdk_1.LockHashAlgorithm.Op_Sha3_256,
  secret,
  alicePublicChainAccount.address,
  bitxor_sdk_1.NetworkType.MAIN_NET,
  bitxor_sdk_1.UInt64.fromUint(2000000),
);

Note

La duración en la cual los fondos se pueden desbloquear debe ser un marco de tiempo más corto que el de TX1. Alice conoce el secreto, por lo que Bob debe asegurarse de tener tiempo suficiente después de que Alice revele el secreto.

Note

Para garantizar que TX1 no pueda ser revertida, Bob debe esperar hasta que TX1 reciba al menos maxRollBackBlocks confirmaciones antes de anunciar 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),
);
  1. Alice anuncia la SecretProofTransaction TX3 en la red pública. Esta transacción define el algoritmo de cifrado utilizado, la prueba original y el secreto:

Propiedad de TX3

Valor

Tipo

SecretProofTransaction

Destinatario

Dirección de Alice (Cadena pública)

Algoritmo

SHA3-256

Secreto

SHA3-256(prueba)

Prueba

prueba

Red

Cadena pública

Note

Para garantizar que TX2 no pueda ser revertida, Alice debe esperar hasta que TX2 reciba al menos maxRollBackBlocks confirmaciones antes de anunciar TX3.

const tx3 = SecretProofTransaction.create(
  Deadline.create(epochAdjustment),
  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 = bitxor_sdk_1.SecretProofTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  bitxor_sdk_1.LockHashAlgorithm.Op_Sha3_256,
  secret,
  alicePublicChainAccount.address,
  proof,
  bitxor_sdk_1.NetworkType.MAIN_NET,
  bitxor_sdk_1.UInt64.fromUint(2000000),
);
const tx3Signed = alicePublicChainAccount.sign(tx3, publicChainGenerationHash);
publicChainTransactionHttp.announce(tx3Signed).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
  1. Una vez que se confirma TX3, se revela la prueba. Se desbloquea la transacción TX2, y Alice recibe los fondos bloqueados.

  2. Bob selecciona la prueba y anuncia la SecretProofTransaction TX4 en la red privada, recibiendo los fondos bloqueados de la TX1.

Note

Para garantizar que TX3 no pueda ser revertida, Bob debe esperar hasta que TX3 reciba al menos maxRollBackBlocks confirmaciones antes de anunciar TX4.

Propiedad de TX4

Valor

Tipo

SecretProofTransaction

Destinatario

Dirección de Bob (Cadena privada)

Algoritmo

SHA3-256

Secreto

SHA3-256(prueba)

Prueba

prueba

Red

Cadena privada

const tx4 = SecretProofTransaction.create(
  Deadline.create(epochAdjustment),
  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 = bitxor_sdk_1.SecretProofTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  bitxor_sdk_1.LockHashAlgorithm.Op_Sha3_256,
  secret,
  bobPrivateChainAccount.address,
  proof,
  bitxor_sdk_1.NetworkType.MIJIN,
);
const tx4Signed = bobPrivateChainAccount.sign(tx4, privateChainGenerationHash);
privateChainTransactionHttp.announce(tx4Signed).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);

¿Es el proceso atómico?

El proceso es atómico, pero debe completarse con mucho tiempo de anticipación antes de las fechas límite:

  • ✅ Bob no quiere anunciar TX2: Alice recibirá sus fondos de vuelta después de 94 horas.

  • ✅ Alice no anuncia TX3: Bob recibirá su reembolso después de 84 horas. Alice también desbloqueará sus fondos después de 94 horas.

  • ⚠️Alice firma y anuncia TX3: Alice recibe los fondos de Bob. Bob tendrá suficiente tiempo para firmar TX4 porque la duración de TX1 es más larga que la de TX2.

  • ⚠️Se produce una reversión y se reescribe el historial: Alice y Bob han esperado al menos maxRollBackBlocks entre cada confirmación de transacción.