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 }