github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/proof/proof.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package proof
    18  
    19  import "fmt"
    20  import "encoding/hex"
    21  
    22  import "github.com/deroproject/derosuite/crypto"
    23  import "github.com/deroproject/derosuite/address"
    24  import "github.com/deroproject/derosuite/transaction"
    25  import "github.com/deroproject/derosuite/crypto/ringct"
    26  //import "github.com/deroproject/derosuite/walletapi" // to decode encrypted payment ID
    27   
    28  
    29  // used to encrypt payment id
    30  const ENCRYPTED_PAYMENT_ID_TAIL = 0x8d
    31  
    32  // this function is used to encrypt/decrypt payment id
    33  // as the operation is symmetric XOR, is the same in both direction
    34  func EncryptDecryptPaymentID(derivation crypto.Key, tx_public crypto.Key, input []byte) (output []byte) {
    35  	// input must be exactly 8 bytes long
    36  	if len(input) != 8 {
    37  		panic("Encrypted payment ID must be exactly 8 bytes long")
    38  	}
    39  
    40  	var tmp_buf [33]byte
    41  	copy(tmp_buf[:], derivation[:]) // copy derivation key to buffer
    42  	tmp_buf[32] = ENCRYPTED_PAYMENT_ID_TAIL
    43  
    44  	// take hash
    45  	hash := crypto.Keccak256(tmp_buf[:]) // take hash of entire 33 bytes, 32 bytes derivation key, 1 byte tail
    46  
    47  	output = make([]byte, 8, 8)
    48  	for i := range input {
    49  		output[i] = input[i] ^ hash[i] // xor the bytes with the hash
    50  	}
    51  
    52  	return
    53  }
    54  
    55  // this function will prove detect and decode output amount for the tx
    56  func Prove(input_key string, input_addr string, input_tx string) (indexes []uint64, amounts []uint64, payids [][]byte, err error) {
    57  	var tx_secret_key crypto.Key
    58  	var tx transaction.Transaction
    59  
    60  	if len(input_key) != 64 {
    61  		err = fmt.Errorf("Invalid input key size")
    62  		return
    63  	}
    64  
    65  	tx_secret_key_raw, err := hex.DecodeString(input_key)
    66  	if err != nil {
    67  		return
    68  	}
    69  	copy(tx_secret_key[:], tx_secret_key_raw[:32])
    70  
    71  	addr, err := address.NewAddress(input_addr)
    72  	if err != nil {
    73  		return
    74  	}
    75  
    76  	tx_hex, err := hex.DecodeString(input_tx)
    77  	if err != nil {
    78  		return
    79  	}
    80  
    81  	err = tx.DeserializeHeader(tx_hex)
    82  	if err != nil {
    83  		return
    84  	}
    85  
    86  	// okay all inputs have been parsed
    87  
    88  	switch tx.RctSignature.Get_Sig_Type() {
    89  	case 0: // miner tx, for miner tx we can only prove that the output belongs to address, TODO
    90  		//fmt.Printf("TX is coinbase and does NOT have encrypted OUTPUTS\n")
    91  		err = fmt.Errorf("TX is coinbase and does NOT have encrypted OUTPUTS")
    92  		return
    93  
    94  	}
    95  	
    96  	var PayID8  []byte
    97  	
    98  	if tx.Parse_Extra() {
    99              // will decrypt payment ID if encrypted 
   100  		if _, ok := tx.PaymentID_map[transaction.TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID]; ok {
   101  			PayID8 = tx.PaymentID_map[transaction.TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID].([]byte)
   102  		}
   103  
   104  	}
   105  
   106  	// ringct full/simple tx come here
   107  	derivation := crypto.KeyDerivation(&addr.ViewKey, &tx_secret_key) // keyderivation using output address
   108  	found := false
   109  
   110  	// Vout can be only specific type rest all make th fail case
   111  	for i := 0; i < len(tx.Vout); i++ {
   112  		index_within_tx := i
   113  
   114  		ehphermal_public_key := derivation.KeyDerivation_To_PublicKey(uint64(index_within_tx), addr.SpendKey)
   115  
   116  		if ehphermal_public_key == tx.Vout[i].Target.(transaction.Txout_to_key).Key {
   117                          found = true
   118  			//fmt.Printf("Output at index %d belongs to  %s\n",i,addr.String())
   119  			indexes = append(indexes, uint64(index_within_tx))
   120  
   121  			// we must decode output amounts also
   122  			scalar_key := *(derivation.KeyDerivationToScalar(uint64(index_within_tx)))
   123  
   124  			mask := tx.RctSignature.OutPk[i].Mask
   125  
   126  			ECDHTuple := tx.RctSignature.ECdhInfo[i]
   127  
   128  			amount, _, result := ringct.Decode_Amount(ECDHTuple, scalar_key, mask)
   129  			if result {
   130  				// fmt.Printf("Amount is ~ %0.8f\n", float64(amount)/(1000000000000.0))
   131  				amounts = append(amounts, amount)
   132                                  
   133                              
   134                              if len(PayID8) == 8 {
   135                                   decrypted_pay_id := EncryptDecryptPaymentID(derivation,scalar_key,PayID8)
   136                                   payids = append(payids,decrypted_pay_id)                                
   137                              }
   138  			
   139  			}else{
   140                              err = fmt.Errorf("TX belongs to user but amount could NOT be decoded")
   141                              return
   142                          }
   143                          
   144  		}
   145  
   146  	}
   147  
   148  	_ = found
   149  	/*if found {
   150  	      fmt.Printf("Found outputs\n")
   151  	  }else{
   152  	      fmt.Printf("Outputs do not belong to this address\n")
   153  	  }*/
   154          
   155          if !found{
   156              err = fmt.Errorf("Wrong TX Key or wrong address or Outputs do not belong to this address")
   157              
   158          }
   159  
   160  	return
   161  
   162  }