NEO多籤工具

0x00 前言

目前已寫好的NEO多籤分爲兩種,一種是基於NEL-GUI的GUI多籤工具,僅支持對鑑權合約的多籤工做;另外一種是基於NEL-ThinSDK-cs的請錢包多籤工具,終端操做,支持鑑權合約和應用合約的多籤工做。本文將分爲兩大部分,第一部分介紹兩個工具的使用方法,第二部分介紹技術實現細節。git

0x01 NEL-GUI多籤

這是第一部分的第一小結,介紹經過NEL-GUI進行鑑權合約的多籤,這個工具的目的主要是方便對多籤合約帳戶的轉帳操做。github

首先下載NEL-GUI,也就是 God of NEO 李總基於NEO-GUI寫的GUI工具。地址:api

git clone https://github.com/NewEconoLab/neo-gui-nel.git

下載完之用VS打開,項目選擇neo-gui:數據結構

項目選擇

而後啓動項目,項目啓動成功後會出現與熟悉的NEO-GUI很像的界面,不一樣的是NEL-GUI在選項卡里多了Plugin選項:工具

Plugin選項卡

這個插件選項裏面就是NEL開發的各類小工具了,我寫的多籤工具也放在這裏面。測試

在Plugin下拉列表種選擇多籤工具,就能夠打開多籤工具的界面:ui

多籤界面

若是要對多籤合約操做,只須要在合約地址列表種選擇多籤合約:插件

選擇多籤合約

而後在下面的WIF框裏添加須要進行簽名的私鑰就能夠了:debug

私鑰導入

最後填好目標帳戶地址和金額就能夠了。3d

0x02 ThinSDK 多籤

這裏是第一部分的第二小節,主要介紹ThinSDK 多籤的終端操做。

首先下載 ThinSDK-cs:

git clone https://github.com/NewEconoLab/neo-thinsdk-cs.git

而後用vs打開,選擇smartContractDemo項目,編譯運行。

終端

輸入multisign進入多籤功能。

多籤

多籤功能裏的 <2> 是進行應用合約多籤的。爲了方便測試,我在測試網發佈了一個多籤的應用合約,合約代碼:

public class Multi : SmartContract
    {
        public readonly static byte[] pubkey0 = { 2, 201, 40, 35, 13, 133, 217, 231, 75, 94, 76, 243, 237, 8, 84, 124, 118, 197, 253, 56, 208, 101, 194, 157, 78, 192, 203, 94, 102, 154, 16, 143, 55 };
        public readonly static byte[] pubkey1 = { 3, 177, 14, 33, 182, 106, 2, 10, 89, 150, 79, 237, 3, 70, 190, 12, 174, 176, 227, 235, 111, 113, 254, 3, 207, 183, 188, 189, 16, 191, 31, 225, 223, };
        public static bool Main(string method, object[] args)
        {
            if(Runtime.CheckWitness(Multi.pubkey0) && Runtime.CheckWitness(Multi.pubkey1))
            {
                return true;
            }
            return false;
        }
    }

其中pubkey0的私鑰對應:L2ME3NL8XgWLa6XVVzCJyccPw3C7bnqHzWhtfdPaeZzzdX8MJSkj pubkey1的私鑰對應:KwuezVnxhfUGiex7HM4ttrKBF4pTQRREkVmL1PW91gBZTRtsrLm9

合約地址爲:0x4c0f57b61d997297560190b1e397fe6d58fce94a

合約信息:

多籤合約

在調用合約時,首先輸入須要驗證的私鑰的數量,我這裏輸入2,而後分別輸出私鑰:

合約調用

等15秒左右以後經過neodebug工具來查看合於執行結果:

執行結果

交易id:0x500f037992dadcfb16fd55882109bd4c1629be8a19665f02e33d7dee9cc77632

能夠看到這裏是執行成功的。爲了肯定這裏是否是真的有效,咱們使用錯誤的私鑰再調用一下:

錯誤的私鑰以下:

  • L4ZntdDCocMJi4ozpTw4uTtxtAFNNCP2mX6m3P9CMJN66Dt2YJqP
  • KwEre52ubrRLr1zcTSnEbxM1eTzwqbUn7SVowknJsT9ypu8oTEku

從新調用多籤合約:

調用失敗

交易id 0x096997ca95011f5a4856b8947fe4a57780aeb8643b0a2515ed0568190962cfc5

經以上測試,多籤工具功能正常。

0x03 多籤分析

首先分析多籤合約帳戶。

多籤合約帳戶的構造代碼以下:

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 sb.ToArray();
            }

能夠分析出多籤合約腳本結構:

多簽結構

腳本執行的時候經過多籤碼進入多籤合約驗證邏輯,而後讀取公鑰數量,從腳本中加載公鑰。

