NEO 構(gòu)建一筆交易分析

構(gòu)建一筆交易

通過MakeTransaction(TransferOutput[] outputs, UInt160 from = null)函數(shù)構(gòu)建。

UInt160[] accounts;
if (from is null)
{
    accounts = GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).ToArray();
}
else
{
    if (!Contains(from))
    throw new ArgumentException($"The address {from.ToString()} was not found in the wallet");
    accounts = new[] { from };
}

如果未指定from賬戶肆氓,則首先調(diào)用GetAccount()獲取錢包中所有非Lock和非WatchOnly的賬戶列表袍祖,按照腳本哈希排序。
若指定from賬戶谢揪,則判斷賬戶是否包含在錢包中蕉陋,不包含則拋出異常。
將獲取的賬戶加入accounts拨扶。
把outputs中的輸出按照(assetId, group, sum)分組凳鬓,其中assetId表示需要輸出的資產(chǎn)ID,group表示輸出該類型資產(chǎn)的output集合,sum表示輸出資產(chǎn)的總量患民。

對于每一種資產(chǎn)缩举,分別對每個賬戶構(gòu)建腳本,進入虛擬機執(zhí)行得到資產(chǎn)余額匹颤,并記錄在balances中仅孩。最后將所有余額相加得到資產(chǎn)總余額,如果小于需要輸出的總量印蓖,則拋出余額不足異常辽慕。余額充足則進入下一步。

foreach (TransferOutput output in group)
{
    balances = balances.OrderBy(p => p.Value).ToList();
    var balances_used = FindPayingAccounts(balances, output.Value.Value);
    cosigners.UnionWith(balances_used.Select(p => p.Account));
    foreach (var (account, value) in balances_used)
    {
        sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value);
        sb.Emit(OpCode.THROWIFNOT);
    }
}

最主要的便是FindPayingAccounts(balances, output.Value.Value);根據(jù)balances和輸出的數(shù)量選擇合適的賬戶付款另伍。
如果balances剛好與輸出相等鼻百,則所有賬戶即為付款賬戶。
否則摆尝,遍歷每個賬戶温艇,如果某個賬戶的余額等于輸出,則該賬戶為付款賬戶堕汞。如果賬戶余額大于輸出勺爱,則該賬戶為付款賬戶,數(shù)量為需要輸出數(shù)量讯检,并更新賬戶余額琐鲁。
如果所有賬戶的余額均小于輸出,則從余額最多的賬戶開始向下遍歷人灼,依次加入付款賬戶围段,直到某賬戶余額大于需要付款的值,此時從余額最小的賬戶向上遍歷投放,找到第一個余額大于需要輸出的賬戶奈泪,將該賬戶作為付款賬戶,并更新余額司恳。

例如有7個賬戶余額分別為1刽锤,2,3贴汪,4冯遂,5蕊肥,6,7蛤肌,輸出為15.5壁却。首先會選擇7,6寻定,兩個賬戶儒洛,此時5>2.5,則從小到大遍歷狼速,其中第一個大于2.5的賬戶為3,所以輸出3卦停。最終的付款賬戶為(7向胡,6,3)惊完,值為(7僵芹,6,2.5)小槐。

隨后記錄所有付款賬戶地址拇派,并按照賬戶依次構(gòu)造虛擬機APPCALL轉(zhuǎn)賬腳本。計算所有賬戶Gas余額凿跳,并調(diào)用
MakeTransaction(snapshot, attributes, script, balances_gas);
該函數(shù)首先將腳本進入虛擬機test模式件豌,計算Gas消耗量。如果Gas消耗超過免費額度控嗜,則超過部分向上取整作為SystemFee茧彤。(對remainder<0不清楚)
接下來計算NetworkFee,

foreach (UInt160 hash in hashes)
                {
                    byte[] witness_script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script;
                    if (witness_script is null) continue;
                    if (witness_script.IsSignatureContract())
                    {
                        size += 66 + witness_script.GetVarSize();
                        tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null);
                    }
                    else if (witness_script.IsMultiSigContract(out int m, out int n))
                    {
                        int size_inv = 65 * m;
                        size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize();
                        tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m;
                        using (ScriptBuilder sb = new ScriptBuilder())
                            tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]];
                        tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n;
                        using (ScriptBuilder sb = new ScriptBuilder())
                            tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]];
                        tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n;
                    }
                    else
                    {
                        //We can support more contract types in the future.
                    }
                }
                tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot);
                if (value >= tx.SystemFee + tx.NetworkFee) return tx;

