Transactions With Multiple Signers

Icon Link多签名者的交易

当一个交易包含一个可花费的输入(例如一个硬币)时,它必须包含该硬币所有者的签名才能被花费。如果硬币所有者也提交了交易,那么这是直截了当的。但是,如果需要外部地址对交易进行签名,则交易必须包含多个签名。在 SDK 中,可以通过在交易请求上调用 addAccountWitnesses 来添加帐户签名到交易中。

考虑一个需要两个签名才能花费的脚本:

script;
 
use std::{b512::B512, ecr::ec_recover_address, tx::{tx_id, tx_witness_data}};
 
fn main(signer: b256) -> bool {
    let witness_data: B512 = tx_witness_data(1);
    let address: b256 = ec_recover_address(witness_data, tx_id()).unwrap().bits();
    return address == signer;
}

在上面的代码片段中,我们使用内置的 Sway 函数 tx_witness_data() 来检索见证签名和 tx_id() 来获取交易哈希。然后,我们检索签名地址以验证脚本。

我们可以通过从调用范围创建一个交易请求来在 SDK 中与此脚本进行交互。对于合约也是一样。考虑以下脚本:

// #import { Script };
 
const script = new Script(bytecode, abi, sender);
const { value } = await script.functions
  .main(signer.address.toB256())
  .addTransfer({
    destination: receiver.address,
    amount: amountToReceiver,
    assetId: baseAssetId,
  })
  .addSigners(signer)
  .call<BN>();

同样的方法也可以用于断言,通过实例化并将其添加到交易请求中。考虑以下断言:

predicate;
 
use std::{b512::B512, ecr::ec_recover_address, tx::{tx_id, tx_witness_data}};
 
fn main(signer: b256) -> bool {
    let witness_data: B512 = tx_witness_data(1);
    let address: b256 = ec_recover_address(witness_data, tx_id()).unwrap().bits();
    return address == signer;
}

我们可以通过以下实现在 SDK 中与此断言进行交互:

// #import { Predicate, ScriptTransactionRequest };
 
// Create and fund the predicate
const predicate = new Predicate<[string]>({
  bytecode,
  abi,
  provider,
  inputData: [signer.address.toB256()],
});
const tx1 = await sender.transfer(predicate.address, 200_000, baseAssetId);
await tx1.waitForResult();
 
// Create the transaction request
const request = new ScriptTransactionRequest();
request.addCoinOutput(receiver.address, amountToReceiver, baseAssetId);
 
// Get the predicate resources and add them and predicate data to the request
const resources = await predicate.getResourcesToSpend([
  {
    assetId: baseAssetId,
    amount: amountToReceiver,
  },
]);
 
request.addResources(resources);
request.addWitness('0x');
 
// Add witnesses including the signer
// Estimate the predicate inputs
const txCost = await provider.getTransactionCost(request, {
  signatureCallback: (tx) => tx.addAccountWitnesses(signer),
  resourcesOwner: predicate,
});
 
request.updatePredicateGasUsed(txCost.estimatedPredicates);
 
request.gasLimit = txCost.gasUsed;
request.maxFee = txCost.maxFee;
 
await predicate.fund(request, txCost);
 
await request.addAccountWitnesses(signer);
 
// Send the transaction
const res = await provider.sendTransaction(request);
await res.waitForResult();