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  }