package wallet import ( "unsafe" "gitlab.33.cn/chain33/chain33/common" privacy "gitlab.33.cn/chain33/plugin/dapp/privacy/crypto" privacytypes "gitlab.33.cn/chain33/plugin/dapp/privacy/types" "gitlab.33.cn/chain33/chain33/types" ) func checkAmountValid(amount int64) bool { if amount <= 0 { return false } // 隐私交易中,交易金额必须是types.Coin的整数倍 // 后续调整了隐私交易中手续费计算以后需要修改 if (int64(float64(amount)/float64(types.Coin)) * types.Coin) != amount { return false } return true } func makeViewSpendPubKeyPairToString(viewPubKey, spendPubKey []byte) string { pair := viewPubKey pair = append(pair, spendPubKey...) return common.Bytes2Hex(pair) } //将amount切分为1,2,5的组合,这样在进行amount混淆的时候就能够方便获取相同额度的utxo func decomAmount2Nature(amount int64, order int64) []int64 { res := make([]int64, 0) if order == 0 { return res } mul := amount / order switch mul { case 3: res = append(res, order) res = append(res, 2*order) case 4: res = append(res, 2*order) res = append(res, 2*order) case 6: res = append(res, 5*order) res = append(res, order) case 7: res = append(res, 5*order) res = append(res, 2*order) case 8: res = append(res, 5*order) res = append(res, 2*order) res = append(res, 1*order) case 9: res = append(res, 5*order) res = append(res, 2*order) res = append(res, 2*order) default: res = append(res, mul*order) return res } return res } // 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold //res:[455827, 7000000, 80000000, 300000000, 2000000000, 60000000000] func decomposeAmount2digits(amount, dust_threshold int64) []int64 { res := make([]int64, 0) if 0 >= amount { return res } is_dust_handled := false var dust int64 = 0 var order int64 = 1 var chunk int64 = 0 for 0 != amount { chunk = (amount % 10) * order amount /= 10 order *= 10 if dust+chunk < dust_threshold { dust += chunk //累加小数,直到超过dust_threshold为止 } else { if !is_dust_handled && 0 != dust { //1st 正常情况下,先把dust保存下来 res = append(res, dust) is_dust_handled = true } if 0 != chunk { //2nd 然后依次将大的整数额度进行保存 goodAmount := decomAmount2Nature(chunk, order/10) res = append(res, goodAmount...) } } } //如果需要被拆分的额度 < dust_threshold,则直接将其进行保存 if !is_dust_handled && 0 != dust { res = append(res, dust) } return res } func parseViewSpendPubKeyPair(in string) (viewPubKey, spendPubKey []byte, err error) { src, err := common.FromHex(in) if err != nil { return nil, nil, err } if 64 != len(src) { bizlog.Error("parseViewSpendPubKeyPair", "pair with len", len(src)) return nil, nil, types.ErrPubKeyLen } viewPubKey = src[:32] spendPubKey = src[32:] return } // genCustomOuts 构建一个交易的输出 // 构建方式是,P=Hs(rA)G+B //func genCustomOuts(viewpubTo, spendpubto *[32]byte, transAmount int64, count int32) (*privacytypes.PrivacyOutput, error) { // decomDigit := make([]int64, count) // for i := range decomDigit { // decomDigit[i] = transAmount // } // // pk := &privacy.PubKeyPrivacy{} // sk := &privacy.PrivKeyPrivacy{} // privacy.GenerateKeyPair(sk, pk) // RtxPublicKey := pk.Bytes() // // sktx := (*[32]byte)(unsafe.Pointer(&sk[0])) // var privacyOutput privacytypes.PrivacyOutput // privacyOutput.RpubKeytx = RtxPublicKey // privacyOutput.Keyoutput = make([]*privacytypes.KeyOutput, len(decomDigit)) // // //添加本次转账的目的接收信息(UTXO),包括一次性公钥和额度 // for index, digit := range decomDigit { // pubkeyOnetime, err := privacy.GenerateOneTimeAddr(viewpubTo, spendpubto, sktx, int64(index)) // if err != nil { // bizlog.Error("genCustomOuts", "Fail to GenerateOneTimeAddr due to cause", err) // return nil, err // } // keyOutput := &privacytypes.KeyOutput{ // Amount: digit, // Onetimepubkey: pubkeyOnetime[:], // } // privacyOutput.Keyoutput[index] = keyOutput // } // // return &privacyOutput, nil //} //最后构造完成的utxo依次是2种类型,不构造交易费utxo,使其直接燃烧消失 //1.进行实际转账utxo //2.进行找零转账utxo func generateOuts(viewpubTo, spendpubto, viewpubChangeto, spendpubChangeto *[32]byte, transAmount, selectedAmount, fee int64) (*privacytypes.PrivacyOutput, error) { decomDigit := decomposeAmount2digits(transAmount, types.BTYDustThreshold) //计算找零 changeAmount := selectedAmount - transAmount - fee var decomChange []int64 if 0 < changeAmount { decomChange = decomposeAmount2digits(changeAmount, types.BTYDustThreshold) } bizlog.Info("generateOuts", "decompose digit for amount", selectedAmount-fee, "decomDigit", decomDigit) pk := &privacy.PubKeyPrivacy{} sk := &privacy.PrivKeyPrivacy{} privacy.GenerateKeyPair(sk, pk) RtxPublicKey := pk.Bytes() sktx := (*[32]byte)(unsafe.Pointer(&sk[0])) var privacyOutput privacytypes.PrivacyOutput privacyOutput.RpubKeytx = RtxPublicKey privacyOutput.Keyoutput = make([]*privacytypes.KeyOutput, len(decomDigit)+len(decomChange)) //添加本次转账的目的接收信息(UTXO),包括一次性公钥和额度 for index, digit := range decomDigit { pubkeyOnetime, err := privacy.GenerateOneTimeAddr(viewpubTo, spendpubto, sktx, int64(index)) if err != nil { bizlog.Error("generateOuts", "Fail to GenerateOneTimeAddr due to cause", err) return nil, err } keyOutput := &privacytypes.KeyOutput{ Amount: digit, Onetimepubkey: pubkeyOnetime[:], } privacyOutput.Keyoutput[index] = keyOutput } //添加本次转账选择的UTXO后的找零后的UTXO for index, digit := range decomChange { pubkeyOnetime, err := privacy.GenerateOneTimeAddr(viewpubChangeto, spendpubChangeto, sktx, int64(index+len(decomDigit))) if err != nil { bizlog.Error("generateOuts", "Fail to GenerateOneTimeAddr for change due to cause", err) return nil, err } keyOutput := &privacytypes.KeyOutput{ Amount: digit, Onetimepubkey: pubkeyOnetime[:], } privacyOutput.Keyoutput[index+len(decomDigit)] = keyOutput } //交易费不产生额外的utxo,方便执行器执行的时候直接燃烧殆尽 if 0 != fee { //viewPub, _ := common.Hex2Bytes(types.ViewPubFee) //spendPub, _ := common.Hex2Bytes(types.SpendPubFee) //viewPublic := (*[32]byte)(unsafe.Pointer(&viewPub[0])) //spendPublic := (*[32]byte)(unsafe.Pointer(&spendPub[0])) // //pubkeyOnetime, err := privacy.GenerateOneTimeAddr(viewPublic, spendPublic, sktx, int64(len(privacyOutput.Keyoutput))) //if err != nil { // bizlog.Error("transPub2PriV2", "Fail to GenerateOneTimeAddr for fee due to cause", err) // return nil, nil, err //} //keyOutput := &types.KeyOutput{ // Amount: fee, // Ometimepubkey: pubkeyOnetime[:], //} //privacyOutput.Keyoutput = append(privacyOutput.Keyoutput, keyOutput) } return &privacyOutput, nil }