Agregando firmas conjuntas a transacciones completas agregadas

Esta guía te mostrará cómo agregar firmas conjuntas a una transacción completa agregada sin usar la caché parcial.

Esto puede ser útil cuando mantienes tus claves privadas en un dispositivo sin conexión (billetera fría) por razones de seguridad. Al agregar las firmas conjuntas sin conexión, los cofirmantes podrán ejecutar transacciones desde tu billetera fría mientras mantienen seguras por completo sus claves privadas. Además, también permite a los usuarios evitar bloquear innecesariamente sus fondos en transacciones agregadas con garantía.

Caso de uso

../../_images/aggregate-escrow-1.png

Transacciones de Escrow de Múltiples Activos

Configuraremos una transacción completa agregada donde Alice (TDPXWF2H5G7U2NKZRJD47QR4KZPRULPAMQ4O54IK) enviará 100 bitxor a Bob (BXRS3AOXFGWGTN2QUUHDCXJ4SBYLIQIPNUPHHA2N) a cambio de 1 token collectible.

Prerrequisitos

  • Tener 2 cuentas de cuenta.

  • Cargar las cuentas con suficiente bitxor para pagar las tarifas de transacción.

  • Una de las cuentas debe ser propietaria de un token que no sea la bitxor.

  • Tanto tu estación de trabajo sin conexión como la en línea <../../getting-started/setup-workstation> están configuradas para Bitxor-CLI y Bitxor-SDK.

Paso 1: Construir la transacción completa agregada

  1. Abre un editor de texto. Luego, construye la Transacción Completa Agregada usando la cuenta de Alice.

const networkType = NetworkType.TEST_NET;

// replace with alice private key
const alicePrivatekey = '';
const aliceAccount = Account.createFromPrivateKey(alicePrivatekey, networkType);

// replace with bob public key
const bobPublicKey = '';
const bobPublicAccount = PublicAccount.createFromPublicKey(
  bobPublicKey,
  networkType,
);

const aliceTransferTransaction = TransferTransaction.create(
  Deadline.create(epochAdjustment),
  bobPublicAccount.address,
  [NetworkCurrencies.PUBLIC.currency.createRelative(1000)],
  PlainMessage.create('payout'),
  networkType,
);

const bobTransferTransaction = TransferTransaction.create(
  Deadline.create(epochAdjustment),
  aliceAccount.address,
  [new Token(new NamespaceId('collectible'), UInt64.fromUint(1))],
  PlainMessage.create('payout'),
  networkType,
);

const aggregateTransaction = AggregateTransaction.createComplete(
  Deadline.create(epochAdjustment),
  [
    aliceTransferTransaction.toAggregate(aliceAccount.publicAccount),
    bobTransferTransaction.toAggregate(bobPublicAccount),
  ],
  networkType,
  [],
  UInt64.fromUint(2000000),
);
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with alice private key
const alicePrivatekey = '';
const aliceAccount = bitxor_sdk_1.Account.createFromPrivateKey(
  alicePrivatekey,
  networkType,
);
// replace with bob public key
const bobPublicKey = '';
const bobPublicAccount = bitxor_sdk_1.PublicAccount.createFromPublicKey(
  bobPublicKey,
  networkType,
);
const aliceTransferTransaction = bitxor_sdk_1.TransferTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  bobPublicAccount.address,
  [bitxor_sdk_1.NetworkCurrencies.PUBLIC.currency.createRelative(1000)],
  bitxor_sdk_1.PlainMessage.create('payout'),
  networkType,
);
const bobTransferTransaction = bitxor_sdk_1.TransferTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  aliceAccount.address,
  [
    new bitxor_sdk_1.Token(
      new bitxor_sdk_1.NamespaceId('collectible'),
      bitxor_sdk_1.UInt64.fromUint(1),
    ),
  ],
  bitxor_sdk_1.PlainMessage.create('payout'),
  networkType,
);
const aggregateTransaction = bitxor_sdk_1.AggregateTransaction.createComplete(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  [
    aliceTransferTransaction.toAggregate(aliceAccount.publicAccount),
    bobTransferTransaction.toAggregate(bobPublicAccount),
  ],
  networkType,
  [],
  bitxor_sdk_1.UInt64.fromUint(2000000),
);
            NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
            NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture()
                .get();

            // replace with alice private key
            String alicePrivatekey = "";
            Account aliceAccount = Account.createFromPrivateKey(alicePrivatekey, networkType);

            // replace with bob public key
            String bobPublicKey = "";
            PublicAccount bobPublicAccount = PublicAccount
                .createFromPublicKey(bobPublicKey, networkType);

            TransferTransaction aliceTransferTransaction = TransferTransactionFactory
                .create(networkType, bobPublicAccount.getAddress(),
                    Collections
                        .singletonList(networkCurrency.createRelative(BigInteger.valueOf(1000))),
                    PlainMessage.create("payout")).build();

            TransferTransaction bobTransferTransaction = TransferTransactionFactory
                .create(networkType, aliceAccount.getAddress(),
                    Collections.singletonList(
                        new Token(new NamespaceId("collectible"), BigInteger.valueOf(1))),
                    PlainMessage.create("payout")).build();

            AggregateTransaction aggregateTransaction = AggregateTransactionFactory
                .createComplete(networkType, Arrays
                    .asList(aliceTransferTransaction.toAggregate(aliceAccount.getPublicAccount()),
                        bobTransferTransaction.toAggregate(bobPublicAccount)))
                .maxFee(BigInteger.valueOf(2000000)).build();

