package walletapi import "fmt" import "math/big" //import "encoding/binary" import mathrand "math/rand" import "github.com/deroproject/derohe/globals" import "github.com/deroproject/derohe/rpc" import "github.com/deroproject/derohe/transaction" import "github.com/deroproject/derohe/cryptography/crypto" import "github.com/deroproject/derohe/cryptography/bn256" // generate proof etc func (w *Wallet_Memory) BuildTransaction(transfers []rpc.Transfer, emap map[string]map[string][]byte, rings [][]*bn256.G1, height uint64, scdata rpc.Arguments, roothash []byte, max_bits_array []int) *transaction.Transaction { var tx transaction.Transaction sender := w.account.Keys.Public.G1() sender_secret := w.account.Keys.Secret.BigInt() tx.Version = 1 tx.Height = height tx.TransactionType = transaction.NORMAL /* if burn_value >= 1 { tx.TransactionType = transaction.BURN_TX tx.Value = burn_value } */ if len(scdata) >= 1 { tx.TransactionType = transaction.SC_TX tx.SCDATA = scdata reg_tx := w.GetRegistrationTX() tx.MinerAddress = reg_tx.MinerAddress tx.S = reg_tx.S tx.C = reg_tx.C } crand := mathrand.New(globals.NewCryptoRandSource()) var witness_list []crypto.Witness for t, _ := range transfers { var publickeylist, C, CLn, CRn []*bn256.G1 var D bn256.G1 receiver_addr, _ := rpc.NewAddress(transfers[t].Destination) receiver := receiver_addr.PublicKey.G1() var witness_index []int for i := 0; i < len(rings[t]); i++ { // todocheck whether this is power of 2 or not witness_index = append(witness_index, i) } max_bits := max_bits_array[t] for ; max_bits%8 != 0; max_bits++ { // round to next higher byte size } //witness_index[3], witness_index[1] = witness_index[1], witness_index[3] for { crand.Shuffle(len(witness_index), func(i, j int) { witness_index[i], witness_index[j] = witness_index[j], witness_index[i] }) // make sure sender and receiver are not both odd or both even // sender will always be at witness_index[0] and receiver will always be at witness_index[1] if witness_index[0]%2 != witness_index[1]%2 { break } } // Lots of ToDo for this, enables satisfying lots of other things anonset_publickeys := rings[t][2:] ebalances_list := make([]*crypto.ElGamal, 0, len(rings[t])) for i := range witness_index { switch i { case witness_index[0]: publickeylist = append(publickeylist, sender) ebalances_list = append(ebalances_list, new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][sender.String()])) case witness_index[1]: publickeylist = append(publickeylist, receiver) ebalances_list = append(ebalances_list, new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][receiver.String()])) default: publickeylist = append(publickeylist, anonset_publickeys[0]) ebalances_list = append(ebalances_list, new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][anonset_publickeys[0].String()])) anonset_publickeys = anonset_publickeys[1:] } // fmt.Printf("adding %d %s (ring count %d) \n", i,publickeylist[i].String(), len(anonset_publickeys)) } // fmt.Printf("len of publickeylist %d \n", len(publickeylist)) // revealing r will disclose the amount and the sender and receiver and separate anonymous ring members // calculate r deterministically, so its different every transaction, in emergency it can be given to other, and still will not allows key attacks rinputs := append([]byte{}, roothash[:]...) for i := range publickeylist { rinputs = append(rinputs, publickeylist[i].EncodeCompressed()...) } rencrypted := new(bn256.G1).ScalarMult(crypto.HashToPoint(crypto.HashtoNumber(append([]byte(crypto.PROTOCOL_CONSTANT), rinputs...))), sender_secret) r := crypto.ReducedHash(rencrypted.EncodeCompressed()) //r := crypto.RandomScalarFixed() //fmt.Printf("r %s\n", r.Text(16)) var asset transaction.AssetPayload asset.SCID = transfers[t].SCID asset.BurnValue = transfers[t].Burn fees := uint64(1) value := transfers[t].Amount burn_value := transfers[t].Burn for i := range publickeylist { // setup commitments var x bn256.G1 switch { case i == witness_index[0]: x.ScalarMult(crypto.G, new(big.Int).SetInt64(0-int64(value)-int64(fees)-int64(burn_value))) // decrease senders balance //fmt.Printf("sender %s \n", x.String()) case i == witness_index[1]: x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(value))) // increase receiver's balance //fmt.Printf("receiver %s \n", x.String()) // lets encrypt the payment id, it's simple, we XOR the paymentID blinder := new(bn256.G1).ScalarMult(publickeylist[i], r) // we must obfuscate it for non-client call if len(publickeylist) >= 512 { panic("currently we donot support ring size >= 512") } asset.RPCType = transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR data, _ := transfers[t].Payload_RPC.CheckPack(transaction.PAYLOAD0_LIMIT) asset.RPCPayload = append([]byte{byte(uint(witness_index[0]))}, data...) //fmt.Printf("%d packed rpc payload %d %x\n ", t, len(data), data) // make sure used data encryption is optional, just in case we would like to play together with ring members crypto.EncryptDecryptUserData(blinder, asset.RPCPayload) default: x.ScalarMult(crypto.G, new(big.Int).SetInt64(0)) } x.Add(new(bn256.G1).Set(&x), new(bn256.G1).ScalarMult(publickeylist[i], r)) // hide all commitments behind r C = append(C, &x) } D.ScalarMult(crypto.G, r) //fmt.Printf("t %d publickeylist %d\n", t, len(publickeylist)) for i := range publickeylist { var ebalance *crypto.ElGamal switch { case i == witness_index[0]: ebalance = new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][sender.String()]) case i == witness_index[1]: ebalance = new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][receiver.String()]) //fmt.Printf("receiver %s \n", x.String()) default: //x.ScalarMult(crypto.G, new(big.Int).SetInt64(0)) // panic("anon ring currently not supported") ebalance = ebalances_list[i] } var ll, rr bn256.G1 //ebalance := b.balances[publickeylist[i].String()] // note these are taken from the chain live ll.Add(ebalance.Left, C[i]) CLn = append(CLn, &ll) // fmt.Printf("%d CLnG %x\n", i,CLn[i].EncodeCompressed()) rr.Add(ebalance.Right, &D) CRn = append(CRn, &rr) // fmt.Printf("%d CRnG %x\n",i, CRn[i].EncodeCompressed()) } // decode balance now balance := w.DecodeEncryptedBalanceNow(new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][sender.String()])) //fmt.Printf("t %d scid %s balance %d\n", t, transfers[t].SCID, balance) // time for bullets-sigma statement := GenerateStatement(CLn, CRn, publickeylist, C, &D, fees) // generate statement copy(statement.Roothash[:], roothash[:]) statement.Bytes_per_publickey = byte(max_bits / 8) witness := GenerateWitness(sender_secret, r, value, balance-value-fees-burn_value, witness_index) witness_list = append(witness_list, witness) // this goes to proof.u //Print(statement, witness) asset.Statement = statement tx.Payloads = append(tx.Payloads, asset) // get ready for another round by internal processing of state for i := range publickeylist { balance := new(crypto.ElGamal).Deserialize(emap[string(transfers[t].SCID.String())][publickeylist[i].String()]) echanges := crypto.ConstructElGamal(statement.C[i], statement.D) balance = balance.Add(echanges) // homomorphic addition of changes emap[string(transfers[t].SCID.String())][publickeylist[i].String()] = balance.Serialize() // reserialize and store } } u := new(bn256.G1).ScalarMult(crypto.HashToPoint(crypto.HashtoNumber(append([]byte(crypto.PROTOCOL_CONSTANT), tx.Payloads[0].Statement.Roothash[:]...))), sender_secret) // this should be moved to generate proof for t := range transfers { tx.Payloads[t].Proof = crypto.GenerateProof(&tx.Payloads[t].Statement, &witness_list[t], u, tx.GetHash(), tx.Payloads[t].BurnValue) } // after the tx is serialized, it loses information which is then fed by blockchain //fmt.Printf("txhash before %s\n", tx.GetHash()) for t := range tx.Payloads { if tx.Payloads[t].Proof.Verify(&tx.Payloads[t].Statement, tx.GetHash(), tx.Payloads[t].BurnValue) { //fmt.Printf("TX verified with proof successfuly %s burn_value %d\n", tx.GetHash(), tx.Payloads[t].BurnValue) //fmt.Printf("Statement %+v\n", tx.Payloads[t].Statement) //fmt.Printf("Proof %+v\n", tx.Payloads[t].Proof) } else { //fmt.Printf("TX verification failed !!!!!!!!!!\n") fmt.Printf("TX verification failed, did u try sending more than you have !!!!!!!!!!\n") } } /* serialized := tx.Serialize() fmt.Printf("serialized kength %d \n", len(serialized)*2) var dtx transaction.Transaction fmt.Printf("err deserialing %s\n", dtx.DeserializeHeader(serialized)) serialized = dtx.Serialize() fmt.Printf("dtx2 serialized kength %d\n", len(serialized)*2) fmt.Printf("txhash after %s scdata %+v\n", dtx.GetHash(), tx.SCDATA) */ return &tx } // generate statement func GenerateStatement(CLn, CRn, publickeylist, C []*bn256.G1, D *bn256.G1, fees uint64) crypto.Statement { return crypto.Statement{CLn: CLn, CRn: CRn, Publickeylist: publickeylist, C: C, D: D, Fees: fees} } // generate witness func GenerateWitness(secretkey, r *big.Int, TransferAmount, Balance uint64, index []int) crypto.Witness { return crypto.Witness{SecretKey: secretkey, R: r, TransferAmount: TransferAmount, Balance: Balance, Index: index} }