github.com/incognitochain/go-incognito-sdk@v1.0.1/privacy/zkp/paymentwitness.go (about) 1 package zkp 2 3 import ( 4 "errors" 5 "github.com/incognitochain/go-incognito-sdk/common" 6 "github.com/incognitochain/go-incognito-sdk/privacy" 7 "github.com/incognitochain/go-incognito-sdk/privacy/zkp/aggregaterange" 8 "github.com/incognitochain/go-incognito-sdk/privacy/zkp/oneoutofmany" 9 "github.com/incognitochain/go-incognito-sdk/privacy/zkp/serialnumbernoprivacy" 10 "github.com/incognitochain/go-incognito-sdk/privacy/zkp/serialnumberprivacy" 11 ) 12 13 // PaymentWitness contains all of witness for proving when spending coins 14 type PaymentWitness struct { 15 privateKey *privacy.Scalar 16 inputCoins []*privacy.InputCoin 17 outputCoins []*privacy.OutputCoin 18 commitmentIndices []uint64 19 myCommitmentIndices []uint64 20 21 oneOfManyWitness []*oneoutofmany.OneOutOfManyWitness 22 serialNumberWitness []*serialnumberprivacy.SNPrivacyWitness 23 serialNumberNoPrivacyWitness []*serialnumbernoprivacy.SNNoPrivacyWitness 24 25 aggregatedRangeWitness *aggregaterange.AggregatedRangeWitness 26 27 comOutputValue []*privacy.Point 28 comOutputSerialNumberDerivator []*privacy.Point 29 comOutputShardID []*privacy.Point 30 31 comInputSecretKey *privacy.Point 32 comInputValue []*privacy.Point 33 comInputSerialNumberDerivator []*privacy.Point 34 comInputShardID *privacy.Point 35 36 randSecretKey *privacy.Scalar 37 } 38 39 func (paymentWitness PaymentWitness) GetRandSecretKey() *privacy.Scalar { 40 return paymentWitness.randSecretKey 41 } 42 43 type PaymentWitnessParam struct { 44 HasPrivacy bool 45 PrivateKey *privacy.Scalar 46 InputCoins []*privacy.InputCoin 47 OutputCoins []*privacy.OutputCoin 48 PublicKeyLastByteSender byte 49 Commitments []*privacy.Point 50 CommitmentIndices []uint64 51 MyCommitmentIndices []uint64 52 Fee uint64 53 } 54 55 // Build prepares witnesses for all protocol need to be proved when create tx 56 // if hashPrivacy = false, witness includes spending key, input coins, output coins 57 // otherwise, witness includes all attributes in PaymentWitness struct 58 func (wit *PaymentWitness) Init(PaymentWitnessParam PaymentWitnessParam) *privacy.PrivacyError { 59 60 hasPrivacy := PaymentWitnessParam.HasPrivacy 61 privateKey := PaymentWitnessParam.PrivateKey 62 inputCoins := PaymentWitnessParam.InputCoins 63 outputCoins := PaymentWitnessParam.OutputCoins 64 publicKeyLastByteSender := PaymentWitnessParam.PublicKeyLastByteSender 65 commitments := PaymentWitnessParam.Commitments 66 commitmentIndices := PaymentWitnessParam.CommitmentIndices 67 myCommitmentIndices := PaymentWitnessParam.MyCommitmentIndices 68 _ = PaymentWitnessParam.Fee 69 70 if !hasPrivacy { 71 for _, outCoin := range outputCoins { 72 outCoin.CoinDetails.SetRandomness(privacy.RandomScalar()) 73 err := outCoin.CoinDetails.CommitAll() 74 if err != nil { 75 return privacy.NewPrivacyErr(privacy.CommitNewOutputCoinNoPrivacyErr, nil) 76 } 77 } 78 wit.privateKey = privateKey 79 wit.inputCoins = inputCoins 80 wit.outputCoins = outputCoins 81 82 if len(inputCoins) > 0 { 83 publicKey := inputCoins[0].CoinDetails.GetPublicKey() 84 85 wit.serialNumberNoPrivacyWitness = make([]*serialnumbernoprivacy.SNNoPrivacyWitness, len(inputCoins)) 86 for i := 0; i < len(inputCoins); i++ { 87 /***** Build witness for proving that serial number is derived from the committed derivator *****/ 88 if wit.serialNumberNoPrivacyWitness[i] == nil { 89 wit.serialNumberNoPrivacyWitness[i] = new(serialnumbernoprivacy.SNNoPrivacyWitness) 90 } 91 wit.serialNumberNoPrivacyWitness[i].Set(inputCoins[i].CoinDetails.GetSerialNumber(), publicKey, inputCoins[i].CoinDetails.GetSNDerivator(), wit.privateKey) 92 } 93 } 94 95 return nil 96 } 97 98 wit.privateKey = privateKey 99 wit.inputCoins = inputCoins 100 wit.outputCoins = outputCoins 101 wit.commitmentIndices = commitmentIndices 102 wit.myCommitmentIndices = myCommitmentIndices 103 104 numInputCoin := len(wit.inputCoins) 105 numOutputCoin := len(wit.outputCoins) 106 107 randInputSK := privacy.RandomScalar() 108 // set rand sk for Schnorr signature 109 wit.randSecretKey = new(privacy.Scalar).Set(randInputSK) 110 111 cmInputSK := privacy.PedCom.CommitAtIndex(wit.privateKey, randInputSK, privacy.PedersenPrivateKeyIndex) 112 wit.comInputSecretKey = new(privacy.Point).Set(cmInputSK) 113 114 // from BCHeightBreakPointFixRandShardCM, we fixed the randomness for shardID commitment 115 // instead of generating it randomly. 116 //randInputShardID := privacy.RandomScalar() 117 randInputShardID := privacy.FixedRandomnessShardID 118 senderShardID := common.GetShardIDFromLastByte(publicKeyLastByteSender) 119 wit.comInputShardID = privacy.PedCom.CommitAtIndex(new(privacy.Scalar).FromUint64(uint64(senderShardID)), randInputShardID, privacy.PedersenShardIDIndex) 120 121 wit.comInputValue = make([]*privacy.Point, numInputCoin) 122 wit.comInputSerialNumberDerivator = make([]*privacy.Point, numInputCoin) 123 // It is used for proving 2 commitments commit to the same value (input) 124 //cmInputSNDIndexSK := make([]*privacy.Point, numInputCoin) 125 126 randInputValue := make([]*privacy.Scalar, numInputCoin) 127 randInputSND := make([]*privacy.Scalar, numInputCoin) 128 //randInputSNDIndexSK := make([]*big.Int, numInputCoin) 129 130 // cmInputValueAll is sum of all input coins' value commitments 131 cmInputValueAll := new(privacy.Point).Identity() 132 randInputValueAll := new(privacy.Scalar).FromUint64(0) 133 134 // Summing all commitments of each input coin into one commitment and proving the knowledge of its Openings 135 cmInputSum := make([]*privacy.Point, numInputCoin) 136 randInputSum := make([]*privacy.Scalar, numInputCoin) 137 // randInputSumAll is sum of all randomess of coin commitments 138 randInputSumAll := new(privacy.Scalar).FromUint64(0) 139 140 wit.oneOfManyWitness = make([]*oneoutofmany.OneOutOfManyWitness, numInputCoin) 141 wit.serialNumberWitness = make([]*serialnumberprivacy.SNPrivacyWitness, numInputCoin) 142 143 commitmentTemps := make([][]*privacy.Point, numInputCoin) 144 randInputIsZero := make([]*privacy.Scalar, numInputCoin) 145 146 preIndex := 0 147 148 for i, inputCoin := range wit.inputCoins { 149 // tx only has fee, no output, Rand_Value_Input = 0 150 if numOutputCoin == 0 { 151 randInputValue[i] = new(privacy.Scalar).FromUint64(0) 152 } else { 153 randInputValue[i] = privacy.RandomScalar() 154 } 155 // commit each component of coin commitment 156 randInputSND[i] = privacy.RandomScalar() 157 158 wit.comInputValue[i] = privacy.PedCom.CommitAtIndex(new(privacy.Scalar).FromUint64(inputCoin.CoinDetails.GetValue()), randInputValue[i], privacy.PedersenValueIndex) 159 wit.comInputSerialNumberDerivator[i] = privacy.PedCom.CommitAtIndex(inputCoin.CoinDetails.GetSNDerivator(), randInputSND[i], privacy.PedersenSndIndex) 160 161 cmInputValueAll.Add(cmInputValueAll, wit.comInputValue[i]) 162 randInputValueAll.Add(randInputValueAll, randInputValue[i]) 163 164 /***** Build witness for proving one-out-of-N commitments is a commitment to the coins being spent *****/ 165 cmInputSum[i] = new(privacy.Point).Add(cmInputSK, wit.comInputValue[i]) 166 cmInputSum[i].Add(cmInputSum[i], wit.comInputSerialNumberDerivator[i]) 167 cmInputSum[i].Add(cmInputSum[i], wit.comInputShardID) 168 169 randInputSum[i] = new(privacy.Scalar).Set(randInputSK) 170 randInputSum[i].Add(randInputSum[i], randInputValue[i]) 171 randInputSum[i].Add(randInputSum[i], randInputSND[i]) 172 randInputSum[i].Add(randInputSum[i], randInputShardID) 173 174 randInputSumAll.Add(randInputSumAll, randInputSum[i]) 175 176 // commitmentTemps is a list of commitments for protocol one-out-of-N 177 commitmentTemps[i] = make([]*privacy.Point, privacy.CommitmentRingSize) 178 179 randInputIsZero[i] = new(privacy.Scalar).FromUint64(0) 180 randInputIsZero[i].Sub(inputCoin.CoinDetails.GetRandomness(), randInputSum[i]) 181 182 for j := 0; j < privacy.CommitmentRingSize; j++ { 183 commitmentTemps[i][j] = new(privacy.Point).Sub(commitments[preIndex+j], cmInputSum[i]) 184 } 185 186 if wit.oneOfManyWitness[i] == nil { 187 wit.oneOfManyWitness[i] = new(oneoutofmany.OneOutOfManyWitness) 188 } 189 indexIsZero := myCommitmentIndices[i] % privacy.CommitmentRingSize 190 191 wit.oneOfManyWitness[i].Set(commitmentTemps[i], randInputIsZero[i], indexIsZero) 192 preIndex = privacy.CommitmentRingSize * (i + 1) 193 // --------------------------------------------------- 194 195 /***** Build witness for proving that serial number is derived from the committed derivator *****/ 196 if wit.serialNumberWitness[i] == nil { 197 wit.serialNumberWitness[i] = new(serialnumberprivacy.SNPrivacyWitness) 198 } 199 stmt := new(serialnumberprivacy.SerialNumberPrivacyStatement) 200 stmt.Set(inputCoin.CoinDetails.GetSerialNumber(), cmInputSK, wit.comInputSerialNumberDerivator[i]) 201 wit.serialNumberWitness[i].Set(stmt, privateKey, randInputSK, inputCoin.CoinDetails.GetSNDerivator(), randInputSND[i]) 202 // --------------------------------------------------- 203 } 204 205 randOutputValue := make([]*privacy.Scalar, numOutputCoin) 206 randOutputSND := make([]*privacy.Scalar, numOutputCoin) 207 cmOutputValue := make([]*privacy.Point, numOutputCoin) 208 cmOutputSND := make([]*privacy.Point, numOutputCoin) 209 210 cmOutputSum := make([]*privacy.Point, numOutputCoin) 211 randOutputSum := make([]*privacy.Scalar, numOutputCoin) 212 213 cmOutputSumAll := new(privacy.Point).Identity() 214 215 // cmOutputValueAll is sum of all value coin commitments 216 cmOutputValueAll := new(privacy.Point).Identity() 217 218 randOutputValueAll := new(privacy.Scalar).FromUint64(0) 219 220 randOutputShardID := make([]*privacy.Scalar, numOutputCoin) 221 cmOutputShardID := make([]*privacy.Point, numOutputCoin) 222 223 for i, outputCoin := range wit.outputCoins { 224 if i == len(outputCoins)-1 { 225 randOutputValue[i] = new(privacy.Scalar).Sub(randInputValueAll, randOutputValueAll) 226 } else { 227 randOutputValue[i] = privacy.RandomScalar() 228 } 229 230 randOutputSND[i] = privacy.RandomScalar() 231 randOutputShardID[i] = privacy.RandomScalar() 232 233 cmOutputValue[i] = privacy.PedCom.CommitAtIndex(new(privacy.Scalar).FromUint64(outputCoin.CoinDetails.GetValue()), randOutputValue[i], privacy.PedersenValueIndex) 234 cmOutputSND[i] = privacy.PedCom.CommitAtIndex(outputCoin.CoinDetails.GetSNDerivator(), randOutputSND[i], privacy.PedersenSndIndex) 235 236 receiverShardID := common.GetShardIDFromLastByte(outputCoins[i].CoinDetails.GetPubKeyLastByte()) 237 cmOutputShardID[i] = privacy.PedCom.CommitAtIndex(new(privacy.Scalar).FromUint64(uint64(receiverShardID)), randOutputShardID[i], privacy.PedersenShardIDIndex) 238 239 randOutputSum[i] = new(privacy.Scalar).FromUint64(0) 240 randOutputSum[i].Add(randOutputValue[i], randOutputSND[i]) 241 randOutputSum[i].Add(randOutputSum[i], randOutputShardID[i]) 242 243 cmOutputSum[i] = new(privacy.Point).Identity() 244 cmOutputSum[i].Add(cmOutputValue[i], cmOutputSND[i]) 245 cmOutputSum[i].Add(cmOutputSum[i], outputCoins[i].CoinDetails.GetPublicKey()) 246 cmOutputSum[i].Add(cmOutputSum[i], cmOutputShardID[i]) 247 248 cmOutputValueAll.Add(cmOutputValueAll, cmOutputValue[i]) 249 randOutputValueAll.Add(randOutputValueAll, randOutputValue[i]) 250 251 // calculate final commitment for output coins 252 outputCoins[i].CoinDetails.SetCoinCommitment(cmOutputSum[i]) 253 outputCoins[i].CoinDetails.SetRandomness(randOutputSum[i]) 254 255 cmOutputSumAll.Add(cmOutputSumAll, cmOutputSum[i]) 256 } 257 258 // For Multi Range Protocol 259 // proving each output value is less than vmax 260 // proving sum of output values is less than vmax 261 outputValue := make([]uint64, numOutputCoin) 262 for i := 0; i < numOutputCoin; i++ { 263 if outputCoins[i].CoinDetails.GetValue() > 0 { 264 outputValue[i] = outputCoins[i].CoinDetails.GetValue() 265 } else { 266 return privacy.NewPrivacyErr(privacy.UnexpectedErr, errors.New("output coin's value is less than 0")) 267 } 268 } 269 if wit.aggregatedRangeWitness == nil { 270 wit.aggregatedRangeWitness = new(aggregaterange.AggregatedRangeWitness) 271 } 272 wit.aggregatedRangeWitness.Set(outputValue, randOutputValue) 273 // --------------------------------------------------- 274 275 // save partial commitments (value, input, shardID) 276 wit.comOutputValue = cmOutputValue 277 wit.comOutputSerialNumberDerivator = cmOutputSND 278 wit.comOutputShardID = cmOutputShardID 279 280 return nil 281 } 282 283 // Prove creates big proof 284 func (wit *PaymentWitness) Prove(hasPrivacy bool) (*PaymentProof, *privacy.PrivacyError) { 285 proof := new(PaymentProof) 286 proof.Init() 287 288 proof.inputCoins = wit.inputCoins 289 proof.outputCoins = wit.outputCoins 290 proof.commitmentOutputValue = wit.comOutputValue 291 proof.commitmentOutputSND = wit.comOutputSerialNumberDerivator 292 proof.commitmentOutputShardID = wit.comOutputShardID 293 294 proof.commitmentInputSecretKey = wit.comInputSecretKey 295 proof.commitmentInputValue = wit.comInputValue 296 proof.commitmentInputSND = wit.comInputSerialNumberDerivator 297 proof.commitmentInputShardID = wit.comInputShardID 298 proof.commitmentIndices = wit.commitmentIndices 299 300 // if hasPrivacy == false, don't need to create the zero knowledge proof 301 // proving user has spending key corresponding with public key in input coins 302 // is proved by signing with spending key 303 if !hasPrivacy { 304 // Proving that serial number is derived from the committed derivator 305 for i := 0; i < len(wit.inputCoins); i++ { 306 snNoPrivacyProof, err := wit.serialNumberNoPrivacyWitness[i].Prove(nil) 307 if err != nil { 308 return nil, privacy.NewPrivacyErr(privacy.ProveSerialNumberNoPrivacyErr, err) 309 } 310 proof.serialNumberNoPrivacyProof = append(proof.serialNumberNoPrivacyProof, snNoPrivacyProof) 311 } 312 return proof, nil 313 } 314 315 // if hasPrivacy == true 316 numInputCoins := len(wit.oneOfManyWitness) 317 318 for i := 0; i < numInputCoins; i++ { 319 // Proving one-out-of-N commitments is a commitment to the coins being spent 320 oneOfManyProof, err := wit.oneOfManyWitness[i].Prove() 321 if err != nil { 322 return nil, privacy.NewPrivacyErr(privacy.ProveOneOutOfManyErr, err) 323 } 324 proof.oneOfManyProof = append(proof.oneOfManyProof, oneOfManyProof) 325 326 // Proving that serial number is derived from the committed derivator 327 serialNumberProof, err := wit.serialNumberWitness[i].Prove(nil) 328 if err != nil { 329 return nil, privacy.NewPrivacyErr(privacy.ProveSerialNumberPrivacyErr, err) 330 } 331 proof.serialNumberProof = append(proof.serialNumberProof, serialNumberProof) 332 } 333 var err error 334 335 // Proving that each output values and sum of them does not exceed v_max 336 proof.aggregatedRangeProof, err = wit.aggregatedRangeWitness.Prove() 337 if err != nil { 338 return nil, privacy.NewPrivacyErr(privacy.ProveAggregatedRangeErr, err) 339 } 340 341 if len(proof.inputCoins) == 0 { 342 proof.commitmentIndices = nil 343 proof.commitmentInputSecretKey = nil 344 proof.commitmentInputShardID = nil 345 proof.commitmentInputSND = nil 346 proof.commitmentInputValue = nil 347 } 348 349 if len(proof.outputCoins) == 0 { 350 proof.commitmentOutputValue = nil 351 proof.commitmentOutputSND = nil 352 proof.commitmentOutputShardID = nil 353 } 354 355 return proof, nil 356 }