在對交易進行多籤的時候,數據結構以下:

{
	"type": "Neo.Core.ContractTransaction",
	"hex": "8000000165046db244a897aeb4b04abdc154847083204f3eb64bbe4edb8736e3a45b6e50000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6000e1f505000000007264db854ba951aa2244150df4345585c310444a",
	"items": {
		"0xc627d59f76070a2239d3ccd1fa86e44916be580e": {
			"script": "52210387419467127d60bef1e11126cb1311609319e7a50c04781950498355cb8e1ad62102976eca673c5ccb15ca293f219734189b2738875b0e799856101e3347e2d7114a52ae",
			"parameters": [{
				"type": "Signature"
			}, {
				"type": "Signature"
			}],
			"signatures": {
				"0387419467127d60bef1e11126cb1311609319e7a50c04781950498355cb8e1ad6": "f02796e55c82657e3f363ac9a3bf1a74e64a45f2051e9437a856d279a6febde38efaad0e038f37cccd7796acce9441852916cf056945ff0fd640497d7d3baf24"
			}
		}
	}
}

能夠看到根據最小簽名的多少,會有相應數量的Signature,咱們對這個signature進行分析,發現這個signatures相互之間沒有關聯,徹底是私鑰對hex字段的內容進行簽名獲得的。

因而遍歷輸入的Wif私鑰,輪流對交易添加簽名:

for (int j = 0; j < wifsList.Items.Count; j++)
{
    var prikey = Wallet.GetPrivateKeyFromWIF(wifsList.Items[j].ToString());
    KeyPair key = new KeyPair(prikey);
    WalletAccount account = plugin_multisign.api.CurrentWallet.GetAccount(context.ScriptHashes[0]);
    byte[] signature = context.Verifiable.Sign(key);
    context.AddSignature(account.Contract, key.PublicKey, signature);
}

因而就能夠實現多簽了。

針對應用合約的多籤於此有點不一樣,在調用合約的時候,首先會對合約中input的來源數量與簽名數量進行驗證:

if (hashes.Length != verifiable.Scripts.Length) return false;

這樣代碼位置在neo/Core/Helper.cs中,用在合約驗證的時候。

hashes是經過input獲取的地址哈希列表:

/// <summary>
        /// 獲取須要校驗的腳本散列值
        /// </summary>
        /// <returns>返回須要校驗的腳本散列值</returns>
        public virtual UInt160[] GetScriptHashesForVerifying()
        {
            if (References == null) throw new InvalidOperationException();
            HashSet<UInt160> hashes = new HashSet<UInt160>(Inputs.Select(p => References[p].ScriptHash));
            hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Script).Select(p => new UInt160(p.Data)));
            foreach (var group in Outputs.GroupBy(p => p.AssetId))
            {
                AssetState asset = Blockchain.Default.GetAssetState(group.Key);
                if (asset == null) throw new InvalidOperationException();
                if (asset.AssetType.HasFlag(AssetType.DutyFlag))
                {
                    hashes.UnionWith(group.Select(p => p.ScriptHash));
                }
            }
            return hashes.OrderBy(p => p).ToArray();
        }

verifiable.Scripts則是添加的簽名數量,也就是說若是想參與多籤,那麼就須要添加相應帳戶的input,這也就意味着,若是這個帳戶裏沒有資產,或者是一個新的帳戶,那麼就沒法參與多籤。 所以在構造合約多籤的時候,我經過遍歷wif,對每一個wif帳戶都添加了input:

List<TransactionInput> list_inputs = new List<TransactionInput>();
            List<TransactionOutput> list_outputs = new List<TransactionOutput>();

            foreach (var prikey in prikeys)
            {
                byte[] pubkey = ThinNeo.Helper.GetPublicKeyFromPrivateKey(prikey);
                string address = ThinNeo.Helper.GetAddressFromPublicKey(pubkey);

                Dictionary<string, List<Utxo>> dir = await Helper.GetBalanceByAddress(Config.api, address);
                if (dir.ContainsKey(Nep55_1.id_GAS) == false)
                {
                    Console.WriteLine("no gas");
                    return null;
                }

                TransactionInput input = new TransactionInput();
                input.hash = dir[Nep55_1.id_GAS][0].txid;
                input.index = (ushort)dir[Nep55_1.id_GAS][0].n;
                list_inputs.Add(input);

                TransactionOutput outputchange = new TransactionOutput();
                outputchange.toAddress = ThinNeo.Helper.GetPublicKeyHashFromAddress(address);
                outputchange.value = dir[Nep55_1.id_GAS][0].value;
                outputchange.assetId = new Hash256(Nep55_1.id_GAS);
                list_outputs.Add(outputchange);
            }
相關文章
相關標籤/搜索