github.com/incognitochain/go-incognito-sdk@v1.0.1/privacy/coin.go (about)

     1  package privacy
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"math/big"
     7  	"strconv"
     8  
     9  	"github.com/incognitochain/go-incognito-sdk/common"
    10  	"github.com/incognitochain/go-incognito-sdk/common/base58"
    11  )
    12  
    13  // Coin represents a coin
    14  type Coin struct {
    15  	publicKey      *Point
    16  	coinCommitment *Point
    17  	snDerivator    *Scalar
    18  	serialNumber   *Point
    19  	randomness     *Scalar
    20  	value          uint64
    21  	info           []byte //256 bytes
    22  }
    23  
    24  // Start GET/SET
    25  func (coin Coin) GetPublicKey() *Point {
    26  	return coin.publicKey
    27  }
    28  
    29  func (coin *Coin) SetPublicKey(v *Point) {
    30  	coin.publicKey = v
    31  }
    32  
    33  func (coin Coin) GetCoinCommitment() *Point {
    34  	return coin.coinCommitment
    35  }
    36  
    37  func (coin *Coin) SetCoinCommitment(v *Point) {
    38  	coin.coinCommitment = v
    39  }
    40  
    41  func (coin Coin) GetSNDerivator() *Scalar {
    42  	return coin.snDerivator
    43  }
    44  
    45  func (coin *Coin) SetSNDerivator(v *Scalar) {
    46  	coin.snDerivator = v
    47  }
    48  
    49  func (coin Coin) GetSerialNumber() *Point {
    50  	return coin.serialNumber
    51  }
    52  
    53  func (coin *Coin) SetSerialNumber(v *Point) {
    54  	coin.serialNumber = v
    55  }
    56  
    57  func (coin Coin) GetRandomness() *Scalar {
    58  	return coin.randomness
    59  }
    60  
    61  func (coin *Coin) SetRandomness(v *Scalar) {
    62  	coin.randomness = v
    63  }
    64  
    65  func (coin Coin) GetValue() uint64 {
    66  	return coin.value
    67  }
    68  
    69  func (coin *Coin) SetValue(v uint64) {
    70  	coin.value = v
    71  }
    72  
    73  func (coin Coin) GetInfo() []byte {
    74  	return coin.info
    75  }
    76  
    77  func (coin *Coin) SetInfo(v []byte) {
    78  	coin.info = make([]byte, len(v))
    79  	copy(coin.info, v)
    80  }
    81  
    82  // Init (Coin) initializes a coin
    83  func (coin *Coin) Init() *Coin {
    84  	coin.publicKey = new(Point).Identity()
    85  
    86  	coin.coinCommitment = new(Point).Identity()
    87  
    88  	coin.snDerivator = new(Scalar).FromUint64(0)
    89  
    90  	coin.serialNumber = new(Point).Identity()
    91  
    92  	coin.randomness = new(Scalar)
    93  
    94  	coin.value = 0
    95  
    96  	return coin
    97  }
    98  
    99  // GetPubKeyLastByte returns the last byte of public key
   100  func (coin *Coin) GetPubKeyLastByte() byte {
   101  	pubKeyBytes := coin.publicKey.ToBytes()
   102  	return pubKeyBytes[Ed25519KeySize-1]
   103  }
   104  
   105  // MarshalJSON (Coin) converts coin to bytes array,
   106  // base58 check encode that bytes array into string
   107  // json.Marshal the string
   108  func (coin Coin) MarshalJSON() ([]byte, error) {
   109  	data := coin.Bytes()
   110  	temp := base58.Base58Check{}.Encode(data, common.ZeroByte)
   111  	return json.Marshal(temp)
   112  }
   113  
   114  // UnmarshalJSON (Coin) receives bytes array of coin (it was be MarshalJSON before),
   115  // json.Unmarshal the bytes array to string
   116  // base58 check decode that string to bytes array
   117  // and set bytes array to coin
   118  func (coin *Coin) UnmarshalJSON(data []byte) error {
   119  	dataStr := ""
   120  	_ = json.Unmarshal(data, &dataStr)
   121  	temp, _, err := base58.Base58Check{}.Decode(dataStr)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	coin.SetBytes(temp)
   126  	return nil
   127  }
   128  
   129  // HashH returns the SHA3-256 hashing of coin bytes array
   130  func (coin *Coin) HashH() *common.Hash {
   131  	hash := common.HashH(coin.Bytes())
   132  	return &hash
   133  }
   134  
   135  //CommitAll commits a coin with 5 attributes include:
   136  // public key, value, serial number derivator, shardID form last byte public key, randomness
   137  func (coin *Coin) CommitAll() error {
   138  	shardID := common.GetShardIDFromLastByte(coin.GetPubKeyLastByte())
   139  	values := []*Scalar{new(Scalar).FromUint64(0), new(Scalar).FromUint64(coin.value), coin.snDerivator, new(Scalar).FromUint64(uint64(shardID)), coin.randomness}
   140  	commitment, err := PedCom.commitAll(values)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	coin.coinCommitment = commitment
   145  	coin.coinCommitment.Add(coin.coinCommitment, coin.publicKey)
   146  
   147  	return nil
   148  }
   149  
   150  // Bytes converts a coin's details to a bytes array
   151  // Each fields in coin is saved in len - body format
   152  func (coin *Coin) Bytes() []byte {
   153  	var coinBytes []byte
   154  
   155  	if coin.publicKey != nil {
   156  		publicKey := coin.publicKey.ToBytesS()
   157  		coinBytes = append(coinBytes, byte(Ed25519KeySize))
   158  		coinBytes = append(coinBytes, publicKey...)
   159  	} else {
   160  		coinBytes = append(coinBytes, byte(0))
   161  	}
   162  
   163  	if coin.coinCommitment != nil {
   164  		coinCommitment := coin.coinCommitment.ToBytesS()
   165  		coinBytes = append(coinBytes, byte(Ed25519KeySize))
   166  		coinBytes = append(coinBytes, coinCommitment...)
   167  	} else {
   168  		coinBytes = append(coinBytes, byte(0))
   169  	}
   170  
   171  	if coin.snDerivator != nil {
   172  		coinBytes = append(coinBytes, byte(Ed25519KeySize))
   173  		coinBytes = append(coinBytes, coin.snDerivator.ToBytesS()...)
   174  	} else {
   175  		coinBytes = append(coinBytes, byte(0))
   176  	}
   177  
   178  	if coin.serialNumber != nil {
   179  		serialNumber := coin.serialNumber.ToBytesS()
   180  		coinBytes = append(coinBytes, byte(Ed25519KeySize))
   181  		coinBytes = append(coinBytes, serialNumber...)
   182  	} else {
   183  		coinBytes = append(coinBytes, byte(0))
   184  	}
   185  
   186  	if coin.randomness != nil {
   187  		coinBytes = append(coinBytes, byte(Ed25519KeySize))
   188  		coinBytes = append(coinBytes, coin.randomness.ToBytesS()...)
   189  	} else {
   190  		coinBytes = append(coinBytes, byte(0))
   191  	}
   192  
   193  	if coin.value > 0 {
   194  		value := new(big.Int).SetUint64(coin.value).Bytes()
   195  		coinBytes = append(coinBytes, byte(len(value)))
   196  		coinBytes = append(coinBytes, value...)
   197  	} else {
   198  		coinBytes = append(coinBytes, byte(0))
   199  	}
   200  
   201  	if len(coin.info) > 0 {
   202  		byteLengthInfo := byte(0)
   203  		if len(coin.info) > MaxSizeInfoCoin {
   204  			// only get 255 byte of info
   205  			byteLengthInfo = byte(MaxSizeInfoCoin)
   206  		} else {
   207  			lengthInfo := len(coin.info)
   208  			byteLengthInfo = byte(lengthInfo)
   209  		}
   210  		coinBytes = append(coinBytes, byteLengthInfo)
   211  		infoBytes := coin.info[0:byteLengthInfo]
   212  		coinBytes = append(coinBytes, infoBytes...)
   213  	} else {
   214  		coinBytes = append(coinBytes, byte(0))
   215  	}
   216  
   217  	return coinBytes
   218  }
   219  
   220  // SetBytes receives a coinBytes (in bytes array), and
   221  // reverts coinBytes to a Coin object
   222  func (coin *Coin) SetBytes(coinBytes []byte) error {
   223  	if len(coinBytes) == 0 {
   224  		return errors.New("coinBytes is empty")
   225  	}
   226  
   227  	offset := 0
   228  	var err error
   229  
   230  	// Parse PublicKey
   231  	lenField := coinBytes[offset]
   232  	offset++
   233  	if lenField != 0 {
   234  		if offset+int(lenField) > len(coinBytes) {
   235  			// out of range
   236  			return errors.New("out of range Parse PublicKey")
   237  		}
   238  		data := coinBytes[offset : offset+int(lenField)]
   239  		coin.publicKey, err = new(Point).FromBytesS(data)
   240  		if err != nil {
   241  			return err
   242  		}
   243  		offset += int(lenField)
   244  	}
   245  
   246  	// Parse CoinCommitment
   247  	if offset > len(coinBytes) {
   248  		// out of range
   249  		return errors.New("out of range Parse CoinCommitment")
   250  	}
   251  	lenField = coinBytes[offset]
   252  	offset++
   253  	if lenField != 0 {
   254  		if offset+int(lenField) > len(coinBytes) {
   255  			// out of range
   256  			return errors.New("out of range Parse CoinCommitment")
   257  		}
   258  		data := coinBytes[offset : offset+int(lenField)]
   259  		coin.coinCommitment, err = new(Point).FromBytesS(data)
   260  		if err != nil {
   261  			return err
   262  		}
   263  		offset += int(lenField)
   264  	}
   265  
   266  	// Parse SNDerivator
   267  	if offset > len(coinBytes) {
   268  		// out of range
   269  		return errors.New("out of range Parse SNDerivator")
   270  	}
   271  	lenField = coinBytes[offset]
   272  	offset++
   273  	if lenField != 0 {
   274  		if offset+int(lenField) > len(coinBytes) {
   275  			// out of range
   276  			return errors.New("out of range Parse SNDerivator")
   277  		}
   278  		data := coinBytes[offset : offset+int(lenField)]
   279  		coin.snDerivator = new(Scalar).FromBytesS(data)
   280  
   281  		offset += int(lenField)
   282  	}
   283  
   284  	//Parse sn
   285  	if offset > len(coinBytes) {
   286  		// out of range
   287  		return errors.New("out of range Parse sn")
   288  	}
   289  	lenField = coinBytes[offset]
   290  	offset++
   291  	if lenField != 0 {
   292  		if offset+int(lenField) > len(coinBytes) {
   293  			// out of range
   294  			return errors.New("out of range Parse sn")
   295  		}
   296  		data := coinBytes[offset : offset+int(lenField)]
   297  		coin.serialNumber, err = new(Point).FromBytesS(data)
   298  		if err != nil {
   299  			return err
   300  		}
   301  		offset += int(lenField)
   302  	}
   303  
   304  	// Parse Randomness
   305  	if offset > len(coinBytes) {
   306  		// out of range
   307  		return errors.New("out of range Parse Randomness")
   308  	}
   309  	lenField = coinBytes[offset]
   310  	offset++
   311  	if lenField != 0 {
   312  		if offset+int(lenField) > len(coinBytes) {
   313  			// out of range
   314  			return errors.New("out of range Parse Randomness")
   315  		}
   316  		data := coinBytes[offset : offset+int(lenField)]
   317  		coin.randomness = new(Scalar).FromBytesS(data)
   318  		offset += int(lenField)
   319  	}
   320  
   321  	// Parse Value
   322  	if offset > len(coinBytes) {
   323  		// out of range
   324  		return errors.New("out of range Parse PublicKey")
   325  	}
   326  	lenField = coinBytes[offset]
   327  	offset++
   328  	if lenField != 0 {
   329  		if offset+int(lenField) > len(coinBytes) {
   330  			// out of range
   331  			return errors.New("out of range Parse PublicKey")
   332  		}
   333  		coin.value = new(big.Int).SetBytes(coinBytes[offset : offset+int(lenField)]).Uint64()
   334  		offset += int(lenField)
   335  	}
   336  
   337  	// Parse Info
   338  	if offset > len(coinBytes) {
   339  		// out of range
   340  		return errors.New("out of range Parse Info")
   341  	}
   342  	lenField = coinBytes[offset]
   343  	offset++
   344  	if lenField != 0 {
   345  		if offset+int(lenField) > len(coinBytes) {
   346  			// out of range
   347  			return errors.New("out of range Parse Info")
   348  		}
   349  		coin.info = make([]byte, lenField)
   350  		copy(coin.info, coinBytes[offset:offset+int(lenField)])
   351  	}
   352  	return nil
   353  }
   354  
   355  // InputCoin represents a input coin of transaction
   356  type InputCoin struct {
   357  	CoinDetails *Coin
   358  }
   359  
   360  // Init (InputCoin) initializes a input coin
   361  func (inputCoin *InputCoin) Init() *InputCoin {
   362  	if inputCoin.CoinDetails == nil {
   363  		inputCoin.CoinDetails = new(Coin).Init()
   364  	}
   365  	return inputCoin
   366  }
   367  
   368  // Bytes (InputCoin) converts a input coin's details to a bytes array
   369  // Each fields in coin is saved in len - body format
   370  func (inputCoin *InputCoin) Bytes() []byte {
   371  	return inputCoin.CoinDetails.Bytes()
   372  }
   373  
   374  // SetBytes (InputCoin) receives a coinBytes (in bytes array), and
   375  // reverts coinBytes to a InputCoin object
   376  func (inputCoin *InputCoin) SetBytes(bytes []byte) error {
   377  	inputCoin.CoinDetails = new(Coin)
   378  	return inputCoin.CoinDetails.SetBytes(bytes)
   379  }
   380  
   381  type CoinObject struct {
   382  	PublicKey      string `json:"PublicKey"`
   383  	CoinCommitment string `json:"CoinCommitment"`
   384  	SNDerivator    string `json:"SNDerivator"`
   385  	SerialNumber   string `json:"SerialNumber"`
   386  	Randomness     string `json:"Randomness"`
   387  	Value          string `json:"Value"`
   388  	Info           string `json:"Info"`
   389  }
   390  
   391  // SetBytes (InputCoin) receives a coinBytes (in bytes array), and
   392  // reverts coinBytes to a InputCoin object
   393  func (inputCoin *InputCoin) ParseCoinObjectToInputCoin(coinObj CoinObject) error {
   394  	inputCoin.CoinDetails = new(Coin).Init()
   395  
   396  	if coinObj.PublicKey != "" {
   397  		publicKey, _, err := base58.Base58Check{}.Decode(coinObj.PublicKey)
   398  		if err != nil {
   399  			return err
   400  		}
   401  
   402  		publicKeyPoint, err := new(Point).FromBytesS(publicKey)
   403  		if err != nil {
   404  			return err
   405  		}
   406  		inputCoin.CoinDetails.SetPublicKey(publicKeyPoint)
   407  	}
   408  
   409  	if coinObj.CoinCommitment != "" {
   410  		coinCommitment, _, err := base58.Base58Check{}.Decode(coinObj.CoinCommitment)
   411  		if err != nil {
   412  			return err
   413  		}
   414  
   415  		coinCommitmentPoint, err := new(Point).FromBytesS(coinCommitment)
   416  		if err != nil {
   417  			return err
   418  		}
   419  		inputCoin.CoinDetails.SetCoinCommitment(coinCommitmentPoint)
   420  	}
   421  
   422  	if coinObj.SNDerivator != "" {
   423  		snderivator, _, err := base58.Base58Check{}.Decode(coinObj.SNDerivator)
   424  		if err != nil {
   425  			return err
   426  		}
   427  
   428  		snderivatorScalar := new(Scalar).FromBytesS(snderivator)
   429  		if err != nil {
   430  			return err
   431  		}
   432  		inputCoin.CoinDetails.SetSNDerivator(snderivatorScalar)
   433  	}
   434  
   435  	if coinObj.SerialNumber != "" {
   436  		serialNumber, _, err := base58.Base58Check{}.Decode(coinObj.SerialNumber)
   437  		if err != nil {
   438  			return err
   439  		}
   440  
   441  		serialNumberPoint, err := new(Point).FromBytesS(serialNumber)
   442  		if err != nil {
   443  			return err
   444  		}
   445  		inputCoin.CoinDetails.SetSerialNumber(serialNumberPoint)
   446  	}
   447  
   448  	if coinObj.Randomness != "" {
   449  		randomness, _, err := base58.Base58Check{}.Decode(coinObj.Randomness)
   450  		if err != nil {
   451  			return err
   452  		}
   453  
   454  		randomnessScalar := new(Scalar).FromBytesS(randomness)
   455  		if err != nil {
   456  			return err
   457  		}
   458  		inputCoin.CoinDetails.SetRandomness(randomnessScalar)
   459  	}
   460  
   461  	if coinObj.Value != "" {
   462  		value, err := strconv.ParseUint(coinObj.Value, 10, 64)
   463  		if err != nil {
   464  			return err
   465  		}
   466  		inputCoin.CoinDetails.SetValue(value)
   467  	}
   468  
   469  	if coinObj.Info != "" {
   470  		infoBytes, _, err := base58.Base58Check{}.Decode(coinObj.Info)
   471  		if err != nil {
   472  			return err
   473  		}
   474  		inputCoin.CoinDetails.SetInfo(infoBytes)
   475  	}
   476  	return nil
   477  }
   478  
   479  // OutputCoin represents a output coin of transaction
   480  // It contains CoinDetails and CoinDetailsEncrypted (encrypted value and randomness)
   481  // CoinDetailsEncrypted is nil when you send tx without privacy
   482  type OutputCoin struct {
   483  	CoinDetails          *Coin
   484  	CoinDetailsEncrypted *HybridCipherText
   485  }
   486  
   487  // Init (OutputCoin) initializes a output coin
   488  func (outputCoin *OutputCoin) Init() *OutputCoin {
   489  	outputCoin.CoinDetails = new(Coin).Init()
   490  	outputCoin.CoinDetailsEncrypted = new(HybridCipherText)
   491  	return outputCoin
   492  }
   493  
   494  // Bytes (OutputCoin) converts a output coin's details to a bytes array
   495  // Each fields in coin is saved in len - body format
   496  func (outputCoin *OutputCoin) Bytes() []byte {
   497  	var outCoinBytes []byte
   498  
   499  	if outputCoin.CoinDetailsEncrypted != nil {
   500  		coinDetailsEncryptedBytes := outputCoin.CoinDetailsEncrypted.Bytes()
   501  		outCoinBytes = append(outCoinBytes, byte(len(coinDetailsEncryptedBytes)))
   502  		outCoinBytes = append(outCoinBytes, coinDetailsEncryptedBytes...)
   503  	} else {
   504  		outCoinBytes = append(outCoinBytes, byte(0))
   505  	}
   506  
   507  	coinDetailBytes := outputCoin.CoinDetails.Bytes()
   508  
   509  	lenCoinDetailBytes := []byte{}
   510  	if len(coinDetailBytes) <= 255 {
   511  		lenCoinDetailBytes = []byte{byte(len(coinDetailBytes))}
   512  	} else {
   513  		lenCoinDetailBytes = common.IntToBytes(len(coinDetailBytes))
   514  	}
   515  
   516  	outCoinBytes = append(outCoinBytes, lenCoinDetailBytes...)
   517  	outCoinBytes = append(outCoinBytes, coinDetailBytes...)
   518  	return outCoinBytes
   519  }
   520  
   521  // SetBytes (OutputCoin) receives a coinBytes (in bytes array), and
   522  // reverts coinBytes to a OutputCoin object
   523  func (outputCoin *OutputCoin) SetBytes(bytes []byte) error {
   524  	if len(bytes) == 0 {
   525  		return errors.New("coinBytes is empty")
   526  	}
   527  
   528  	offset := 0
   529  	lenCoinDetailEncrypted := int(bytes[0])
   530  	offset += 1
   531  
   532  	if lenCoinDetailEncrypted > 0 {
   533  		if offset+lenCoinDetailEncrypted > len(bytes) {
   534  			// out of range
   535  			return errors.New("out of range Parse CoinDetailsEncrypted")
   536  		}
   537  		outputCoin.CoinDetailsEncrypted = new(HybridCipherText)
   538  		err := outputCoin.CoinDetailsEncrypted.SetBytes(bytes[offset : offset+lenCoinDetailEncrypted])
   539  		if err != nil {
   540  			return err
   541  		}
   542  		offset += lenCoinDetailEncrypted
   543  	}
   544  
   545  	// try get 1-byte for len
   546  	if offset > len(bytes) {
   547  		// out of range
   548  		return errors.New("out of range Parse CoinDetails")
   549  	}
   550  	lenOutputCoin := int(bytes[offset])
   551  	outputCoin.CoinDetails = new(Coin)
   552  	if lenOutputCoin != 0 {
   553  		offset += 1
   554  		if offset+lenOutputCoin > len(bytes) {
   555  			// out of range
   556  			return errors.New("out of range Parse output coin details")
   557  		}
   558  		err := outputCoin.CoinDetails.SetBytes(bytes[offset : offset+lenOutputCoin])
   559  		if err != nil {
   560  			// 1-byte is wrong
   561  			// try get 2-byte for len
   562  			if offset+1 > len(bytes) {
   563  				// out of range
   564  				return errors.New("out of range Parse output coin details")
   565  			}
   566  			lenOutputCoin = common.BytesToInt(bytes[offset-1 : offset+1])
   567  			offset += 1
   568  			if offset+lenOutputCoin > len(bytes) {
   569  				// out of range
   570  				return errors.New("out of range Parse output coin details")
   571  			}
   572  			err1 := outputCoin.CoinDetails.SetBytes(bytes[offset : offset+lenOutputCoin])
   573  			return err1
   574  		}
   575  	} else {
   576  		// 1-byte is wrong
   577  		// try get 2-byte for len
   578  		if offset+2 > len(bytes) {
   579  			// out of range
   580  			return errors.New("out of range Parse output coin details")
   581  		}
   582  		lenOutputCoin = common.BytesToInt(bytes[offset : offset+2])
   583  		offset += 2
   584  		if offset+lenOutputCoin > len(bytes) {
   585  			// out of range
   586  			return errors.New("out of range Parse output coin details")
   587  		}
   588  		err1 := outputCoin.CoinDetails.SetBytes(bytes[offset : offset+lenOutputCoin])
   589  		return err1
   590  	}
   591  
   592  	return nil
   593  }
   594  
   595  // Encrypt returns a ciphertext encrypting for a coin using a hybrid cryptosystem,
   596  // in which AES encryption scheme is used as a data encapsulation scheme,
   597  // and ElGamal cryptosystem is used as a key encapsulation scheme.
   598  func (outputCoin *OutputCoin) Encrypt(recipientTK TransmissionKey) *PrivacyError {
   599  	// 32-byte first: Randomness, the rest of msg is value of coin
   600  	msg := append(outputCoin.CoinDetails.randomness.ToBytesS(), new(big.Int).SetUint64(outputCoin.CoinDetails.value).Bytes()...)
   601  
   602  	pubKeyPoint, err := new(Point).FromBytesS(recipientTK)
   603  	if err != nil {
   604  		return NewPrivacyErr(EncryptOutputCoinErr, err)
   605  	}
   606  
   607  	outputCoin.CoinDetailsEncrypted, err = HybridEncrypt(msg, pubKeyPoint)
   608  	if err != nil {
   609  		return NewPrivacyErr(EncryptOutputCoinErr, err)
   610  	}
   611  
   612  	return nil
   613  }
   614  
   615  // Decrypt decrypts a ciphertext encrypting for coin with recipient's receiving key
   616  func (outputCoin *OutputCoin) Decrypt(viewingKey ViewingKey) *PrivacyError {
   617  	msg, err := HybridDecrypt(outputCoin.CoinDetailsEncrypted, new(Scalar).FromBytesS(viewingKey.Rk))
   618  	if err != nil {
   619  		return NewPrivacyErr(DecryptOutputCoinErr, err)
   620  	}
   621  
   622  	// Assign randomness and value to outputCoin details
   623  	outputCoin.CoinDetails.randomness = new(Scalar).FromBytesS(msg[0:Ed25519KeySize])
   624  	outputCoin.CoinDetails.value = new(big.Int).SetBytes(msg[Ed25519KeySize:]).Uint64()
   625  
   626  	return nil
   627  }