Actualizando entradas de metadatos

Actualizar cualquier entrada de metadatos existente.

Caso de uso

Las transacciones de metadatos se almacenan en la cadena de bloques. Una vez que una transacción se incluye en un bloque y el bloque recibe suficientes confirmaciones, no es posible modificar el registro sin invalidar toda la cadena.

Lo que podemos hacer para actualizar una entrada de metadatos es anunciar una segunda transacción de metadatos. Esta acción registrará una nueva transacción mientras se mantiene la historia inmutable. Sin embargo, ¿cómo podemos recuperar el valor de metadatos más reciente asignado a un activo sin consultar toda la cadena? Bitxor hace esto posible al mantener una copia del valor más nuevo asignado a una entrada de metadatos como un estado.

Esta guía te muestra cómo actualizar una entrada de metadatos adjunta a una cuenta. Sin embargo, puedes seguir un enfoque similar para actualizar las entradas de metadatos de nombres de espacio y tokens.

Prerrequisitos

  • Completa la sección de comenzar.

  • Completa la guía de asignar entradas de metadatos a una cuenta.

Método #01: Usando el SDK

Bob, el notario de la guía de asignar entradas de metadatos a una cuenta, recibe la solicitud de eliminar la entrada de metadatos de la cuenta de Alice llamada “CERT�?porque el certificado ha expirado.

../../_images/metadata-update.png

1. Define una nueva AccountMetadataTransaction estableciendo la cuenta de Alice como el objetivo de los metadatos. Para indicar que el certificado ha expirado, Bob decide agregar el nuevo valor �?00000�?a la entrada de metadatos con clave “CERT�? Sin embargo, necesitas pasar un parámetro adicional que no era necesario al asignar una entrada de metadatos por primera vez.

Por definición, las cadenas de bloques pueden revertirse hasta una cierta profundidad preestablecida para resolver bifurcaciones. En caso de que el estado necesite revertirse, debes indicar la diferencia de tamaño entre el “previousValue�?asignado a la entrada de metadatos y el “newValue�?

A) Recupera el valor de metadatos anterior y calcula la diferencia de tamaño con el valor más nuevo. Luego, devuelve el objeto AccountMetadataTransaction.

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with bob private key
const bobPrivateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
// replace with alice public key
const alicePublicKey =
  'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = PublicAccount.createFromPublicKey(
  alicePublicKey,
  networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const metadataHttp = repositoryFactory.createMetadataRepository();

// replace with key and new value
const key = KeyGenerator.generateUInt64Key('CERT');
const newValue = '000000';
const newValueBytes = Convert.utf8ToUint8(newValue);

const searchCriteria = {
  targetAddress: alicePublicAccount.address,
  scopedMetadataKey: key.toString(),
  sourceAddress: bobAccount.address,
};
const accountMetadataTransaction = metadataHttp.search(searchCriteria).pipe(
  mergeMap((metadata) => {
    const currentValueBytes = Convert.utf8ToUint8(
      metadata.data[0].metadataEntry.value,
    );
    return of(
      AccountMetadataTransaction.create(
        Deadline.create(epochAdjustment),
        alicePublicAccount.address,
        key,
        newValueBytes.length - currentValueBytes.length,
        Convert.decodeHex(Convert.xor(currentValueBytes, newValueBytes)),
        networkType,
      ),
    );
  }),
);
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with bob private key
const bobPrivateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const bobAccount = bitxor_sdk_1.Account.createFromPrivateKey(
  bobPrivateKey,
  networkType,
);
// replace with alice public key
const alicePublicKey =
  'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = bitxor_sdk_1.PublicAccount.createFromPublicKey(
  alicePublicKey,
  networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const metadataHttp = repositoryFactory.createMetadataRepository();
// replace with key and new value
const key = bitxor_sdk_1.KeyGenerator.generateUInt64Key('CERT');
const newValue = '000000';
const newValueBytes = bitxor_sdk_1.Convert.utf8ToUint8(newValue);
const searchCriteria = {
  targetAddress: alicePublicAccount.address,
  scopedMetadataKey: key.toString(),
  sourceAddress: bobAccount.address,
};
const accountMetadataTransaction = metadataHttp.search(searchCriteria).pipe(
  operators_1.mergeMap((metadata) => {
    const currentValueBytes = bitxor_sdk_1.Convert.utf8ToUint8(
      metadata.data[0].metadataEntry.value,
    );
    return rxjs_1.of(
      bitxor_sdk_1.AccountMetadataTransaction.create(
        bitxor_sdk_1.Deadline.create(epochAdjustment),
        alicePublicAccount.address,
        key,
        newValueBytes.length - currentValueBytes.length,
        bitxor_sdk_1.Convert.decodeHex(
          bitxor_sdk_1.Convert.xor(currentValueBytes, newValueBytes),
        ),
        networkType,
      ),
    );
  }),
);

