derohe-miniblock-mod/walletapi/transaction_build.go

181 lines
6.3 KiB
Go
Raw Normal View History

package walletapi
//import "fmt"
import "math/big"
import mathrand "math/rand"
import "github.com/deroproject/derohe/globals"
import "github.com/deroproject/derohe/transaction"
import "github.com/deroproject/derohe/crypto"
import "github.com/deroproject/derohe/crypto/bn256"
// generate proof etc
func BuildTransaction(sender *bn256.G1, sender_secret *big.Int, receiver *bn256.G1, sender_ebalance, receiver_ebalance *crypto.ElGamal, balance, value uint64, anonset_publickeys []*bn256.G1, anonset_ebalance []*crypto.ElGamal, fees uint64, height uint64, payment_id []byte, roothash []byte) *transaction.Transaction {
var tx transaction.Transaction
var publickeylist, C, CLn, CRn []*bn256.G1
var D bn256.G1
tx.Version = 1
tx.Height = height
tx.TransactionType = transaction.NORMAL
crand := mathrand.New(globals.NewCryptoRandSource())
var witness_index []int
for i := 0; i < 2+len(anonset_publickeys); i++ { // todocheck whether this is power of 2 or not
witness_index = append(witness_index, i)
}
//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
ebalances_list := make([]*crypto.ElGamal, 0, 2+len(anonset_publickeys))
for i := range witness_index {
switch i {
case witness_index[0]:
publickeylist = append(publickeylist, sender)
//publickeylist = append(publickeylist, new(bn256.G1).ScalarMult(crypto.G, sender_secret))
ebalances_list = append(ebalances_list, sender_ebalance)
case witness_index[1]:
publickeylist = append(publickeylist, receiver)
ebalances_list = append(ebalances_list, receiver_ebalance)
default:
publickeylist = append(publickeylist, anonset_publickeys[0])
anonset_publickeys = anonset_publickeys[1:]
ebalances_list = append(ebalances_list, anonset_ebalance[0])
anonset_ebalance = anonset_ebalance[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))
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))) // 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)
output := crypto.EncryptDecryptPaymentID(blinder, payment_id[:])
copy(tx.PaymentID[:], output[:])
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)
for i := range publickeylist {
var ebalance *crypto.ElGamal
switch {
case i == witness_index[0]:
ebalance = sender_ebalance
case i == witness_index[1]:
ebalance = receiver_ebalance
//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())
}
// time for bullets-sigma
statement := GenerateStatement(CLn, CRn, publickeylist, C, &D, fees) // generate statement
copy(statement.Roothash[:], roothash[:])
witness := GenerateWitness(sender_secret, r, value, balance-value-fees, witness_index)
// this goes to proof.u
u := new(bn256.G1).ScalarMult(crypto.HashToPoint(crypto.HashtoNumber(append([]byte(crypto.PROTOCOL_CONSTANT), statement.Roothash[:]...))), sender_secret) // this should be moved to generate proof
//Print(statement, witness)
tx.Statement = statement
tx.Proof = crypto.GenerateProof(&statement, &witness, u, tx.GetHash())
// after the tx is serialized, it loses information which is then fed by blockchain
if tx.Proof.Verify(&tx.Statement, tx.GetHash()) {
//fmt.Printf("TX verified with proof successfuly value %d\n", value)
} else {
//fmt.Printf("TX verification failed !!!!!!!!!!\n")
panic("TX verification failed !!!!!!!!!!")
}
/*
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)
*/
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}
}