mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
Merge branch 'dev' into add-uptime-to-getInfo
This commit is contained in:
commit
7ccd90cf3f
@ -686,37 +686,28 @@ func (flow *handleIBDFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) resolveVirtual(estimatedVirtualDAAScoreTarget uint64) error {
|
||||
virtualDAAScoreStart, err := flow.Domain().Consensus().GetVirtualDAAScore()
|
||||
err := flow.Domain().Consensus().ResolveVirtual(func(virtualDAAScoreStart uint64, virtualDAAScore uint64) {
|
||||
var percents int
|
||||
if estimatedVirtualDAAScoreTarget-virtualDAAScoreStart <= 0 {
|
||||
percents = 100
|
||||
} else {
|
||||
percents = int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
|
||||
}
|
||||
if percents < 0 {
|
||||
percents = 0
|
||||
} else if percents > 100 {
|
||||
percents = 100
|
||||
}
|
||||
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
if i%10 == 0 {
|
||||
virtualDAAScore, err := flow.Domain().Consensus().GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var percents int
|
||||
if estimatedVirtualDAAScoreTarget-virtualDAAScoreStart <= 0 {
|
||||
percents = 100
|
||||
} else {
|
||||
percents = int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
|
||||
}
|
||||
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
|
||||
}
|
||||
isCompletelyResolved, err := flow.Domain().Consensus().ResolveVirtual()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompletelyResolved {
|
||||
log.Infof("Resolved virtual")
|
||||
err = flow.OnNewBlockTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
log.Infof("Resolved virtual")
|
||||
err = flow.OnNewBlockTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func (flow *handleRelayedTransactionsFlow) requestInvTransactions(
|
||||
func (flow *handleRelayedTransactionsFlow) isKnownTransaction(txID *externalapi.DomainTransactionID) bool {
|
||||
// Ask the transaction memory pool if the transaction is known
|
||||
// to it in any form (main pool or orphan).
|
||||
if _, ok := flow.Domain().MiningManager().GetTransaction(txID); ok {
|
||||
if _, _, ok := flow.Domain().MiningManager().GetTransaction(txID, true, true); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func (flow *handleRequestedTransactionsFlow) start() error {
|
||||
}
|
||||
|
||||
for _, transactionID := range msgRequestTransactions.IDs {
|
||||
tx, ok := flow.Domain().MiningManager().GetTransaction(transactionID)
|
||||
tx, _, ok := flow.Domain().MiningManager().GetTransaction(transactionID, true, false)
|
||||
|
||||
if !ok {
|
||||
msgTransactionNotFound := appmessage.NewMsgTransactionNotFound(transactionID)
|
||||
@ -40,7 +40,6 @@ func (flow *handleRequestedTransactionsFlow) start() error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.DomainTransactionToMsgTx(tx))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -223,18 +223,9 @@ func (m *Manager) notifyVirtualSelectedParentChainChanged(virtualChangeSet *exte
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
||||
defer onEnd()
|
||||
|
||||
listenersThatPropagateSelectedParentChanged :=
|
||||
m.context.NotificationManager.AllListenersThatPropagateVirtualSelectedParentChainChanged()
|
||||
if len(listenersThatPropagateSelectedParentChanged) > 0 {
|
||||
// Generating acceptedTransactionIDs is a heavy operation, so we check if it's needed by any listener.
|
||||
includeAcceptedTransactionIDs := false
|
||||
for _, listener := range listenersThatPropagateSelectedParentChanged {
|
||||
if listener.IncludeAcceptedTransactionIDsInVirtualSelectedParentChainChangedNotifications() {
|
||||
includeAcceptedTransactionIDs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
hasListeners, includeAcceptedTransactionIDs := m.context.NotificationManager.HasListenersThatPropagateVirtualSelectedParentChainChanged()
|
||||
|
||||
if hasListeners {
|
||||
notification, err := m.context.ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage(
|
||||
virtualChangeSet.VirtualSelectedParentChainChanges, includeAcceptedTransactionIDs)
|
||||
if err != nil {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
@ -141,16 +142,29 @@ func (nm *NotificationManager) NotifyVirtualSelectedParentChainChanged(
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllListenersThatPropagateVirtualSelectedParentChainChanged returns true if there's any listener that is
|
||||
// subscribed to VirtualSelectedParentChainChanged notifications.
|
||||
func (nm *NotificationManager) AllListenersThatPropagateVirtualSelectedParentChainChanged() []*NotificationListener {
|
||||
var listenersThatPropagate []*NotificationListener
|
||||
// HasListenersThatPropagateVirtualSelectedParentChainChanged returns whether there's any listener that is
|
||||
// subscribed to VirtualSelectedParentChainChanged notifications as well as checks if any such listener requested
|
||||
// to include AcceptedTransactionIDs.
|
||||
func (nm *NotificationManager) HasListenersThatPropagateVirtualSelectedParentChainChanged() (hasListeners, hasListenersThatRequireAcceptedTransactionIDs bool) {
|
||||
|
||||
nm.RLock()
|
||||
defer nm.RUnlock()
|
||||
|
||||
hasListeners = false
|
||||
hasListenersThatRequireAcceptedTransactionIDs = false
|
||||
|
||||
for _, listener := range nm.listeners {
|
||||
if listener.propagateVirtualSelectedParentChainChangedNotifications {
|
||||
listenersThatPropagate = append(listenersThatPropagate, listener)
|
||||
hasListeners = true
|
||||
// Generating acceptedTransactionIDs is a heavy operation, so we check if it's needed by any listener.
|
||||
if listener.includeAcceptedTransactionIDsInVirtualSelectedParentChainChangedNotifications {
|
||||
hasListenersThatRequireAcceptedTransactionIDs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return listenersThatPropagate
|
||||
|
||||
return hasListeners, hasListenersThatRequireAcceptedTransactionIDs
|
||||
}
|
||||
|
||||
// NotifyFinalityConflict notifies the notification manager that there's a finality conflict in the DAG
|
||||
@ -337,7 +351,11 @@ func (nl *NotificationListener) PropagateFinalityConflictResolvedNotifications()
|
||||
// to the remote listener for the given addresses. Subsequent calls instruct the listener to
|
||||
// send UTXOs changed notifications for those addresses along with the old ones. Duplicate addresses
|
||||
// are ignored.
|
||||
func (nl *NotificationListener) PropagateUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
|
||||
func (nm *NotificationManager) PropagateUTXOsChangedNotifications(nl *NotificationListener, addresses []*UTXOsChangedNotificationAddress) {
|
||||
// Apply a write-lock since the internal listener address map is modified
|
||||
nm.Lock()
|
||||
defer nm.Unlock()
|
||||
|
||||
if !nl.propagateUTXOsChangedNotifications {
|
||||
nl.propagateUTXOsChangedNotifications = true
|
||||
nl.propagateUTXOsChangedNotificationAddresses =
|
||||
@ -352,7 +370,11 @@ func (nl *NotificationListener) PropagateUTXOsChangedNotifications(addresses []*
|
||||
// StopPropagatingUTXOsChangedNotifications instructs the listener to stop sending UTXOs
|
||||
// changed notifications to the remote listener for the given addresses. Addresses for which
|
||||
// notifications are not currently sent are ignored.
|
||||
func (nl *NotificationListener) StopPropagatingUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
|
||||
func (nm *NotificationManager) StopPropagatingUTXOsChangedNotifications(nl *NotificationListener, addresses []*UTXOsChangedNotificationAddress) {
|
||||
// Apply a write-lock since the internal listener address map is modified
|
||||
nm.Lock()
|
||||
defer nm.Unlock()
|
||||
|
||||
if !nl.propagateUTXOsChangedNotifications {
|
||||
return
|
||||
}
|
||||
@ -421,7 +443,7 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||
}
|
||||
|
||||
func (nl *NotificationListener) scriptPubKeyStringToAddressString(scriptPublicKeyString utxoindex.ScriptPublicKeyString) (string, error) {
|
||||
scriptPubKey := utxoindex.ConvertStringToScriptPublicKey(scriptPublicKeyString)
|
||||
scriptPubKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
|
||||
|
||||
// ignore error because it is often returned when the script is of unknown type
|
||||
scriptType, address, err := txscript.ExtractScriptPubKeyAddress(scriptPubKey, nl.params)
|
||||
|
@ -32,22 +32,6 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
|
||||
return utxosByAddressesEntries
|
||||
}
|
||||
|
||||
// convertUTXOOutpointsToUTXOsByAddressesEntries converts
|
||||
// UTXOOutpoints to a slice of UTXOsByAddressesEntry
|
||||
func convertUTXOOutpointsToUTXOsByAddressesEntries(address string, outpoints utxoindex.UTXOOutpoints) []*appmessage.UTXOsByAddressesEntry {
|
||||
utxosByAddressesEntries := make([]*appmessage.UTXOsByAddressesEntry, 0, len(outpoints))
|
||||
for outpoint := range outpoints {
|
||||
utxosByAddressesEntries = append(utxosByAddressesEntries, &appmessage.UTXOsByAddressesEntry{
|
||||
Address: address,
|
||||
Outpoint: &appmessage.RPCOutpoint{
|
||||
TransactionID: outpoint.TransactionID.String(),
|
||||
Index: outpoint.Index,
|
||||
},
|
||||
})
|
||||
}
|
||||
return utxosByAddressesEntries
|
||||
}
|
||||
|
||||
// ConvertAddressStringsToUTXOsChangedNotificationAddresses converts address strings
|
||||
// to UTXOsChangedNotificationAddresses
|
||||
func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
|
||||
@ -63,7 +47,7 @@ func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
|
||||
}
|
||||
scriptPublicKeyString := utxoindex.ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||
scriptPublicKeyString := utxoindex.ScriptPublicKeyString(scriptPublicKey.String())
|
||||
addresses[i] = &UTXOsChangedNotificationAddress{
|
||||
Address: addressString,
|
||||
ScriptPublicKeyString: scriptPublicKeyString,
|
||||
|
@ -16,7 +16,7 @@ func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.M
|
||||
|
||||
response := appmessage.NewGetInfoResponseMessage(
|
||||
context.NetAdapter.ID().String(),
|
||||
uint64(context.Domain.MiningManager().TransactionCount()),
|
||||
uint64(context.Domain.MiningManager().TransactionCount(true, false)),
|
||||
version.Version(),
|
||||
context.Config.UTXOIndex,
|
||||
context.ProtocolManager.Context().HasPeers() && isNearlySynced,
|
||||
|
@ -12,58 +12,36 @@ func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, requ
|
||||
|
||||
entries := make([]*appmessage.MempoolEntry, 0)
|
||||
|
||||
transactionPoolTransactions, orphanPoolTransactions := context.Domain.MiningManager().AllTransactions(!getMempoolEntriesRequest.FilterTransactionPool, getMempoolEntriesRequest.IncludeOrphanPool)
|
||||
|
||||
if !getMempoolEntriesRequest.FilterTransactionPool {
|
||||
transactionPoolEntries, err := getTransactionPoolMempoolEntries(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, transaction := range transactionPoolTransactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
})
|
||||
}
|
||||
|
||||
entries = append(entries, transactionPoolEntries...)
|
||||
}
|
||||
|
||||
if getMempoolEntriesRequest.IncludeOrphanPool {
|
||||
orphanPoolEntries, err := getOrphanPoolMempoolEntries(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, transaction := range orphanPoolTransactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
})
|
||||
}
|
||||
entries = append(entries, orphanPoolEntries...)
|
||||
}
|
||||
|
||||
return appmessage.NewGetMempoolEntriesResponseMessage(entries), nil
|
||||
}
|
||||
|
||||
func getTransactionPoolMempoolEntries(context *rpccontext.Context) ([]*appmessage.MempoolEntry, error) {
|
||||
transactions := context.Domain.MiningManager().AllTransactions()
|
||||
entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
|
||||
for _, transaction := range transactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
})
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func getOrphanPoolMempoolEntries(context *rpccontext.Context) ([]*appmessage.MempoolEntry, error) {
|
||||
orphanTransactions := context.Domain.MiningManager().AllOrphanTransactions()
|
||||
entries := make([]*appmessage.MempoolEntry, 0, len(orphanTransactions))
|
||||
for _, orphanTransaction := range orphanTransactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(orphanTransaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: orphanTransaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
})
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
@ -1,14 +1,10 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
@ -20,132 +16,107 @@ func HandleGetMempoolEntriesByAddresses(context *rpccontext.Context, _ *router.R
|
||||
|
||||
mempoolEntriesByAddresses := make([]*appmessage.MempoolEntryByAddress, 0)
|
||||
|
||||
if !getMempoolEntriesByAddressesRequest.FilterTransactionPool {
|
||||
transactionPoolTransactions := context.Domain.MiningManager().AllTransactions()
|
||||
transactionPoolEntriesByAddresses, err := extractMempoolEntriesByAddressesFromTransactions(
|
||||
context,
|
||||
getMempoolEntriesByAddressesRequest.Addresses,
|
||||
transactionPoolTransactions,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
rpcError := &appmessage.RPCError{}
|
||||
if !errors.As(err, &rpcError) {
|
||||
return nil, err
|
||||
}
|
||||
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
|
||||
errorMessage.Error = rpcError
|
||||
return errorMessage, nil
|
||||
}
|
||||
mempoolEntriesByAddresses = append(mempoolEntriesByAddresses, transactionPoolEntriesByAddresses...)
|
||||
sendingInTransactionPool, receivingInTransactionPool, sendingInOrphanPool, receivingInOrphanPool, err := context.Domain.MiningManager().GetTransactionsByAddresses(!getMempoolEntriesByAddressesRequest.FilterTransactionPool, getMempoolEntriesByAddressesRequest.IncludeOrphanPool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if getMempoolEntriesByAddressesRequest.IncludeOrphanPool {
|
||||
for _, addressString := range getMempoolEntriesByAddressesRequest.Addresses {
|
||||
|
||||
orphanPoolTransactions := context.Domain.MiningManager().AllOrphanTransactions()
|
||||
orphanPoolEntriesByAddress, err := extractMempoolEntriesByAddressesFromTransactions(
|
||||
context,
|
||||
getMempoolEntriesByAddressesRequest.Addresses,
|
||||
orphanPoolTransactions,
|
||||
true,
|
||||
)
|
||||
address, err := util.DecodeAddress(addressString, context.Config.NetParams().Prefix)
|
||||
if err != nil {
|
||||
rpcError := &appmessage.RPCError{}
|
||||
if !errors.As(err, &rpcError) {
|
||||
return nil, err
|
||||
}
|
||||
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
|
||||
errorMessage.Error = rpcError
|
||||
errorMessage := &appmessage.GetMempoolEntriesByAddressesResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
mempoolEntriesByAddresses = append(mempoolEntriesByAddresses, orphanPoolEntriesByAddress...)
|
||||
}
|
||||
|
||||
return appmessage.NewGetMempoolEntriesByAddressesResponseMessage(mempoolEntriesByAddresses), nil
|
||||
}
|
||||
|
||||
//TO DO: optimize extractMempoolEntriesByAddressesFromTransactions
|
||||
func extractMempoolEntriesByAddressesFromTransactions(context *rpccontext.Context, addresses []string, transactions []*externalapi.DomainTransaction, areOrphans bool) ([]*appmessage.MempoolEntryByAddress, error) {
|
||||
mempoolEntriesByAddresses := make([]*appmessage.MempoolEntryByAddress, 0)
|
||||
for _, addressString := range addresses {
|
||||
_, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
return nil, appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
|
||||
}
|
||||
|
||||
sending := make([]*appmessage.MempoolEntry, 0)
|
||||
receiving := make([]*appmessage.MempoolEntry, 0)
|
||||
|
||||
for _, transaction := range transactions {
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.GetMempoolEntriesByAddressesResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not extract scriptPublicKey from address '%s': %s", addressString, err)
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
for i, input := range transaction.Inputs {
|
||||
if input.UTXOEntry == nil {
|
||||
if !areOrphans { // Orphans can legitimately have `input.UTXOEntry == nil`
|
||||
// TODO: Fix the underlying cause of the bug for non-orphan entries
|
||||
log.Debugf(
|
||||
"Couldn't find UTXO entry for input %d in mempool transaction %s. This is a bug and should be fixed.",
|
||||
i, consensushashing.TransactionID(transaction))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !getMempoolEntriesByAddressesRequest.FilterTransactionPool {
|
||||
|
||||
_, transactionSendingAddress, err := txscript.ExtractScriptPubKeyAddress(
|
||||
input.UTXOEntry.ScriptPublicKey(),
|
||||
context.Config.ActiveNetParams)
|
||||
if transaction, found := sendingInTransactionPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addressString == transactionSendingAddress.String() {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
sending = append(
|
||||
sending,
|
||||
&appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: areOrphans,
|
||||
},
|
||||
)
|
||||
break //one input is enough
|
||||
}
|
||||
}
|
||||
|
||||
for _, output := range transaction.Outputs {
|
||||
_, transactionReceivingAddress, err := txscript.ExtractScriptPubKeyAddress(
|
||||
output.ScriptPublicKey,
|
||||
context.Config.ActiveNetParams,
|
||||
sending = append(sending, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if transaction, found := receivingInTransactionPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addressString == transactionReceivingAddress.String() {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
receiving = append(
|
||||
receiving,
|
||||
&appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: areOrphans,
|
||||
},
|
||||
)
|
||||
break //one output is enough
|
||||
}
|
||||
}
|
||||
|
||||
//Only append mempoolEntriesByAddress, if at least 1 mempoolEntry for the address is found.
|
||||
//This mimics the behaviour of GetUtxosByAddresses RPC call.
|
||||
if len(sending) > 0 || len(receiving) > 0 {
|
||||
mempoolEntriesByAddresses = append(
|
||||
mempoolEntriesByAddresses,
|
||||
&appmessage.MempoolEntryByAddress{
|
||||
Address: addressString,
|
||||
Sending: sending,
|
||||
Receiving: receiving,
|
||||
},
|
||||
receiving = append(receiving, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
if getMempoolEntriesByAddressesRequest.IncludeOrphanPool {
|
||||
|
||||
if transaction, found := sendingInOrphanPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sending = append(sending, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if transaction, found := receivingInOrphanPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receiving = append(receiving, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(sending) > 0 || len(receiving) > 0 {
|
||||
mempoolEntriesByAddresses = append(
|
||||
mempoolEntriesByAddresses,
|
||||
&appmessage.MempoolEntryByAddress{
|
||||
Address: address.String(),
|
||||
Sending: sending,
|
||||
Receiving: receiving,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
return mempoolEntriesByAddresses, nil
|
||||
|
||||
return appmessage.NewGetMempoolEntriesByAddressesResponseMessage(mempoolEntriesByAddresses), nil
|
||||
}
|
||||
|
@ -24,14 +24,7 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
if !getMempoolEntryRequest.FilterTransactionPool {
|
||||
transaction, found = context.Domain.MiningManager().GetTransaction(transactionID)
|
||||
}
|
||||
|
||||
if getMempoolEntryRequest.IncludeOrphanPool && !found {
|
||||
transaction, found = context.Domain.MiningManager().GetOrphanTransaction(transactionID)
|
||||
isOrphan = true
|
||||
}
|
||||
mempoolTransaction, isOrphan, found := context.Domain.MiningManager().GetTransaction(transactionID, !getMempoolEntryRequest.FilterTransactionPool, getMempoolEntryRequest.IncludeOrphanPool)
|
||||
|
||||
if !found {
|
||||
errorMessage := &appmessage.GetMempoolEntryResponseMessage{}
|
||||
@ -39,7 +32,7 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(mempoolTransaction)
|
||||
err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -26,7 +26,7 @@ func HandleNotifyUTXOsChanged(context *rpccontext.Context, router *router.Router
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener.PropagateUTXOsChangedNotifications(addresses)
|
||||
context.NotificationManager.PropagateUTXOsChangedNotifications(listener, addresses)
|
||||
|
||||
response := appmessage.NewNotifyUTXOsChangedResponseMessage()
|
||||
return response, nil
|
||||
|
@ -26,7 +26,7 @@ func HandleStopNotifyingUTXOsChanged(context *rpccontext.Context, router *router
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener.StopPropagatingUTXOsChangedNotifications(addresses)
|
||||
context.NotificationManager.StopPropagatingUTXOsChangedNotifications(listener, addresses)
|
||||
|
||||
response := appmessage.NewStopNotifyingUTXOsChangedResponseMessage()
|
||||
return response, nil
|
||||
|
@ -1,3 +1,31 @@
|
||||
Kaspad v0.12.5 - 2022-08-28
|
||||
===========================
|
||||
|
||||
* Add tests for hash writers (#2120)
|
||||
* Replace daglabs's dnsseeder with Wolfie's (#2119)
|
||||
* Change testnet dnsseeder (#2126)
|
||||
* Add RPC timeout parameter to wallet daemon (#2104)
|
||||
|
||||
Wallet new features:
|
||||
* Add UseExistingChangeAddress option to the wallet (#2127)
|
||||
|
||||
Bug fixes:
|
||||
* Call update pruning point if required on resolve virtual or startup (#2129)
|
||||
* Add missing locks to notification listener modifications (#2124)
|
||||
* Calculate pruning point utxo set from acceptance data (#2123)
|
||||
* Fix RPC client memory/goroutine leak (#2122)
|
||||
* Fix a subtle lock sync issue in consensus insert block (#2121)
|
||||
* Mempool: Retrieve stable state of the mempool. Optimze get mempool entries by addresses (#2111)
|
||||
* Kaspawallet.send(): Make separate context for Broadcast, to prolong timeout (#2131)
|
||||
|
||||
|
||||
|
||||
Kaspad v0.12.4 - 2022-07-17
|
||||
===========================
|
||||
|
||||
* Crucial fix for the UTXO difference mechanism (#2114)
|
||||
* Implement multi-layer auto-compound (#2115)
|
||||
|
||||
Kaspad v0.12.3 - 2022-06-29
|
||||
===========================
|
||||
|
||||
|
@ -52,12 +52,13 @@ type balanceConfig struct {
|
||||
}
|
||||
|
||||
type sendConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
@ -68,10 +69,11 @@ type sweepConfig struct {
|
||||
}
|
||||
|
||||
type createUnsignedTransactionConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
@ -111,7 +113,8 @@ type startDaemonConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
Listen string `short:"l" long:"listen" description:"Address to listen on (default: 0.0.0.0:8082)"`
|
||||
Listen string `long:"listen" short:"l" description:"Address to listen on (default: 0.0.0.0:8082)"`
|
||||
Timeout uint32 `long:"wait-timeout" short:"w" description:"Waiting timeout for RPC calls, seconds (default: 30 s)"`
|
||||
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
@ -181,7 +184,6 @@ func parseCommandLine() (subCommand string, config interface{}) {
|
||||
parser.AddCommand(startDaemonSubCmd, "Start the wallet daemon", "Start the wallet daemon", startDaemonConf)
|
||||
|
||||
_, err := parser.Parse()
|
||||
|
||||
if err != nil {
|
||||
var flagsErr *flags.Error
|
||||
if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp {
|
||||
|
@ -22,9 +22,10 @@ func createUnsignedTransaction(conf *createUnsignedTransactionConfig) error {
|
||||
|
||||
sendAmountSompi := uint64(conf.SendAmount * constants.SompiPerKaspa)
|
||||
response, err := daemonClient.CreateUnsignedTransactions(ctx, &pb.CreateUnsignedTransactionsRequest{
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
UseExistingChangeAddress: conf.UseExistingChangeAddress,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1,12 +1,13 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc v3.17.2
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.12.3
|
||||
// source: kaspawalletd.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@ -20,6 +21,10 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type GetBalanceRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -189,9 +194,10 @@ type CreateUnsignedTransactionsRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
From []string `protobuf:"bytes,3,rep,name=from,proto3" json:"from,omitempty"`
|
||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
From []string `protobuf:"bytes,3,rep,name=from,proto3" json:"from,omitempty"`
|
||||
UseExistingChangeAddress bool `protobuf:"varint,4,opt,name=useExistingChangeAddress,proto3" json:"useExistingChangeAddress,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CreateUnsignedTransactionsRequest) Reset() {
|
||||
@ -247,6 +253,13 @@ func (x *CreateUnsignedTransactionsRequest) GetFrom() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CreateUnsignedTransactionsRequest) GetUseExistingChangeAddress() bool {
|
||||
if x != nil {
|
||||
return x.UseExistingChangeAddress
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type CreateUnsignedTransactionsResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -986,10 +999,11 @@ type SendRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ToAddress string `protobuf:"bytes,1,opt,name=toAddress,proto3" json:"toAddress,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
||||
From []string `protobuf:"bytes,4,rep,name=from,proto3" json:"from,omitempty"`
|
||||
ToAddress string `protobuf:"bytes,1,opt,name=toAddress,proto3" json:"toAddress,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
||||
From []string `protobuf:"bytes,4,rep,name=from,proto3" json:"from,omitempty"`
|
||||
UseExistingChangeAddress bool `protobuf:"varint,5,opt,name=useExistingChangeAddress,proto3" json:"useExistingChangeAddress,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SendRequest) Reset() {
|
||||
@ -1052,6 +1066,13 @@ func (x *SendRequest) GetFrom() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendRequest) GetUseExistingChangeAddress() bool {
|
||||
if x != nil {
|
||||
return x.UseExistingChangeAddress
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SendResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1224,155 +1245,162 @@ var file_kaspawalletd_proto_rawDesc = []byte{
|
||||
0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65,
|
||||
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x70, 0x65, 0x6e,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x22, 0x69, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e,
|
||||
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66,
|
||||
0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x22,
|
||||
0x58, 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65,
|
||||
0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65,
|
||||
0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x68, 0x6f,
|
||||
0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x22, 0x31, 0x0a, 0x15, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x4e, 0x65, 0x77,
|
||||
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x10, 0x42, 0x72, 0x6f,
|
||||
0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x69, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x08, 0x69, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52,
|
||||
0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x0a,
|
||||
0x11, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53,
|
||||
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x46, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
|
||||
0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x9c, 0x01, 0x0a, 0x15, 0x55, 0x74, 0x78, 0x6f,
|
||||
0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x6f,
|
||||
0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
|
||||
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4f, 0x75, 0x74,
|
||||
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
|
||||
0x35, 0x0a, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
|
||||
0x64, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x75, 0x74, 0x78,
|
||||
0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x55, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0xb2, 0x01,
|
||||
0x0a, 0x09, 0x55, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61,
|
||||
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b,
|
||||
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x63, 0x72, 0x69,
|
||||
0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x0f, 0x73, 0x63, 0x72,
|
||||
0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d,
|
||||
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61,
|
||||
0x73, 0x65, 0x22, 0x3c, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x22, 0x62, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53,
|
||||
0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
|
||||
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x79, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x69, 0x65, 0x73, 0x22, 0x73, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x04, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0x24, 0x0a, 0x0c, 0x53, 0x65, 0x6e,
|
||||
0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49,
|
||||
0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22,
|
||||
0x5d, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32,
|
||||
0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x75, 0x6e,
|
||||
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3e,
|
||||
0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e,
|
||||
0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb3,
|
||||
0x06, 0x0a, 0x0c, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12,
|
||||
0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e,
|
||||
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20,
|
||||
0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x7e, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x12,
|
||||
0x2e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
|
||||
0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x51, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
|
||||
0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x12, 0x1d, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
|
||||
0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12,
|
||||
0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42,
|
||||
0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42,
|
||||
0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
|
||||
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x64, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x22, 0xa5, 0x01, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55,
|
||||
0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x66, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d,
|
||||
0x12, 0x3a, 0x0a, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43,
|
||||
0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43,
|
||||
0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x58, 0x0a, 0x22,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c,
|
||||
0x52, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x31,
|
||||
0x0a, 0x15, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x22, 0x13, 0x0a, 0x11, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x10, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
|
||||
0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x0a, 0x11, 0x42, 0x72,
|
||||
0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
|
||||
0x74, 0x78, 0x49, 0x44, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75, 0x74,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x08,
|
||||
0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x22, 0x9c, 0x01, 0x0a, 0x15, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x79,
|
||||
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70,
|
||||
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x09,
|
||||
0x75, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x17, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x55,
|
||||
0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x45, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x22, 0x55, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x28, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70,
|
||||
0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0xb2, 0x01, 0x0a, 0x09, 0x55,
|
||||
0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x12, 0x47, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50,
|
||||
0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12,
|
||||
0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x22,
|
||||
0x3c, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70,
|
||||
0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x62, 0x0a,
|
||||
0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e,
|
||||
0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65,
|
||||
0x74, 0x64, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65,
|
||||
0x73, 0x22, 0xaf, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x3a, 0x0a, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78,
|
||||
0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78,
|
||||
0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x22, 0x24, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22, 0x5d, 0x0a, 0x0b, 0x53, 0x69, 0x67,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69,
|
||||
0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3e, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb3, 0x06, 0x0a, 0x0c, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74,
|
||||
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61,
|
||||
0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x19,
|
||||
0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x12, 0x2e, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58,
|
||||
0x4f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58,
|
||||
0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a,
|
||||
0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b,
|
||||
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
||||
0x12, 0x5a, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65,
|
||||
0x73, 0x12, 0x22, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0a,
|
||||
0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
||||
0x4b, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64,
|
||||
0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09,
|
||||
0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61,
|
||||
0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61,
|
||||
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04,
|
||||
0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53,
|
||||
0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a,
|
||||
0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
|
||||
0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x36,
|
||||
0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x63, 0x6d, 0x64,
|
||||
0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -36,6 +36,7 @@ message CreateUnsignedTransactionsRequest {
|
||||
string address = 1;
|
||||
uint64 amount = 2;
|
||||
repeated string from = 3;
|
||||
bool useExistingChangeAddress = 4;
|
||||
}
|
||||
|
||||
message CreateUnsignedTransactionsResponse {
|
||||
@ -57,8 +58,8 @@ message NewAddressResponse {
|
||||
}
|
||||
|
||||
message BroadcastRequest {
|
||||
bool isDomain = 1;
|
||||
repeated bytes transactions = 2;
|
||||
bool isDomain = 1;
|
||||
repeated bytes transactions = 2;
|
||||
}
|
||||
|
||||
message BroadcastResponse {
|
||||
@ -107,6 +108,7 @@ message SendRequest{
|
||||
uint64 amount = 2;
|
||||
string password = 3;
|
||||
repeated string from = 4;
|
||||
bool useExistingChangeAddress = 5;
|
||||
}
|
||||
|
||||
message SendResponse{
|
||||
|
@ -1,8 +1,4 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.17.2
|
||||
// source: kaspawalletd.proto
|
||||
|
||||
package pb
|
||||
|
||||
@ -15,8 +11,7 @@ import (
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// KaspawalletdClient is the client API for Kaspawalletd service.
|
||||
//
|
||||
@ -146,44 +141,37 @@ type KaspawalletdServer interface {
|
||||
type UnimplementedKaspawalletdServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedKaspawalletdServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBalance not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) GetExternalSpendableUTXOs(context.Context, *GetExternalSpendableUTXOsRequest) (*GetExternalSpendableUTXOsResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) GetExternalSpendableUTXOs(context.Context, *GetExternalSpendableUTXOsRequest) (*GetExternalSpendableUTXOsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetExternalSpendableUTXOs not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) CreateUnsignedTransactions(context.Context, *CreateUnsignedTransactionsRequest) (*CreateUnsignedTransactionsResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) CreateUnsignedTransactions(context.Context, *CreateUnsignedTransactionsRequest) (*CreateUnsignedTransactionsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateUnsignedTransactions not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) ShowAddresses(context.Context, *ShowAddressesRequest) (*ShowAddressesResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) ShowAddresses(context.Context, *ShowAddressesRequest) (*ShowAddressesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ShowAddresses not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) NewAddress(context.Context, *NewAddressRequest) (*NewAddressResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) NewAddress(context.Context, *NewAddressRequest) (*NewAddressResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NewAddress not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) Send(context.Context, *SendRequest) (*SendResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) Send(context.Context, *SendRequest) (*SendResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) Sign(context.Context, *SignRequest) (*SignResponse, error) {
|
||||
func (*UnimplementedKaspawalletdServer) Sign(context.Context, *SignRequest) (*SignResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {}
|
||||
func (*UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {}
|
||||
|
||||
// UnsafeKaspawalletdServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to KaspawalletdServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeKaspawalletdServer interface {
|
||||
mustEmbedUnimplementedKaspawalletdServer()
|
||||
}
|
||||
|
||||
func RegisterKaspawalletdServer(s grpc.ServiceRegistrar, srv KaspawalletdServer) {
|
||||
s.RegisterService(&Kaspawalletd_ServiceDesc, srv)
|
||||
func RegisterKaspawalletdServer(s *grpc.Server, srv KaspawalletdServer) {
|
||||
s.RegisterService(&_Kaspawalletd_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Kaspawalletd_GetBalance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
@ -348,10 +336,7 @@ func _Kaspawalletd_Sign_Handler(srv interface{}, ctx context.Context, dec func(i
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Kaspawalletd_ServiceDesc is the grpc.ServiceDesc for Kaspawalletd service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Kaspawalletd_ServiceDesc = grpc.ServiceDesc{
|
||||
var _Kaspawalletd_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "kaspawalletd.kaspawalletd",
|
||||
HandlerType: (*KaspawalletdServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
|
@ -10,19 +10,24 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (s *server) changeAddress() (util.Address, *walletAddress, error) {
|
||||
err := s.keysFile.SetLastUsedInternalIndex(s.keysFile.LastUsedInternalIndex() + 1)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
func (s *server) changeAddress(useFirst bool) (util.Address, *walletAddress, error) {
|
||||
internalIndex := uint32(0)
|
||||
if !useFirst {
|
||||
err := s.keysFile.SetLastUsedInternalIndex(s.keysFile.LastUsedInternalIndex() + 1)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = s.keysFile.Save()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
err = s.keysFile.Save()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
internalIndex = s.keysFile.LastUsedInternalIndex()
|
||||
}
|
||||
|
||||
walletAddr := &walletAddress{
|
||||
index: s.keysFile.LastUsedInternalIndex(),
|
||||
index: internalIndex,
|
||||
cosignerIndex: s.keysFile.CosignerIndex,
|
||||
keyChain: libkaspawallet.InternalKeychain,
|
||||
}
|
||||
|
@ -3,24 +3,26 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/exp/slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO: Implement a better fee estimation mechanism
|
||||
const feePerInput = 10000
|
||||
|
||||
func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.CreateUnsignedTransactionsRequest) (
|
||||
*pb.CreateUnsignedTransactionsResponse, error) {
|
||||
*pb.CreateUnsignedTransactionsResponse, error,
|
||||
) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount, request.From)
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount, request.From, request.UseExistingChangeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -28,17 +30,19 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
|
||||
return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
|
||||
}
|
||||
|
||||
func (s *server) createUnsignedTransactions(address string, amount uint64, fromAddressesString []string) ([][]byte, error) {
|
||||
func (s *server) createUnsignedTransactions(address string, amount uint64, fromAddressesString []string, useExistingChangeAddress bool) ([][]byte, error) {
|
||||
if !s.isSynced() {
|
||||
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
|
||||
}
|
||||
|
||||
err := s.refreshUTXOs()
|
||||
// make sure address string is correct before proceeding to a
|
||||
// potentially long UTXO refreshment operation
|
||||
toAddress, err := util.DecodeAddress(address, s.params.Prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toAddress, err := util.DecodeAddress(address, s.params.Prefix)
|
||||
err = s.refreshUTXOs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -57,7 +61,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA
|
||||
return nil, err
|
||||
}
|
||||
|
||||
changeAddress, changeWalletAddress, err := s.changeAddress()
|
||||
changeAddress, changeWalletAddress, err := s.changeAddress(useExistingChangeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -87,8 +91,8 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA
|
||||
}
|
||||
|
||||
func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64, fromAddresses []*walletAddress) (
|
||||
selectedUTXOs []*libkaspawallet.UTXO, changeSompi uint64, err error) {
|
||||
|
||||
selectedUTXOs []*libkaspawallet.UTXO, changeSompi uint64, err error,
|
||||
) {
|
||||
selectedUTXOs = []*libkaspawallet.UTXO{}
|
||||
totalValue := uint64(0)
|
||||
|
||||
|
@ -1,15 +1,26 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
)
|
||||
|
||||
func connectToRPC(params *dagconfig.Params, rpcServer string) (*rpcclient.RPCClient, error) {
|
||||
func connectToRPC(params *dagconfig.Params, rpcServer string, timeout uint32) (*rpcclient.RPCClient, error) {
|
||||
rpcAddress, err := params.NormalizeRPCServerAddress(rpcServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rpcclient.NewRPCClient(rpcAddress)
|
||||
rpcClient, err := rpcclient.NewRPCClient(rpcAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if timeout != 0 {
|
||||
rpcClient.SetTimeout(time.Duration(timeout) * time.Second)
|
||||
}
|
||||
|
||||
return rpcClient, err
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ func (s *server) Send(_ context.Context, request *pb.SendRequest) (*pb.SendRespo
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount, request.From)
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount, request.From, request.UseExistingChangeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ type server struct {
|
||||
}
|
||||
|
||||
// Start starts the kaspawalletd server
|
||||
func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath string, profile string) error {
|
||||
func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath string, profile string, timeout uint32) error {
|
||||
initLog(defaultLogFile, defaultErrLogFile)
|
||||
|
||||
defer panics.HandlePanic(log, "MAIN", nil)
|
||||
@ -62,7 +62,7 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
|
||||
log.Infof("Listening to TCP on %s", listen)
|
||||
|
||||
log.Infof("Connecting to a node at %s...", rpcServer)
|
||||
rpcClient, err := connectToRPC(params, rpcServer)
|
||||
rpcClient, err := connectToRPC(params, rpcServer, timeout)
|
||||
if err != nil {
|
||||
return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer))
|
||||
}
|
||||
|
@ -27,18 +27,10 @@ func (s *server) maybeAutoCompoundTransaction(transactionBytes []byte, toAddress
|
||||
return nil, err
|
||||
}
|
||||
|
||||
splitTransactions, err := s.maybeSplitTransaction(transaction, changeAddress)
|
||||
splitTransactions, err := s.maybeSplitAndMergeTransaction(transaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(splitTransactions) > 1 {
|
||||
mergeTransaction, err := s.mergeTransaction(splitTransactions, transaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
splitTransactions = append(splitTransactions, mergeTransaction)
|
||||
}
|
||||
|
||||
splitTransactionsBytes := make([][]byte, len(splitTransactions))
|
||||
for i, splitTransaction := range splitTransactions {
|
||||
splitTransactionsBytes[i], err = serialization.SerializePartiallySignedTransaction(splitTransaction)
|
||||
@ -113,8 +105,8 @@ func (s *server) mergeTransaction(
|
||||
return serialization.DeserializePartiallySignedTransaction(mergeTransactionBytes)
|
||||
}
|
||||
|
||||
func (s *server) maybeSplitTransaction(transaction *serialization.PartiallySignedTransaction,
|
||||
changeAddress util.Address) ([]*serialization.PartiallySignedTransaction, error) {
|
||||
func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.PartiallySignedTransaction, toAddress util.Address,
|
||||
changeAddress util.Address, changeWalletAddress *walletAddress) ([]*serialization.PartiallySignedTransaction, error) {
|
||||
|
||||
transactionMass, err := s.estimateMassAfterSignatures(transaction)
|
||||
if err != nil {
|
||||
@ -141,6 +133,20 @@ func (s *server) maybeSplitTransaction(transaction *serialization.PartiallySigne
|
||||
}
|
||||
}
|
||||
|
||||
if len(splitTransactions) > 1 {
|
||||
mergeTransaction, err := s.mergeTransaction(splitTransactions, transaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Recursion will be 2-3 iterations deep even in the rarest` cases, so considered safe..
|
||||
splitMergeTransaction, err := s.maybeSplitAndMergeTransaction(mergeTransaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
splitTransactions = append(splitTransactions, splitMergeTransaction...)
|
||||
|
||||
}
|
||||
|
||||
return splitTransactions, nil
|
||||
}
|
||||
|
||||
|
@ -35,9 +35,10 @@ func send(conf *sendConfig) error {
|
||||
|
||||
createUnsignedTransactionsResponse, err :=
|
||||
daemonClient.CreateUnsignedTransactions(ctx, &pb.CreateUnsignedTransactionsRequest{
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
UseExistingChangeAddress: conf.UseExistingChangeAddress,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -64,7 +65,12 @@ func send(conf *sendConfig) error {
|
||||
fmt.Printf("Broadcasting %d transactions\n", len(signedTransactions))
|
||||
}
|
||||
|
||||
response, err := daemonClient.Broadcast(ctx, &pb.BroadcastRequest{Transactions: signedTransactions})
|
||||
// Since we waited for user input when getting the password, which could take unbound amount of time -
|
||||
// create a new context for broadcast, to reset the timeout.
|
||||
broadcastCtx, broadcastCancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer broadcastCancel()
|
||||
|
||||
response, err := daemonClient.Broadcast(broadcastCtx, &pb.BroadcastRequest{Transactions: signedTransactions})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ package main
|
||||
import "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/server"
|
||||
|
||||
func startDaemon(conf *startDaemonConfig) error {
|
||||
return server.Start(conf.NetParams(), conf.Listen, conf.RPCServer, conf.KeysFile, conf.Profile)
|
||||
return server.Start(conf.NetParams(), conf.Listen, conf.RPCServer, conf.KeysFile, conf.Profile, conf.Timeout)
|
||||
}
|
||||
|
@ -64,6 +64,12 @@ type consensus struct {
|
||||
virtualNotUpdated bool
|
||||
}
|
||||
|
||||
// In order to prevent a situation that the consensus lock is held for too much time, we
|
||||
// release the lock each time we resolve 100 blocks.
|
||||
// Note: `virtualResolveChunk` should be smaller than `params.FinalityDuration` in order to avoid a situation
|
||||
// where UpdatePruningPointByVirtual skips a pruning point.
|
||||
const virtualResolveChunk = 100
|
||||
|
||||
func (s *consensus) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
@ -194,11 +200,50 @@ func (s *consensus) BuildBlockTemplate(coinbaseData *externalapi.DomainCoinbaseD
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) error {
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, updateVirtual bool) error {
|
||||
if updateVirtual {
|
||||
s.lock.Lock()
|
||||
if s.virtualNotUpdated {
|
||||
// We enter the loop in locked state
|
||||
for {
|
||||
_, isCompletelyResolved, err := s.resolveVirtualChunkNoLock(virtualResolveChunk)
|
||||
if err != nil {
|
||||
s.lock.Unlock()
|
||||
return err
|
||||
}
|
||||
if isCompletelyResolved {
|
||||
// Make sure we enter the block insertion function w/o releasing the lock.
|
||||
// Otherwise, we might actually enter it in `s.virtualNotUpdated == true` state
|
||||
_, err = s.validateAndInsertBlockNoLock(block, updateVirtual)
|
||||
// Finally, unlock for the last iteration and return
|
||||
s.lock.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Unlock to allow other threads to enter consensus
|
||||
s.lock.Unlock()
|
||||
// Lock for the next iteration
|
||||
s.lock.Lock()
|
||||
}
|
||||
}
|
||||
_, err := s.validateAndInsertBlockNoLock(block, updateVirtual)
|
||||
s.lock.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.validateAndInsertBlockWithLock(block, updateVirtual)
|
||||
}
|
||||
|
||||
func (s *consensus) validateAndInsertBlockWithLock(block *externalapi.DomainBlock, updateVirtual bool) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
_, err := s.validateAndInsertBlockNoLock(block, shouldValidateAgainstUTXO)
|
||||
_, err := s.validateAndInsertBlockNoLock(block, updateVirtual)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -206,19 +251,6 @@ func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, shoul
|
||||
}
|
||||
|
||||
func (s *consensus) validateAndInsertBlockNoLock(block *externalapi.DomainBlock, updateVirtual bool) (*externalapi.VirtualChangeSet, error) {
|
||||
// If virtual is in non-updated state, and the caller requests updating virtual -- then we must first
|
||||
// resolve virtual so that the new block can be fully processed properly
|
||||
if updateVirtual && s.virtualNotUpdated {
|
||||
for s.virtualNotUpdated {
|
||||
// We use 10000 << finality interval. See comment in `ResolveVirtual`.
|
||||
// We give up responsiveness of consensus in this rare case.
|
||||
_, err := s.resolveVirtualNoLock(10000) // Note `s.virtualNotUpdated` is updated within the call
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtualChangeSet, blockStatus, err := s.blockProcessor.ValidateAndInsertBlock(block, updateVirtual)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -257,7 +289,7 @@ func (s *consensus) sendBlockAddedEvent(block *externalapi.DomainBlock, blockSta
|
||||
}
|
||||
|
||||
func (s *consensus) sendVirtualChangedEvent(virtualChangeSet *externalapi.VirtualChangeSet, wasVirtualUpdated bool) error {
|
||||
if !wasVirtualUpdated || s.consensusEventsChan == nil {
|
||||
if !wasVirtualUpdated || s.consensusEventsChan == nil || virtualChangeSet == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -888,41 +920,69 @@ func (s *consensus) PopulateMass(transaction *externalapi.DomainTransaction) {
|
||||
s.transactionValidator.PopulateMass(transaction)
|
||||
}
|
||||
|
||||
func (s *consensus) ResolveVirtual() (bool, error) {
|
||||
func (s *consensus) ResolveVirtual(progressReportCallback func(uint64, uint64)) error {
|
||||
virtualDAAScoreStart, err := s.GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
if i%10 == 0 && progressReportCallback != nil {
|
||||
virtualDAAScore, err := s.GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
progressReportCallback(virtualDAAScoreStart, virtualDAAScore)
|
||||
}
|
||||
|
||||
_, isCompletelyResolved, err := s.resolveVirtualChunkWithLock(virtualResolveChunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isCompletelyResolved {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *consensus) resolveVirtualChunkWithLock(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
// In order to prevent a situation that the consensus lock is held for too much time, we
|
||||
// release the lock each time resolve 100 blocks.
|
||||
// Note: maxBlocksToResolve should be smaller than finality interval in order to avoid a situation
|
||||
// where UpdatePruningPointByVirtual skips a pruning point.
|
||||
return s.resolveVirtualNoLock(100)
|
||||
return s.resolveVirtualChunkNoLock(maxBlocksToResolve)
|
||||
}
|
||||
|
||||
func (s *consensus) resolveVirtualNoLock(maxBlocksToResolve uint64) (bool, error) {
|
||||
func (s *consensus) resolveVirtualChunkNoLock(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
virtualChangeSet, isCompletelyResolved, err := s.consensusStateManager.ResolveVirtual(maxBlocksToResolve)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
s.virtualNotUpdated = !isCompletelyResolved
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
err = s.pruningManager.UpdatePruningPointByVirtual(stagingArea)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = staging.CommitAllChanges(s.databaseContext, stagingArea)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = s.pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = s.sendVirtualChangedEvent(virtualChangeSet, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return isCompletelyResolved, nil
|
||||
return virtualChangeSet, isCompletelyResolved, nil
|
||||
}
|
||||
|
||||
func (s *consensus) BuildPruningPointProof() (*externalapi.PruningPointProof, error) {
|
||||
|
@ -515,6 +515,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
blocksWithTrustedDataDAAWindowStore: daaWindowStore,
|
||||
|
||||
consensusEventsChan: consensusEventsChan,
|
||||
virtualNotUpdated: true,
|
||||
}
|
||||
|
||||
if isOldReachabilityInitialized {
|
||||
@ -539,6 +540,8 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// If the virtual moved before shutdown but the pruning point hasn't, we
|
||||
// move it if needed.
|
||||
stagingArea := model.NewStagingArea()
|
||||
err = pruningManager.UpdatePruningPointByVirtual(stagingArea)
|
||||
if err != nil {
|
||||
@ -550,6 +553,11 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return c, false, nil
|
||||
}
|
||||
|
||||
|
@ -589,18 +589,13 @@ func TestFinalityResolveVirtual(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
isCompletelyResolved, err := tc.ResolveVirtual()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if isCompletelyResolved {
|
||||
t.Log("Resolved virtual")
|
||||
break
|
||||
}
|
||||
err = tc.ResolveVirtual(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
t.Log("Resolved virtual")
|
||||
|
||||
sideChainTipGHOSTDAGData, err = tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), stagingArea, sideChainTipHash, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -5,7 +5,7 @@ type Consensus interface {
|
||||
Init(skipAddingGenesis bool) error
|
||||
BuildBlock(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlock, error)
|
||||
BuildBlockTemplate(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlockTemplate, error)
|
||||
ValidateAndInsertBlock(block *DomainBlock, shouldValidateAgainstUTXO bool) error
|
||||
ValidateAndInsertBlock(block *DomainBlock, updateVirtual bool) error
|
||||
ValidateAndInsertBlockWithTrustedData(block *BlockWithTrustedData, validateUTXO bool) error
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
|
||||
ImportPruningPoints(pruningPoints []BlockHeader) error
|
||||
@ -48,7 +48,7 @@ type Consensus interface {
|
||||
Anticone(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
EstimateNetworkHashesPerSecond(startHash *DomainHash, windowSize int) (uint64, error)
|
||||
PopulateMass(transaction *DomainTransaction)
|
||||
ResolveVirtual() (bool, error)
|
||||
ResolveVirtual(progressReportCallback func(uint64, uint64)) error
|
||||
BlockDAAWindowHashes(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
TrustedDataDataDAAHeader(trustedBlockHash, daaBlockHash *DomainHash, daaBlockWindowIndex uint64) (*TrustedDataDataDAAHeader, error)
|
||||
TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
|
@ -2,6 +2,7 @@ package externalapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -242,6 +243,23 @@ func (spk *ScriptPublicKey) Equal(other *ScriptPublicKey) bool {
|
||||
return bytes.Equal(spk.Script, other.Script)
|
||||
}
|
||||
|
||||
// String stringifies a ScriptPublicKey.
|
||||
func (spk *ScriptPublicKey) String() string {
|
||||
var versionBytes = make([]byte, 2) // uint16
|
||||
binary.LittleEndian.PutUint16(versionBytes, spk.Version)
|
||||
versionString := string(versionBytes)
|
||||
scriptString := string(spk.Script)
|
||||
return versionString + scriptString
|
||||
}
|
||||
|
||||
// NewScriptPublicKeyFromString converts the given string to a scriptPublicKey
|
||||
func NewScriptPublicKeyFromString(ScriptPublicKeyString string) *ScriptPublicKey {
|
||||
bytes := []byte(ScriptPublicKeyString)
|
||||
version := binary.LittleEndian.Uint16(bytes[:2])
|
||||
script := bytes[2:]
|
||||
return &ScriptPublicKey{Script: script, Version: version}
|
||||
}
|
||||
|
||||
// DomainTransactionOutput represents a Kaspad transaction output
|
||||
type DomainTransactionOutput struct {
|
||||
Value uint64
|
||||
|
@ -49,7 +49,7 @@ type TestConsensus interface {
|
||||
*externalapi.VirtualChangeSet, error)
|
||||
UpdatePruningPointByVirtual() error
|
||||
|
||||
ResolveVirtualWithMaxParam(maxBlocksToResolve uint64) (bool, error)
|
||||
ResolveVirtualWithMaxParam(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error)
|
||||
|
||||
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
|
||||
ToJSON(w io.Writer) error
|
||||
|
@ -5,22 +5,22 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"github.com/pkg/errors"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.ResolveVirtual")
|
||||
defer onEnd()
|
||||
|
||||
readStagingArea := model.NewStagingArea()
|
||||
tips, err := csm.consensusStateStore.Tips(readStagingArea, csm.databaseContext)
|
||||
// tipsInDecreasingGHOSTDAGParentSelectionOrder returns the current DAG tips in decreasing parent selection order.
|
||||
// This means that the first tip in the resulting list would be the GHOSTDAG selected parent, and if removed from the list,
|
||||
// the second tip would be the selected parent, and so on.
|
||||
func (csm *consensusStateManager) tipsInDecreasingGHOSTDAGParentSelectionOrder(stagingArea *model.StagingArea) ([]*externalapi.DomainHash, error) {
|
||||
tips, err := csm.consensusStateStore.Tips(stagingArea, csm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sortErr error
|
||||
sort.Slice(tips, func(i, j int) bool {
|
||||
selectedParent, err := csm.ghostdagManager.ChooseSelectedParent(readStagingArea, tips[i], tips[j])
|
||||
selectedParent, err := csm.ghostdagManager.ChooseSelectedParent(stagingArea, tips[i], tips[j])
|
||||
if err != nil {
|
||||
sortErr = err
|
||||
return false
|
||||
@ -29,16 +29,22 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*ex
|
||||
return selectedParent.Equal(tips[i])
|
||||
})
|
||||
if sortErr != nil {
|
||||
return nil, false, sortErr
|
||||
return nil, sortErr
|
||||
}
|
||||
return tips, nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) findNextPendingTip(stagingArea *model.StagingArea) (*externalapi.DomainHash, externalapi.BlockStatus, error) {
|
||||
orderedTips, err := csm.tipsInDecreasingGHOSTDAGParentSelectionOrder(stagingArea)
|
||||
if err != nil {
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
}
|
||||
|
||||
var selectedTip *externalapi.DomainHash
|
||||
isCompletelyResolved := true
|
||||
for _, tip := range tips {
|
||||
for _, tip := range orderedTips {
|
||||
log.Debugf("Resolving tip %s", tip)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(readStagingArea, tip)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(stagingArea, tip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
}
|
||||
|
||||
if isViolatingFinality {
|
||||
@ -49,55 +55,147 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*ex
|
||||
continue
|
||||
}
|
||||
|
||||
resolveStagingArea := model.NewStagingArea()
|
||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(resolveStagingArea, tip)
|
||||
status, err := csm.blockStatusStore.Get(csm.databaseContext, stagingArea, tip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
}
|
||||
|
||||
resolveTip := tip
|
||||
hasMoreUnverifiedThanMax := maxBlocksToResolve != 0 && uint64(len(unverifiedBlocks)) > maxBlocksToResolve
|
||||
if hasMoreUnverifiedThanMax {
|
||||
resolveTip = unverifiedBlocks[uint64(len(unverifiedBlocks))-maxBlocksToResolve]
|
||||
log.Debugf("Has more than %d blocks to resolve. Changing the resolve tip to %s", maxBlocksToResolve, resolveTip)
|
||||
}
|
||||
|
||||
blockStatus, reversalData, err := csm.resolveBlockStatus(resolveStagingArea, resolveTip, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if blockStatus == externalapi.StatusUTXOValid {
|
||||
selectedTip = resolveTip
|
||||
isCompletelyResolved = !hasMoreUnverifiedThanMax
|
||||
|
||||
err = staging.CommitAllChanges(csm.databaseContext, resolveStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if reversalData != nil {
|
||||
err = csm.ReverseUTXODiffs(resolveTip, reversalData)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
break
|
||||
if status == externalapi.StatusUTXOValid || status == externalapi.StatusUTXOPendingVerification {
|
||||
return tip, status, nil
|
||||
}
|
||||
}
|
||||
|
||||
if selectedTip == nil {
|
||||
log.Warnf("Non of the DAG tips are valid")
|
||||
return nil, true, nil
|
||||
return nil, externalapi.StatusInvalid, nil
|
||||
}
|
||||
|
||||
// getGHOSTDAGLowerTips returns the set of tips which are lower in GHOSTDAG parent selection order than `pendingTip`. i.e.,
|
||||
// they can be added to virtual parents but `pendingTip` will remain the virtual selected parent
|
||||
func (csm *consensusStateManager) getGHOSTDAGLowerTips(stagingArea *model.StagingArea, pendingTip *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
tips, err := csm.consensusStateStore.Tips(stagingArea, csm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, readStagingArea, model.VirtualBlockHash, false)
|
||||
lowerTips := []*externalapi.DomainHash{pendingTip}
|
||||
for _, tip := range tips {
|
||||
if tip.Equal(pendingTip) {
|
||||
continue
|
||||
}
|
||||
selectedParent, err := csm.ghostdagManager.ChooseSelectedParent(stagingArea, tip, pendingTip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if selectedParent.Equal(pendingTip) {
|
||||
lowerTips = append(lowerTips, tip)
|
||||
}
|
||||
}
|
||||
return lowerTips, nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.ResolveVirtual")
|
||||
defer onEnd()
|
||||
|
||||
// We use a read-only staging area for some read-only actions, to avoid
|
||||
// confusion with the resolve/updateVirtual staging areas below
|
||||
readStagingArea := model.NewStagingArea()
|
||||
|
||||
pendingTip, pendingTipStatus, err := csm.findNextPendingTip(readStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if pendingTip == nil {
|
||||
log.Warnf("None of the DAG tips are valid")
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
previousVirtualSelectedParent, err := csm.virtualSelectedParent(readStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if pendingTipStatus == externalapi.StatusUTXOValid && previousVirtualSelectedParent.Equal(pendingTip) {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
// Resolve a chunk from the pending chain
|
||||
resolveStagingArea := model.NewStagingArea()
|
||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(resolveStagingArea, pendingTip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Initially set the resolve processing point to the pending tip
|
||||
processingPoint := pendingTip
|
||||
|
||||
// Too many blocks to verify, so we only process a chunk and return
|
||||
if maxBlocksToResolve != 0 && uint64(len(unverifiedBlocks)) > maxBlocksToResolve {
|
||||
processingPointIndex := uint64(len(unverifiedBlocks)) - maxBlocksToResolve
|
||||
processingPoint = unverifiedBlocks[processingPointIndex]
|
||||
isNewVirtualSelectedParent, err := csm.isNewSelectedTip(readStagingArea, processingPoint, previousVirtualSelectedParent)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// We must find a processing point which wins previous virtual selected parent
|
||||
// even if we process more than `maxBlocksToResolve` for that.
|
||||
// Otherwise, internal UTXO diff logic gets all messed up
|
||||
for !isNewVirtualSelectedParent {
|
||||
if processingPointIndex == 0 {
|
||||
return nil, false, errors.Errorf(
|
||||
"Expecting the pending tip %s to overcome the previous selected parent %s", pendingTip, previousVirtualSelectedParent)
|
||||
}
|
||||
processingPointIndex--
|
||||
processingPoint = unverifiedBlocks[processingPointIndex]
|
||||
isNewVirtualSelectedParent, err = csm.isNewSelectedTip(readStagingArea, processingPoint, previousVirtualSelectedParent)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
log.Debugf("Has more than %d blocks to resolve. Setting the resolve processing point to %s", maxBlocksToResolve, processingPoint)
|
||||
}
|
||||
|
||||
processingPointStatus, reversalData, err := csm.resolveBlockStatus(
|
||||
resolveStagingArea, processingPoint, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if processingPointStatus == externalapi.StatusUTXOValid {
|
||||
err = staging.CommitAllChanges(csm.databaseContext, resolveStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if reversalData != nil {
|
||||
err = csm.ReverseUTXODiffs(processingPoint, reversalData)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isActualTip := processingPoint.Equal(pendingTip)
|
||||
isCompletelyResolved := isActualTip && processingPointStatus == externalapi.StatusUTXOValid
|
||||
|
||||
updateVirtualStagingArea := model.NewStagingArea()
|
||||
virtualUTXODiff, err := csm.updateVirtualWithParents(updateVirtualStagingArea, []*externalapi.DomainHash{selectedTip})
|
||||
|
||||
virtualParents := []*externalapi.DomainHash{processingPoint}
|
||||
// If `isCompletelyResolved`, set virtual correctly with all tips which have less blue work than pending
|
||||
if isCompletelyResolved {
|
||||
lowerTips, err := csm.getGHOSTDAGLowerTips(readStagingArea, pendingTip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
log.Debugf("Picking virtual parents from relevant tips len: %d", len(lowerTips))
|
||||
|
||||
virtualParents, err = csm.pickVirtualParents(readStagingArea, lowerTips)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
log.Debugf("Picked virtual parents: %s", virtualParents)
|
||||
}
|
||||
virtualUTXODiff, err := csm.updateVirtualWithParents(updateVirtualStagingArea, virtualParents)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -108,12 +206,12 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*ex
|
||||
}
|
||||
|
||||
selectedParentChainChanges, err := csm.dagTraversalManager.
|
||||
CalculateChainPath(readStagingArea, oldVirtualGHOSTDAGData.SelectedParent(), selectedTip)
|
||||
CalculateChainPath(updateVirtualStagingArea, previousVirtualSelectedParent, processingPoint)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
virtualParents, err := csm.dagTopologyManager.Parents(readStagingArea, model.VirtualBlockHash)
|
||||
virtualParentsOutcome, err := csm.dagTopologyManager.Parents(updateVirtualStagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -121,6 +219,6 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*ex
|
||||
return &externalapi.VirtualChangeSet{
|
||||
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
||||
VirtualUTXODiff: virtualUTXODiff,
|
||||
VirtualParents: virtualParents,
|
||||
VirtualParents: virtualParentsOutcome,
|
||||
}, isCompletelyResolved, nil
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.St
|
||||
return externalapi.StatusUTXOValid, nil, nil
|
||||
}
|
||||
|
||||
oldSelectedTip, err := csm.selectedTip(stagingArea)
|
||||
oldSelectedTip, err := csm.virtualSelectedParent(stagingArea)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@ -298,7 +298,7 @@ func (csm *consensusStateManager) isNewSelectedTip(stagingArea *model.StagingAre
|
||||
return blockHash.Equal(newSelectedTip), nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) selectedTip(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
func (csm *consensusStateManager) virtualSelectedParent(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
virtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, model.VirtualBlockHash, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1,6 +1,9 @@
|
||||
package consensusstatemanager_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"testing"
|
||||
|
||||
@ -21,11 +24,14 @@ func TestAddBlockBetweenResolveVirtualCalls(t *testing.T) {
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 10
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
@ -40,6 +46,7 @@ func TestAddBlockBetweenResolveVirtualCalls(t *testing.T) {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
@ -49,7 +56,7 @@ func TestAddBlockBetweenResolveVirtualCalls(t *testing.T) {
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
_, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
_, _, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
@ -68,7 +75,7 @@ func TestAddBlockBetweenResolveVirtualCalls(t *testing.T) {
|
||||
}
|
||||
|
||||
// Resolve one more step
|
||||
isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
@ -78,14 +85,17 @@ func TestAddBlockBetweenResolveVirtualCalls(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block during virtual resolution of reorg: %+v", err)
|
||||
}
|
||||
hashes = append(hashes, consensushashing.BlockHash(blockTemplate.Block))
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
@ -100,11 +110,14 @@ func TestAddGenesisChildAfterOneResolveVirtualCall(t *testing.T) {
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 6
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
@ -119,6 +132,7 @@ func TestAddGenesisChildAfterOneResolveVirtualCall(t *testing.T) {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
@ -128,7 +142,7 @@ func TestAddGenesisChildAfterOneResolveVirtualCall(t *testing.T) {
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
@ -140,11 +154,13 @@ func TestAddGenesisChildAfterOneResolveVirtualCall(t *testing.T) {
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
@ -159,11 +175,14 @@ func TestAddGenesisChildAfterTwoResolveVirtualCalls(t *testing.T) {
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 6
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
@ -178,6 +197,7 @@ func TestAddGenesisChildAfterTwoResolveVirtualCalls(t *testing.T) {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
@ -187,13 +207,13 @@ func TestAddGenesisChildAfterTwoResolveVirtualCalls(t *testing.T) {
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
_, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
_, _, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
// Resolve one more step
|
||||
isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
@ -205,10 +225,220 @@ func TestAddGenesisChildAfterTwoResolveVirtualCalls(t *testing.T) {
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
func TestResolveVirtualBackAndForthReorgs(t *testing.T) {
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestAddGenesisChildAfterTwoResolveVirtualCalls")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
blocks := make(map[externalapi.DomainHash]string)
|
||||
blocks[*consensusConfig.GenesisHash] = "g"
|
||||
blocks[*model.VirtualBlockHash] = "v"
|
||||
printfDebug("%s\n\n", consensusConfig.GenesisHash)
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 6
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
blocks[*previousBlockHash] = fmt.Sprintf("A_%d", i)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
printfDebug("A_%d: %s\n", i, previousBlockHash)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
printfDebug("\n")
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
|
||||
firstChainTip := previousBlockHash
|
||||
|
||||
// Mine a chain with more blocks, to re-organize the DAG
|
||||
const reorgChainLength = 12 // initialChainLength + 1
|
||||
previousBlockHash = consensusConfig.GenesisHash
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
blocks[*previousBlockHash] = fmt.Sprintf("B_%d", i)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
printfDebug("B_%d: %s\n", i, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
printfDebug("\n")
|
||||
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
|
||||
previousVirtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
virtualChangeSet, _, err := tc.ResolveVirtualWithMaxParam(3)
|
||||
if err != nil {
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
newVirtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Make sure the reported change-set is compatible with actual changes.
|
||||
// Checking this for one call should suffice to avoid possible bugs.
|
||||
reportedPreviousVirtualSelectedParent := virtualChangeSet.VirtualSelectedParentChainChanges.Removed[0]
|
||||
reportedNewVirtualSelectedParent := virtualChangeSet.VirtualSelectedParentChainChanges.
|
||||
Added[len(virtualChangeSet.VirtualSelectedParentChainChanges.Added)-1]
|
||||
|
||||
if !previousVirtualSelectedParent.Equal(reportedPreviousVirtualSelectedParent) {
|
||||
t.Fatalf("The reported changeset is incompatible with actual changes")
|
||||
}
|
||||
if !newVirtualSelectedParent.Equal(reportedNewVirtualSelectedParent) {
|
||||
t.Fatalf("The reported changeset is incompatible with actual changes")
|
||||
}
|
||||
|
||||
// Resolve one more step
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
|
||||
// Now get the first chain back to the wining position
|
||||
previousBlockHash = firstChainTip
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
blocks[*previousBlockHash] = fmt.Sprintf("A_%d", initialChainLength+i)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
printfDebug("A_%d: %s\n", initialChainLength+i, previousBlockHash)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", initialChainLength+i, err)
|
||||
}
|
||||
}
|
||||
|
||||
printfDebug("\n")
|
||||
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
func verifyUtxoDiffPathToRoot(t *testing.T, tc testapi.TestConsensus, stagingArea *model.StagingArea, block, utxoDiffRoot *externalapi.DomainHash) {
|
||||
current := block
|
||||
for !current.Equal(utxoDiffRoot) {
|
||||
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, current)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
if !hasUTXODiffChild {
|
||||
t.Fatalf("%s is expected to have a UTXO diff child", current)
|
||||
}
|
||||
current, err = tc.UTXODiffStore().UTXODiffChild(tc.DatabaseContext(), stagingArea, current)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyUtxoDiffPaths(t *testing.T, tc testapi.TestConsensus, hashes []*externalapi.DomainHash) {
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
virtualGHOSTDAGData, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), stagingArea, model.VirtualBlockHash, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
utxoDiffRoot := virtualGHOSTDAGData.SelectedParent()
|
||||
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, utxoDiffRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
if hasUTXODiffChild {
|
||||
t.Fatalf("Virtual selected parent is not expected to have an explicit diff child")
|
||||
}
|
||||
_, err = tc.UTXODiffStore().UTXODiff(tc.DatabaseContext(), stagingArea, utxoDiffRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Virtual selected parent is expected to have a utxo diff: %+v", err)
|
||||
}
|
||||
|
||||
for _, block := range hashes {
|
||||
hasUTXODiffChild, err = tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
isOnVirtualSelectedChain, err := tc.DAGTopologyManager().IsInSelectedParentChainOf(stagingArea, block, utxoDiffRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// We expect a valid path to root in both cases: (i) block has a diff child, (ii) block is on the virtual selected chain
|
||||
if hasUTXODiffChild || isOnVirtualSelectedChain {
|
||||
verifyUtxoDiffPathToRoot(t, tc, stagingArea, block, utxoDiffRoot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printfDebug(format string, a ...any) {
|
||||
// Uncomment below when debugging the test
|
||||
//fmt.Printf(format, a...)
|
||||
}
|
||||
|
||||
func printUtxoDiffChildren(t *testing.T, tc testapi.TestConsensus, hashes []*externalapi.DomainHash, blocks map[externalapi.DomainHash]string) {
|
||||
printfDebug("\n===============================\nBlock\t\tDiff child\n")
|
||||
stagingArea := model.NewStagingArea()
|
||||
for _, block := range hashes {
|
||||
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
if hasUTXODiffChild {
|
||||
utxoDiffChild, err := tc.UTXODiffStore().UTXODiffChild(tc.DatabaseContext(), stagingArea, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
printfDebug("%s\t\t\t%s\n", blocks[*block], blocks[*utxoDiffChild])
|
||||
} else {
|
||||
printfDebug("%s\n", blocks[*block])
|
||||
}
|
||||
}
|
||||
printfDebug("\n===============================\n")
|
||||
}
|
||||
|
@ -56,12 +56,6 @@ func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHa
|
||||
return err
|
||||
}
|
||||
|
||||
// We stop reversing when current's UTXODiffChild is not current's SelectedParent
|
||||
if !currentBlockGHOSTDAGData.SelectedParent().Equal(currentBlockUTXODiffChild) {
|
||||
log.Debugf("Block %s's UTXODiffChild is not it's selected parent - finish reversing", currentBlock)
|
||||
break
|
||||
}
|
||||
|
||||
currentUTXODiff := previousUTXODiff.Reversed()
|
||||
|
||||
// retrieve current utxoDiff for Bi, to be used by next block
|
||||
@ -75,6 +69,12 @@ func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHa
|
||||
return err
|
||||
}
|
||||
|
||||
// We stop reversing when current's UTXODiffChild is not current's SelectedParent
|
||||
if !currentBlockGHOSTDAGData.SelectedParent().Equal(currentBlockUTXODiffChild) {
|
||||
log.Debugf("Block %s's UTXODiffChild is not it's selected parent - finish reversing", currentBlock)
|
||||
break
|
||||
}
|
||||
|
||||
previousBlock = currentBlock
|
||||
previousBlockGHOSTDAGData = currentBlockGHOSTDAGData
|
||||
|
||||
|
@ -110,7 +110,7 @@ func (csm *consensusStateManager) updateSelectedTipUTXODiff(
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "updateSelectedTipUTXODiff")
|
||||
defer onEnd()
|
||||
|
||||
selectedTip, err := csm.selectedTip(stagingArea)
|
||||
selectedTip, err := csm.virtualSelectedParent(stagingArea)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -772,78 +772,41 @@ func (pm *pruningManager) calculateDiffBetweenPreviousAndCurrentPruningPoints(st
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentPruningGhostDAG, err := pm.ghostdagDataStore.Get(pm.databaseContext, stagingArea, currentPruningHash, false)
|
||||
|
||||
utxoDiff := utxo.NewMutableUTXODiff()
|
||||
|
||||
iterator, err := pm.dagTraversalManager.SelectedChildIterator(stagingArea, currentPruningHash, previousPruningHash, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
previousPruningGhostDAG, err := pm.ghostdagDataStore.Get(pm.databaseContext, stagingArea, previousPruningHash, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
defer iterator.Close()
|
||||
|
||||
for ok := iterator.First(); ok; ok = iterator.Next() {
|
||||
child, err := iterator.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chainBlockAcceptanceData, err := pm.acceptanceDataStore.Get(pm.databaseContext, stagingArea, child)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chainBlockHeader, err := pm.blockHeaderStore.BlockHeader(pm.databaseContext, stagingArea, child)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, blockAcceptanceData := range chainBlockAcceptanceData {
|
||||
for _, transactionAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
||||
if transactionAcceptanceData.IsAccepted {
|
||||
err = utxoDiff.AddTransaction(transactionAcceptanceData.Transaction, chainBlockHeader.DAAScore())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentPruningCurrentDiffChild := currentPruningHash
|
||||
previousPruningCurrentDiffChild := previousPruningHash
|
||||
// We need to use BlueWork because it's the only thing that's monotonic in the whole DAG
|
||||
// We use the BlueWork to know which point is currently lower on the DAG so we can keep climbing its children,
|
||||
// that way we keep climbing on the lowest point until they both reach the exact same descendant
|
||||
currentPruningCurrentDiffChildBlueWork := currentPruningGhostDAG.BlueWork()
|
||||
previousPruningCurrentDiffChildBlueWork := previousPruningGhostDAG.BlueWork()
|
||||
|
||||
var diffHashesFromPrevious []*externalapi.DomainHash
|
||||
var diffHashesFromCurrent []*externalapi.DomainHash
|
||||
for {
|
||||
// if currentPruningCurrentDiffChildBlueWork > previousPruningCurrentDiffChildBlueWork
|
||||
if currentPruningCurrentDiffChildBlueWork.Cmp(previousPruningCurrentDiffChildBlueWork) == 1 {
|
||||
diffHashesFromPrevious = append(diffHashesFromPrevious, previousPruningCurrentDiffChild)
|
||||
previousPruningCurrentDiffChild, err = pm.utxoDiffStore.UTXODiffChild(pm.databaseContext, stagingArea, previousPruningCurrentDiffChild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diffChildGhostDag, err := pm.ghostdagDataStore.Get(pm.databaseContext, stagingArea, previousPruningCurrentDiffChild, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
previousPruningCurrentDiffChildBlueWork = diffChildGhostDag.BlueWork()
|
||||
} else if currentPruningCurrentDiffChild.Equal(previousPruningCurrentDiffChild) {
|
||||
break
|
||||
} else {
|
||||
diffHashesFromCurrent = append(diffHashesFromCurrent, currentPruningCurrentDiffChild)
|
||||
currentPruningCurrentDiffChild, err = pm.utxoDiffStore.UTXODiffChild(pm.databaseContext, stagingArea, currentPruningCurrentDiffChild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diffChildGhostDag, err := pm.ghostdagDataStore.Get(pm.databaseContext, stagingArea, currentPruningCurrentDiffChild, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentPruningCurrentDiffChildBlueWork = diffChildGhostDag.BlueWork()
|
||||
}
|
||||
}
|
||||
// The order in which we apply the diffs should be from top to bottom, but we traversed from bottom to top
|
||||
// so we apply the diffs in reverse order.
|
||||
oldDiff := utxo.NewMutableUTXODiff()
|
||||
for i := len(diffHashesFromPrevious) - 1; i >= 0; i-- {
|
||||
utxoDiff, err := pm.utxoDiffStore.UTXODiff(pm.databaseContext, stagingArea, diffHashesFromPrevious[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = oldDiff.WithDiffInPlace(utxoDiff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
newDiff := utxo.NewMutableUTXODiff()
|
||||
for i := len(diffHashesFromCurrent) - 1; i >= 0; i-- {
|
||||
utxoDiff, err := pm.utxoDiffStore.UTXODiff(pm.databaseContext, stagingArea, diffHashesFromCurrent[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = newDiff.WithDiffInPlace(utxoDiff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return oldDiff.DiffFrom(newDiff.ToImmutable())
|
||||
return utxoDiff.ToImmutable(), err
|
||||
}
|
||||
|
||||
// finalityScore is the number of finality intervals passed since
|
||||
@ -937,7 +900,7 @@ func (pm *pruningManager) updatePruningPoint() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pm.shouldSanityCheckPruningUTXOSet {
|
||||
if pm.shouldSanityCheckPruningUTXOSet && !pruningPoint.Equal(pm.genesisHash) {
|
||||
err = pm.validateUTXOSetFitsCommitment(stagingArea, pruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -112,11 +112,11 @@ func (tc *testConsensus) AddUTXOInvalidBlock(parentHashes []*externalapi.DomainH
|
||||
return consensushashing.BlockHash(block), virtualChangeSet, nil
|
||||
}
|
||||
|
||||
func (tc *testConsensus) ResolveVirtualWithMaxParam(maxBlocksToResolve uint64) (bool, error) {
|
||||
func (tc *testConsensus) ResolveVirtualWithMaxParam(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
tc.lock.Lock()
|
||||
defer tc.lock.Unlock()
|
||||
|
||||
return tc.resolveVirtualNoLock(maxBlocksToResolve)
|
||||
return tc.resolveVirtualChunkNoLock(maxBlocksToResolve)
|
||||
}
|
||||
|
||||
// jsonBlock is a json representation of a block in mine format
|
||||
|
@ -1,10 +1,80 @@
|
||||
package hashes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewBlockHash(t *testing.T) {
|
||||
datas := [][]byte{
|
||||
{},
|
||||
{1},
|
||||
{5, 199, 126, 44, 71, 32, 82, 139, 122, 217, 43, 48, 52, 112, 40, 209, 180, 83, 139, 231, 72, 48, 136, 48, 168, 226, 133, 7, 60, 4, 160, 205},
|
||||
{42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
tests := []struct {
|
||||
hasher HashWriter
|
||||
expected []string
|
||||
}{
|
||||
{NewTransactionHashWriter(), []string{
|
||||
"50272a9e37c728026f93d0eda6ab4467f627338b879076483c88d291193cb3bf",
|
||||
"f9bf7e04c712621a0f4bb75d763f9ef5f73af6c438fd15b80744393bc96398ad",
|
||||
"8e791f3edcc92b71b8de2778efbc4666ee5bd146acbe8723a55bca26b022b0e0",
|
||||
"a6dab1a3088548c62d13a082fa28e870fdbbe51adcd8c364e2ea37e473c04d81",
|
||||
"3b79b78b967233843ad30f707b165eb3d6a91af8338076be8755c46a963c3d1d",
|
||||
}},
|
||||
{NewTransactionIDWriter(), []string{
|
||||
"e5f65efda0894d2b0590c2e9e46e9acc03032f505a1522f5e8c78c5ec70b1d9c",
|
||||
"aea52cf5e5a13da13a52dd69abd636eb1b0f86e58bc1dda6b17886b94593415a",
|
||||
"a50a2f87bdce075740189e9e23907ae22b5addbd875ccb70c116811b1fa5fb18",
|
||||
"0db7a485f7013a346a8f7f5caf73d52ca3c3b5ee101ad8753adedd4235b7236b",
|
||||
"2afc9c855854b0a6e94a722c3451d0cdfc8c11748b78ef65b9786f87b48d0d07",
|
||||
}},
|
||||
{NewTransactionSigningHashWriter(), []string{
|
||||
"34c75037ad62740d4b3228f88f844f7901c07bfacd55a045be518eabc15e52ce",
|
||||
"8523b0471bcbea04575ccaa635eef9f9114f2890bda54367e5ff8caa3878bf82",
|
||||
"a51c49d9eb3d13f9de16e1aa8d1ff17668d55633ce00f36a643ac714b0fb137f",
|
||||
"487f199ef74c3e893e85bd37770e6334575a2d4d113b2e10474593c49807de93",
|
||||
"6392adc33a8e24e9a0a0c4c5f07f9c1cc958ad40c16d7a9a276e374cebb4e32b",
|
||||
}},
|
||||
{NewTransactionSigningHashECDSAWriter(), []string{
|
||||
"b31ad1fbbe41b0e2a90e07c84708b38ba581f0c0e9185416913a04fb6d342027",
|
||||
"c43e1f75ea9df6379b56a95074c2b6289ed8c5a01fff2d49d9d44ad5575c164b",
|
||||
"49085f99fa0084b5436663f757a5916b1e4290c3321707fb76921ed4e47844ec",
|
||||
"3f887e866428de813c1d0463b14eef3ca1363c8187e917dda1eee0ec5996490b",
|
||||
"56de89a8c75f0fee2de61b11ab05d0d42e29ed50879467cf128dd80800a52ada",
|
||||
}},
|
||||
{NewBlockHashWriter(), []string{
|
||||
"a80b6aa20f20b15ebabe2b1949527f78a257594a732e774de637d85e6973a768",
|
||||
"5643023add641f9421187b8c9aa3c6c73227d5ec34131c61a08d35b43e7e4b65",
|
||||
"4dc3bf72045431e46f8839a7d390898f27c887fddd8637149bfb70f732f04334",
|
||||
"15d7648e69023dca65c949a61ea166192049f449c604523494813873b19918a7",
|
||||
"3ac41af8385ea5d902ce6d47f509b7accc9c631f1d57a719d777874467f6d877",
|
||||
}},
|
||||
{NewMerkleBranchHashWriter(), []string{
|
||||
"4de3617db456d01248173f17ec58196e92fbd994b636476db4b875ed2ec84054",
|
||||
"5737cd8b6fca5a30c19a491323a14e6b7021641cb3f8875f10c7a2eafd3cf43f",
|
||||
"a49eeda61cc75e0a8e5915829752fe0ad97620d6d32de7c9883595b0810ca33e",
|
||||
"28f33681dcff1313674e07dacc2d74c3089f6d8cea7a4f8792a71fd870988ee5",
|
||||
"2d53a43a42020a5091c125230bcd8a4cf0eeb188333e68325d4bce58a1c75ca3",
|
||||
}},
|
||||
}
|
||||
|
||||
for _, testVector := range tests {
|
||||
hasher := testVector.hasher
|
||||
for i, data := range datas {
|
||||
hasher.InfallibleWrite(data)
|
||||
res := hasher.Finalize().String()
|
||||
if res != testVector.expected[i] {
|
||||
panic(fmt.Sprintf("expected: %s, got: %s", testVector.expected[i], res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkNewBlockHashWriterSmall(b *testing.B) {
|
||||
r := rand.New(rand.NewSource(0))
|
||||
var someBytes [32]byte
|
||||
|
@ -214,7 +214,8 @@ var MainnetParams = Params{
|
||||
RPCPort: "16110",
|
||||
DefaultPort: "16111",
|
||||
DNSSeeds: []string{
|
||||
"mainnet-dnsseed.daglabs-dev.com",
|
||||
// This DNS seeder is run by Wolfie
|
||||
"mainnet-dnsseed.kas.pa",
|
||||
// This DNS seeder is run by Denis Mashkevich
|
||||
"mainnet-dnsseed-1.kaspanet.org",
|
||||
// This DNS seeder is run by Denis Mashkevich
|
||||
@ -296,7 +297,7 @@ var TestnetParams = Params{
|
||||
Net: appmessage.Testnet,
|
||||
RPCPort: "16210",
|
||||
DefaultPort: "16211",
|
||||
DNSSeeds: []string{"testnet-9-dnsseed.daglabs-dev.com"},
|
||||
DNSSeeds: []string{"testnet-10-dnsseed.kas.pa"},
|
||||
|
||||
// DAG parameters
|
||||
GenesisBlock: &testnetGenesisBlock,
|
||||
|
@ -214,34 +214,19 @@ func syncConsensuses(syncer, syncee externalapi.Consensus) error {
|
||||
return err
|
||||
}
|
||||
|
||||
virtualDAAScoreStart, err := syncee.GetVirtualDAAScore()
|
||||
err = syncer.ResolveVirtual(func(virtualDAAScoreStart uint64, virtualDAAScore uint64) {
|
||||
if estimatedVirtualDAAScoreTarget-virtualDAAScoreStart <= 0 {
|
||||
percents = 100
|
||||
} else {
|
||||
percents = int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
|
||||
}
|
||||
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
percents = 0
|
||||
for i := 0; ; i++ {
|
||||
if i%10 == 0 {
|
||||
virtualDAAScore, err := syncee.GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newPercents := int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
|
||||
if newPercents > percents {
|
||||
percents = newPercents
|
||||
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
|
||||
}
|
||||
}
|
||||
isCompletelyResolved, err := syncee.ResolveVirtual()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompletelyResolved {
|
||||
log.Infof("Resolved virtual")
|
||||
break
|
||||
}
|
||||
}
|
||||
log.Infof("Resolved virtual")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package mempool
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensusreference"
|
||||
"sync"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensusreference"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
miningmanagermodel "github.com/kaspanet/kaspad/domain/miningmanager/model"
|
||||
)
|
||||
@ -42,39 +43,89 @@ func (mp *mempool) ValidateAndInsertTransaction(transaction *externalapi.DomainT
|
||||
return mp.validateAndInsertTransaction(transaction, isHighPriority, allowOrphan)
|
||||
}
|
||||
|
||||
func (mp *mempool) GetTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
||||
func (mp *mempool) GetTransaction(transactionID *externalapi.DomainTransactionID,
|
||||
includeTransactionPool bool,
|
||||
includeOrphanPool bool) (
|
||||
transaction *externalapi.DomainTransaction,
|
||||
isOrphan bool,
|
||||
found bool) {
|
||||
|
||||
mp.mtx.RLock()
|
||||
defer mp.mtx.RUnlock()
|
||||
|
||||
return mp.transactionsPool.getTransaction(transactionID)
|
||||
var transactionfound bool
|
||||
isOrphan = false
|
||||
|
||||
if includeTransactionPool {
|
||||
transaction, transactionfound = mp.transactionsPool.getTransaction(transactionID, true)
|
||||
isOrphan = false
|
||||
}
|
||||
if !transactionfound && includeOrphanPool {
|
||||
transaction, transactionfound = mp.orphansPool.getOrphanTransaction(transactionID)
|
||||
isOrphan = true
|
||||
}
|
||||
|
||||
return transaction, isOrphan, transactionfound
|
||||
}
|
||||
|
||||
func (mp *mempool) AllTransactions() []*externalapi.DomainTransaction {
|
||||
func (mp *mempool) GetTransactionsByAddresses(includeTransactionPool bool, includeOrphanPool bool) (
|
||||
sendingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
sendingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
err error) {
|
||||
mp.mtx.RLock()
|
||||
defer mp.mtx.RUnlock()
|
||||
|
||||
return mp.transactionsPool.getAllTransactions()
|
||||
if includeTransactionPool {
|
||||
sendingInTransactionPool, receivingInTransactionPool, err = mp.transactionsPool.getTransactionsByAddresses()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if includeOrphanPool {
|
||||
sendingInTransactionPool, receivingInOrphanPool, err = mp.orphansPool.getOrphanTransactionsByAddresses()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return sendingInTransactionPool, receivingInTransactionPool, sendingInTransactionPool, receivingInOrphanPool, nil
|
||||
}
|
||||
|
||||
func (mp *mempool) GetOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
||||
func (mp *mempool) AllTransactions(includeTransactionPool bool, includeOrphanPool bool) (
|
||||
transactionPoolTransactions []*externalapi.DomainTransaction,
|
||||
orphanPoolTransactions []*externalapi.DomainTransaction) {
|
||||
|
||||
mp.mtx.RLock()
|
||||
defer mp.mtx.RUnlock()
|
||||
|
||||
return mp.orphansPool.getOrphanTransaction(transactionID)
|
||||
if includeTransactionPool {
|
||||
transactionPoolTransactions = mp.transactionsPool.getAllTransactions()
|
||||
}
|
||||
|
||||
if includeOrphanPool {
|
||||
orphanPoolTransactions = mp.orphansPool.getAllOrphanTransactions()
|
||||
}
|
||||
|
||||
return transactionPoolTransactions, orphanPoolTransactions
|
||||
}
|
||||
|
||||
func (mp *mempool) AllOrphanTransactions() []*externalapi.DomainTransaction {
|
||||
func (mp *mempool) TransactionCount(includeTransactionPool bool, includeOrphanPool bool) int {
|
||||
mp.mtx.RLock()
|
||||
defer mp.mtx.RUnlock()
|
||||
|
||||
return mp.orphansPool.getAllOrphanTransactions()
|
||||
}
|
||||
transactionCount := 0
|
||||
|
||||
func (mp *mempool) TransactionCount() int {
|
||||
mp.mtx.RLock()
|
||||
defer mp.mtx.RUnlock()
|
||||
if includeOrphanPool {
|
||||
transactionCount += mp.orphansPool.orphanTransactionCount()
|
||||
}
|
||||
if includeTransactionPool {
|
||||
transactionCount += mp.transactionsPool.transactionCount()
|
||||
}
|
||||
|
||||
return mp.transactionsPool.transactionCount()
|
||||
return transactionCount
|
||||
}
|
||||
|
||||
func (mp *mempool) HandleNewBlockTransactions(transactions []*externalapi.DomainTransaction) (
|
||||
|
@ -51,7 +51,7 @@ func (mpus *mempoolUTXOSet) addTransaction(transaction *model.MempoolTransaction
|
||||
func (mpus *mempoolUTXOSet) removeTransaction(transaction *model.MempoolTransaction) {
|
||||
for _, input := range transaction.Transaction().Inputs {
|
||||
// If the transaction creating the output spent by this input is in the mempool - restore it's UTXO
|
||||
if _, ok := mpus.mempool.transactionsPool.getTransaction(&input.PreviousOutpoint.TransactionID); ok {
|
||||
if _, ok := mpus.mempool.transactionsPool.getTransaction(&input.PreviousOutpoint.TransactionID, false); ok {
|
||||
mpus.poolUnspentOutputs[input.PreviousOutpoint] = input.UTXOEntry
|
||||
}
|
||||
delete(mpus.transactionByPreviousOutpoint, input.PreviousOutpoint)
|
||||
|
@ -15,3 +15,6 @@ type OutpointToUTXOEntryMap map[externalapi.DomainOutpoint]externalapi.UTXOEntry
|
||||
|
||||
// OutpointToTransactionMap maps an outpoint to a MempoolTransaction
|
||||
type OutpointToTransactionMap map[externalapi.DomainOutpoint]*MempoolTransaction
|
||||
|
||||
// ScriptPublicKeyStringToDomainTransaction maps an outpoint to a DomainTransaction
|
||||
type ScriptPublicKeyStringToDomainTransaction map[string]*externalapi.DomainTransaction
|
||||
|
@ -169,7 +169,7 @@ func (op *orphansPool) processOrphansAfterAcceptedTransaction(acceptedTransactio
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
acceptedOrphans = append(acceptedOrphans, orphan.Transaction())
|
||||
acceptedOrphans = append(acceptedOrphans, orphan.Transaction().Clone()) //these pointers leave the mempool, hence the clone
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -331,15 +331,45 @@ func (op *orphansPool) randomNonHighPriorityOrphan() *model.OrphanTransaction {
|
||||
|
||||
func (op *orphansPool) getOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
||||
if orphanTransaction, ok := op.allOrphans[*transactionID]; ok {
|
||||
return orphanTransaction.Transaction(), true
|
||||
return orphanTransaction.Transaction().Clone(), true //this pointer leaves the mempool, hence we clone.
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (op *orphansPool) getAllOrphanTransactions() []*externalapi.DomainTransaction {
|
||||
allOrphanTransactions := make([]*externalapi.DomainTransaction, 0, len(op.allOrphans))
|
||||
func (op *orphansPool) getOrphanTransactionsByAddresses() (
|
||||
sending model.ScriptPublicKeyStringToDomainTransaction,
|
||||
receiving model.ScriptPublicKeyStringToDomainTransaction,
|
||||
err error) {
|
||||
sending = make(model.ScriptPublicKeyStringToDomainTransaction)
|
||||
receiving = make(model.ScriptPublicKeyStringToDomainTransaction, op.orphanTransactionCount())
|
||||
var transaction *externalapi.DomainTransaction
|
||||
for _, mempoolTransaction := range op.allOrphans {
|
||||
allOrphanTransactions = append(allOrphanTransactions, mempoolTransaction.Transaction())
|
||||
transaction = mempoolTransaction.Transaction().Clone() //these pointers leave the mempool, hence we clone.
|
||||
for _, input := range transaction.Inputs {
|
||||
if input.UTXOEntry == nil { //this is not a bug, but a valid state of orphan transactions with missing outpoints.
|
||||
continue
|
||||
}
|
||||
|
||||
sending[input.UTXOEntry.ScriptPublicKey().String()] = transaction
|
||||
}
|
||||
for _, output := range transaction.Outputs {
|
||||
receiving[output.ScriptPublicKey.String()] = transaction
|
||||
|
||||
}
|
||||
}
|
||||
return sending, receiving, nil
|
||||
}
|
||||
|
||||
func (op *orphansPool) getAllOrphanTransactions() []*externalapi.DomainTransaction {
|
||||
allOrphanTransactions := make([]*externalapi.DomainTransaction, len(op.allOrphans))
|
||||
i := 0
|
||||
for _, mempoolTransaction := range op.allOrphans {
|
||||
allOrphanTransactions[i] = mempoolTransaction.Transaction().Clone() //these pointers leave the mempool, hence we clone.
|
||||
i++
|
||||
}
|
||||
return allOrphanTransactions
|
||||
}
|
||||
|
||||
func (op *orphansPool) orphanTransactionCount() int {
|
||||
return len(op.allOrphans)
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ func (mp *mempool) revalidateHighPriorityTransactions() ([]*externalapi.DomainTr
|
||||
defer onEnd()
|
||||
|
||||
validTransactions := []*externalapi.DomainTransaction{}
|
||||
|
||||
for _, transaction := range mp.transactionsPool.highPriorityTransactions {
|
||||
isValid, err := mp.revalidateTransaction(transaction)
|
||||
if err != nil {
|
||||
@ -21,7 +20,7 @@ func (mp *mempool) revalidateHighPriorityTransactions() ([]*externalapi.DomainTr
|
||||
continue
|
||||
}
|
||||
|
||||
validTransactions = append(validTransactions, transaction.Transaction())
|
||||
validTransactions = append(validTransactions, transaction.Transaction().Clone())
|
||||
}
|
||||
|
||||
return validTransactions, nil
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/miningmanager/mempool/model"
|
||||
)
|
||||
|
||||
@ -135,7 +136,7 @@ func (tp *transactionsPool) allReadyTransactions() []*externalapi.DomainTransact
|
||||
|
||||
for _, mempoolTransaction := range tp.allTransactions {
|
||||
if len(mempoolTransaction.ParentTransactionsInPool()) == 0 {
|
||||
result = append(result, mempoolTransaction.Transaction())
|
||||
result = append(result, mempoolTransaction.Transaction().Clone()) //this pointer leaves the mempool, and gets its utxo set to nil, hence we clone.
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,17 +205,44 @@ func (tp *transactionsPool) limitTransactionCount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tp *transactionsPool) getTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
||||
func (tp *transactionsPool) getTransaction(transactionID *externalapi.DomainTransactionID, clone bool) (*externalapi.DomainTransaction, bool) {
|
||||
if mempoolTransaction, ok := tp.allTransactions[*transactionID]; ok {
|
||||
if clone {
|
||||
return mempoolTransaction.Transaction().Clone(), true //this pointer leaves the mempool, hence we clone.
|
||||
}
|
||||
return mempoolTransaction.Transaction(), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (tp *transactionsPool) getAllTransactions() []*externalapi.DomainTransaction {
|
||||
allTransactions := make([]*externalapi.DomainTransaction, 0, len(tp.allTransactions))
|
||||
func (tp *transactionsPool) getTransactionsByAddresses() (
|
||||
sending model.ScriptPublicKeyStringToDomainTransaction,
|
||||
receiving model.ScriptPublicKeyStringToDomainTransaction,
|
||||
err error) {
|
||||
sending = make(model.ScriptPublicKeyStringToDomainTransaction, tp.transactionCount())
|
||||
receiving = make(model.ScriptPublicKeyStringToDomainTransaction, tp.transactionCount())
|
||||
var transaction *externalapi.DomainTransaction
|
||||
for _, mempoolTransaction := range tp.allTransactions {
|
||||
allTransactions = append(allTransactions, mempoolTransaction.Transaction())
|
||||
transaction = mempoolTransaction.Transaction().Clone() //this pointer leaves the mempool, hence we clone.
|
||||
for _, input := range transaction.Inputs {
|
||||
if input.UTXOEntry == nil {
|
||||
return nil, nil, errors.Errorf("Mempool transaction %s is missing an UTXOEntry. This should be fixed, and not happen", consensushashing.TransactionID(transaction).String())
|
||||
}
|
||||
sending[input.UTXOEntry.ScriptPublicKey().String()] = transaction
|
||||
}
|
||||
for _, output := range transaction.Outputs {
|
||||
receiving[output.ScriptPublicKey.String()] = transaction
|
||||
}
|
||||
}
|
||||
return sending, receiving, nil
|
||||
}
|
||||
|
||||
func (tp *transactionsPool) getAllTransactions() []*externalapi.DomainTransaction {
|
||||
allTransactions := make([]*externalapi.DomainTransaction, len(tp.allTransactions))
|
||||
i := 0
|
||||
for _, mempoolTransaction := range tp.allTransactions {
|
||||
allTransactions[i] = mempoolTransaction.Transaction().Clone() //this pointer leaves the mempool, hence we clone.
|
||||
i++
|
||||
}
|
||||
return allTransactions
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func (mp *mempool) validateAndInsertTransaction(transaction *externalapi.DomainT
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acceptedTransactions = append([]*externalapi.DomainTransaction{transaction}, acceptedOrphans...)
|
||||
acceptedTransactions = append([]*externalapi.DomainTransaction{transaction.Clone()}, acceptedOrphans...) //these pointer leave the mempool, hence we clone.
|
||||
|
||||
err = mp.transactionsPool.limitTransactionCount()
|
||||
if err != nil {
|
||||
|
@ -15,11 +15,20 @@ type MiningManager interface {
|
||||
GetBlockTemplate(coinbaseData *externalapi.DomainCoinbaseData) (block *externalapi.DomainBlock, isNearlySynced bool, err error)
|
||||
ClearBlockTemplate()
|
||||
GetBlockTemplateBuilder() miningmanagermodel.BlockTemplateBuilder
|
||||
GetTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
|
||||
AllTransactions() []*externalapi.DomainTransaction
|
||||
GetOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
|
||||
AllOrphanTransactions() []*externalapi.DomainTransaction
|
||||
TransactionCount() int
|
||||
GetTransaction(transactionID *externalapi.DomainTransactionID, includeTransactionPool bool, includeOrphanPool bool) (
|
||||
transactionPoolTransaction *externalapi.DomainTransaction,
|
||||
isOrphan bool,
|
||||
found bool)
|
||||
GetTransactionsByAddresses(includeTransactionPool bool, includeOrphanPool bool) (
|
||||
sendingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
sendingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
err error)
|
||||
AllTransactions(includeTransactionPool bool, includeOrphanPool bool) (
|
||||
transactionPoolTransactions []*externalapi.DomainTransaction,
|
||||
orphanPoolTransactions []*externalapi.DomainTransaction)
|
||||
TransactionCount(includeTransactionPool bool, includeOrphanPool bool) int
|
||||
HandleNewBlockTransactions(txs []*externalapi.DomainTransaction) ([]*externalapi.DomainTransaction, error)
|
||||
ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) (
|
||||
acceptedTransactions []*externalapi.DomainTransaction, err error)
|
||||
@ -109,28 +118,35 @@ func (mm *miningManager) ValidateAndInsertTransaction(transaction *externalapi.D
|
||||
}
|
||||
|
||||
func (mm *miningManager) GetTransaction(
|
||||
transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
||||
transactionID *externalapi.DomainTransactionID,
|
||||
includeTransactionPool bool,
|
||||
includeOrphanPool bool) (
|
||||
transactionPoolTransaction *externalapi.DomainTransaction,
|
||||
isOrphan bool,
|
||||
found bool) {
|
||||
|
||||
return mm.mempool.GetTransaction(transactionID)
|
||||
return mm.mempool.GetTransaction(transactionID, includeTransactionPool, includeOrphanPool)
|
||||
}
|
||||
|
||||
func (mm *miningManager) AllTransactions() []*externalapi.DomainTransaction {
|
||||
return mm.mempool.AllTransactions()
|
||||
func (mm *miningManager) AllTransactions(includeTransactionPool bool, includeOrphanPool bool) (
|
||||
transactionPoolTransactions []*externalapi.DomainTransaction,
|
||||
orphanPoolTransactions []*externalapi.DomainTransaction) {
|
||||
|
||||
return mm.mempool.AllTransactions(includeTransactionPool, includeOrphanPool)
|
||||
}
|
||||
|
||||
func (mm *miningManager) GetOrphanTransaction(
|
||||
transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
||||
func (mm *miningManager) GetTransactionsByAddresses(includeTransactionPool bool, includeOrphanPool bool) (
|
||||
sendingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
sendingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
err error) {
|
||||
|
||||
return mm.mempool.GetOrphanTransaction(transactionID)
|
||||
return mm.mempool.GetTransactionsByAddresses(includeTransactionPool, includeOrphanPool)
|
||||
}
|
||||
|
||||
func (mm *miningManager) AllOrphanTransactions() []*externalapi.DomainTransaction {
|
||||
|
||||
return mm.mempool.AllOrphanTransactions()
|
||||
}
|
||||
|
||||
func (mm *miningManager) TransactionCount() int {
|
||||
return mm.mempool.TransactionCount()
|
||||
func (mm *miningManager) TransactionCount(includeTransactionPool bool, includeOrphanPool bool) int {
|
||||
return mm.mempool.TransactionCount(includeTransactionPool, includeOrphanPool)
|
||||
}
|
||||
|
||||
func (mm *miningManager) RevalidateHighPriorityTransactions() (
|
||||
|
@ -52,7 +52,7 @@ func TestValidateAndInsertTransaction(t *testing.T) {
|
||||
}
|
||||
// The UTXOEntry was filled manually for those transactions, so the transactions won't be considered orphans.
|
||||
// Therefore, all the transactions expected to be contained in the mempool.
|
||||
transactionsFromMempool := miningManager.AllTransactions()
|
||||
transactionsFromMempool, _ := miningManager.AllTransactions(true, false)
|
||||
if len(transactionsToInsert) != len(transactionsFromMempool) {
|
||||
t.Fatalf("Wrong number of transactions in mempool: expected: %d, got: %d", len(transactionsToInsert), len(transactionsFromMempool))
|
||||
}
|
||||
@ -72,7 +72,7 @@ func TestValidateAndInsertTransaction(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertTransaction: %v", err)
|
||||
}
|
||||
transactionsFromMempool = miningManager.AllTransactions()
|
||||
transactionsFromMempool, _ = miningManager.AllTransactions(true, false)
|
||||
if !contains(transactionNotAnOrphan, transactionsFromMempool) {
|
||||
t.Fatalf("Missing transaction %s in the mempool", consensushashing.TransactionID(transactionNotAnOrphan))
|
||||
}
|
||||
@ -99,7 +99,7 @@ func TestImmatureSpend(t *testing.T) {
|
||||
if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool.RejectImmatureSpend {
|
||||
t.Fatalf("Unexpected error %+v", err)
|
||||
}
|
||||
transactionsFromMempool := miningManager.AllTransactions()
|
||||
transactionsFromMempool, _ := miningManager.AllTransactions(true, false)
|
||||
if contains(tx, transactionsFromMempool) {
|
||||
t.Fatalf("Mempool contains a transaction with immature coinbase")
|
||||
}
|
||||
@ -205,7 +205,7 @@ func TestHandleNewBlockTransactions(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HandleNewBlockTransactions: %v", err)
|
||||
}
|
||||
mempoolTransactions := miningManager.AllTransactions()
|
||||
mempoolTransactions, _ := miningManager.AllTransactions(true, false)
|
||||
for _, removedTransaction := range blockWithFirstPartOfTheTransactions {
|
||||
if contains(removedTransaction, mempoolTransactions) {
|
||||
t.Fatalf("This transaction shouldnt be in mempool: %s", consensushashing.TransactionID(removedTransaction))
|
||||
@ -214,7 +214,7 @@ func TestHandleNewBlockTransactions(t *testing.T) {
|
||||
|
||||
// There are no chained/double-spends transactions, and hence it is expected that all the other
|
||||
// transactions, will still be included in the mempool.
|
||||
mempoolTransactions = miningManager.AllTransactions()
|
||||
mempoolTransactions, _ = miningManager.AllTransactions(true, false)
|
||||
for _, transaction := range blockWithRestOfTheTransactions[transactionhelper.CoinbaseTransactionIndex+1:] {
|
||||
if !contains(transaction, mempoolTransactions) {
|
||||
t.Fatalf("This transaction %s should be in mempool.", consensushashing.TransactionID(transaction))
|
||||
@ -225,8 +225,9 @@ func TestHandleNewBlockTransactions(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HandleNewBlockTransactions: %v", err)
|
||||
}
|
||||
if len(miningManager.AllTransactions()) != 0 {
|
||||
blockIDs := domainBlocksToBlockIds(miningManager.AllTransactions())
|
||||
mempoolTransactions, _ = miningManager.AllTransactions(true, false)
|
||||
if len(mempoolTransactions) != 0 {
|
||||
blockIDs := domainBlocksToBlockIds(mempoolTransactions)
|
||||
t.Fatalf("The mempool contains unexpected transactions: %s", blockIDs)
|
||||
}
|
||||
})
|
||||
@ -269,7 +270,8 @@ func TestDoubleSpendWithBlock(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HandleNewBlockTransactions: %v", err)
|
||||
}
|
||||
if contains(transactionInTheMempool, miningManager.AllTransactions()) {
|
||||
mempoolTransactions, _ := miningManager.AllTransactions(true, false)
|
||||
if contains(transactionInTheMempool, mempoolTransactions) {
|
||||
t.Fatalf("The transaction %s, shouldn't be in the mempool, since at least one "+
|
||||
"output was already spent.", consensushashing.TransactionID(transactionInTheMempool))
|
||||
}
|
||||
@ -303,7 +305,7 @@ func TestOrphanTransactions(t *testing.T) {
|
||||
t.Fatalf("ValidateAndInsertTransaction: %v", err)
|
||||
}
|
||||
}
|
||||
transactionsMempool := miningManager.AllTransactions()
|
||||
transactionsMempool, _ := miningManager.AllTransactions(true, false)
|
||||
for _, transaction := range transactionsMempool {
|
||||
if contains(transaction, childTransactions) {
|
||||
t.Fatalf("Error: an orphan transaction is exist in the mempool")
|
||||
@ -345,7 +347,7 @@ func TestOrphanTransactions(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HandleNewBlockTransactions: %+v", err)
|
||||
}
|
||||
transactionsMempool = miningManager.AllTransactions()
|
||||
transactionsMempool, _ = miningManager.AllTransactions(true, false)
|
||||
if len(transactionsMempool) != len(childTransactions) {
|
||||
t.Fatalf("Expected %d transactions in the mempool but got %d", len(childTransactions), len(transactionsMempool))
|
||||
}
|
||||
@ -553,8 +555,8 @@ func TestRevalidateHighPriorityTransactions(t *testing.T) {
|
||||
}
|
||||
|
||||
// Make sure spendingTransaction is still in mempool
|
||||
allTransactions := miningManager.AllTransactions()
|
||||
if len(allTransactions) != 1 || !allTransactions[0].Equal(spendingTransaction) {
|
||||
mempoolTransactions, _ := miningManager.AllTransactions(true, false)
|
||||
if len(mempoolTransactions) != 1 || !mempoolTransactions[0].Equal(spendingTransaction) {
|
||||
t.Fatalf("Expected to have spendingTransaction as only validTransaction returned from "+
|
||||
"RevalidateHighPriorityTransactions, but got %v instead", validTransactions)
|
||||
}
|
||||
@ -568,9 +570,9 @@ func TestRevalidateHighPriorityTransactions(t *testing.T) {
|
||||
t.Fatalf("Expected to have empty validTransactions, but got %v instead", validTransactions)
|
||||
}
|
||||
// And also AllTransactions should be empty as well
|
||||
allTransactions = miningManager.AllTransactions()
|
||||
if len(allTransactions) != 0 {
|
||||
t.Fatalf("Expected to have empty allTransactions, but got %v instead", allTransactions)
|
||||
mempoolTransactions, _ = miningManager.AllTransactions(true, false)
|
||||
if len(mempoolTransactions) != 0 {
|
||||
t.Fatalf("Expected to have empty allTransactions, but got %v instead", mempoolTransactions)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -605,7 +607,7 @@ func TestModifyBlockTemplate(t *testing.T) {
|
||||
t.Fatalf("ValidateAndInsertTransaction: %v", err)
|
||||
}
|
||||
}
|
||||
transactionsMempool := miningManager.AllTransactions()
|
||||
transactionsMempool, _ := miningManager.AllTransactions(true, false)
|
||||
for _, transaction := range transactionsMempool {
|
||||
if contains(transaction, childTransactions) {
|
||||
t.Fatalf("Error: an orphan transaction is exist in the mempool")
|
||||
@ -654,7 +656,7 @@ func TestModifyBlockTemplate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HandleNewBlockTransactions: %+v", err)
|
||||
}
|
||||
transactionsMempool = miningManager.AllTransactions()
|
||||
transactionsMempool, _ = miningManager.AllTransactions(true, false)
|
||||
if len(transactionsMempool) != len(childTransactions) {
|
||||
t.Fatalf("Expected %d transactions in the mempool but got %d", len(childTransactions), len(transactionsMempool))
|
||||
}
|
||||
|
@ -12,11 +12,31 @@ type Mempool interface {
|
||||
ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) (
|
||||
acceptedTransactions []*externalapi.DomainTransaction, err error)
|
||||
RemoveTransactions(txs []*externalapi.DomainTransaction, removeRedeemers bool) error
|
||||
GetTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
|
||||
AllTransactions() []*externalapi.DomainTransaction
|
||||
GetOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
|
||||
AllOrphanTransactions() []*externalapi.DomainTransaction
|
||||
TransactionCount() int
|
||||
GetTransaction(
|
||||
transactionID *externalapi.DomainTransactionID,
|
||||
includeTransactionPool bool,
|
||||
includeOrphanPool bool,
|
||||
) (
|
||||
transactionPoolTransaction *externalapi.DomainTransaction,
|
||||
isOrphan bool,
|
||||
found bool)
|
||||
GetTransactionsByAddresses(
|
||||
includeTransactionPool bool,
|
||||
includeOrphanPool bool) (
|
||||
sendingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInTransactionPool map[string]*externalapi.DomainTransaction,
|
||||
sendingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
receivingInOrphanPool map[string]*externalapi.DomainTransaction,
|
||||
err error)
|
||||
AllTransactions(
|
||||
includeTransactionPool bool,
|
||||
includeOrphanPool bool,
|
||||
) (
|
||||
transactionPoolTransactions []*externalapi.DomainTransaction,
|
||||
orphanPoolTransactions []*externalapi.DomainTransaction)
|
||||
TransactionCount(
|
||||
includeTransactionPool bool,
|
||||
includeOrphanPool bool) int
|
||||
RevalidateHighPriorityTransactions() (validTransactions []*externalapi.DomainTransaction, err error)
|
||||
IsTransactionOutputDust(output *externalapi.DomainTransactionOutput) bool
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package utxoindex
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
@ -22,22 +21,3 @@ type UTXOChanges struct {
|
||||
Added map[ScriptPublicKeyString]UTXOOutpointEntryPairs
|
||||
Removed map[ScriptPublicKeyString]UTXOOutpointEntryPairs
|
||||
}
|
||||
|
||||
// ConvertScriptPublicKeyToString converts the given scriptPublicKey to a string
|
||||
func ConvertScriptPublicKeyToString(scriptPublicKey *externalapi.ScriptPublicKey) ScriptPublicKeyString {
|
||||
var versionBytes = make([]byte, 2) // uint16
|
||||
binary.LittleEndian.PutUint16(versionBytes, scriptPublicKey.Version)
|
||||
versionString := ScriptPublicKeyString(versionBytes)
|
||||
scriptString := ScriptPublicKeyString(scriptPublicKey.Script)
|
||||
return versionString + scriptString
|
||||
|
||||
}
|
||||
|
||||
// ConvertStringToScriptPublicKey converts the given string to a scriptPublicKey
|
||||
func ConvertStringToScriptPublicKey(string ScriptPublicKeyString) *externalapi.ScriptPublicKey {
|
||||
bytes := []byte(string)
|
||||
version := binary.LittleEndian.Uint16(bytes[:2])
|
||||
script := bytes[2:]
|
||||
return &externalapi.ScriptPublicKey{Script: script, Version: version}
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func newUTXOIndexStore(database database.Database) *utxoIndexStore {
|
||||
|
||||
func (uis *utxoIndexStore) add(scriptPublicKey *externalapi.ScriptPublicKey, outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry) error {
|
||||
|
||||
key := ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||
key := ScriptPublicKeyString(scriptPublicKey.String())
|
||||
log.Tracef("Adding outpoint %s:%d to scriptPublicKey %s",
|
||||
outpoint.TransactionID, outpoint.Index, key)
|
||||
|
||||
@ -66,7 +66,7 @@ func (uis *utxoIndexStore) add(scriptPublicKey *externalapi.ScriptPublicKey, out
|
||||
}
|
||||
|
||||
func (uis *utxoIndexStore) remove(scriptPublicKey *externalapi.ScriptPublicKey, outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry) error {
|
||||
key := ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||
key := ScriptPublicKeyString(scriptPublicKey.String())
|
||||
log.Tracef("Removing outpoint %s:%d from scriptPublicKey %s",
|
||||
outpoint.TransactionID, outpoint.Index, key)
|
||||
|
||||
@ -122,7 +122,7 @@ func (uis *utxoIndexStore) commit() error {
|
||||
toRemoveSompiSupply := uint64(0)
|
||||
|
||||
for scriptPublicKeyString, toRemoveUTXOOutpointEntryPairs := range uis.toRemove {
|
||||
scriptPublicKey := ConvertStringToScriptPublicKey(scriptPublicKeyString)
|
||||
scriptPublicKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
|
||||
bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
|
||||
for outpointToRemove, utxoEntryToRemove := range toRemoveUTXOOutpointEntryPairs {
|
||||
key, err := uis.convertOutpointToKey(bucket, &outpointToRemove)
|
||||
@ -140,7 +140,7 @@ func (uis *utxoIndexStore) commit() error {
|
||||
toAddSompiSupply := uint64(0)
|
||||
|
||||
for scriptPublicKeyString, toAddUTXOOutpointEntryPairs := range uis.toAdd {
|
||||
scriptPublicKey := ConvertStringToScriptPublicKey(scriptPublicKeyString)
|
||||
scriptPublicKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
|
||||
bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
|
||||
for outpointToAdd, utxoEntryToAdd := range toAddUTXOOutpointEntryPairs {
|
||||
key, err := uis.convertOutpointToKey(bucket, &outpointToAdd)
|
||||
|
@ -22,6 +22,7 @@ type OnDisconnectedHandler func()
|
||||
// GRPCClient is a gRPC-based RPC client
|
||||
type GRPCClient struct {
|
||||
stream protowire.RPC_MessageStreamClient
|
||||
connection *grpc.ClientConn
|
||||
onErrorHandler OnErrorHandler
|
||||
onDisconnectedHandler OnDisconnectedHandler
|
||||
}
|
||||
@ -43,7 +44,12 @@ func Connect(address string) (*GRPCClient, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting client stream for %s", address)
|
||||
}
|
||||
return &GRPCClient{stream: stream}, nil
|
||||
return &GRPCClient{stream: stream, connection: gRPCConnection}, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying grpc connection
|
||||
func (c *GRPCClient) Close() error {
|
||||
return c.connection.Close()
|
||||
}
|
||||
|
||||
// Disconnect disconnects from the RPC server
|
||||
|
20
infrastructure/network/rpcclient/rpc_ban.go
Normal file
20
infrastructure/network/rpcclient/rpc_ban.go
Normal file
@ -0,0 +1,20 @@
|
||||
package rpcclient
|
||||
|
||||
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||
|
||||
// Ban sends an RPC request respective to the function's name and returns the RPC server's response
|
||||
func (c *RPCClient) Ban(ip string) (*appmessage.BanResponseMessage, error) {
|
||||
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewBanRequestMessage(ip))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := c.route(appmessage.CmdBanRequestMessage).DequeueWithTimeout(c.timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
banResponse := response.(*appmessage.BanResponseMessage)
|
||||
if banResponse.Error != nil {
|
||||
return nil, c.convertRPCError(banResponse.Error)
|
||||
}
|
||||
return banResponse, nil
|
||||
}
|
20
infrastructure/network/rpcclient/rpc_unban.go
Normal file
20
infrastructure/network/rpcclient/rpc_unban.go
Normal file
@ -0,0 +1,20 @@
|
||||
package rpcclient
|
||||
|
||||
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||
|
||||
// Unban sends an RPC request respective to the function's name and returns the RPC server's response
|
||||
func (c *RPCClient) Unban(ip string) (*appmessage.UnbanResponseMessage, error) {
|
||||
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewUnbanRequestMessage(ip))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := c.route(appmessage.CmdUnbanRequestMessage).DequeueWithTimeout(c.timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unbanResponse := response.(*appmessage.UnbanResponseMessage)
|
||||
if unbanResponse.Error != nil {
|
||||
return nil, c.convertRPCError(unbanResponse.Error)
|
||||
}
|
||||
return unbanResponse, nil
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package rpcclient
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
@ -8,8 +11,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
"github.com/kaspanet/kaspad/version"
|
||||
"github.com/pkg/errors"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultTimeout = 30 * time.Second
|
||||
@ -28,7 +29,7 @@ type RPCClient struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewRPCClient creates a new RPC client
|
||||
// NewRPCClient сreates a new RPC client with a default call timeout value
|
||||
func NewRPCClient(rpcAddress string) (*RPCClient, error) {
|
||||
rpcClient := &RPCClient{
|
||||
rpcAddress: rpcAddress,
|
||||
@ -142,6 +143,9 @@ func (c *RPCClient) handleClientDisconnected() {
|
||||
}
|
||||
|
||||
func (c *RPCClient) handleClientError(err error) {
|
||||
if atomic.LoadUint32(&c.isClosed) == 1 {
|
||||
return
|
||||
}
|
||||
log.Warnf("Received error from client: %s", err)
|
||||
c.handleClientDisconnected()
|
||||
}
|
||||
@ -158,7 +162,7 @@ func (c *RPCClient) Close() error {
|
||||
return errors.Errorf("Cannot close a client that had already been closed")
|
||||
}
|
||||
c.rpcRouter.router.Close()
|
||||
return nil
|
||||
return c.GRPCClient.Close()
|
||||
}
|
||||
|
||||
// Address returns the address the RPC client connected to
|
||||
|
@ -2,6 +2,7 @@ package integration
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -26,6 +27,37 @@ func newTestRPCClient(rpcAddress string) (*testRPCClient, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func connectAndClose(rpcAddress string) error {
|
||||
client, err := rpcclient.NewRPCClient(rpcAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRPCClientGoroutineLeak(t *testing.T) {
|
||||
_, teardown := setupHarness(t, &harnessParams{
|
||||
p2pAddress: p2pAddress1,
|
||||
rpcAddress: rpcAddress1,
|
||||
miningAddress: miningAddress1,
|
||||
miningAddressPrivateKey: miningAddress1PrivateKey,
|
||||
})
|
||||
defer teardown()
|
||||
numGoroutinesBefore := runtime.NumGoroutine()
|
||||
for i := 1; i < 100; i++ {
|
||||
err := connectAndClose(rpcAddress1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up an RPC client: %s", err)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if runtime.NumGoroutine() > numGoroutinesBefore+10 {
|
||||
t.Fatalf("Number of goroutines is increasing for each RPC client open (%d -> %d), which indicates a memory leak",
|
||||
numGoroutinesBefore, runtime.NumGoroutine())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRPCMaxInboundConnections(t *testing.T) {
|
||||
harness, teardown := setupHarness(t, &harnessParams{
|
||||
p2pAddress: p2pAddress1,
|
||||
|
@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs
|
||||
const (
|
||||
appMajor uint = 0
|
||||
appMinor uint = 12
|
||||
appPatch uint = 3
|
||||
appPatch uint = 5
|
||||
)
|
||||
|
||||
// appBuild is defined as a variable so it can be overridden during the build
|
||||
|
Loading…
x
Reference in New Issue
Block a user