NetworkFee分單簽和多簽的情況。且NetworkFee主要由交易size費用以及操作碼費用兩部分組成疆栏。

對于單簽曾掂,首先是區(qū)塊的固定部分size:

int size = Transaction.HeaderSize + attributes.GetVarSize()
+ script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length);

加上腳本的size:

size += 66 + witness_script.GetVarSize();

66代表單簽?zāi)_本驗證時需要添加的PUSHBYTES64+64Bytes的長度(還有1不知道是什么)。

tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null);

這部分包括單簽驗證需要的opcode的費用壁顶。
最后加上所有size與每字節(jié)的費用:

tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot);

就得到了NetworkFee的總量珠洗。
類似的,對于多簽?zāi)_本:

int size_inv = 65 * m;
size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize();
tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m;
using (ScriptBuilder sb = new ScriptBuilder())
    tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]];
    tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n;
using (ScriptBuilder sb = new ScriptBuilder())
    tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]];
    tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n;

同樣是包括總的size費用以及多簽驗證腳本的操作碼對應(yīng)總費用若专。

tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot);
                if (value >= tx.SystemFee + tx.NetworkFee) return tx;

最后計算SystemFee和NetworkFee的和许蓖,如果Gas余額大于之和,則返回Tx,否則拋出異常蛔糯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拯腮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蚁飒,更是在濱河造成了極大的恐慌动壤,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淮逻,死亡現(xiàn)場離奇詭異琼懊,居然都是意外死亡,警方通過查閱死者的電腦和手機爬早,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門哼丈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人筛严,你說我怎么就攤上這事醉旦。” “怎么了桨啃?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵车胡,是天一觀的道長。 經(jīng)常有香客問我照瘾,道長匈棘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任析命,我火速辦了婚禮主卫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鹃愤。我一直安慰自己簇搅,他們只是感情好,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布昼浦。 她就那樣靜靜地躺著馍资,像睡著了一般。 火紅的嫁衣襯著肌膚如雪关噪。 梳的紋絲不亂的頭發(fā)上鸟蟹,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天,我揣著相機與錄音使兔,去河邊找鬼建钥。 笑死,一個胖子當著我的面吹牛虐沥,可吹牛的內(nèi)容都是我干的熊经。 我是一名探鬼主播泽艘,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼镐依!你這毒婦竟也來了匹涮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤槐壳,失蹤者是張志新(化名)和其女友劉穎然低,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體务唐,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡雳攘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了枫笛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吨灭。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖刑巧,靈堂內(nèi)的尸體忽然破棺而出喧兄,到底是詐尸還是另有隱情,我是刑警寧澤海诲,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布繁莹,位于F島的核電站,受9級特大地震影響特幔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闸昨,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一蚯斯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饵较,春花似錦拍嵌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至茄猫,卻和暖如春狈蚤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背划纽。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工脆侮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人勇劣。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓靖避,卻偏偏與公主長得像潭枣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子幻捏,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 以太坊(Ethereum ):下一代智能合約和去中心化應(yīng)用平臺 翻譯:巨蟹 盆犁、少平 譯者注:中文讀者可以到以太坊愛...
    車圣閱讀 3,763評論 1 7
  • 【中文版】以太坊白皮書 翻譯:少平、 Seven當中本聰在 2009 年 1 月啟動比特幣區(qū)塊鏈時篡九,他同時向世界引...
    __Seven__閱讀 4,244評論 0 10
  • 以太坊白皮書地址:https://github.com/ethereum/wiki/wiki/White-Pape...
    rectinajh閱讀 17,840評論 0 46
  • 原文地址 https://mbinary.coding.me/introduction-to-bitcoin.ht...
    mbinary閱讀 5,174評論 0 4
  • 最近一段時間谐岁,關(guān)于智能合約漏洞的問題層出不窮。我將以SMT為藍本進行復(fù)盤瓮下,通過復(fù)盤去深層次了解和防止類似事件發(fā)生翰铡。...
    廖全磊LesterLiao閱讀 4,464評論 0 4