Asegúrate de colocar la clave privada de Alice y la clave pública de Bob en los lugares apropiados.

  1. Firma la transacción con la clave de Alice.

// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const generationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';

const signedTransactionNotComplete = aliceAccount.sign(
  aggregateTransaction,
  generationHash,
);
console.log(signedTransactionNotComplete.payload);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const generationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransactionNotComplete = aliceAccount.sign(
  aggregateTransaction,
  generationHash,
);
console.log(signedTransactionNotComplete.payload);
            String generationHash = repositoryFactory.getGenerationHash().toFuture().get();

            SignedTransaction signedTransactionNotComplete = aliceAccount
                .sign(aggregateTransaction, generationHash);
            System.out.println(signedTransactionNotComplete.getPayload());

3. Guarda el archivo TypeScript, luego ejecútalo en tu terminal. Copia el payload devuelto y envíalo a Bob.

Paso 2: Agregar la segunda firma conjunta

  1. Bob firma conjuntamente el payload obtenido en el paso anterior.

// replace with bob private key
const bobPrivateKey = '';
const bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
const cosignedTransactionBob = CosignatureTransaction.signTransactionPayload(
  bobAccount,
  signedTransactionNotComplete.payload,
  generationHash,
);
console.log(cosignedTransactionBob.signature);
console.log(cosignedTransactionBob.parentHash);
// replace with bob private key
const bobPrivateKey = '';
const bobAccount = bitxor_sdk_1.Account.createFromPrivateKey(
  bobPrivateKey,
  networkType,
);
const cosignedTransactionBob = bitxor_sdk_1.CosignatureTransaction.signTransactionPayload(
  bobAccount,
  signedTransactionNotComplete.payload,
  generationHash,
);
console.log(cosignedTransactionBob.signature);
console.log(cosignedTransactionBob.parentHash);
            // replace with bob private key
            String bobPrivateKey = "";
            Account bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
            CosignatureSignedTransaction cosignedTransactionBob = CosignatureTransaction
                .create(aggregateTransaction)
                .signWith(bobAccount);

            System.out.println(cosignedTransactionBob.getSignature());
            System.out.println(cosignedTransactionBob.getParentHash());

2. Bob ejecuta el fragmento de código en la terminal y obtiene la firma de transacción y el hash principal. Finalmente, comparte la información con Alice.

Paso 3: Anunciar la Transacción Completa Agregada

Usando la clave pública de Bob, el hash de transacción de firma conjunta y la firma, recrea la transacción y anúnciala en la red como completa.

const cosignatureSignedTransactions = [
  new CosignatureSignedTransaction(
    cosignedTransactionBob.parentHash,
    cosignedTransactionBob.signature,
    cosignedTransactionBob.signerPublicKey,
  ),
];
const rectreatedAggregateTransactionFromPayload = TransactionMapping.createFromPayload(
  signedTransactionNotComplete.payload,
) as AggregateTransaction;

const signedTransactionComplete = aliceAccount.signTransactionGivenSignatures(
  rectreatedAggregateTransactionFromPayload,
  cosignatureSignedTransactions,
  generationHash,
);
console.log(signedTransactionComplete.hash);

// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();

transactionHttp.announce(signedTransactionComplete).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
const cosignatureSignedTransactions = [
  new bitxor_sdk_1.CosignatureSignedTransaction(
    cosignedTransactionBob.parentHash,
    cosignedTransactionBob.signature,
    cosignedTransactionBob.signerPublicKey,
  ),
];
const rectreatedAggregateTransactionFromPayload = bitxor_sdk_1.TransactionMapping.createFromPayload(
  signedTransactionNotComplete.payload,
);
const signedTransactionComplete = aliceAccount.signTransactionGivenSignatures(
  rectreatedAggregateTransactionFromPayload,
  cosignatureSignedTransactions,
  generationHash,
);
console.log(signedTransactionComplete.hash);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransactionComplete).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
            BinarySerialization serialization = BinarySerializationImpl.INSTANCE;

            AggregateTransactionFactory rectreatedAggregateTransactionFromPayload = (AggregateTransactionFactory) serialization
                .deserializeToFactory(
                    ConvertUtils.getBytes(signedTransactionNotComplete.getPayload()));

            //Added a new cosignature.
            rectreatedAggregateTransactionFromPayload.addCosignatures(cosignedTransactionBob);

            SignedTransaction signedTransactionComplete = aliceAccount
                .sign(rectreatedAggregateTransactionFromPayload.build(), generationHash);
            System.out.println(signedTransactionComplete.getHash());

            TransactionRepository transactionHttp = repositoryFactory.createTransactionRepository();

            transactionHttp.announce(signedTransactionComplete).toFuture().get();

Si tienes éxito, Alice habrá enviado 100 bitxor a Bob y recibido 1 token collectible a cambio.