要建立一个多方签名合约,首先需要再gui中填入最小签名数量和公钥列表。点击确定后调用代码如下:
private void 多方签名MToolStripMenuItem_Click(object sender, EventArgs e)
{
using (CreateMultiSigContractDialog dialog = new CreateMultiSigContractDialog())
{
if (dialog.ShowDialog() != DialogResult.OK) return;
Contract contract = dialog.GetContract();
if (contract == null)
{
MessageBox.Show(Strings.AddContractFailedMessage);
return;
}
WalletAccount account = Program.CurrentWallet.CreateAccount(contract, dialog.GetKey());
if (Program.CurrentWallet is NEP6Wallet wallet)
wallet.Save();
listView1.SelectedIndices.Clear();
AddAccount(account, true);
}
}
这里首先会获取签名数量m和公钥列表publicKeys,然后调用CreateMultiSigContract(),这里会先检查m是否符合要求。如果通过则新建一个脚本,依次向栈中压入m,按顺序排列的公钥,公钥个数,CHECKMULTISIG。
public static Contract CreateMultiSigContract(int m, params ECPoint[] publicKeys)
{
TR.Enter();
return TR.Exit(new Contract
{
Script = CreateMultiSigRedeemScript(m, publicKeys),
ParameterList = Enumerable.Repeat(ContractParameterType.Signature, m).ToArray()
});
}
public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKeys)
{
TR.Enter();
if (!(1 <= m && m <= publicKeys.Length && publicKeys.Length <= 1024))
{
TR.Exit();
throw new ArgumentException();
}
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitPush(m);
foreach (ECPoint publicKey in publicKeys.OrderBy(p => p))
{
sb.EmitPush(publicKey.EncodePoint(true));
}
sb.EmitPush(publicKeys.Length);
sb.Emit(OpCode.CHECKMULTISIG);
return TR.Exit(sb.ToArray());
}
}
下面便是将mn压栈时的代码,从这里可以看到多签脚本中mn的两种可能的赋值方式。这部分在多签检测的时候会用到。
public ScriptBuilder EmitPush(BigInteger number)
{
TR.Enter();
if (number == -1) return TR.Exit(Emit(OpCode.PUSHM1));
if (number == 0) return TR.Exit(Emit(OpCode.PUSH0));
if (number > 0 && number <= 16) return TR.Exit(Emit(OpCode.PUSH1 - 1 + (byte)number));
return TR.Exit(EmitPush(number.ToByteArray()));
}
public ScriptBuilder EmitPush(byte[] data)
{
TR.Enter();
if (data == null)
{
TR.Exit();
throw new ArgumentNullException();
}
if (data.Length <= (int)OpCode.PUSHBYTES75)
{
writer.Write((byte)data.Length);
writer.Write(data);
}
else if (data.Length < 0x100)
{
Emit(OpCode.PUSHDATA1);
writer.Write((byte)data.Length);
writer.Write(data);
}
else if (data.Length < 0x10000)
{
Emit(OpCode.PUSHDATA2);
writer.Write((ushort)data.Length);
writer.Write(data);
}
else// if (data.Length < 0x100000000L)
{
Emit(OpCode.PUSHDATA4);
writer.Write(data.Length);
writer.Write(data);
}
return TR.Exit(this);
}
脚本创建完成后会根据合约以及当前账户的秘钥对新建一个多方签名账户。
创建智能合约账户函数: 位置 neo\Implementations\Wallets\EntityFramework\UserWallet.cs
public override WalletAccount CreateAccount(SmartContract.Contract contract, KeyPair key = null)
{
TR.Enter();
VerificationContract verification_contract = contract as VerificationContract;
if (verification_contract == null)
{
verification_contract = new VerificationContract
{
Script = contract.Script,
ParameterList = contract.ParameterList
};
}
UserWalletAccount account = new UserWalletAccount(verification_contract.ScriptHash)
{
Key = key,
Contract = verification_contract
};
AddAccount(account, false);
return TR.Exit(account);
}
这样便完成了一个多方签名合约的创建。
后面发送交易过程类似,只是在检查签名时如果发现context.Completed = false会显示签名不完整,需要2个人签名才能通过并成功发送交易。