From cb00b6133eb690501044a561985a74d91006d403 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 28 May 2020 17:04:32 +0300 Subject: [PATCH] Update withdraw mechanism Withdraw operation works a bit different with neo:morph. User calls "Deposit" and "Withdraw" methods that produce notifications for the inner ring nodes. "Deposit" method does not produce feedback, but "Withdraw" does. Inner ring nodes check if user eligible to withdraw assets. If so, nodes invoke "Cheque" method. This method collects invocations from inner ring nods. When there are 2/3n + 1 invocations, smart-contract transfers assets back to the user and produces notifications. --- neofs_contract.go | 121 +++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 01984f0..d033a36 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -19,8 +19,7 @@ type ( } check struct { - id []byte - height []byte + id []byte } ) @@ -148,39 +147,74 @@ func Main(op string, args []interface{}) interface{} { runtime.Log("funds have been transferred") + var rcv = []byte{} + if len(args) == 3 { + rcv = args[2].([]byte) // todo: check if rcv value is valid + } + + runtime.Notify("Deposit", pk, amount, rcv) + return true case "Withdraw": - from := engine.GetExecutingScriptHash() - data := args[0].([]byte) - message := data[0:66] - uuid := data[0:25] - owner := data[25:50] - value := data[50:58] - height := data[58:66] - offset := 68 - usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) - - c := check{ - id: uuid, - height: height, - } - if containsCheck(usedList, c) { - panic("verification check has already been used") + if len(args) != 2 { + panic("withdraw: bad arguments") } + user := args[0].([]byte) + if !runtime.CheckWitness(user) { + // todo: consider something different with neoID + panic("withdraw: you should be the owner of the account") + } + + amount := args[1].(int) + if amount > 0 { + amount = amount * 100000000 + } + + runtime.Notify("Withdraw", user, amount) + + return true + case "Cheque": + if len(args) != 4 { + panic("cheque: bad arguments") + } + + id := args[0].([]byte) // unique cheque id + user := args[1].([]byte) // GAS receiver + amount := args[2].(int) // amount of GAS + lockAcc := args[3].([]byte) // lock account from internal banking that must be cashed out + + ctx := storage.GetContext() + + hashID := crypto.Hash256(id) irList := getSerialized(ctx, "InnerRingList").([]node) - if !verifySignatures(irList, data, message, offset) { - panic("can't verify signatures") + usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) + threshold := len(irList)/3*2 + 1 + + if !isInnerRingRequest(irList) { + panic("cheque: invoked by non inner ring node") } - h := owner[1:21] - params := []interface{}{from, h, value} - transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) - if !transferred { - panic("failed to transfer funds, aborting") + c := check{id: id} // todo: use different cheque id for inner ring update and withdraw + if containsCheck(usedList, c) { + panic("cheque: non unique id") } - putSerialized(ctx, "UsedVerifCheckList", c) + n := vote(ctx, hashID) + if n >= threshold { + removeVotes(ctx, hashID) + + from := engine.GetExecutingScriptHash() + params := []interface{}{from, user, amount} + + transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) + if !transferred { + panic("cheque: failed to transfer funds, aborting") + } + + putSerialized(ctx, "UsedVerifCheckList", c) + runtime.Notify("Cheque", id, user, amount, lockAcc) + } return true case "InnerRingUpdate": @@ -387,41 +421,6 @@ func delSerializedIR(ctx storage.Context, key string, value []byte) bool { runtime.Log("target element has not been removed") return false } -func verifySignatures(irList []node, data []byte, message []byte, offset int) bool { - n := len(irList) - f := (n - 1) / 3 - s := n - f - if s < 3 { - runtime.Log("not enough inner ring nodes for consensus") - return false - } - - used := [][]byte{} - count := 0 - for count < s && offset < len(data) { - pubkey := data[offset : offset+33] - signature := data[offset+33 : offset+97] - if containsPub(irList, pubkey) { - if crypto.VerifySignature(message, signature, pubkey) { - count++ - for i := 0; i < len(used); i++ { - if util.Equals(used[i], pubkey) { - panic("duplicate public keys") - } - } - used = append(used, pubkey) - } - } - offset += 97 - } - - if count >= s { - return true - } - - runtime.Log("not enough verified signatures") - return false -} // isInnerRingRequest returns true if contract was invoked by inner ring node. func isInnerRingRequest(irList []node) bool {