B) Puedes lograr el mismo resultado con menos esfuerzo usando el MetadataService. Detrás de escena, el Bitxor SDK maneja la complejidad de actualizar las entradas de metadatos.

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with bob private key
const bobPrivateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
// replace with alice public key
const alicePublicKey =
  'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = PublicAccount.createFromPublicKey(
  alicePublicKey,
  networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const metadataHttp = new MetadataHttp(nodeUrl);
const metadataService = new MetadataTransactionService(metadataHttp);

// replace with key and new value
const key = KeyGenerator.generateUInt64Key('CERT');
const newValue = '000000';

const accountMetadataTransaction = metadataService.createAccountMetadataTransaction(
  Deadline.create(epochAdjustment),
  networkType,
  alicePublicAccount.address,
  key,
  newValue,
  bobAccount.publicAccount.address,
  UInt64.fromUint(0),
);

2. Para evitar llenar la cuenta con metadatos no válidos, todos los metadatos se adjuntan solo con el consentimiento del propietario de la cuenta a través de transacciones agregadas (Aggregate Transactions). Por lo tanto, Alice tendrá que optar si desea que los metadatos se actualicen. Envuelve la AccountMetadataTransaction dentro de una AggregateBondedTransaction y firma la transacción usando la cuenta de Bob.

// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedAggregateTransaction = accountMetadataTransaction.pipe(
  mergeMap((transaction) => {
    const aggregateTransaction = AggregateTransaction.createBonded(
      Deadline.create(epochAdjustment),
      [transaction.toAggregate(bobAccount.publicAccount)],
      networkType,
      [],
      UInt64.fromUint(2000000),
    );
    const signedTransaction = bobAccount.sign(
      aggregateTransaction,
      networkGenerationHash,
    );
    return of(signedTransaction);
  }),
);

3. Antes de enviar una transacción agregada a la red, Bob tiene que bloquear 10 bitxor. Define una nueva HashLockTransaction y fírmala con la cuenta de Bob, bloqueando la cantidad de bitxor requerida para anunciar la transacción agregada.

interface SignedAggregateHashLock {
  readonly aggregate: SignedTransaction;
  readonly hashLock: SignedTransaction;
}

// replace with bitxor id
const networkCurrencyTokenId = new TokenId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;

const signedAggregateHashLock = signedAggregateTransaction.pipe(
  mergeMap((signedAggregateTransaction) => {
    const hashLockTransaction = HashLockTransaction.create(
      Deadline.create(epochAdjustment),
      new Token(
        networkCurrencyTokenId,
        UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility)),
      ),
      UInt64.fromUint(480),
      signedAggregateTransaction,
      networkType,
      UInt64.fromUint(2000000),
    );
    const signedTransaction = bobAccount.sign(
      hashLockTransaction,
      networkGenerationHash,
    );
    const signedAggregateHashLock: SignedAggregateHashLock = {
      aggregate: signedAggregateTransaction,
      hashLock: signedTransaction,
    };
    console.log(
      'Aggregate Transaction Hash:',
      signedAggregateTransaction.hash + '\n',
    );
    console.log('HashLock Transaction Hash:', signedTransaction.hash + '\n');
    return of(signedAggregateHashLock);
  }),
);

Note

Bob recibirá los fondos bloqueados de vuelta si Alice cofirma la transacción agregada durante los próximos �?80�?bloques.

4. Anuncia la HashLockTransaction. Monitorea la red hasta que la transacción sea confirmada y luego anuncia la AggregateTransaction que contiene la AccountMetadataTransaction.

const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new TransactionService(transactionHttp, receiptHttp);

listener.open().then(() => {
  signedAggregateHashLock
    .pipe(
      mergeMap((signedAggregateHashLock) =>
        transactionService.announceHashLockAggregateBonded(
          signedAggregateHashLock.hashLock,
          signedAggregateHashLock.aggregate,
          listener,
        ),
      ),
    )
    .subscribe(
      () => console.log('Transaction confirmed'),
      (err) => console.log(err),
      () => listener.close(),
    );
});
  1. Una vez que la transacción sea confirmada, cofirma el hash obtenido en el tercer paso usando el perfil de Alice.

bitxor-cli transaction cosign --hash <transaction-hash> --profile alice
  1. Recupera las entradas de metadatos asignadas a la cuenta de Alice siguiendo la próxima guía.