2020-12-19 10:01:29 +00:00
package walletapi
2021-02-22 17:48:14 +00:00
import "fmt"
2020-12-19 10:01:29 +00:00
import "math/big"
2021-02-22 17:48:14 +00:00
//import "encoding/binary"
2020-12-19 10:01:29 +00:00
import mathrand "math/rand"
import "github.com/deroproject/derohe/globals"
2021-02-22 17:48:14 +00:00
import "github.com/deroproject/derohe/rpc"
2020-12-19 10:01:29 +00:00
import "github.com/deroproject/derohe/transaction"
2021-02-22 17:48:14 +00:00
import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/cryptography/bn256"
2020-12-19 10:01:29 +00:00
// generate proof etc
2021-02-22 17:48:14 +00:00
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 {
2020-12-19 10:01:29 +00:00
var tx transaction . Transaction
2021-02-22 17:48:14 +00:00
sender := w . account . Keys . Public . G1 ( )
sender_secret := w . account . Keys . Secret . BigInt ( )
2020-12-19 10:01:29 +00:00
tx . Version = 1
tx . Height = height
tx . TransactionType = transaction . NORMAL
2021-02-22 17:48:14 +00:00
/ *
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
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
reg_tx := w . GetRegistrationTX ( )
tx . MinerAddress = reg_tx . MinerAddress
tx . S = reg_tx . S
tx . C = reg_tx . C
2020-12-19 10:01:29 +00:00
}
2021-02-22 17:48:14 +00:00
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 ( )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
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
2020-12-19 10:01:29 +00:00
}
2021-02-22 17:48:14 +00:00
//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
}
2020-12-19 10:01:29 +00:00
}
2021-02-22 17:48:14 +00:00
// 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))
}
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
// fmt.Printf("len of publickeylist %d \n", len(publickeylist))
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
// 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
2020-12-19 10:01:29 +00:00
2021-02-27 07:47:44 +00:00
fees := uint64 ( 0 )
2021-02-22 17:48:14 +00:00
value := transfers [ t ] . Amount
burn_value := transfers [ t ] . Burn
2021-02-27 07:47:44 +00:00
if asset . SCID . IsZero ( ) && ( value + burn_value ) == 0 {
fees = 1
}
2021-02-22 17:48:14 +00:00
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())
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
// lets encrypt the payment id, it's simple, we XOR the paymentID
blinder := new ( bn256 . G1 ) . ScalarMult ( publickeylist [ i ] , r )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
// we must obfuscate it for non-client call
if len ( publickeylist ) >= 512 {
panic ( "currently we donot support ring size >= 512" )
}
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
asset . RPCType = transaction . ENCRYPTED_DEFAULT_PAYLOAD_CBOR
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
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 )
2020-12-19 10:01:29 +00:00
}
2021-02-22 17:48:14 +00:00
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())
2020-12-19 10:01:29 +00:00
}
2021-02-22 17:48:14 +00:00
// decode balance now
balance := w . DecodeEncryptedBalanceNow ( new ( crypto . ElGamal ) . Deserialize ( emap [ string ( transfers [ t ] . SCID . String ( ) ) ] [ sender . String ( ) ] ) )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
//fmt.Printf("t %d scid %s balance %d\n", t, transfers[t].SCID, balance)
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
// 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 )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
witness := GenerateWitness ( sender_secret , r , value , balance - value - fees - burn_value , witness_index )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
witness_list = append ( witness_list , witness )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
// this goes to proof.u
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
//Print(statement, witness)
asset . Statement = statement
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
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
}
2020-12-19 10:01:29 +00:00
}
2021-02-22 17:48:14 +00:00
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)
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
//fmt.Printf("Statement %+v\n", tx.Payloads[t].Statement)
//fmt.Printf("Proof %+v\n", tx.Payloads[t].Proof)
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
} 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 )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
var dtx transaction . Transaction
fmt . Printf ( "err deserialing %s\n" , dtx . DeserializeHeader ( serialized ) )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
serialized = dtx . Serialize ( )
fmt . Printf ( "dtx2 serialized kength %d\n" , len ( serialized ) * 2 )
2020-12-19 10:01:29 +00:00
2021-02-22 17:48:14 +00:00
fmt . Printf ( "txhash after %s scdata %+v\n" , dtx . GetHash ( ) , tx . SCDATA )
2020-12-19 10:01:29 +00:00
* /
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 }
}