Ori Newman cf4073b773
Remove HF activation code (#2152)
* Remove HF activation code

* Remove unused var totalInputs
2022-10-02 19:17:33 +03:00

243 lines
7.1 KiB
Go

package main
import (
"context"
"encoding/hex"
"fmt"
"github.com/kaspanet/go-secp256k1"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization"
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/txmass"
"github.com/pkg/errors"
)
const feePerInput = 10000
func sweep(conf *sweepConfig) error {
privateKeyBytes, err := hex.DecodeString(conf.PrivateKey)
if err != nil {
return err
}
publicKeybytes, err := libkaspawallet.PublicKeyFromPrivateKey(privateKeyBytes)
if err != nil {
return err
}
addressPubKey, err := util.NewAddressPublicKey(publicKeybytes, conf.NetParams().Prefix)
if err != nil {
return err
}
address, err := util.DecodeAddress(addressPubKey.String(), conf.NetParams().Prefix)
if err != nil {
return err
}
daemonClient, tearDown, err := client.Connect(conf.DaemonAddress)
if err != nil {
return err
}
defer tearDown()
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
defer cancel()
getExternalSpendableUTXOsResponse, err := daemonClient.GetExternalSpendableUTXOs(ctx, &pb.GetExternalSpendableUTXOsRequest{
Address: address.String(),
})
if err != nil {
return err
}
UTXOs, err := libkaspawallet.KaspawalletdUTXOsTolibkaspawalletUTXOs(getExternalSpendableUTXOsResponse.Entries)
if err != nil {
return err
}
paymentAmount := uint64(0)
if len(UTXOs) == 0 {
return errors.Errorf("Could not find any spendable UTXOs in %s", addressPubKey)
}
for _, UTXO := range UTXOs {
paymentAmount = paymentAmount + UTXO.UTXOEntry.Amount()
}
newAddressResponse, err := daemonClient.NewAddress(ctx, &pb.NewAddressRequest{})
if err != nil {
return err
}
toAddress, err := util.DecodeAddress(newAddressResponse.Address, conf.ActiveNetParams.Prefix)
if err != nil {
return err
}
splitTransactions, err := createSplitTransactionsWithSchnorrPrivteKey(conf.NetParams(), UTXOs, toAddress, feePerInput)
if err != nil {
return err
}
serializedSplitTransactions, err := signWithSchnorrPrivateKey(conf.NetParams(), privateKeyBytes, splitTransactions)
if err != nil {
return err
}
fmt.Println("\nSweeping...")
fmt.Println("\tFrom:\t", addressPubKey)
fmt.Println("\tTo:\t", toAddress)
response, err := daemonClient.Broadcast(ctx, &pb.BroadcastRequest{
IsDomain: true,
Transactions: serializedSplitTransactions,
})
if err != nil {
return err
}
totalExtracted := uint64(0)
fmt.Println("\nTransaction ID(s):")
for i, txID := range response.TxIDs {
fmt.Printf("\t%s\n", txID)
fmt.Println("\tSwept:\t", utils.FormatKas(splitTransactions[i].Outputs[0].Value), " KAS")
totalExtracted = totalExtracted + splitTransactions[i].Outputs[0].Value
}
fmt.Println("\nTotal Funds swept (including transaction fees):")
fmt.Println("\t", utils.FormatKas(totalExtracted), " KAS")
return nil
}
func newDummyTransaction() *externalapi.DomainTransaction {
return &externalapi.DomainTransaction{
Version: constants.MaxTransactionVersion,
Inputs: make([]*externalapi.DomainTransactionInput, 0), //we create empty inputs
LockTime: 0,
Outputs: make([]*externalapi.DomainTransactionOutput, 1), // we should always have 1 output to the toAdress
SubnetworkID: subnetworks.SubnetworkIDNative,
Gas: 0,
Payload: nil,
}
}
func createSplitTransactionsWithSchnorrPrivteKey(
params *dagconfig.Params,
selectedUTXOs []*libkaspawallet.UTXO,
toAddress util.Address,
feePerInput int) ([]*externalapi.DomainTransaction, error) {
var splitTransactions []*externalapi.DomainTransaction
extraMass := uint64(7000) // Account for future signatures.
massCalculater := txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp)
scriptPublicKey, err := txscript.PayToAddrScript(toAddress)
if err != nil {
return nil, err
}
totalSplitAmount := uint64(0)
lastValidTx := newDummyTransaction()
currentTx := newDummyTransaction() //i.e. the tested tx
//loop through utxos commit segments that don't violate max mass
for i, currentUTXO := range selectedUTXOs {
totalSplitAmount = totalSplitAmount + currentUTXO.UTXOEntry.Amount()
currentTx.Inputs = append(
currentTx.Inputs,
&externalapi.DomainTransactionInput{
PreviousOutpoint: *currentUTXO.Outpoint,
UTXOEntry: utxo.NewUTXOEntry(
currentUTXO.UTXOEntry.Amount(),
currentUTXO.UTXOEntry.ScriptPublicKey(),
false,
constants.UnacceptedDAAScore,
),
SigOpCount: 1,
},
)
currentTx.Outputs[0] = &externalapi.DomainTransactionOutput{
Value: totalSplitAmount - uint64(len(currentTx.Inputs)*feePerInput),
ScriptPublicKey: scriptPublicKey,
}
if massCalculater.CalculateTransactionMass(currentTx)+extraMass >= mempool.MaximumStandardTransactionMass {
//in this loop we assume a transaction with one input and one output cannot violate max transaction mass, hence a sanity check.
if len(currentTx.Inputs) == 1 {
return nil, errors.Errorf("transaction with one input and one output violates transaction mass")
}
splitTransactions = append(splitTransactions, lastValidTx)
totalSplitAmount = 0
lastValidTx = newDummyTransaction()
currentTx = newDummyTransaction()
continue
}
//Special case, end of inputs, with no violation, where we can assign currentTX to split and break
if i == len(selectedUTXOs)-1 {
splitTransactions = append(splitTransactions, currentTx)
break
}
lastValidTx = currentTx.Clone()
currentTx.Outputs = make([]*externalapi.DomainTransactionOutput, 1)
}
return splitTransactions, nil
}
func signWithSchnorrPrivateKey(params *dagconfig.Params, privateKeyBytes []byte, domainTransactions []*externalapi.DomainTransaction) ([][]byte, error) {
schnorrkeyPair, err := secp256k1.DeserializeSchnorrPrivateKeyFromSlice(privateKeyBytes)
if err != nil {
return nil, err
}
serializedDomainTransactions := make([][]byte, len(domainTransactions))
for i1, domainTransaction := range domainTransactions {
sighashReusedValues := &consensushashing.SighashReusedValues{}
for i2, input := range domainTransaction.Inputs {
signature, err := txscript.SignatureScript(domainTransaction, i2, consensushashing.SigHashAll, schnorrkeyPair, sighashReusedValues)
if err != nil {
return nil, err
}
input.SignatureScript = signature
}
serializedDomainTransactions[i1], err = serialization.SerializeDomainTransaction(domainTransaction)
if err != nil {
return nil, err
}
}
return serializedDomainTransactions, nil
}