/
transfer.go
235 lines (195 loc) · 8 KB
/
transfer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"github.com/consensys/gnark/frontend"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/types"
mixTy "github.com/33cn/plugin/plugin/dapp/mix/types"
"github.com/consensys/gnark-crypto/ecc/bn254/twistededwards"
dbm "github.com/33cn/chain33/common/db"
"github.com/pkg/errors"
)
/*
1. verify(zk-proof)
2. check if exist in authorize pool and nullifier pool
*/
func transferInput(cfg *types.Chain33Config, db dbm.KV, execer, symbol string, proof *mixTy.ZkProofInfo) (*mixTy.TransferInputCircuit, error) {
var input mixTy.TransferInputCircuit
err := mixTy.ConstructCircuitPubInput(proof.PublicInput, &input)
if err != nil {
return nil, errors.Wrapf(err, "decode string=%s", proof.PublicInput)
}
treeRootHash := frontend.FromInterface(frontend.GetAssignedValue(input.TreeRootHash))
nullifierHash := frontend.FromInterface(frontend.GetAssignedValue(input.NullifierHash))
authSpendHash := frontend.FromInterface(frontend.GetAssignedValue(input.AuthorizeSpendHash))
err = spendVerify(db, execer, symbol, treeRootHash.String(), nullifierHash.String(), authSpendHash.String())
if err != nil {
return nil, errors.Wrap(err, "transferInput verify spendVerify")
}
//确保用户使用的和链配置的一致,不能私自篡改
conf := types.ConfSub(cfg, mixTy.MixX)
pointHX := conf.GStr("pointHX")
pointHY := conf.GStr("pointHY")
inputHX := frontend.FromInterface(frontend.GetAssignedValue(input.ShieldPointHX))
inputHY := frontend.FromInterface(frontend.GetAssignedValue(input.ShieldPointHY))
if pointHX != inputHX.String() || pointHY != inputHY.String() {
return nil, errors.Wrapf(types.ErrInvalidParam, "input circuit H point=%s-%s not match config", inputHX.String(), inputHY.String())
}
err = zkProofVerify(db, proof, mixTy.VerifyType_TRANSFERINPUT)
if err != nil {
return nil, errors.Wrap(err, "transferInput verify proof verify")
}
return &input, nil
}
/*
1. verify(zk-proof)
2. check if exist in authorize pool and nullifier pool
*/
func transferOutputVerify(cfg *types.Chain33Config, db dbm.KV, proof *mixTy.ZkProofInfo) (*mixTy.TransferOutputCircuit, error) {
var input mixTy.TransferOutputCircuit
err := mixTy.ConstructCircuitPubInput(proof.PublicInput, &input)
if err != nil {
return nil, errors.Wrapf(err, "decode string=%s", proof.PublicInput)
}
//确保用户使用的和链配置的一致,不能私自篡改
conf := types.ConfSub(cfg, mixTy.MixX)
pointHX := conf.GStr("pointHX")
pointHY := conf.GStr("pointHY")
inputHX := frontend.FromInterface(frontend.GetAssignedValue(input.ShieldPointHX))
inputHY := frontend.FromInterface(frontend.GetAssignedValue(input.ShieldPointHY))
if pointHX != inputHX.String() || pointHY != inputHY.String() {
return nil, errors.Wrapf(types.ErrInvalidParam, "output circuit H point=%s-%s not match config", inputHX.String(), inputHY.String())
}
err = zkProofVerify(db, proof, mixTy.VerifyType_TRANSFEROUTPUT)
if err != nil {
return nil, errors.Wrap(err, "Output verify proof verify")
}
return &input, nil
}
func VerifyCommitValues(inputs []*mixTy.TransferInputCircuit, outputs []*mixTy.TransferOutputCircuit, txFee uint64) bool {
var inputPoints, outputPoints []*twistededwards.PointAffine
for _, in := range inputs {
var p twistededwards.PointAffine
p.X.SetInterface(frontend.GetAssignedValue(in.ShieldAmountX))
p.Y.SetInterface(frontend.GetAssignedValue(in.ShieldAmountY))
inputPoints = append(inputPoints, &p)
}
for _, out := range outputs {
var p twistededwards.PointAffine
p.X.SetInterface(frontend.GetAssignedValue(out.ShieldAmountX))
p.Y.SetInterface(frontend.GetAssignedValue(out.ShieldAmountY))
outputPoints = append(outputPoints, &p)
}
//out value add fee
//对于平行链来说, 隐私交易需要一个公共账户扣主链的手续费,隐私交易只需要扣平行链执行器内的费用即可
//由于平行链的隐私交易没有实际扣平行链mix合约的手续费,平行链Mix合约会有手续费留下,平行链隐私可以考虑手续费为0
outputPoints = append(outputPoints, mixTy.MulCurvePointG(txFee))
//sum input and output
sumInput := inputPoints[0]
sumOutput := outputPoints[0]
for _, p := range inputPoints[1:] {
sumInput.Add(sumInput, p)
}
for _, p := range outputPoints[1:] {
sumOutput.Add(sumOutput, p)
}
if sumInput.X.Equal(&sumOutput.X) && sumInput.Y.Equal(&sumOutput.Y) {
return true
}
return false
}
func MixTransferInfoVerify(cfg *types.Chain33Config, db dbm.KV, transfer *mixTy.MixTransferAction) ([]*mixTy.TransferInputCircuit, []*mixTy.TransferOutputCircuit, error) {
var inputs []*mixTy.TransferInputCircuit
var outputs []*mixTy.TransferOutputCircuit
execer, symbol := mixTy.GetAssetExecSymbol(cfg, transfer.AssetExec, transfer.AssetSymbol)
txFee := mixTy.GetTransferTxFee(cfg, execer)
//inputs
for _, i := range transfer.Inputs {
in, err := transferInput(cfg, db, execer, symbol, i)
if err != nil {
return nil, nil, err
}
inputs = append(inputs, in)
}
//output
out, err := transferOutputVerify(cfg, db, transfer.Output)
if err != nil {
return nil, nil, err
}
outputs = append(outputs, out)
//change
change, err := transferOutputVerify(cfg, db, transfer.Change)
if err != nil {
return nil, nil, err
}
outputs = append(outputs, change)
if !VerifyCommitValues(inputs, outputs, uint64(txFee)) {
return nil, nil, errors.Wrap(mixTy.ErrSpendInOutValueNotMatch, "verify shieldValue")
}
return inputs, outputs, nil
}
//1. 如果
func (a *action) processTransferFee(exec, symbol string) (*types.Receipt, error) {
cfg := a.api.GetConfig()
accoutDb, err := createAccount(cfg, exec, symbol, a.db)
if err != nil {
return nil, err
}
txFee := mixTy.GetTransferTxFee(cfg, exec)
execAddr := address.ExecAddress(string(a.tx.Execer))
//需要mix执行器下的mix账户扣fee, 和mix 扣coins或token手续费保持一致,不然会看到mix的coins账户下和mix的mix账户下不一致
accFrom := accoutDb.LoadExecAccount(execAddr, execAddr)
if accFrom.GetBalance()-txFee >= 0 {
copyfrom := *accFrom
accFrom.Balance = accFrom.GetBalance() - txFee
receiptBalance := &types.ReceiptAccountTransfer{Prev: ©from, Current: accFrom}
set := accoutDb.GetExecKVSet(execAddr, accFrom)
feelog := &types.ReceiptLog{Ty: types.TyLogFee, Log: types.Encode(receiptBalance)}
return &types.Receipt{
Ty: types.ExecOk,
KV: set,
Logs: append([]*types.ReceiptLog{}, feelog),
}, nil
}
return nil, types.ErrNoBalance
}
/*
1. verify(zk-proof, sum value of spend and new commits)
2. check if exist in authorize pool and nullifier pool
3. add nullifier to pool
*/
func (a *action) Transfer(transfer *mixTy.MixTransferAction) (*types.Receipt, error) {
inputs, outputs, err := MixTransferInfoVerify(a.api.GetConfig(), a.db, transfer)
if err != nil {
return nil, errors.Wrap(err, "Transfer.MixTransferInfoVerify")
}
receipt := &types.Receipt{Ty: types.ExecOk}
execer, symbol := mixTy.GetAssetExecSymbol(a.api.GetConfig(), transfer.AssetExec, transfer.AssetSymbol)
//扣除交易费
rTxFee, err := a.processTransferFee(execer, symbol)
if err != nil {
return nil, errors.Wrapf(err, "processTransferFee fail")
}
mergeReceipt(receipt, rTxFee)
for _, k := range inputs {
nullHash := frontend.FromInterface(frontend.GetAssignedValue(k.NullifierHash))
r := makeNullifierSetReceipt(nullHash.String(), &mixTy.ExistValue{Nullifier: nullHash.String(), Exist: true})
mergeReceipt(receipt, r)
}
//push new commit to merkle tree
var leaves [][]byte
for _, h := range outputs {
noteHash := frontend.FromInterface(frontend.GetAssignedValue(h.NoteHash))
leaves = append(leaves, mixTy.Str2Byte(noteHash.String()))
}
conf := types.ConfSub(a.api.GetConfig(), mixTy.MixX)
maxTreeLeaves := conf.GInt("maxTreeLeaves")
rpt, err := pushTree(a.db, execer, symbol, leaves, int32(maxTreeLeaves))
if err != nil {
return nil, errors.Wrap(err, "transfer.pushTree")
}
mergeReceipt(receipt, rpt)
return receipt, nil
}