Compare commits

..

1 Commits

Author SHA1 Message Date
stasatdaglabs
5f3fb0bf9f [NOD-1238] Fix acceptance index never being initialized. (#859) 2020-08-11 12:04:54 +03:00
660 changed files with 30375 additions and 25427 deletions

View File

@@ -40,8 +40,10 @@ recommended that `GOPATH` is set to a directory in your home directory such as
```bash
$ git clone https://github.com/kaspanet/kaspad $GOPATH/src/github.com/kaspanet/kaspad
$ cd $GOPATH/src/github.com/kaspanet/kaspad
$ ./test.sh
$ go install . ./cmd/...
```
`./test.sh` tests can be skipped, but some things might not run correctly on your system if tests fail.
- Kaspad (and utilities) should now be installed in `$GOPATH/bin`. If you did
not already add the bin directory to your system path during Go installation,

View File

@@ -9,23 +9,21 @@ import (
crand "crypto/rand" // for seeding
"encoding/binary"
"encoding/gob"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/config"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/pkg/errors"
"io"
"math/rand"
"net"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util/daghash"
)
@@ -94,7 +92,7 @@ type PeersStateForSerialization struct {
}
type localAddress struct {
netAddress *appmessage.NetAddress
netAddress *domainmessage.NetAddress
score AddressPriority
}
@@ -194,7 +192,7 @@ const (
var ErrAddressNotFound = errors.New("address not found")
// New returns a new Kaspa address manager.
func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext) (*AddressManager, error) {
func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext) *AddressManager {
addressManager := AddressManager{
cfg: cfg,
databaseContext: databaseContext,
@@ -204,17 +202,13 @@ func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext) (*Addres
localAddresses: make(map[AddressKey]*localAddress),
localSubnetworkID: cfg.SubnetworkID,
}
err := addressManager.initListeners()
if err != nil {
return nil, err
}
addressManager.reset()
return &addressManager, nil
return &addressManager
}
// updateAddress is a helper function to either update an address already known
// to the address manager, or to add the address if not already known.
func (am *AddressManager) updateAddress(netAddress, sourceAddress *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
func (am *AddressManager) updateAddress(netAddress, sourceAddress *domainmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
// Filter out non-routable addresses. Note that non-routable
// also includes invalid and local addresses.
if !am.IsRoutable(netAddress) {
@@ -224,6 +218,7 @@ func (am *AddressManager) updateAddress(netAddress, sourceAddress *appmessage.Ne
addressKey := NetAddressKey(netAddress)
knownAddress := am.knownAddress(netAddress)
if knownAddress != nil {
// TODO: only update addresses periodically.
// Update the last seen time and services.
// note that to prevent causing excess garbage on getaddr
// messages the netaddresses in addrmaanger are *immutable*,
@@ -376,7 +371,7 @@ func (am *AddressManager) pickTried(subnetworkID *subnetworkid.SubnetworkID, buc
return oldest, oldestIndex
}
func (am *AddressManager) newAddressBucketIndex(netAddress, srcAddress *appmessage.NetAddress) int {
func (am *AddressManager) newAddressBucketIndex(netAddress, srcAddress *domainmessage.NetAddress) int {
// doublesha256(key + sourcegroup + int64(doublesha256(key + group + sourcegroup))%bucket_per_source_group) % num_new_buckets
data1 := []byte{}
@@ -397,7 +392,7 @@ func (am *AddressManager) newAddressBucketIndex(netAddress, srcAddress *appmessa
return int(binary.LittleEndian.Uint64(hash2) % NewBucketCount)
}
func (am *AddressManager) triedAddressBucketIndex(netAddress *appmessage.NetAddress) int {
func (am *AddressManager) triedAddressBucketIndex(netAddress *domainmessage.NetAddress) int {
// doublesha256(key + group + truncate_to_64bits(doublesha256(key)) % buckets_per_group) % num_buckets
data1 := []byte{}
data1 = append(data1, am.key[:]...)
@@ -710,8 +705,8 @@ func (am *AddressManager) deserializePeersState(serializedPeerState []byte) erro
return nil
}
// DeserializeNetAddress converts a given address string to a *appmessage.NetAddress
func (am *AddressManager) DeserializeNetAddress(addressKey AddressKey) (*appmessage.NetAddress, error) {
// DeserializeNetAddress converts a given address string to a *domainmessage.NetAddress
func (am *AddressManager) DeserializeNetAddress(addressKey AddressKey) (*domainmessage.NetAddress, error) {
host, portString, err := net.SplitHostPort(string(addressKey))
if err != nil {
return nil, err
@@ -721,7 +716,7 @@ func (am *AddressManager) DeserializeNetAddress(addressKey AddressKey) (*appmess
return nil, err
}
return am.HostToNetAddress(host, uint16(port), appmessage.SFNodeNetwork)
return am.HostToNetAddress(host, uint16(port), domainmessage.SFNodeNetwork)
}
// Start begins the core address handler which manages a pool of known
@@ -763,7 +758,7 @@ func (am *AddressManager) Stop() error {
// AddAddresses adds new addresses to the address manager. It enforces a max
// number of addresses and silently ignores duplicate addresses. It is
// safe for concurrent access.
func (am *AddressManager) AddAddresses(addresses []*appmessage.NetAddress, sourceAddress *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
func (am *AddressManager) AddAddresses(addresses []*domainmessage.NetAddress, sourceAddress *domainmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
am.mutex.Lock()
defer am.mutex.Unlock()
@@ -775,13 +770,35 @@ func (am *AddressManager) AddAddresses(addresses []*appmessage.NetAddress, sourc
// AddAddress adds a new address to the address manager. It enforces a max
// number of addresses and silently ignores duplicate addresses. It is
// safe for concurrent access.
func (am *AddressManager) AddAddress(address, sourceAddress *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
func (am *AddressManager) AddAddress(address, sourceAddress *domainmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
am.mutex.Lock()
defer am.mutex.Unlock()
am.updateAddress(address, sourceAddress, subnetworkID)
}
// AddAddressByIP adds an address where we are given an ip:port and not a
// domainmessage.NetAddress.
func (am *AddressManager) AddAddressByIP(addressIP string, subnetworkID *subnetworkid.SubnetworkID) error {
// Split IP and port
ipString, portString, err := net.SplitHostPort(addressIP)
if err != nil {
return err
}
// Put it in domainmessage.Netaddress
ip := net.ParseIP(ipString)
if ip == nil {
return errors.Errorf("invalid ip %s", ipString)
}
port, err := strconv.ParseUint(portString, 10, 0)
if err != nil {
return errors.Errorf("invalid port %s: %s", portString, err)
}
netAddress := domainmessage.NewNetAddressIPPort(ip, uint16(port), 0)
am.AddAddress(netAddress, netAddress, subnetworkID) // XXX use correct src address
return nil
}
// numAddresses returns the number of addresses that belongs to a specific subnetwork id
// which are known to the address manager.
func (am *AddressManager) numAddresses(subnetworkID *subnetworkid.SubnetworkID) int {
@@ -826,7 +843,7 @@ func (am *AddressManager) NeedMoreAddresses() bool {
// AddressCache returns the current address cache. It must be treated as
// read-only (but since it is a copy now, this is not as dangerous).
func (am *AddressManager) AddressCache(includeAllSubnetworks bool, subnetworkID *subnetworkid.SubnetworkID) []*appmessage.NetAddress {
func (am *AddressManager) AddressCache(includeAllSubnetworks bool, subnetworkID *subnetworkid.SubnetworkID) []*domainmessage.NetAddress {
am.mutex.Lock()
defer am.mutex.Unlock()
@@ -834,7 +851,7 @@ func (am *AddressManager) AddressCache(includeAllSubnetworks bool, subnetworkID
return nil
}
allAddresses := []*appmessage.NetAddress{}
allAddresses := []*domainmessage.NetAddress{}
// Iteration order is undefined here, but we randomise it anyway.
for _, v := range am.addressIndex {
if includeAllSubnetworks || v.SubnetworkID().IsEqual(subnetworkID) {
@@ -890,7 +907,7 @@ func (am *AddressManager) reset() {
// HostToNetAddress returns a netaddress given a host address. If
// the host is not an IP address it will be resolved.
func (am *AddressManager) HostToNetAddress(host string, port uint16, services appmessage.ServiceFlag) (*appmessage.NetAddress, error) {
func (am *AddressManager) HostToNetAddress(host string, port uint16, services domainmessage.ServiceFlag) (*domainmessage.NetAddress, error) {
ip := net.ParseIP(host)
if ip == nil {
ips, err := am.lookupFunc(host)
@@ -903,12 +920,12 @@ func (am *AddressManager) HostToNetAddress(host string, port uint16, services ap
ip = ips[0]
}
return appmessage.NewNetAddressIPPort(ip, port, services), nil
return domainmessage.NewNetAddressIPPort(ip, port, services), nil
}
// NetAddressKey returns a key in the form of ip:port for IPv4 addresses
// or [ip]:port for IPv6 addresses for use as keys in maps.
func NetAddressKey(netAddress *appmessage.NetAddress) AddressKey {
func NetAddressKey(netAddress *domainmessage.NetAddress) AddressKey {
port := strconv.FormatUint(uint64(netAddress.Port), 10)
return AddressKey(net.JoinHostPort(netAddress.IP.String(), port))
@@ -1014,13 +1031,13 @@ func (tb *triedAddressBucketArray) name() string {
return "tried"
}
func (am *AddressManager) knownAddress(address *appmessage.NetAddress) *KnownAddress {
func (am *AddressManager) knownAddress(address *domainmessage.NetAddress) *KnownAddress {
return am.addressIndex[NetAddressKey(address)]
}
// Attempt increases the given address' attempt counter and updates
// the last attempt time.
func (am *AddressManager) Attempt(address *appmessage.NetAddress) {
func (am *AddressManager) Attempt(address *domainmessage.NetAddress) {
am.mutex.Lock()
defer am.mutex.Unlock()
@@ -1038,7 +1055,7 @@ func (am *AddressManager) Attempt(address *appmessage.NetAddress) {
// Connected Marks the given address as currently connected and working at the
// current time. The address must already be known to AddressManager else it will
// be ignored.
func (am *AddressManager) Connected(address *appmessage.NetAddress) {
func (am *AddressManager) Connected(address *domainmessage.NetAddress) {
am.mutex.Lock()
defer am.mutex.Unlock()
@@ -1061,7 +1078,7 @@ func (am *AddressManager) Connected(address *appmessage.NetAddress) {
// Good marks the given address as good. To be called after a successful
// connection and version exchange. If the address is unknown to the address
// manager it will be ignored.
func (am *AddressManager) Good(address *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
func (am *AddressManager) Good(address *domainmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
am.mutex.Lock()
defer am.mutex.Unlock()
@@ -1237,7 +1254,7 @@ func (am *AddressManager) incrementTriedAddressCount(subnetworkID *subnetworkid.
// AddLocalAddress adds netAddress to the list of known local addresses to advertise
// with the given priority.
func (am *AddressManager) AddLocalAddress(netAddress *appmessage.NetAddress, priority AddressPriority) error {
func (am *AddressManager) AddLocalAddress(netAddress *domainmessage.NetAddress, priority AddressPriority) error {
if !am.IsRoutable(netAddress) {
return errors.Errorf("address %s is not routable", netAddress.IP)
}
@@ -1262,7 +1279,7 @@ func (am *AddressManager) AddLocalAddress(netAddress *appmessage.NetAddress, pri
// getReachabilityFrom returns the relative reachability of the provided local
// address to the provided remote address.
func (am *AddressManager) getReachabilityFrom(localAddress, remoteAddress *appmessage.NetAddress) int {
func (am *AddressManager) getReachabilityFrom(localAddress, remoteAddress *domainmessage.NetAddress) int {
const (
Unreachable = 0
Default = iota
@@ -1329,13 +1346,13 @@ func (am *AddressManager) getReachabilityFrom(localAddress, remoteAddress *appme
// GetBestLocalAddress returns the most appropriate local address to use
// for the given remote address.
func (am *AddressManager) GetBestLocalAddress(remoteAddress *appmessage.NetAddress) *appmessage.NetAddress {
func (am *AddressManager) GetBestLocalAddress(remoteAddress *domainmessage.NetAddress) *domainmessage.NetAddress {
am.localAddressesLock.Lock()
defer am.localAddressesLock.Unlock()
bestReach := 0
var bestScore AddressPriority
var bestAddress *appmessage.NetAddress
var bestAddress *domainmessage.NetAddress
for _, localAddress := range am.localAddresses {
reach := am.getReachabilityFrom(localAddress.netAddress, remoteAddress)
if reach > bestReach ||
@@ -1359,24 +1376,24 @@ func (am *AddressManager) GetBestLocalAddress(remoteAddress *appmessage.NetAddre
} else {
ip = net.IPv4zero
}
services := appmessage.SFNodeNetwork | appmessage.SFNodeBloom
bestAddress = appmessage.NewNetAddressIPPort(ip, 0, services)
services := domainmessage.SFNodeNetwork | domainmessage.SFNodeBloom
bestAddress = domainmessage.NewNetAddressIPPort(ip, 0, services)
}
return bestAddress
}
// Ban marks the given address as banned
func (am *AddressManager) Ban(address *appmessage.NetAddress) error {
func (am *AddressManager) Ban(address *domainmessage.NetAddress) error {
return am.setBanned(address, true, mstime.Now())
}
// Unban marks the given address as not banned
func (am *AddressManager) Unban(address *appmessage.NetAddress) error {
func (am *AddressManager) Unban(address *domainmessage.NetAddress) error {
return am.setBanned(address, false, mstime.Time{})
}
func (am *AddressManager) setBanned(address *appmessage.NetAddress, isBanned bool, bannedTime mstime.Time) error {
func (am *AddressManager) setBanned(address *domainmessage.NetAddress, isBanned bool, bannedTime mstime.Time) error {
am.localAddressesLock.Lock()
defer am.localAddressesLock.Unlock()
@@ -1391,7 +1408,7 @@ func (am *AddressManager) setBanned(address *appmessage.NetAddress, isBanned boo
}
// IsBanned returns whether the given address is banned
func (am *AddressManager) IsBanned(address *appmessage.NetAddress) (bool, error) {
func (am *AddressManager) IsBanned(address *domainmessage.NetAddress) (bool, error) {
am.localAddressesLock.Lock()
defer am.localAddressesLock.Unlock()
@@ -1402,188 +1419,3 @@ func (am *AddressManager) IsBanned(address *appmessage.NetAddress) (bool, error)
}
return knownAddress.isBanned, nil
}
// initListeners initializes the configured net listeners and adds any bound
// addresses to the address manager
func (am *AddressManager) initListeners() error {
if len(am.cfg.ExternalIPs) != 0 {
defaultPort, err := strconv.ParseUint(am.cfg.NetParams().DefaultPort, 10, 16)
if err != nil {
log.Errorf("Can not parse default port %s for active DAG: %s",
am.cfg.NetParams().DefaultPort, err)
return err
}
for _, sip := range am.cfg.ExternalIPs {
eport := uint16(defaultPort)
host, portstr, err := net.SplitHostPort(sip)
if err != nil {
// no port, use default.
host = sip
} else {
port, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
log.Warnf("Can not parse port from %s for "+
"externalip: %s", sip, err)
continue
}
eport = uint16(port)
}
na, err := am.HostToNetAddress(host, eport, appmessage.DefaultServices)
if err != nil {
log.Warnf("Not adding %s as externalip: %s", sip, err)
continue
}
err = am.AddLocalAddress(na, ManualPrio)
if err != nil {
log.Warnf("Skipping specified external IP: %s", err)
}
}
} else {
// Listen for TCP connections at the configured addresses
netAddrs, err := parseListeners(am.cfg.Listeners)
if err != nil {
return err
}
// Add bound addresses to address manager to be advertised to peers.
for _, addr := range netAddrs {
listener, err := net.Listen(addr.Network(), addr.String())
if err != nil {
log.Warnf("Can't listen on %s: %s", addr, err)
continue
}
addr := listener.Addr().String()
err = listener.Close()
if err != nil {
return err
}
err = am.addLocalAddress(addr)
if err != nil {
log.Warnf("Skipping bound address %s: %s", addr, err)
}
}
}
return nil
}
// parseListeners determines whether each listen address is IPv4 and IPv6 and
// returns a slice of appropriate net.Addrs to listen on with TCP. It also
// properly detects addresses which apply to "all interfaces" and adds the
// address as both IPv4 and IPv6.
func parseListeners(addrs []string) ([]net.Addr, error) {
netAddrs := make([]net.Addr, 0, len(addrs)*2)
for _, addr := range addrs {
host, _, err := net.SplitHostPort(addr)
if err != nil {
// Shouldn't happen due to already being normalized.
return nil, err
}
// Empty host or host of * on plan9 is both IPv4 and IPv6.
if host == "" || (host == "*" && runtime.GOOS == "plan9") {
netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr})
netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr})
continue
}
// Strip IPv6 zone id if present since net.ParseIP does not
// handle it.
zoneIndex := strings.LastIndex(host, "%")
if zoneIndex > 0 {
host = host[:zoneIndex]
}
// Parse the IP.
ip := net.ParseIP(host)
if ip == nil {
hostAddrs, err := net.LookupHost(host)
if err != nil {
return nil, err
}
ip = net.ParseIP(hostAddrs[0])
if ip == nil {
return nil, errors.Errorf("Cannot resolve IP address for host '%s'", host)
}
}
// To4 returns nil when the IP is not an IPv4 address, so use
// this determine the address type.
if ip.To4() == nil {
netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr})
} else {
netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr})
}
}
return netAddrs, nil
}
// addLocalAddress adds an address that this node is listening on to the
// address manager so that it may be relayed to peers.
func (am *AddressManager) addLocalAddress(addr string) error {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
return err
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return err
}
if ip := net.ParseIP(host); ip != nil && ip.IsUnspecified() {
// If bound to unspecified address, advertise all local interfaces
addrs, err := net.InterfaceAddrs()
if err != nil {
return err
}
for _, addr := range addrs {
ifaceIP, _, err := net.ParseCIDR(addr.String())
if err != nil {
continue
}
// If bound to 0.0.0.0, do not add IPv6 interfaces and if bound to
// ::, do not add IPv4 interfaces.
if (ip.To4() == nil) != (ifaceIP.To4() == nil) {
continue
}
netAddr := appmessage.NewNetAddressIPPort(ifaceIP, uint16(port), appmessage.DefaultServices)
am.AddLocalAddress(netAddr, BoundPrio)
}
} else {
netAddr, err := am.HostToNetAddress(host, uint16(port), appmessage.DefaultServices)
if err != nil {
return err
}
am.AddLocalAddress(netAddr, BoundPrio)
}
return nil
}
// simpleAddr implements the net.Addr interface with two struct fields
type simpleAddr struct {
net, addr string
}
// String returns the address.
//
// This is part of the net.Addr interface.
func (a simpleAddr) String() string {
return a.addr
}
// Network returns the network.
//
// This is part of the net.Addr interface.
func (a simpleAddr) Network() string {
return a.net
}
// Ensure simpleAddr implements the net.Addr interface.
var _ net.Addr = simpleAddr{}

View File

@@ -6,25 +6,26 @@ package addressmanager
import (
"fmt"
"github.com/kaspanet/kaspad/app/appmessage"
"io/ioutil"
"net"
"reflect"
"testing"
"time"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/config"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domainmessage"
)
// naTest is used to describe a test to be performed against the NetAddressKey
// method.
type naTest struct {
in appmessage.NetAddress
in domainmessage.NetAddress
want AddressKey
}
@@ -98,7 +99,7 @@ func addNaTests() {
func addNaTest(ip string, port uint16, want AddressKey) {
nip := net.ParseIP(ip)
na := *appmessage.NewNetAddressIPPort(nip, port, appmessage.SFNodeNetwork)
na := *domainmessage.NewNetAddressIPPort(nip, port, domainmessage.SFNodeNetwork)
test := naTest{na, want}
naTests = append(naTests, test)
}
@@ -123,10 +124,7 @@ func newAddrManagerForTest(t *testing.T, testName string,
t.Fatalf("error creating db: %s", err)
}
addressManager, err = New(cfg, databaseContext)
if err != nil {
t.Fatalf("error creating address manager: %s", err)
}
addressManager = New(cfg, databaseContext)
return addressManager, func() {
err := databaseContext.Close()
@@ -177,7 +175,7 @@ func TestAddAddressByIP(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil)
defer teardown()
for i, test := range tests {
err := AddAddressByIP(amgr, test.addrIP, nil)
err := amgr.AddAddressByIP(test.addrIP, nil)
if test.err != nil && err == nil {
t.Errorf("TestAddAddressByIP test %d failed expected an error and got none", i)
continue
@@ -196,37 +194,37 @@ func TestAddAddressByIP(t *testing.T) {
func TestAddLocalAddress(t *testing.T) {
var tests = []struct {
address appmessage.NetAddress
address domainmessage.NetAddress
priority AddressPriority
valid bool
}{
{
appmessage.NetAddress{IP: net.ParseIP("192.168.0.100")},
domainmessage.NetAddress{IP: net.ParseIP("192.168.0.100")},
InterfacePrio,
false,
},
{
appmessage.NetAddress{IP: net.ParseIP("204.124.1.1")},
domainmessage.NetAddress{IP: net.ParseIP("204.124.1.1")},
InterfacePrio,
true,
},
{
appmessage.NetAddress{IP: net.ParseIP("204.124.1.1")},
domainmessage.NetAddress{IP: net.ParseIP("204.124.1.1")},
BoundPrio,
true,
},
{
appmessage.NetAddress{IP: net.ParseIP("::1")},
domainmessage.NetAddress{IP: net.ParseIP("::1")},
InterfacePrio,
false,
},
{
appmessage.NetAddress{IP: net.ParseIP("fe80::1")},
domainmessage.NetAddress{IP: net.ParseIP("fe80::1")},
InterfacePrio,
false,
},
{
appmessage.NetAddress{IP: net.ParseIP("2620:100::1")},
domainmessage.NetAddress{IP: net.ParseIP("2620:100::1")},
InterfacePrio,
true,
},
@@ -253,7 +251,7 @@ func TestAttempt(t *testing.T) {
defer teardown()
// Add a new address and get it
err := AddAddressByIP(amgr, someIP+":8333", nil)
err := amgr.AddAddressByIP(someIP+":8333", nil)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
@@ -276,7 +274,7 @@ func TestConnected(t *testing.T) {
defer teardown()
// Add a new address and get it
err := AddAddressByIP(amgr, someIP+":8333", nil)
err := amgr.AddAddressByIP(someIP+":8333", nil)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
@@ -300,7 +298,7 @@ func TestNeedMoreAddresses(t *testing.T) {
if !b {
t.Errorf("Expected that we need more addresses")
}
addrs := make([]*appmessage.NetAddress, addrsToAdd)
addrs := make([]*domainmessage.NetAddress, addrsToAdd)
var err error
for i := 0; i < addrsToAdd; i++ {
@@ -311,7 +309,7 @@ func TestNeedMoreAddresses(t *testing.T) {
}
}
srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
srcAddr := domainmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
amgr.AddAddresses(addrs, srcAddr, nil)
numAddrs := amgr.TotalNumAddresses()
@@ -329,7 +327,7 @@ func TestGood(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestGood", nil)
defer teardown()
addrsToAdd := 64 * 64
addrs := make([]*appmessage.NetAddress, addrsToAdd)
addrs := make([]*domainmessage.NetAddress, addrsToAdd)
subnetworkCount := 32
subnetworkIDs := make([]*subnetworkid.SubnetworkID, subnetworkCount)
@@ -346,7 +344,7 @@ func TestGood(t *testing.T) {
subnetworkIDs[i] = &subnetworkid.SubnetworkID{0xff - byte(i)}
}
srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
srcAddr := domainmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
amgr.AddAddresses(addrs, srcAddr, nil)
for i, addr := range addrs {
@@ -376,9 +374,9 @@ func TestGood(t *testing.T) {
func TestGoodChangeSubnetworkID(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestGoodChangeSubnetworkID", nil)
defer teardown()
addr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
addr := domainmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
addrKey := NetAddressKey(addr)
srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
srcAddr := domainmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
oldSubnetwork := subnetworkid.SubnetworkIDNative
amgr.AddAddress(addr, srcAddr, oldSubnetwork)
@@ -454,7 +452,7 @@ func TestGetAddress(t *testing.T) {
}
// Add a new address and get it
err := AddAddressByIP(amgr, someIP+":8332", localSubnetworkID)
err := amgr.AddAddressByIP(someIP+":8332", localSubnetworkID)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
@@ -481,7 +479,7 @@ func TestGetAddress(t *testing.T) {
// Now we repeat the same process, but now the address has the expected subnetwork ID.
// Add a new address and get it
err = AddAddressByIP(amgr, someIP+":8333", localSubnetworkID)
err = amgr.AddAddressByIP(someIP+":8333", localSubnetworkID)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
@@ -517,7 +515,7 @@ func TestGetAddress(t *testing.T) {
}
func TestGetBestLocalAddress(t *testing.T) {
localAddrs := []appmessage.NetAddress{
localAddrs := []domainmessage.NetAddress{
{IP: net.ParseIP("192.168.0.100")},
{IP: net.ParseIP("::1")},
{IP: net.ParseIP("fe80::1")},
@@ -525,36 +523,45 @@ func TestGetBestLocalAddress(t *testing.T) {
}
var tests = []struct {
remoteAddr appmessage.NetAddress
want0 appmessage.NetAddress
want1 appmessage.NetAddress
want2 appmessage.NetAddress
want3 appmessage.NetAddress
remoteAddr domainmessage.NetAddress
want0 domainmessage.NetAddress
want1 domainmessage.NetAddress
want2 domainmessage.NetAddress
want3 domainmessage.NetAddress
}{
{
// Remote connection from public IPv4
appmessage.NetAddress{IP: net.ParseIP("204.124.8.1")},
appmessage.NetAddress{IP: net.IPv4zero},
appmessage.NetAddress{IP: net.IPv4zero},
appmessage.NetAddress{IP: net.ParseIP("204.124.8.100")},
appmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")},
domainmessage.NetAddress{IP: net.ParseIP("204.124.8.1")},
domainmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.ParseIP("204.124.8.100")},
domainmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")},
},
{
// Remote connection from private IPv4
appmessage.NetAddress{IP: net.ParseIP("172.16.0.254")},
appmessage.NetAddress{IP: net.IPv4zero},
appmessage.NetAddress{IP: net.IPv4zero},
appmessage.NetAddress{IP: net.IPv4zero},
appmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.ParseIP("172.16.0.254")},
domainmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.IPv4zero},
},
{
// Remote connection from public IPv6
appmessage.NetAddress{IP: net.ParseIP("2602:100:abcd::102")},
appmessage.NetAddress{IP: net.IPv6zero},
appmessage.NetAddress{IP: net.ParseIP("2001:470::1")},
appmessage.NetAddress{IP: net.ParseIP("2001:470::1")},
appmessage.NetAddress{IP: net.ParseIP("2001:470::1")},
domainmessage.NetAddress{IP: net.ParseIP("2602:100:abcd::102")},
domainmessage.NetAddress{IP: net.IPv6zero},
domainmessage.NetAddress{IP: net.ParseIP("2001:470::1")},
domainmessage.NetAddress{IP: net.ParseIP("2001:470::1")},
domainmessage.NetAddress{IP: net.ParseIP("2001:470::1")},
},
/* XXX
{
// Remote connection from Tor
domainmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43::100")},
domainmessage.NetAddress{IP: net.IPv4zero},
domainmessage.NetAddress{IP: net.ParseIP("204.124.8.100")},
domainmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")},
},
*/
}
amgr, teardown := newAddrManagerForTest(t, "TestGetBestLocalAddress", nil)
@@ -585,7 +592,7 @@ func TestGetBestLocalAddress(t *testing.T) {
}
// Add a public IP to the list of local addresses.
localAddr := appmessage.NetAddress{IP: net.ParseIP("204.124.8.100")}
localAddr := domainmessage.NetAddress{IP: net.ParseIP("204.124.8.100")}
amgr.AddLocalAddress(&localAddr, InterfacePrio)
// Test against want2
@@ -599,7 +606,7 @@ func TestGetBestLocalAddress(t *testing.T) {
}
/*
// Add a Tor generated IP address
localAddr = appmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")}
localAddr = domainmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")}
amgr.AddLocalAddress(&localAddr, ManualPrio)
// Test against want3
for x, test := range tests {
@@ -624,4 +631,5 @@ func TestNetAddressKey(t *testing.T) {
continue
}
}
}

View File

@@ -5,7 +5,7 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util/mstime"
)
@@ -17,7 +17,7 @@ func TstKnownAddressChance(ka *KnownAddress) float64 {
return ka.chance()
}
func TstNewKnownAddress(na *appmessage.NetAddress, attempts int,
func TstNewKnownAddress(na *domainmessage.NetAddress, attempts int,
lastattempt, lastsuccess mstime.Time, tried bool, refs int) *KnownAddress {
return &KnownAddress{netAddress: na, attempts: attempts, lastAttempt: lastattempt,
lastSuccess: lastsuccess, tried: tried, referenceCount: refs}

View File

@@ -5,18 +5,19 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/mstime"
"time"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/domainmessage"
)
// KnownAddress tracks information about a known network address that is used
// to determine how viable an address is.
type KnownAddress struct {
netAddress *appmessage.NetAddress
sourceAddress *appmessage.NetAddress
netAddress *domainmessage.NetAddress
sourceAddress *domainmessage.NetAddress
attempts int
lastAttempt mstime.Time
lastSuccess mstime.Time
@@ -27,9 +28,9 @@ type KnownAddress struct {
bannedTime mstime.Time
}
// NetAddress returns the underlying appmessage.NetAddress associated with the
// NetAddress returns the underlying domainmessage.NetAddress associated with the
// known address.
func (ka *KnownAddress) NetAddress() *appmessage.NetAddress {
func (ka *KnownAddress) NetAddress() *domainmessage.NetAddress {
return ka.netAddress
}

View File

@@ -5,13 +5,13 @@
package addressmanager_test
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/mstime"
"math"
"testing"
"time"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/addressmanager"
"github.com/kaspanet/kaspad/domainmessage"
)
func TestChance(t *testing.T) {
@@ -22,27 +22,27 @@ func TestChance(t *testing.T) {
}{
{
//Test normal case
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addressmanager.TstNewKnownAddress(&domainmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0),
1.0,
}, {
//Test case in which lastseen < 0
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(20 * time.Second)},
addressmanager.TstNewKnownAddress(&domainmessage.NetAddress{Timestamp: now.Add(20 * time.Second)},
0, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0),
1.0,
}, {
//Test case in which lastAttempt < 0
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addressmanager.TstNewKnownAddress(&domainmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, mstime.Now().Add(30*time.Minute), mstime.Now(), false, 0),
1.0 * .01,
}, {
//Test case in which lastAttempt < ten minutes
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addressmanager.TstNewKnownAddress(&domainmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, mstime.Now().Add(-5*time.Minute), mstime.Now(), false, 0),
1.0 * .01,
}, {
//Test case with several failed attempts.
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addressmanager.TstNewKnownAddress(&domainmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
2, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0),
1 / 1.5 / 1.5,
},
@@ -66,10 +66,10 @@ func TestIsBad(t *testing.T) {
hoursOld := now.Add(-5 * time.Hour)
zeroTime := mstime.Time{}
futureNa := &appmessage.NetAddress{Timestamp: future}
minutesOldNa := &appmessage.NetAddress{Timestamp: minutesOld}
monthOldNa := &appmessage.NetAddress{Timestamp: monthOld}
currentNa := &appmessage.NetAddress{Timestamp: secondsOld}
futureNa := &domainmessage.NetAddress{Timestamp: future}
minutesOldNa := &domainmessage.NetAddress{Timestamp: minutesOld}
monthOldNa := &domainmessage.NetAddress{Timestamp: monthOld}
currentNa := &domainmessage.NetAddress{Timestamp: secondsOld}
//Test addresses that have been tried in the last minute.
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) {

View File

@@ -5,7 +5,7 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)

View File

@@ -5,8 +5,9 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"net"
"github.com/kaspanet/kaspad/domainmessage"
)
var (
@@ -85,19 +86,19 @@ func ipNet(ip string, ones, bits int) net.IPNet {
}
// IsIPv4 returns whether or not the given address is an IPv4 address.
func IsIPv4(na *appmessage.NetAddress) bool {
func IsIPv4(na *domainmessage.NetAddress) bool {
return na.IP.To4() != nil
}
// IsLocal returns whether or not the given address is a local address.
func IsLocal(na *appmessage.NetAddress) bool {
func IsLocal(na *domainmessage.NetAddress) bool {
return na.IP.IsLoopback() || zero4Net.Contains(na.IP)
}
// IsRFC1918 returns whether or not the passed address is part of the IPv4
// private network address space as defined by RFC1918 (10.0.0.0/8,
// 172.16.0.0/12, or 192.168.0.0/16).
func IsRFC1918(na *appmessage.NetAddress) bool {
func IsRFC1918(na *domainmessage.NetAddress) bool {
for _, rfc := range rfc1918Nets {
if rfc.Contains(na.IP) {
return true
@@ -108,56 +109,56 @@ func IsRFC1918(na *appmessage.NetAddress) bool {
// IsRFC2544 returns whether or not the passed address is part of the IPv4
// address space as defined by RFC2544 (198.18.0.0/15)
func IsRFC2544(na *appmessage.NetAddress) bool {
func IsRFC2544(na *domainmessage.NetAddress) bool {
return rfc2544Net.Contains(na.IP)
}
// IsRFC3849 returns whether or not the passed address is part of the IPv6
// documentation range as defined by RFC3849 (2001:DB8::/32).
func IsRFC3849(na *appmessage.NetAddress) bool {
func IsRFC3849(na *domainmessage.NetAddress) bool {
return rfc3849Net.Contains(na.IP)
}
// IsRFC3927 returns whether or not the passed address is part of the IPv4
// autoconfiguration range as defined by RFC3927 (169.254.0.0/16).
func IsRFC3927(na *appmessage.NetAddress) bool {
func IsRFC3927(na *domainmessage.NetAddress) bool {
return rfc3927Net.Contains(na.IP)
}
// IsRFC3964 returns whether or not the passed address is part of the IPv6 to
// IPv4 encapsulation range as defined by RFC3964 (2002::/16).
func IsRFC3964(na *appmessage.NetAddress) bool {
func IsRFC3964(na *domainmessage.NetAddress) bool {
return rfc3964Net.Contains(na.IP)
}
// IsRFC4193 returns whether or not the passed address is part of the IPv6
// unique local range as defined by RFC4193 (FC00::/7).
func IsRFC4193(na *appmessage.NetAddress) bool {
func IsRFC4193(na *domainmessage.NetAddress) bool {
return rfc4193Net.Contains(na.IP)
}
// IsRFC4380 returns whether or not the passed address is part of the IPv6
// teredo tunneling over UDP range as defined by RFC4380 (2001::/32).
func IsRFC4380(na *appmessage.NetAddress) bool {
func IsRFC4380(na *domainmessage.NetAddress) bool {
return rfc4380Net.Contains(na.IP)
}
// IsRFC4843 returns whether or not the passed address is part of the IPv6
// ORCHID range as defined by RFC4843 (2001:10::/28).
func IsRFC4843(na *appmessage.NetAddress) bool {
func IsRFC4843(na *domainmessage.NetAddress) bool {
return rfc4843Net.Contains(na.IP)
}
// IsRFC4862 returns whether or not the passed address is part of the IPv6
// stateless address autoconfiguration range as defined by RFC4862 (FE80::/64).
func IsRFC4862(na *appmessage.NetAddress) bool {
func IsRFC4862(na *domainmessage.NetAddress) bool {
return rfc4862Net.Contains(na.IP)
}
// IsRFC5737 returns whether or not the passed address is part of the IPv4
// documentation address space as defined by RFC5737 (192.0.2.0/24,
// 198.51.100.0/24, 203.0.113.0/24)
func IsRFC5737(na *appmessage.NetAddress) bool {
func IsRFC5737(na *domainmessage.NetAddress) bool {
for _, rfc := range rfc5737Net {
if rfc.Contains(na.IP) {
return true
@@ -169,19 +170,19 @@ func IsRFC5737(na *appmessage.NetAddress) bool {
// IsRFC6052 returns whether or not the passed address is part of the IPv6
// well-known prefix range as defined by RFC6052 (64:FF9B::/96).
func IsRFC6052(na *appmessage.NetAddress) bool {
func IsRFC6052(na *domainmessage.NetAddress) bool {
return rfc6052Net.Contains(na.IP)
}
// IsRFC6145 returns whether or not the passed address is part of the IPv6 to
// IPv4 translated address range as defined by RFC6145 (::FFFF:0:0:0/96).
func IsRFC6145(na *appmessage.NetAddress) bool {
func IsRFC6145(na *domainmessage.NetAddress) bool {
return rfc6145Net.Contains(na.IP)
}
// IsRFC6598 returns whether or not the passed address is part of the IPv4
// shared address space specified by RFC6598 (100.64.0.0/10)
func IsRFC6598(na *appmessage.NetAddress) bool {
func IsRFC6598(na *domainmessage.NetAddress) bool {
return rfc6598Net.Contains(na.IP)
}
@@ -189,7 +190,7 @@ func IsRFC6598(na *appmessage.NetAddress) bool {
// considered invalid under the following circumstances:
// IPv4: It is either a zero or all bits set address.
// IPv6: It is either a zero or RFC3849 documentation address.
func IsValid(na *appmessage.NetAddress) bool {
func IsValid(na *domainmessage.NetAddress) bool {
// IsUnspecified returns if address is 0, so only all bits set, and
// RFC3849 need to be explicitly checked.
return na.IP != nil && !(na.IP.IsUnspecified() ||
@@ -199,7 +200,7 @@ func IsValid(na *appmessage.NetAddress) bool {
// IsRoutable returns whether or not the passed address is routable over
// the public internet. This is true as long as the address is valid and is not
// in any reserved ranges.
func (am *AddressManager) IsRoutable(na *appmessage.NetAddress) bool {
func (am *AddressManager) IsRoutable(na *domainmessage.NetAddress) bool {
if am.cfg.NetParams().AcceptUnroutable {
return !IsLocal(na)
}
@@ -214,7 +215,7 @@ func (am *AddressManager) IsRoutable(na *appmessage.NetAddress) bool {
// of. This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string
// "local" for a local address, and the string "unroutable" for an unroutable
// address.
func (am *AddressManager) GroupKey(na *appmessage.NetAddress) string {
func (am *AddressManager) GroupKey(na *domainmessage.NetAddress) string {
if IsLocal(na) {
return "local"
}

View File

@@ -5,9 +5,10 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"net"
"testing"
"github.com/kaspanet/kaspad/domainmessage"
)
// TestIPTypes ensures the various functions which determine the type of an IP
@@ -16,7 +17,7 @@ func TestIPTypes(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil)
defer teardown()
type ipTest struct {
in appmessage.NetAddress
in domainmessage.NetAddress
rfc1918 bool
rfc2544 bool
rfc3849 bool
@@ -39,7 +40,7 @@ func TestIPTypes(t *testing.T) {
rfc4193, rfc4380, rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598,
local, valid, routable bool) ipTest {
nip := net.ParseIP(ip)
na := *appmessage.NewNetAddressIPPort(nip, 16111, appmessage.SFNodeNetwork)
na := *domainmessage.NewNetAddressIPPort(nip, 16111, domainmessage.SFNodeNetwork)
test := ipTest{na, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964, rfc4193, rfc4380,
rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, local, valid, routable}
return test
@@ -195,7 +196,7 @@ func TestGroupKey(t *testing.T) {
for i, test := range tests {
nip := net.ParseIP(test.ip)
na := *appmessage.NewNetAddressIPPort(nip, 8333, appmessage.SFNodeNetwork)
na := *domainmessage.NewNetAddressIPPort(nip, 8333, domainmessage.SFNodeNetwork)
if key := amgr.GroupKey(&na); key != test.expected {
t.Errorf("TestGroupKey #%d (%s): unexpected group key "+
"- got '%s', want '%s'", i, test.name,

View File

@@ -4,23 +4,24 @@ import (
"fmt"
"sync/atomic"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
"github.com/kaspanet/kaspad/netadapter/id"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol"
"github.com/kaspanet/kaspad/app/rpc"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/domain/blockdag/indexers"
"github.com/kaspanet/kaspad/domain/mempool"
"github.com/kaspanet/kaspad/domain/mining"
"github.com/kaspanet/kaspad/domain/txscript"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
"github.com/kaspanet/kaspad/infrastructure/network/dnsseed"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/blockdag/indexers"
"github.com/kaspanet/kaspad/config"
"github.com/kaspanet/kaspad/connmanager"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/dnsseed"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/mempool"
"github.com/kaspanet/kaspad/mining"
"github.com/kaspanet/kaspad/netadapter"
"github.com/kaspanet/kaspad/protocol"
"github.com/kaspanet/kaspad/rpc"
"github.com/kaspanet/kaspad/signal"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/panics"
)
@@ -28,9 +29,9 @@ import (
// App is a wrapper for all the kaspad services
type App struct {
cfg *config.Config
rpcServer *rpc.Server
addressManager *addressmanager.AddressManager
protocolManager *protocol.Manager
rpcManager *rpc.Manager
connectionManager *connmanager.ConnectionManager
netAdapter *netadapter.NetAdapter
@@ -46,39 +47,46 @@ func (a *App) Start() {
log.Trace("Starting kaspad")
err := a.netAdapter.Start()
err := a.protocolManager.Start()
if err != nil {
panics.Exit(log, fmt.Sprintf("Error starting the net adapter: %+v", err))
panics.Exit(log, fmt.Sprintf("Error starting the p2p protocol: %+v", err))
}
a.maybeSeedFromDNS()
a.connectionManager.Start()
if !a.cfg.DisableRPC {
a.rpcServer.Start()
}
}
// Stop gracefully shuts down all the kaspad services.
func (a *App) Stop() {
func (a *App) Stop() error {
// Make sure this only happens once.
if atomic.AddInt32(&a.shutdown, 1) != 1 {
log.Infof("Kaspad is already in the process of shutting down")
return
return nil
}
log.Warnf("Kaspad shutting down")
a.connectionManager.Stop()
err := a.netAdapter.Stop()
err := a.protocolManager.Stop()
if err != nil {
log.Errorf("Error stopping the net adapter: %+v", err)
log.Errorf("Error stopping the p2p protocol: %+v", err)
}
err = a.addressManager.Stop()
if err != nil {
log.Errorf("Error stopping address manager: %s", err)
// Shutdown the RPC server if it's not disabled.
if !a.cfg.DisableRPC {
err := a.rpcServer.Stop()
if err != nil {
log.Errorf("Error stopping rpcServer: %+v", err)
}
}
return
return nil
}
// New returns a new App instance configured to listen on addr for the
@@ -101,101 +109,43 @@ func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext, interrup
if err != nil {
return nil, err
}
addressManager, err := addressmanager.New(cfg, databaseContext)
if err != nil {
return nil, err
}
addressManager := addressmanager.New(cfg, databaseContext)
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
if err != nil {
return nil, err
}
protocolManager, err := protocol.NewManager(cfg, dag, netAdapter, addressManager, txMempool, connectionManager)
if err != nil {
return nil, err
}
rpcManager := setupRPC(cfg, txMempool, dag, sigCache, netAdapter, protocolManager, connectionManager, addressManager, acceptanceIndex)
rpcServer, err := setupRPC(
cfg, dag, txMempool, sigCache, acceptanceIndex, connectionManager, addressManager, protocolManager)
if err != nil {
return nil, err
}
return &App{
cfg: cfg,
rpcServer: rpcServer,
protocolManager: protocolManager,
rpcManager: rpcManager,
connectionManager: connectionManager,
netAdapter: netAdapter,
addressManager: addressManager,
}, nil
}
func setupRPC(
cfg *config.Config,
txMempool *mempool.TxPool,
dag *blockdag.BlockDAG,
sigCache *txscript.SigCache,
netAdapter *netadapter.NetAdapter,
protocolManager *protocol.Manager,
connectionManager *connmanager.ConnectionManager,
addressManager *addressmanager.AddressManager,
acceptanceIndex *indexers.AcceptanceIndex) *rpc.Manager {
blockTemplateGenerator := mining.NewBlkTmplGenerator(&mining.Policy{BlockMaxMass: cfg.BlockMaxMass}, txMempool, dag, sigCache)
rpcManager := rpc.NewManager(cfg, netAdapter, dag, protocolManager, connectionManager, blockTemplateGenerator, txMempool, addressManager, acceptanceIndex)
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
protocolManager.SetOnTransactionAddedToMempoolHandler(rpcManager.NotifyTransactionAddedToMempool)
dag.Subscribe(func(notification *blockdag.Notification) {
err := handleBlockDAGNotifications(notification, acceptanceIndex, rpcManager)
if err != nil {
panic(err)
}
})
return rpcManager
}
func handleBlockDAGNotifications(notification *blockdag.Notification,
acceptanceIndex *indexers.AcceptanceIndex, rpcManager *rpc.Manager) error {
switch notification.Type {
case blockdag.NTChainChanged:
if acceptanceIndex == nil {
return nil
}
chainChangedNotificationData := notification.Data.(*blockdag.ChainChangedNotificationData)
err := rpcManager.NotifyChainChanged(chainChangedNotificationData.RemovedChainBlockHashes,
chainChangedNotificationData.AddedChainBlockHashes)
if err != nil {
return err
}
case blockdag.NTFinalityConflict:
finalityConflictNotificationData := notification.Data.(*blockdag.FinalityConflictNotificationData)
err := rpcManager.NotifyFinalityConflict(finalityConflictNotificationData.ViolatingBlockHash.String())
if err != nil {
return err
}
case blockdag.NTFinalityConflictResolved:
finalityConflictResolvedNotificationData := notification.Data.(*blockdag.FinalityConflictResolvedNotificationData)
err := rpcManager.NotifyFinalityConflictResolved(finalityConflictResolvedNotificationData.FinalityBlockHash.String())
if err != nil {
return err
}
}
return nil
}
func (a *App) maybeSeedFromDNS() {
if !a.cfg.DisableDNSSeed {
dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, appmessage.SFNodeNetwork, false, nil,
a.cfg.Lookup, func(addresses []*appmessage.NetAddress) {
dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, domainmessage.SFNodeNetwork, false, nil,
a.cfg.Lookup, func(addresses []*domainmessage.NetAddress) {
// Kaspad uses a lookup of the dns seeder here. Since seeder returns
// IPs of nodes and not its own IP, we can not know real IP of
// source. So we'll take first returned address as source.
a.addressManager.AddAddresses(addresses, addresses[0], nil)
})
}
if a.cfg.GRPCSeed != "" {
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil,
func(addresses []*appmessage.NetAddress) {
a.addressManager.AddAddresses(addresses, addresses[0], nil)
})
}
}
func setupDAG(cfg *config.Config, databaseContext *dbaccess.DatabaseContext, interrupt <-chan struct{},
sigCache *txscript.SigCache, indexManager blockdag.IndexManager) (*blockdag.BlockDAG, error) {
@@ -240,15 +190,48 @@ func setupMempool(cfg *config.Config, dag *blockdag.BlockDAG, sigCache *txscript
MaxTxVersion: 1,
},
CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) {
return dag.CalcSequenceLockNoLock(tx, utxoSet)
return dag.CalcSequenceLockNoLock(tx, utxoSet, true)
},
SigCache: sigCache,
DAG: dag,
IsDeploymentActive: dag.IsDeploymentActive,
SigCache: sigCache,
DAG: dag,
}
return mempool.New(&mempoolConfig)
}
func setupRPC(cfg *config.Config,
dag *blockdag.BlockDAG,
txMempool *mempool.TxPool,
sigCache *txscript.SigCache,
acceptanceIndex *indexers.AcceptanceIndex,
connectionManager *connmanager.ConnectionManager,
addressManager *addressmanager.AddressManager,
protocolManager *protocol.Manager) (*rpc.Server, error) {
if !cfg.DisableRPC {
policy := mining.Policy{
BlockMaxMass: cfg.BlockMaxMass,
}
blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, txMempool, dag, sigCache)
rpcServer, err := rpc.NewRPCServer(cfg, dag, txMempool, acceptanceIndex, blockTemplateGenerator,
connectionManager, addressManager, protocolManager)
if err != nil {
return nil, err
}
// Signal process shutdown when the RPC server requests it.
spawn("setupRPC-handleShutdownRequest", func() {
<-rpcServer.RequestedProcessShutdown()
signal.ShutdownRequestChannel <- struct{}{}
})
return rpcServer, nil
}
return nil, nil
}
// P2PNodeID returns the network ID associated with this App
func (a *App) P2PNodeID() *id.ID {
return a.netAdapter.ID()
@@ -258,3 +241,9 @@ func (a *App) P2PNodeID() *id.ID {
func (a *App) AddressManager() *addressmanager.AddressManager {
return a.addressManager
}
// WaitForShutdown blocks until the main listener and peer handlers are stopped.
func (a *App) WaitForShutdown() {
// TODO(libp2p)
// a.p2pServer.WaitForShutdown()
}

View File

@@ -1,24 +0,0 @@
package appmessage
import "time"
type baseMessage struct {
messageNumber uint64
receivedAt time.Time
}
func (b *baseMessage) MessageNumber() uint64 {
return b.messageNumber
}
func (b *baseMessage) SetMessageNumber(messageNumber uint64) {
b.messageNumber = messageNumber
}
func (b *baseMessage) ReceivedAt() time.Time {
return b.receivedAt
}
func (b *baseMessage) SetReceivedAt(receivedAt time.Time) {
b.receivedAt = receivedAt
}

View File

@@ -1,185 +0,0 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package appmessage
import (
"fmt"
"time"
)
// MaxMessagePayload is the maximum bytes a message can be regardless of other
// individual limits imposed by messages themselves.
const MaxMessagePayload = 1024 * 1024 * 32 // 32MB
// MessageCommand is a number in the header of a message that represents its type.
type MessageCommand uint32
func (cmd MessageCommand) String() string {
cmdString, ok := ProtocolMessageCommandToString[cmd]
if !ok {
cmdString, ok = RPCMessageCommandToString[cmd]
}
if !ok {
cmdString = "unknown command"
}
return fmt.Sprintf("%s [code %d]", cmdString, uint8(cmd))
}
// Commands used in kaspa message headers which describe the type of message.
const (
// protocol
CmdVersion MessageCommand = iota
CmdVerAck
CmdRequestAddresses
CmdAddresses
CmdRequestIBDBlocks
CmdBlock
CmdTx
CmdPing
CmdPong
CmdRequestBlockLocator
CmdBlockLocator
CmdSelectedTip
CmdRequestSelectedTip
CmdInvRelayBlock
CmdRequestRelayBlocks
CmdInvTransaction
CmdRequestTransactions
CmdIBDBlock
CmdRequestNextIBDBlocks
CmdDoneIBDBlocks
CmdTransactionNotFound
CmdReject
// rpc
CmdGetCurrentNetworkRequestMessage
CmdGetCurrentNetworkResponseMessage
CmdSubmitBlockRequestMessage
CmdSubmitBlockResponseMessage
CmdGetBlockTemplateRequestMessage
CmdGetBlockTemplateResponseMessage
CmdGetBlockTemplateTransactionMessage
CmdNotifyBlockAddedRequestMessage
CmdNotifyBlockAddedResponseMessage
CmdBlockAddedNotificationMessage
CmdGetPeerAddressesRequestMessage
CmdGetPeerAddressesResponseMessage
CmdGetSelectedTipHashRequestMessage
CmdGetSelectedTipHashResponseMessage
CmdGetMempoolEntryRequestMessage
CmdGetMempoolEntryResponseMessage
CmdGetConnectedPeerInfoRequestMessage
CmdGetConnectedPeerInfoResponseMessage
CmdAddPeerRequestMessage
CmdAddPeerResponseMessage
CmdSubmitTransactionRequestMessage
CmdSubmitTransactionResponseMessage
CmdNotifyChainChangedRequestMessage
CmdNotifyChainChangedResponseMessage
CmdChainChangedNotificationMessage
CmdGetBlockRequestMessage
CmdGetBlockResponseMessage
CmdGetSubnetworkRequestMessage
CmdGetSubnetworkResponseMessage
CmdGetChainFromBlockRequestMessage
CmdGetChainFromBlockResponseMessage
CmdGetBlocksRequestMessage
CmdGetBlocksResponseMessage
CmdGetBlockCountRequestMessage
CmdGetBlockCountResponseMessage
CmdGetBlockDAGInfoRequestMessage
CmdGetBlockDAGInfoResponseMessage
CmdResolveFinalityConflictRequestMessage
CmdResolveFinalityConflictResponseMessage
CmdNotifyFinalityConflictsRequestMessage
CmdNotifyFinalityConflictsResponseMessage
CmdFinalityConflictNotificationMessage
CmdFinalityConflictResolvedNotificationMessage
)
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
var ProtocolMessageCommandToString = map[MessageCommand]string{
CmdVersion: "Version",
CmdVerAck: "VerAck",
CmdRequestAddresses: "RequestAddresses",
CmdAddresses: "Addresses",
CmdRequestIBDBlocks: "RequestBlocks",
CmdBlock: "Block",
CmdTx: "Tx",
CmdPing: "Ping",
CmdPong: "Pong",
CmdRequestBlockLocator: "RequestBlockLocator",
CmdBlockLocator: "BlockLocator",
CmdSelectedTip: "SelectedTip",
CmdRequestSelectedTip: "RequestSelectedTip",
CmdInvRelayBlock: "InvRelayBlock",
CmdRequestRelayBlocks: "RequestRelayBlocks",
CmdInvTransaction: "InvTransaction",
CmdRequestTransactions: "RequestTransactions",
CmdIBDBlock: "IBDBlock",
CmdRequestNextIBDBlocks: "RequestNextIBDBlocks",
CmdDoneIBDBlocks: "DoneIBDBlocks",
CmdTransactionNotFound: "TransactionNotFound",
CmdReject: "Reject",
}
// RPCMessageCommandToString maps all MessageCommands to their string representation
var RPCMessageCommandToString = map[MessageCommand]string{
CmdGetCurrentNetworkRequestMessage: "GetCurrentNetworkRequest",
CmdGetCurrentNetworkResponseMessage: "GetCurrentNetworkResponse",
CmdSubmitBlockRequestMessage: "SubmitBlockRequest",
CmdSubmitBlockResponseMessage: "SubmitBlockResponse",
CmdGetBlockTemplateRequestMessage: "GetBlockTemplateRequest",
CmdGetBlockTemplateResponseMessage: "GetBlockTemplateResponse",
CmdGetBlockTemplateTransactionMessage: "CmdGetBlockTemplateTransaction",
CmdNotifyBlockAddedRequestMessage: "NotifyBlockAddedRequest",
CmdNotifyBlockAddedResponseMessage: "NotifyBlockAddedResponse",
CmdBlockAddedNotificationMessage: "BlockAddedNotification",
CmdGetPeerAddressesRequestMessage: "GetPeerAddressesRequest",
CmdGetPeerAddressesResponseMessage: "GetPeerAddressesResponse",
CmdGetSelectedTipHashRequestMessage: "GetSelectedTipHashRequest",
CmdGetSelectedTipHashResponseMessage: "GetSelectedTipHashResponse",
CmdGetMempoolEntryRequestMessage: "GetMempoolEntryRequest",
CmdGetMempoolEntryResponseMessage: "GetMempoolEntryResponse",
CmdGetConnectedPeerInfoRequestMessage: "GetConnectedPeerInfoRequest",
CmdGetConnectedPeerInfoResponseMessage: "GetConnectedPeerInfoResponse",
CmdAddPeerRequestMessage: "AddPeerRequest",
CmdAddPeerResponseMessage: "AddPeerResponse",
CmdSubmitTransactionRequestMessage: "SubmitTransactionRequest",
CmdSubmitTransactionResponseMessage: "SubmitTransactionResponse",
CmdNotifyChainChangedRequestMessage: "NotifyChainChangedRequest",
CmdNotifyChainChangedResponseMessage: "NotifyChainChangedResponse",
CmdChainChangedNotificationMessage: "ChainChangedNotification",
CmdGetBlockRequestMessage: "GetBlockRequest",
CmdGetBlockResponseMessage: "GetBlockResponse",
CmdGetSubnetworkRequestMessage: "GetSubnetworkRequest",
CmdGetSubnetworkResponseMessage: "GetSubnetworkResponse",
CmdGetChainFromBlockRequestMessage: "GetChainFromBlockRequest",
CmdGetChainFromBlockResponseMessage: "GetChainFromBlockResponse",
CmdGetBlocksRequestMessage: "GetBlocksRequest",
CmdGetBlocksResponseMessage: "GetBlocksResponse",
CmdGetBlockCountRequestMessage: "GetBlockCountRequest",
CmdGetBlockCountResponseMessage: "GetBlockCountResponse",
CmdGetBlockDAGInfoRequestMessage: "GetBlockDAGInfoRequest",
CmdGetBlockDAGInfoResponseMessage: "GetBlockDAGInfoResponse",
CmdResolveFinalityConflictRequestMessage: "ResolveFinalityConflictRequest",
CmdResolveFinalityConflictResponseMessage: "ResolveFinalityConflictResponse",
CmdNotifyFinalityConflictsRequestMessage: "NotifyFinalityConflictsRequest",
CmdNotifyFinalityConflictsResponseMessage: "NotifyFinalityConflictsResponse",
CmdFinalityConflictNotificationMessage: "FinalityConflictNotification",
CmdFinalityConflictResolvedNotificationMessage: "FinalityConflictResolvedNotification",
}
// Message is an interface that describes a kaspa message. A type that
// implements Message has complete control over the representation of its data
// and may therefore contain additional or fewer fields than those which
// are used directly in the protocol encoded message.
type Message interface {
Command() MessageCommand
MessageNumber() uint64
SetMessageNumber(index uint64)
ReceivedAt() time.Time
SetReceivedAt(receivedAt time.Time)
}

View File

@@ -1,22 +0,0 @@
package appmessage
// MsgReject implements the Message interface and represents a kaspa
// Reject message. It is used to notify peers why they are banned.
type MsgReject struct {
baseMessage
Reason string
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgReject) Command() MessageCommand {
return CmdReject
}
// NewMsgReject returns a new kaspa Reject message that conforms to the
// Message interface.
func NewMsgReject(reason string) *MsgReject {
return &MsgReject{
Reason: reason,
}
}

View File

@@ -1,39 +0,0 @@
package appmessage
// AddPeerRequestMessage is an appmessage corresponding to
// its respective RPC message
type AddPeerRequestMessage struct {
baseMessage
Address string
IsPermanent bool
}
// Command returns the protocol command string for the message
func (msg *AddPeerRequestMessage) Command() MessageCommand {
return CmdAddPeerRequestMessage
}
// NewAddPeerRequestMessage returns a instance of the message
func NewAddPeerRequestMessage(address string, isPermanent bool) *AddPeerRequestMessage {
return &AddPeerRequestMessage{
Address: address,
IsPermanent: isPermanent,
}
}
// AddPeerResponseMessage is an appmessage corresponding to
// its respective RPC message
type AddPeerResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *AddPeerResponseMessage) Command() MessageCommand {
return CmdAddPeerResponseMessage
}
// NewAddPeerResponseMessage returns a instance of the message
func NewAddPeerResponseMessage() *AddPeerResponseMessage {
return &AddPeerResponseMessage{}
}

View File

@@ -1,123 +0,0 @@
package appmessage
// GetBlockRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockRequestMessage struct {
baseMessage
Hash string
SubnetworkID string
IncludeBlockHex bool
IncludeBlockVerboseData bool
IncludeTransactionVerboseData bool
}
// Command returns the protocol command string for the message
func (msg *GetBlockRequestMessage) Command() MessageCommand {
return CmdGetBlockRequestMessage
}
// NewGetBlockRequestMessage returns a instance of the message
func NewGetBlockRequestMessage(hash string, subnetworkID string, includeBlockHex bool,
includeBlockVerboseData bool, includeTransactionVerboseData bool) *GetBlockRequestMessage {
return &GetBlockRequestMessage{
Hash: hash,
SubnetworkID: subnetworkID,
IncludeBlockHex: includeBlockHex,
IncludeBlockVerboseData: includeBlockVerboseData,
IncludeTransactionVerboseData: includeTransactionVerboseData,
}
}
// GetBlockResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockResponseMessage struct {
baseMessage
BlockHex string
BlockVerboseData *BlockVerboseData
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetBlockResponseMessage) Command() MessageCommand {
return CmdGetBlockResponseMessage
}
// NewGetBlockResponseMessage returns a instance of the message
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
return &GetBlockResponseMessage{}
}
// BlockVerboseData holds verbose data about a block
type BlockVerboseData struct {
Hash string
Confirmations uint64
Size int32
BlueScore uint64
IsChainBlock bool
Version int32
VersionHex string
HashMerkleRoot string
AcceptedIDMerkleRoot string
UTXOCommitment string
TxIDs []string
TransactionVerboseData []*TransactionVerboseData
Time int64
Nonce uint64
Bits string
Difficulty float64
ParentHashes []string
SelectedParentHash string
ChildHashes []string
AcceptedBlockHashes []string
}
// TransactionVerboseData holds verbose data about a transaction
type TransactionVerboseData struct {
Hex string
TxID string
Hash string
Size int32
Version int32
LockTime uint64
SubnetworkID string
Gas uint64
PayloadHash string
Payload string
TransactionVerboseInputs []*TransactionVerboseInput
TransactionVerboseOutputs []*TransactionVerboseOutput
BlockHash string
AcceptedBy string
IsInMempool bool
Time uint64
BlockTime uint64
}
// TransactionVerboseInput holds data about a transaction input
type TransactionVerboseInput struct {
TxID string
OutputIndex uint32
ScriptSig *ScriptSig
Sequence uint64
}
// ScriptSig holds data about a script signature
type ScriptSig struct {
Asm string
Hex string
}
// TransactionVerboseOutput holds data about a transaction output
type TransactionVerboseOutput struct {
Value uint64
Index uint32
ScriptPubKey *ScriptPubKeyResult
}
// ScriptPubKeyResult holds data about a script public key
type ScriptPubKeyResult struct {
Asm string
Hex string
Type string
Address string
}

View File

@@ -1,38 +0,0 @@
package appmessage
// GetBlockCountRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockCountRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetBlockCountRequestMessage) Command() MessageCommand {
return CmdGetBlockCountRequestMessage
}
// NewGetBlockCountRequestMessage returns a instance of the message
func NewGetBlockCountRequestMessage() *GetBlockCountRequestMessage {
return &GetBlockCountRequestMessage{}
}
// GetBlockCountResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockCountResponseMessage struct {
baseMessage
BlockCount uint64
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetBlockCountResponseMessage) Command() MessageCommand {
return CmdGetBlockCountResponseMessage
}
// NewGetBlockCountResponseMessage returns a instance of the message
func NewGetBlockCountResponseMessage(blockCount uint64) *GetBlockCountResponseMessage {
return &GetBlockCountResponseMessage{
BlockCount: blockCount,
}
}

View File

@@ -1,41 +0,0 @@
package appmessage
// GetBlockDAGInfoRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockDAGInfoRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetBlockDAGInfoRequestMessage) Command() MessageCommand {
return CmdGetBlockDAGInfoRequestMessage
}
// NewGetBlockDAGInfoRequestMessage returns a instance of the message
func NewGetBlockDAGInfoRequestMessage() *GetBlockDAGInfoRequestMessage {
return &GetBlockDAGInfoRequestMessage{}
}
// GetBlockDAGInfoResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockDAGInfoResponseMessage struct {
baseMessage
NetworkName string
BlockCount uint64
TipHashes []string
VirtualParentHashes []string
Difficulty float64
PastMedianTime int64
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetBlockDAGInfoResponseMessage) Command() MessageCommand {
return CmdGetBlockDAGInfoResponseMessage
}
// NewGetBlockDAGInfoResponseMessage returns a instance of the message
func NewGetBlockDAGInfoResponseMessage() *GetBlockDAGInfoResponseMessage {
return &GetBlockDAGInfoResponseMessage{}
}

View File

@@ -1,78 +0,0 @@
package appmessage
// GetBlockTemplateRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockTemplateRequestMessage struct {
baseMessage
PayAddress string
LongPollID string
}
// Command returns the protocol command string for the message
func (msg *GetBlockTemplateRequestMessage) Command() MessageCommand {
return CmdGetBlockTemplateRequestMessage
}
// NewGetBlockTemplateRequestMessage returns a instance of the message
func NewGetBlockTemplateRequestMessage(payAddress string, longPollID string) *GetBlockTemplateRequestMessage {
return &GetBlockTemplateRequestMessage{
PayAddress: payAddress,
LongPollID: longPollID,
}
}
// GetBlockTemplateResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockTemplateResponseMessage struct {
baseMessage
Bits string
CurrentTime int64
ParentHashes []string
MassLimit int
Transactions []GetBlockTemplateTransactionMessage
HashMerkleRoot string
AcceptedIDMerkleRoot string
UTXOCommitment string
Version int32
LongPollID string
TargetDifficulty string
MinTime int64
MaxTime int64
MutableFields []string
NonceRange string
IsSynced bool
IsConnected bool
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
return CmdGetBlockTemplateResponseMessage
}
// NewGetBlockTemplateResponseMessage returns a instance of the message
func NewGetBlockTemplateResponseMessage() *GetBlockTemplateResponseMessage {
return &GetBlockTemplateResponseMessage{}
}
// GetBlockTemplateTransactionMessage is an appmessage corresponding to
// its respective RPC message
type GetBlockTemplateTransactionMessage struct {
baseMessage
Data string
ID string
Depends []int64
Mass uint64
Fee uint64
}
// Command returns the protocol command string for the message
func (msg *GetBlockTemplateTransactionMessage) Command() MessageCommand {
return CmdGetBlockTemplateTransactionMessage
}
// NewGetBlockTemplateTransactionMessage returns a instance of the message
func NewGetBlockTemplateTransactionMessage() *GetBlockTemplateTransactionMessage {
return &GetBlockTemplateTransactionMessage{}
}

View File

@@ -1,51 +0,0 @@
package appmessage
// GetBlocksRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetBlocksRequestMessage struct {
baseMessage
LowHash string
IncludeBlockHexes bool
IncludeBlockVerboseData bool
}
// Command returns the protocol command string for the message
func (msg *GetBlocksRequestMessage) Command() MessageCommand {
return CmdGetBlocksRequestMessage
}
// NewGetBlocksRequestMessage returns a instance of the message
func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeBlockVerboseData bool) *GetBlocksRequestMessage {
return &GetBlocksRequestMessage{
LowHash: lowHash,
IncludeBlockHexes: includeBlockHexes,
IncludeBlockVerboseData: includeBlockVerboseData,
}
}
// GetBlocksResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetBlocksResponseMessage struct {
baseMessage
BlockHashes []string
BlockHexes []string
BlockVerboseData []*BlockVerboseData
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetBlocksResponseMessage) Command() MessageCommand {
return CmdGetBlocksResponseMessage
}
// NewGetBlocksResponseMessage returns a instance of the message
func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage {
return &GetBlocksResponseMessage{
BlockHashes: blockHashes,
BlockHexes: blockHexes,
BlockVerboseData: blockVerboseData,
}
}

View File

@@ -1,49 +0,0 @@
package appmessage
// GetChainFromBlockRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetChainFromBlockRequestMessage struct {
baseMessage
StartHash string
IncludeBlockVerboseData bool
}
// Command returns the protocol command string for the message
func (msg *GetChainFromBlockRequestMessage) Command() MessageCommand {
return CmdGetChainFromBlockRequestMessage
}
// NewGetChainFromBlockRequestMessage returns a instance of the message
func NewGetChainFromBlockRequestMessage(startHash string, includeBlockVerboseData bool) *GetChainFromBlockRequestMessage {
return &GetChainFromBlockRequestMessage{
StartHash: startHash,
IncludeBlockVerboseData: includeBlockVerboseData,
}
}
// GetChainFromBlockResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetChainFromBlockResponseMessage struct {
baseMessage
RemovedChainBlockHashes []string
AddedChainBlocks []*ChainBlock
BlockVerboseData []*BlockVerboseData
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetChainFromBlockResponseMessage) Command() MessageCommand {
return CmdGetChainFromBlockResponseMessage
}
// NewGetChainFromBlockResponseMessage returns a instance of the message
func NewGetChainFromBlockResponseMessage(removedChainBlockHashes []string,
addedChainBlocks []*ChainBlock, blockVerboseData []*BlockVerboseData) *GetChainFromBlockResponseMessage {
return &GetChainFromBlockResponseMessage{
RemovedChainBlockHashes: removedChainBlockHashes,
AddedChainBlocks: addedChainBlocks,
BlockVerboseData: blockVerboseData,
}
}

View File

@@ -1,51 +0,0 @@
package appmessage
// GetConnectedPeerInfoRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetConnectedPeerInfoRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetConnectedPeerInfoRequestMessage) Command() MessageCommand {
return CmdGetConnectedPeerInfoRequestMessage
}
// NewGetConnectedPeerInfoRequestMessage returns a instance of the message
func NewGetConnectedPeerInfoRequestMessage() *GetConnectedPeerInfoRequestMessage {
return &GetConnectedPeerInfoRequestMessage{}
}
// GetConnectedPeerInfoResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetConnectedPeerInfoResponseMessage struct {
baseMessage
Infos []*GetConnectedPeerInfoMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetConnectedPeerInfoResponseMessage) Command() MessageCommand {
return CmdGetConnectedPeerInfoResponseMessage
}
// NewGetConnectedPeerInfoResponseMessage returns a instance of the message
func NewGetConnectedPeerInfoResponseMessage(infos []*GetConnectedPeerInfoMessage) *GetConnectedPeerInfoResponseMessage {
return &GetConnectedPeerInfoResponseMessage{
Infos: infos,
}
}
// GetConnectedPeerInfoMessage holds information about a connected peer
type GetConnectedPeerInfoMessage struct {
ID string
Address string
LastPingDuration int64
SelectedTipHash string
IsSyncNode bool
IsOutbound bool
TimeOffset int64
UserAgent string
AdvertisedProtocolVersion uint32
TimeConnected int64
}

View File

@@ -1,38 +0,0 @@
package appmessage
// GetCurrentNetworkRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetCurrentNetworkRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetCurrentNetworkRequestMessage) Command() MessageCommand {
return CmdGetCurrentNetworkRequestMessage
}
// NewGetCurrentNetworkRequestMessage returns a instance of the message
func NewGetCurrentNetworkRequestMessage() *GetCurrentNetworkRequestMessage {
return &GetCurrentNetworkRequestMessage{}
}
// GetCurrentNetworkResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetCurrentNetworkResponseMessage struct {
baseMessage
CurrentNetwork string
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetCurrentNetworkResponseMessage) Command() MessageCommand {
return CmdGetCurrentNetworkResponseMessage
}
// NewGetCurrentNetworkResponseMessage returns a instance of the message
func NewGetCurrentNetworkResponseMessage(currentNetwork string) *GetCurrentNetworkResponseMessage {
return &GetCurrentNetworkResponseMessage{
CurrentNetwork: currentNetwork,
}
}

View File

@@ -1,35 +0,0 @@
package appmessage
// GetMempoolEntryRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetMempoolEntryRequestMessage struct {
baseMessage
TxID string
}
// Command returns the protocol command string for the message
func (msg *GetMempoolEntryRequestMessage) Command() MessageCommand {
return CmdGetMempoolEntryRequestMessage
}
// NewGetMempoolEntryRequestMessage returns a instance of the message
func NewGetMempoolEntryRequestMessage(txID string) *GetMempoolEntryRequestMessage {
return &GetMempoolEntryRequestMessage{TxID: txID}
}
// GetMempoolEntryResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetMempoolEntryResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetMempoolEntryResponseMessage) Command() MessageCommand {
return CmdGetMempoolEntryResponseMessage
}
// NewGetMempoolEntryResponseMessage returns a instance of the message
func NewGetMempoolEntryResponseMessage() *GetMempoolEntryResponseMessage {
return &GetMempoolEntryResponseMessage{}
}

View File

@@ -1,44 +0,0 @@
package appmessage
// GetPeerAddressesRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetPeerAddressesRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetPeerAddressesRequestMessage) Command() MessageCommand {
return CmdGetPeerAddressesRequestMessage
}
// NewGetPeerAddressesRequestMessage returns a instance of the message
func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage {
return &GetPeerAddressesRequestMessage{}
}
// GetPeerAddressesResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetPeerAddressesResponseMessage struct {
baseMessage
Addresses []*GetPeerAddressesKnownAddressMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetPeerAddressesResponseMessage) Command() MessageCommand {
return CmdGetPeerAddressesResponseMessage
}
// NewGetPeerAddressesResponseMessage returns a instance of the message
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
return &GetPeerAddressesResponseMessage{
Addresses: addresses,
}
}
// GetPeerAddressesKnownAddressMessage is an appmessage corresponding to
// its respective RPC message
type GetPeerAddressesKnownAddressMessage struct {
Addr string
}

View File

@@ -1,38 +0,0 @@
package appmessage
// GetSelectedTipHashRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetSelectedTipHashRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetSelectedTipHashRequestMessage) Command() MessageCommand {
return CmdGetSelectedTipHashRequestMessage
}
// NewGetSelectedTipHashRequestMessage returns a instance of the message
func NewGetSelectedTipHashRequestMessage() *GetSelectedTipHashRequestMessage {
return &GetSelectedTipHashRequestMessage{}
}
// GetSelectedTipHashResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetSelectedTipHashResponseMessage struct {
baseMessage
SelectedTipHash string
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetSelectedTipHashResponseMessage) Command() MessageCommand {
return CmdGetSelectedTipHashResponseMessage
}
// NewGetSelectedTipHashResponseMessage returns a instance of the message
func NewGetSelectedTipHashResponseMessage(selectedTipHash string) *GetSelectedTipHashResponseMessage {
return &GetSelectedTipHashResponseMessage{
SelectedTipHash: selectedTipHash,
}
}

View File

@@ -1,41 +0,0 @@
package appmessage
// GetSubnetworkRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetSubnetworkRequestMessage struct {
baseMessage
SubnetworkID string
}
// Command returns the protocol command string for the message
func (msg *GetSubnetworkRequestMessage) Command() MessageCommand {
return CmdGetSubnetworkRequestMessage
}
// NewGetSubnetworkRequestMessage returns a instance of the message
func NewGetSubnetworkRequestMessage(subnetworkID string) *GetSubnetworkRequestMessage {
return &GetSubnetworkRequestMessage{
SubnetworkID: subnetworkID,
}
}
// GetSubnetworkResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetSubnetworkResponseMessage struct {
baseMessage
GasLimit uint64
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetSubnetworkResponseMessage) Command() MessageCommand {
return CmdGetSubnetworkResponseMessage
}
// NewGetSubnetworkResponseMessage returns a instance of the message
func NewGetSubnetworkResponseMessage(gasLimit uint64) *GetSubnetworkResponseMessage {
return &GetSubnetworkResponseMessage{
GasLimit: gasLimit,
}
}

View File

@@ -1,53 +0,0 @@
package appmessage
// NotifyBlockAddedRequestMessage is an appmessage corresponding to
// its respective RPC message
type NotifyBlockAddedRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *NotifyBlockAddedRequestMessage) Command() MessageCommand {
return CmdNotifyBlockAddedRequestMessage
}
// NewNotifyBlockAddedRequestMessage returns a instance of the message
func NewNotifyBlockAddedRequestMessage() *NotifyBlockAddedRequestMessage {
return &NotifyBlockAddedRequestMessage{}
}
// NotifyBlockAddedResponseMessage is an appmessage corresponding to
// its respective RPC message
type NotifyBlockAddedResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *NotifyBlockAddedResponseMessage) Command() MessageCommand {
return CmdNotifyBlockAddedResponseMessage
}
// NewNotifyBlockAddedResponseMessage returns a instance of the message
func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
return &NotifyBlockAddedResponseMessage{}
}
// BlockAddedNotificationMessage is an appmessage corresponding to
// its respective RPC message
type BlockAddedNotificationMessage struct {
baseMessage
Block *MsgBlock
}
// Command returns the protocol command string for the message
func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
return CmdBlockAddedNotificationMessage
}
// NewBlockAddedNotificationMessage returns a instance of the message
func NewBlockAddedNotificationMessage(block *MsgBlock) *BlockAddedNotificationMessage {
return &BlockAddedNotificationMessage{
Block: block,
}
}

View File

@@ -1,69 +0,0 @@
package appmessage
// NotifyChainChangedRequestMessage is an appmessage corresponding to
// its respective RPC message
type NotifyChainChangedRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *NotifyChainChangedRequestMessage) Command() MessageCommand {
return CmdNotifyChainChangedRequestMessage
}
// NewNotifyChainChangedRequestMessage returns a instance of the message
func NewNotifyChainChangedRequestMessage() *NotifyChainChangedRequestMessage {
return &NotifyChainChangedRequestMessage{}
}
// NotifyChainChangedResponseMessage is an appmessage corresponding to
// its respective RPC message
type NotifyChainChangedResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *NotifyChainChangedResponseMessage) Command() MessageCommand {
return CmdNotifyChainChangedResponseMessage
}
// NewNotifyChainChangedResponseMessage returns a instance of the message
func NewNotifyChainChangedResponseMessage() *NotifyChainChangedResponseMessage {
return &NotifyChainChangedResponseMessage{}
}
// ChainChangedNotificationMessage is an appmessage corresponding to
// its respective RPC message
type ChainChangedNotificationMessage struct {
baseMessage
RemovedChainBlockHashes []string
AddedChainBlocks []*ChainBlock
}
// ChainBlock represents a DAG chain-block
type ChainBlock struct {
Hash string
AcceptedBlocks []*AcceptedBlock
}
// AcceptedBlock represents a block accepted into the DAG
type AcceptedBlock struct {
Hash string
AcceptedTxIDs []string
}
// Command returns the protocol command string for the message
func (msg *ChainChangedNotificationMessage) Command() MessageCommand {
return CmdChainChangedNotificationMessage
}
// NewChainChangedNotificationMessage returns a instance of the message
func NewChainChangedNotificationMessage(removedChainBlockHashes []string,
addedChainBlocks []*ChainBlock) *ChainChangedNotificationMessage {
return &ChainChangedNotificationMessage{
RemovedChainBlockHashes: removedChainBlockHashes,
AddedChainBlocks: addedChainBlocks,
}
}

View File

@@ -1,72 +0,0 @@
package appmessage
// NotifyFinalityConflictsRequestMessage is an appmessage corresponding to
// its respective RPC message
type NotifyFinalityConflictsRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *NotifyFinalityConflictsRequestMessage) Command() MessageCommand {
return CmdNotifyFinalityConflictsRequestMessage
}
// NewNotifyFinalityConflictsRequestMessage returns a instance of the message
func NewNotifyFinalityConflictsRequestMessage() *NotifyFinalityConflictsRequestMessage {
return &NotifyFinalityConflictsRequestMessage{}
}
// NotifyFinalityConflictsResponseMessage is an appmessage corresponding to
// its respective RPC message
type NotifyFinalityConflictsResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *NotifyFinalityConflictsResponseMessage) Command() MessageCommand {
return CmdNotifyFinalityConflictsResponseMessage
}
// NewNotifyFinalityConflictsResponseMessage returns a instance of the message
func NewNotifyFinalityConflictsResponseMessage() *NotifyFinalityConflictsResponseMessage {
return &NotifyFinalityConflictsResponseMessage{}
}
// FinalityConflictNotificationMessage is an appmessage corresponding to
// its respective RPC message
type FinalityConflictNotificationMessage struct {
baseMessage
ViolatingBlockHash string
}
// Command returns the protocol command string for the message
func (msg *FinalityConflictNotificationMessage) Command() MessageCommand {
return CmdFinalityConflictNotificationMessage
}
// NewFinalityConflictNotificationMessage returns a instance of the message
func NewFinalityConflictNotificationMessage(violatingBlockHash string) *FinalityConflictNotificationMessage {
return &FinalityConflictNotificationMessage{
ViolatingBlockHash: violatingBlockHash,
}
}
// FinalityConflictResolvedNotificationMessage is an appmessage corresponding to
// its respective RPC message
type FinalityConflictResolvedNotificationMessage struct {
baseMessage
FinalityBlockHash string
}
// Command returns the protocol command string for the message
func (msg *FinalityConflictResolvedNotificationMessage) Command() MessageCommand {
return CmdFinalityConflictResolvedNotificationMessage
}
// NewFinalityConflictResolvedNotificationMessage returns a instance of the message
func NewFinalityConflictResolvedNotificationMessage(finalityBlockHash string) *FinalityConflictResolvedNotificationMessage {
return &FinalityConflictResolvedNotificationMessage{
FinalityBlockHash: finalityBlockHash,
}
}

View File

@@ -1,37 +0,0 @@
package appmessage
// ResolveFinalityConflictRequestMessage is an appmessage corresponding to
// its respective RPC message
type ResolveFinalityConflictRequestMessage struct {
baseMessage
FinalityBlockHash string
}
// Command returns the protocol command string for the message
func (msg *ResolveFinalityConflictRequestMessage) Command() MessageCommand {
return CmdResolveFinalityConflictRequestMessage
}
// NewResolveFinalityConflictRequestMessage returns a instance of the message
func NewResolveFinalityConflictRequestMessage(finalityBlockHash string) *ResolveFinalityConflictRequestMessage {
return &ResolveFinalityConflictRequestMessage{
FinalityBlockHash: finalityBlockHash,
}
}
// ResolveFinalityConflictResponseMessage is an appmessage corresponding to
// its respective RPC message
type ResolveFinalityConflictResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *ResolveFinalityConflictResponseMessage) Command() MessageCommand {
return CmdResolveFinalityConflictResponseMessage
}
// NewResolveFinalityConflictResponseMessage returns a instance of the message
func NewResolveFinalityConflictResponseMessage() *ResolveFinalityConflictResponseMessage {
return &ResolveFinalityConflictResponseMessage{}
}

View File

@@ -1,37 +0,0 @@
package appmessage
// SubmitBlockRequestMessage is an appmessage corresponding to
// its respective RPC message
type SubmitBlockRequestMessage struct {
baseMessage
BlockHex string
}
// Command returns the protocol command string for the message
func (msg *SubmitBlockRequestMessage) Command() MessageCommand {
return CmdSubmitBlockRequestMessage
}
// NewSubmitBlockRequestMessage returns a instance of the message
func NewSubmitBlockRequestMessage(blockHex string) *SubmitBlockRequestMessage {
return &SubmitBlockRequestMessage{
BlockHex: blockHex,
}
}
// SubmitBlockResponseMessage is an appmessage corresponding to
// its respective RPC message
type SubmitBlockResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *SubmitBlockResponseMessage) Command() MessageCommand {
return CmdSubmitBlockResponseMessage
}
// NewSubmitBlockResponseMessage returns a instance of the message
func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage {
return &SubmitBlockResponseMessage{}
}

View File

@@ -1,41 +0,0 @@
package appmessage
// SubmitTransactionRequestMessage is an appmessage corresponding to
// its respective RPC message
type SubmitTransactionRequestMessage struct {
baseMessage
TransactionHex string
}
// Command returns the protocol command string for the message
func (msg *SubmitTransactionRequestMessage) Command() MessageCommand {
return CmdSubmitTransactionRequestMessage
}
// NewSubmitTransactionRequestMessage returns a instance of the message
func NewSubmitTransactionRequestMessage(transactionHex string) *SubmitTransactionRequestMessage {
return &SubmitTransactionRequestMessage{
TransactionHex: transactionHex,
}
}
// SubmitTransactionResponseMessage is an appmessage corresponding to
// its respective RPC message
type SubmitTransactionResponseMessage struct {
baseMessage
TxID string
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *SubmitTransactionResponseMessage) Command() MessageCommand {
return CmdSubmitTransactionResponseMessage
}
// NewSubmitTransactionResponseMessage returns a instance of the message
func NewSubmitTransactionResponseMessage(txID string) *SubmitTransactionResponseMessage {
return &SubmitTransactionResponseMessage{
TxID: txID,
}
}

View File

@@ -6,7 +6,7 @@
package app
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)

View File

@@ -1,31 +0,0 @@
package flowcontext
import (
"errors"
"sync/atomic"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
)
// HandleError handles an error from a flow,
// It sends the error to errChan if isStopping == 0 and increments isStopping
//
// If this is ErrRouteClosed - forward it to errChan
// If this is ProtocolError - logs the error, and forward it to errChan
// Otherwise - panics
func (*FlowContext) HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error) {
isErrRouteClosed := errors.Is(err, router.ErrRouteClosed)
if !isErrRouteClosed {
if protocolErr := &(protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) {
panic(err)
}
log.Errorf("error from %s: %+v", flowName, err)
}
if atomic.AddUint32(isStopping, 1) == 1 {
errChan <- err
}
}

View File

@@ -1,42 +0,0 @@
package rejects
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleRejectsContext is the interface for the context needed for the HandleRejects flow.
type HandleRejectsContext interface {
}
type handleRejectsFlow struct {
HandleRejectsContext
incomingRoute, outgoingRoute *router.Route
}
// HandleRejects handles all reject messages coming through incomingRoute.
// This function assumes that incomingRoute will only return MsgReject.
func HandleRejects(context HandleRejectsContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
flow := &handleRejectsFlow{
HandleRejectsContext: context,
incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute,
}
return flow.start()
}
func (flow *handleRejectsFlow) start() error {
message, err := flow.incomingRoute.Dequeue()
if err != nil {
return err
}
rejectMessage := message.(*appmessage.MsgReject)
const maxReasonLength = 255
if len(rejectMessage.Reason) > maxReasonLength {
return protocolerrors.Errorf(false, "got reject message longer than %d", maxReasonLength)
}
return protocolerrors.Errorf(false, "got reject message: `%s`", rejectMessage.Reason)
}

View File

@@ -1,9 +0,0 @@
package rpc
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var log, _ = logger.Get(logger.SubsystemTags.RPCS)
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -1,94 +0,0 @@
package rpc
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/domain/blockdag/indexers"
"github.com/kaspanet/kaspad/domain/mempool"
"github.com/kaspanet/kaspad/domain/mining"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
// Manager is an RPC manager
type Manager struct {
context *rpccontext.Context
}
// NewManager creates a new RPC Manager
func NewManager(
cfg *config.Config,
netAdapter *netadapter.NetAdapter,
dag *blockdag.BlockDAG,
protocolManager *protocol.Manager,
connectionManager *connmanager.ConnectionManager,
blockTemplateGenerator *mining.BlkTmplGenerator,
mempool *mempool.TxPool,
addressManager *addressmanager.AddressManager,
acceptanceIndex *indexers.AcceptanceIndex) *Manager {
manager := Manager{
context: rpccontext.NewContext(
cfg,
netAdapter,
dag,
protocolManager,
connectionManager,
blockTemplateGenerator,
mempool,
addressManager,
acceptanceIndex,
),
}
netAdapter.SetRPCRouterInitializer(manager.routerInitializer)
return &manager
}
// NotifyBlockAddedToDAG notifies the manager that a block has been added to the DAG
func (m *Manager) NotifyBlockAddedToDAG(block *util.Block) {
m.context.BlockTemplateState.NotifyBlockAdded(block)
notification := appmessage.NewBlockAddedNotificationMessage(block.MsgBlock())
m.context.NotificationManager.NotifyBlockAdded(notification)
}
// NotifyChainChanged notifies the manager that the DAG's selected parent chain has changed
func (m *Manager) NotifyChainChanged(removedChainBlockHashes []*daghash.Hash, addedChainBlockHashes []*daghash.Hash) error {
addedChainBlocks, err := m.context.CollectChainBlocks(addedChainBlockHashes)
if err != nil {
return err
}
removedChainBlockHashStrings := make([]string, len(removedChainBlockHashes))
for i, removedChainBlockHash := range removedChainBlockHashes {
removedChainBlockHashStrings[i] = removedChainBlockHash.String()
}
notification := appmessage.NewChainChangedNotificationMessage(removedChainBlockHashStrings, addedChainBlocks)
m.context.NotificationManager.NotifyChainChanged(notification)
return nil
}
// NotifyFinalityConflict notifies the manager that there's a finality conflict in the DAG
func (m *Manager) NotifyFinalityConflict(violatingBlockHash string) error {
notification := appmessage.NewFinalityConflictNotificationMessage(violatingBlockHash)
m.context.NotificationManager.NotifyFinalityConflict(notification)
return nil
}
// NotifyFinalityConflictResolved notifies the manager that a finality conflict in the DAG has been resolved
func (m *Manager) NotifyFinalityConflictResolved(finalityBlockHash string) error {
notification := appmessage.NewFinalityConflictResolvedNotificationMessage(finalityBlockHash)
m.context.NotificationManager.NotifyFinalityConflictResolved(notification)
return nil
}
// NotifyTransactionAddedToMempool notifies the manager that a transaction has been added to the mempool
func (m *Manager) NotifyTransactionAddedToMempool() {
m.context.BlockTemplateState.NotifyMempoolTx()
}

View File

@@ -1,100 +0,0 @@
package rpc
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
type handler func(context *rpccontext.Context, router *router.Router, request appmessage.Message) (appmessage.Message, error)
var handlers = map[appmessage.MessageCommand]handler{
appmessage.CmdGetCurrentNetworkRequestMessage: rpchandlers.HandleGetCurrentNetwork,
appmessage.CmdSubmitBlockRequestMessage: rpchandlers.HandleSubmitBlock,
appmessage.CmdGetBlockTemplateRequestMessage: rpchandlers.HandleGetBlockTemplate,
appmessage.CmdNotifyBlockAddedRequestMessage: rpchandlers.HandleNotifyBlockAdded,
appmessage.CmdGetPeerAddressesRequestMessage: rpchandlers.HandleGetPeerAddresses,
appmessage.CmdGetSelectedTipHashRequestMessage: rpchandlers.HandleGetSelectedTipHash,
appmessage.CmdGetMempoolEntryRequestMessage: rpchandlers.HandleGetMempoolEntry,
appmessage.CmdGetConnectedPeerInfoRequestMessage: rpchandlers.HandleGetConnectedPeerInfo,
appmessage.CmdAddPeerRequestMessage: rpchandlers.HandleAddPeer,
appmessage.CmdSubmitTransactionRequestMessage: rpchandlers.HandleSubmitTransaction,
appmessage.CmdNotifyChainChangedRequestMessage: rpchandlers.HandleNotifyChainChanged,
appmessage.CmdGetBlockRequestMessage: rpchandlers.HandleGetBlock,
appmessage.CmdGetSubnetworkRequestMessage: rpchandlers.HandleGetSubnetwork,
appmessage.CmdGetChainFromBlockRequestMessage: rpchandlers.HandleGetChainFromBlock,
appmessage.CmdGetBlocksRequestMessage: rpchandlers.HandleGetBlocks,
appmessage.CmdGetBlockCountRequestMessage: rpchandlers.HandleGetBlockCount,
appmessage.CmdGetBlockDAGInfoRequestMessage: rpchandlers.HandleGetBlockDAGInfo,
appmessage.CmdResolveFinalityConflictRequestMessage: rpchandlers.HandleResolveFinalityConflict,
appmessage.CmdNotifyFinalityConflictsRequestMessage: rpchandlers.HandleNotifyFinalityConflicts,
}
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
messageTypes := make([]appmessage.MessageCommand, 0, len(handlers))
for messageType := range handlers {
messageTypes = append(messageTypes, messageType)
}
incomingRoute, err := router.AddIncomingRoute(messageTypes)
if err != nil {
panic(err)
}
spawn("routerInitializer-handleIncomingMessages", func() {
err := m.handleIncomingMessages(router, incomingRoute)
m.handleError(err, netConnection)
})
notificationListener := m.context.NotificationManager.AddListener(router)
spawn("routerInitializer-handleOutgoingNotifications", func() {
defer m.context.NotificationManager.RemoveListener(router)
err := m.handleOutgoingNotifications(notificationListener)
m.handleError(err, netConnection)
})
}
func (m *Manager) handleIncomingMessages(router *router.Router, incomingRoute *router.Route) error {
outgoingRoute := router.OutgoingRoute()
for {
request, err := incomingRoute.Dequeue()
if err != nil {
return err
}
handler, ok := handlers[request.Command()]
if !ok {
return err
}
response, err := handler(m.context, router, request)
if err != nil {
return err
}
err = outgoingRoute.Enqueue(response)
if err != nil {
return err
}
}
}
func (m *Manager) handleOutgoingNotifications(notificationListener *rpccontext.NotificationListener) error {
for {
err := notificationListener.ProcessNextNotification()
if err != nil {
return err
}
}
}
func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection) {
if errors.Is(err, router.ErrTimeout) {
log.Warnf("Got timeout from %s. Disconnecting...", netConnection)
netConnection.Disconnect()
return
}
if errors.Is(err, router.ErrRouteClosed) {
return
}
panic(err)
}

View File

@@ -1,476 +0,0 @@
package rpccontext
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/mining"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/kaspanet/kaspad/util/random"
"github.com/pkg/errors"
"strconv"
"strings"
"sync"
"time"
)
const (
// blockTemplateNonceRange is two 64-bit big-endian hexadecimal integers which
// represent the valid ranges of nonces returned by the getBlockTemplate
// RPC.
blockTemplateNonceRange = "000000000000ffffffffffff"
// blockTemplateRegenerateSeconds is the number of seconds that must pass before
// a new template is generated when the parent block hashes has not
// changed and there have been changes to the available transactions
// in the memory pool.
blockTemplateRegenerateSeconds = 60
)
var (
// blockTemplateMutableFields are the manipulations the server allows to be made
// to block templates generated by the getBlockTemplate RPC. It is
// declared here to avoid the overhead of creating the slice on every
// invocation for constant data.
blockTemplateMutableFields = []string{
"time", "transactions/add", "parentblock", "coinbase/append",
}
)
// BlockTemplateState houses state that is used in between multiple RPC invocations to
// getBlockTemplate.
type BlockTemplateState struct {
sync.Mutex
context *Context
lastTxUpdate mstime.Time
lastGenerated mstime.Time
virtualParentHashes []*daghash.Hash
minTimestamp mstime.Time
template *mining.BlockTemplate
notifyMap map[string]map[int64]chan struct{}
payAddress util.Address
}
// NewBlockTemplateState returns a new instance of a BlockTemplateState with all internal
// fields initialized and ready to use.
func NewBlockTemplateState(context *Context) *BlockTemplateState {
return &BlockTemplateState{
context: context,
notifyMap: make(map[string]map[int64]chan struct{}),
}
}
// Update updates the block template state
func (bt *BlockTemplateState) Update(payAddress util.Address) error {
generator := bt.context.BlockTemplateGenerator
lastTxUpdate := generator.TxSource().LastUpdated()
if lastTxUpdate.IsZero() {
lastTxUpdate = mstime.Now()
}
// Generate a new block template when the current best block has
// changed or the transactions in the memory pool have been updated and
// it has been at least gbtRegenerateSecond since the last template was
// generated.
var msgBlock *appmessage.MsgBlock
var targetDifficulty string
virtualParentHashes := bt.context.DAG.VirtualParentHashes()
template := bt.template
if template == nil || bt.virtualParentHashes == nil ||
!daghash.AreEqual(bt.virtualParentHashes, virtualParentHashes) ||
bt.payAddress.String() != payAddress.String() ||
(bt.lastTxUpdate != lastTxUpdate &&
mstime.Now().After(bt.lastGenerated.Add(time.Second*
blockTemplateRegenerateSeconds))) {
// Reset the previous best hash the block template was generated
// against so any errors below cause the next invocation to try
// again.
bt.virtualParentHashes = nil
// Create a new block template that has a coinbase which anyone
// can redeem. This is only acceptable because the returned
// block template doesn't include the coinbase, so the caller
// will ultimately create their own coinbase which pays to the
// appropriate address(es).
extraNonce, err := random.Uint64()
if err != nil {
return errors.Wrapf(err, "failed to randomize extra nonce")
}
blockTemplate, err := generator.NewBlockTemplate(payAddress, extraNonce)
if err != nil {
return errors.Wrapf(err, "failed to create new block template")
}
template = blockTemplate
msgBlock = template.Block
targetDifficulty = fmt.Sprintf("%064x", util.CompactToBig(msgBlock.Header.Bits))
// Get the minimum allowed timestamp for the block based on the
// median timestamp of the last several blocks per the DAG
// consensus rules.
minTimestamp := bt.context.DAG.NextBlockMinimumTime()
// Update work state to ensure another block template isn't
// generated until needed.
bt.template = template
bt.lastGenerated = mstime.Now()
bt.lastTxUpdate = lastTxUpdate
bt.virtualParentHashes = virtualParentHashes
bt.minTimestamp = minTimestamp
bt.payAddress = payAddress
log.Debugf("Generated block template (timestamp %s, "+
"target %s, merkle root %s)",
msgBlock.Header.Timestamp, targetDifficulty,
msgBlock.Header.HashMerkleRoot)
// Notify any clients that are long polling about the new
// template.
bt.notifyLongPollers(virtualParentHashes, lastTxUpdate)
} else {
// At this point, there is a saved block template and another
// request for a template was made, but either the available
// transactions haven't change or it hasn't been long enough to
// trigger a new block template to be generated. So, update the
// existing block template.
// Set locals for convenience.
msgBlock = template.Block
targetDifficulty = fmt.Sprintf("%064x",
util.CompactToBig(msgBlock.Header.Bits))
// Update the time of the block template to the current time
// while accounting for the median time of the past several
// blocks per the DAG consensus rules.
err := generator.UpdateBlockTime(msgBlock)
if err != nil {
return errors.Wrapf(err, "failed to update block time")
}
msgBlock.Header.Nonce = 0
log.Debugf("Updated block template (timestamp %s, "+
"target %s)", msgBlock.Header.Timestamp,
targetDifficulty)
}
return nil
}
// Response builds a GetBlockTemplateResponseMessage from the current state
func (bt *BlockTemplateState) Response() (*appmessage.GetBlockTemplateResponseMessage, error) {
dag := bt.context.DAG
// Ensure the timestamps are still in valid range for the template.
// This should really only ever happen if the local clock is changed
// after the template is generated, but it's important to avoid serving
// block templates that will be delayed on other nodes.
template := bt.template
msgBlock := template.Block
header := &msgBlock.Header
adjustedTime := dag.Now()
maxTime := adjustedTime.Add(time.Millisecond * time.Duration(dag.TimestampDeviationTolerance))
if header.Timestamp.After(maxTime) {
errorMessage := &appmessage.GetBlockTemplateResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("The template time is after the "+
"maximum allowed time for a block - template "+
"time %s, maximum time %s", adjustedTime,
maxTime)
return errorMessage, nil
}
// Convert each transaction in the block template to a template result
// transaction. The result does not include the coinbase, so notice
// the adjustments to the various lengths and indices.
numTx := len(msgBlock.Transactions)
transactions := make([]appmessage.GetBlockTemplateTransactionMessage, 0, numTx-1)
txIndex := make(map[daghash.TxID]int64, numTx)
for i, tx := range msgBlock.Transactions {
txID := tx.TxID()
txIndex[*txID] = int64(i)
// Create an array of 1-based indices to transactions that come
// before this one in the transactions list which this one
// depends on. This is necessary since the created block must
// ensure proper ordering of the dependencies. A map is used
// before creating the final array to prevent duplicate entries
// when multiple inputs reference the same transaction.
dependsMap := make(map[int64]struct{})
for _, txIn := range tx.TxIn {
if idx, ok := txIndex[txIn.PreviousOutpoint.TxID]; ok {
dependsMap[idx] = struct{}{}
}
}
depends := make([]int64, 0, len(dependsMap))
for idx := range dependsMap {
depends = append(depends, idx)
}
// Serialize the transaction for later conversion to hex.
txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(txBuf); err != nil {
errorMessage := &appmessage.GetBlockTemplateResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Failed to serialize transaction: %s", err)
return errorMessage, nil
}
resultTx := appmessage.GetBlockTemplateTransactionMessage{
Data: hex.EncodeToString(txBuf.Bytes()),
ID: txID.String(),
Depends: depends,
Mass: template.TxMasses[i],
Fee: template.Fees[i],
}
transactions = append(transactions, resultTx)
}
// Generate the block template reply. Note that following mutations are
// implied by the included or omission of fields:
// Including MinTime -> time/decrement
// Omitting CoinbaseTxn -> coinbase, generation
targetDifficulty := fmt.Sprintf("%064x", util.CompactToBig(header.Bits))
longPollID := bt.encodeLongPollID(bt.virtualParentHashes, bt.payAddress, bt.lastGenerated)
// Check whether this node is synced with the rest of of the
// network. There's almost never a good reason to mine on top
// of an unsynced DAG, and miners are generally expected not to
// mine when isSynced is false.
// This is not a straight-up error because the choice of whether
// to mine or not is the responsibility of the miner rather
// than the node's.
isSynced := bt.context.BlockTemplateGenerator.IsSynced()
isConnected := len(bt.context.ProtocolManager.Peers()) > 0
reply := appmessage.GetBlockTemplateResponseMessage{
Bits: strconv.FormatInt(int64(header.Bits), 16),
CurrentTime: header.Timestamp.UnixMilliseconds(),
ParentHashes: daghash.Strings(header.ParentHashes),
MassLimit: appmessage.MaxMassAcceptedByBlock,
Transactions: transactions,
HashMerkleRoot: header.HashMerkleRoot.String(),
AcceptedIDMerkleRoot: header.AcceptedIDMerkleRoot.String(),
UTXOCommitment: header.UTXOCommitment.String(),
Version: header.Version,
LongPollID: longPollID,
TargetDifficulty: targetDifficulty,
MinTime: bt.minTimestamp.UnixMilliseconds(),
MaxTime: maxTime.UnixMilliseconds(),
MutableFields: blockTemplateMutableFields,
NonceRange: blockTemplateNonceRange,
IsSynced: isSynced,
IsConnected: isConnected,
}
return &reply, nil
}
// notifyLongPollers notifies any channels that have been registered to be
// notified when block templates are stale.
//
// This function MUST be called with the state locked.
func (bt *BlockTemplateState) notifyLongPollers(parentHashes []*daghash.Hash, lastGenerated mstime.Time) {
// Notify anything that is waiting for a block template update from
// hashes which are not the current parent hashes.
parentHashesStr := daghash.JoinHashesStrings(parentHashes, "")
for hashesStr, channels := range bt.notifyMap {
if hashesStr != parentHashesStr {
for _, c := range channels {
close(c)
}
delete(bt.notifyMap, hashesStr)
}
}
// Return now if the provided last generated timestamp has not been
// initialized.
if lastGenerated.IsZero() {
return
}
// Return now if there is nothing registered for updates to the current
// best block hash.
channels, ok := bt.notifyMap[parentHashesStr]
if !ok {
return
}
// Notify anything that is waiting for a block template update from a
// block template generated before the most recently generated block
// template.
lastGeneratedUnix := lastGenerated.UnixSeconds()
for lastGen, c := range channels {
if lastGen < lastGeneratedUnix {
close(c)
delete(channels, lastGen)
}
}
// Remove the entry altogether if there are no more registered
// channels.
if len(channels) == 0 {
delete(bt.notifyMap, parentHashesStr)
}
}
// NotifyBlockAdded uses the newly-added block to notify any long poll
// clients with a new block template when their existing block template is
// stale due to the newly added block.
func (bt *BlockTemplateState) NotifyBlockAdded(block *util.Block) {
spawn("BlockTemplateState.NotifyBlockAdded", func() {
bt.Lock()
defer bt.Unlock()
bt.notifyLongPollers(block.MsgBlock().Header.ParentHashes, bt.lastTxUpdate)
})
}
// NotifyMempoolTx uses the new last updated time for the transaction memory
// pool to notify any long poll clients with a new block template when their
// existing block template is stale due to enough time passing and the contents
// of the memory pool changing.
func (bt *BlockTemplateState) NotifyMempoolTx() {
lastUpdated := bt.context.Mempool.LastUpdated()
spawn("BlockTemplateState", func() {
bt.Lock()
defer bt.Unlock()
// No need to notify anything if no block templates have been generated
// yet.
if bt.virtualParentHashes == nil || bt.lastGenerated.IsZero() {
return
}
if mstime.Now().After(bt.lastGenerated.Add(time.Second *
blockTemplateRegenerateSeconds)) {
bt.notifyLongPollers(bt.virtualParentHashes, lastUpdated)
}
})
}
// BlockTemplateOrLongPollChan returns a block template if the
// template identified by the provided long poll ID is stale or
// invalid. Otherwise, it returns a channel that will notify
// when there's a more current template.
func (bt *BlockTemplateState) BlockTemplateOrLongPollChan(longPollID string,
payAddress util.Address) (*appmessage.GetBlockTemplateResponseMessage, chan struct{}, error) {
bt.Lock()
defer bt.Unlock()
if err := bt.Update(payAddress); err != nil {
return nil, nil, err
}
// Just return the current block template if the long poll ID provided by
// the caller is invalid.
parentHashes, lastGenerated, err := bt.decodeLongPollID(longPollID)
if err != nil {
result, err := bt.Response()
if err != nil {
return nil, nil, err
}
return result, nil, nil
}
// Return the block template now if the specific block template
// identified by the long poll ID no longer matches the current block
// template as this means the provided template is stale.
areHashesEqual := daghash.AreEqual(bt.template.Block.Header.ParentHashes, parentHashes)
if !areHashesEqual ||
lastGenerated != bt.lastGenerated.UnixSeconds() {
// Include whether or not it is valid to submit work against the
// old block template depending on whether or not a solution has
// already been found and added to the block DAG.
result, err := bt.Response()
if err != nil {
return nil, nil, err
}
return result, nil, nil
}
// Register the parent hashes and last generated time for notifications
// Get a channel that will be notified when the template associated with
// the provided ID is stale and a new block template should be returned to
// the caller.
longPollChan := bt.templateUpdateChan(parentHashes, lastGenerated)
return nil, longPollChan, nil
}
// templateUpdateChan returns a channel that will be closed once the block
// template associated with the passed parent hashes and last generated time
// is stale. The function will return existing channels for duplicate
// parameters which allows multiple clients to wait for the same block template
// without requiring a different channel for each client.
//
// This function MUST be called with the state locked.
func (bt *BlockTemplateState) templateUpdateChan(parentHashes []*daghash.Hash, lastGenerated int64) chan struct{} {
parentHashesStr := daghash.JoinHashesStrings(parentHashes, "")
// Either get the current list of channels waiting for updates about
// changes to block template for the parent hashes or create a new one.
channels, ok := bt.notifyMap[parentHashesStr]
if !ok {
m := make(map[int64]chan struct{})
bt.notifyMap[parentHashesStr] = m
channels = m
}
// Get the current channel associated with the time the block template
// was last generated or create a new one.
c, ok := channels[lastGenerated]
if !ok {
c = make(chan struct{})
channels[lastGenerated] = c
}
return c
}
// encodeLongPollID encodes the passed details into an ID that can be used to
// uniquely identify a block template.
func (bt *BlockTemplateState) encodeLongPollID(parentHashes []*daghash.Hash, miningAddress util.Address, lastGenerated mstime.Time) string {
return fmt.Sprintf("%s-%s-%d", daghash.JoinHashesStrings(parentHashes, ""), miningAddress, lastGenerated.UnixSeconds())
}
// decodeLongPollID decodes an ID that is used to uniquely identify a block
// template. This is mainly used as a mechanism to track when to update clients
// that are using long polling for block templates. The ID consists of the
// parent blocks hashes for the associated template and the time the associated
// template was generated.
func (bt *BlockTemplateState) decodeLongPollID(longPollID string) ([]*daghash.Hash, int64, error) {
fields := strings.Split(longPollID, "-")
if len(fields) != 2 {
return nil, 0, errors.New("decodeLongPollID: invalid number of fields")
}
parentHashesStr := fields[0]
if len(parentHashesStr)%daghash.HashSize != 0 {
return nil, 0, errors.New("decodeLongPollID: invalid parent hashes format")
}
numberOfHashes := len(parentHashesStr) / daghash.HashSize
parentHashes := make([]*daghash.Hash, 0, numberOfHashes)
for i := 0; i < len(parentHashesStr); i += daghash.HashSize {
hash, err := daghash.NewHashFromStr(parentHashesStr[i : i+daghash.HashSize])
if err != nil {
return nil, 0, errors.Errorf("decodeLongPollID: NewHashFromStr: %s", err)
}
parentHashes = append(parentHashes, hash)
}
lastGenerated, err := strconv.ParseInt(fields[1], 10, 64)
if err != nil {
return nil, 0, errors.Errorf("decodeLongPollID: Cannot parse timestamp %s: %s", fields[1], err)
}
return parentHashes, lastGenerated, nil
}

View File

@@ -1,40 +0,0 @@
package rpccontext
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
)
// CollectChainBlocks creates a slice of chain blocks from the given hashes
func (ctx *Context) CollectChainBlocks(hashes []*daghash.Hash) ([]*appmessage.ChainBlock, error) {
chainBlocks := make([]*appmessage.ChainBlock, 0, len(hashes))
for _, hash := range hashes {
acceptanceData, err := ctx.AcceptanceIndex.TxsAcceptanceData(hash)
if err != nil {
return nil, errors.Errorf("could not retrieve acceptance data for block %s", hash)
}
acceptedBlocks := make([]*appmessage.AcceptedBlock, 0, len(acceptanceData))
for _, blockAcceptanceData := range acceptanceData {
acceptedTxIds := make([]string, 0, len(blockAcceptanceData.TxAcceptanceData))
for _, txAcceptanceData := range blockAcceptanceData.TxAcceptanceData {
if txAcceptanceData.IsAccepted {
acceptedTxIds = append(acceptedTxIds, txAcceptanceData.Tx.ID().String())
}
}
acceptedBlock := &appmessage.AcceptedBlock{
Hash: blockAcceptanceData.BlockHash.String(),
AcceptedTxIDs: acceptedTxIds,
}
acceptedBlocks = append(acceptedBlocks, acceptedBlock)
}
chainBlock := &appmessage.ChainBlock{
Hash: hash.String(),
AcceptedBlocks: acceptedBlocks,
}
chainBlocks = append(chainBlocks, chainBlock)
}
return chainBlocks, nil
}

View File

@@ -1,56 +0,0 @@
package rpccontext
import (
"github.com/kaspanet/kaspad/app/protocol"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/domain/blockdag/indexers"
"github.com/kaspanet/kaspad/domain/mempool"
"github.com/kaspanet/kaspad/domain/mining"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
)
// Context represents the RPC context
type Context struct {
Config *config.Config
NetAdapter *netadapter.NetAdapter
DAG *blockdag.BlockDAG
ProtocolManager *protocol.Manager
ConnectionManager *connmanager.ConnectionManager
BlockTemplateGenerator *mining.BlkTmplGenerator
Mempool *mempool.TxPool
AddressManager *addressmanager.AddressManager
AcceptanceIndex *indexers.AcceptanceIndex
BlockTemplateState *BlockTemplateState
NotificationManager *NotificationManager
}
// NewContext creates a new RPC context
func NewContext(
cfg *config.Config,
netAdapter *netadapter.NetAdapter,
dag *blockdag.BlockDAG,
protocolManager *protocol.Manager,
connectionManager *connmanager.ConnectionManager,
blockTemplateGenerator *mining.BlkTmplGenerator,
mempool *mempool.TxPool,
addressManager *addressmanager.AddressManager,
acceptanceIndex *indexers.AcceptanceIndex) *Context {
context := &Context{
Config: cfg,
NetAdapter: netAdapter,
DAG: dag,
ProtocolManager: protocolManager,
ConnectionManager: connectionManager,
BlockTemplateGenerator: blockTemplateGenerator,
Mempool: mempool,
AddressManager: addressManager,
AcceptanceIndex: acceptanceIndex,
}
context.BlockTemplateState = NewBlockTemplateState(context)
context.NotificationManager = NewNotificationManager()
return context
}

View File

@@ -1,9 +0,0 @@
package rpccontext
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var log, _ = logger.Get(logger.SubsystemTags.RPCS)
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -1,197 +0,0 @@
package rpccontext
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
"sync"
)
// NotificationManager manages notifications for the RPC
type NotificationManager struct {
sync.RWMutex
listeners map[*router.Router]*NotificationListener
}
// OnBlockAddedListener is a listener function for when a block is added to the DAG
type OnBlockAddedListener func(notification *appmessage.BlockAddedNotificationMessage) error
// OnChainChangedListener is a listener function for when the DAG's selected parent chain changes
type OnChainChangedListener func(notification *appmessage.ChainChangedNotificationMessage) error
// OnFinalityConflictListener is a listener function for when there's a finality conflict in the DAG
type OnFinalityConflictListener func(notification *appmessage.FinalityConflictNotificationMessage) error
// OnFinalityConflictResolvedListener is a listener function for when a finality conflict in the DAG has been resolved
type OnFinalityConflictResolvedListener func(notification *appmessage.FinalityConflictResolvedNotificationMessage) error
// NotificationListener represents a registered RPC notification listener
type NotificationListener struct {
onBlockAddedListener OnBlockAddedListener
onBlockAddedNotificationChan chan *appmessage.BlockAddedNotificationMessage
onChainChangedListener OnChainChangedListener
onChainChangedNotificationChan chan *appmessage.ChainChangedNotificationMessage
onFinalityConflictListener OnFinalityConflictListener
onFinalityConflictNotificationChan chan *appmessage.FinalityConflictNotificationMessage
onFinalityConflictResolvedListener OnFinalityConflictResolvedListener
onFinalityConflictResolvedNotificationChan chan *appmessage.FinalityConflictResolvedNotificationMessage
closeChan chan struct{}
}
// NewNotificationManager creates a new NotificationManager
func NewNotificationManager() *NotificationManager {
return &NotificationManager{
listeners: make(map[*router.Router]*NotificationListener),
}
}
// AddListener registers a listener with the given router
func (nm *NotificationManager) AddListener(router *router.Router) *NotificationListener {
nm.Lock()
defer nm.Unlock()
listener := newNotificationListener()
nm.listeners[router] = listener
return listener
}
// RemoveListener unregisters the given router
func (nm *NotificationManager) RemoveListener(router *router.Router) {
nm.Lock()
defer nm.Unlock()
listener, ok := nm.listeners[router]
if !ok {
return
}
listener.close()
delete(nm.listeners, router)
}
// Listener retrieves the listener registered with the given router
func (nm *NotificationManager) Listener(router *router.Router) (*NotificationListener, error) {
nm.RLock()
defer nm.RUnlock()
listener, ok := nm.listeners[router]
if !ok {
return nil, errors.Errorf("listener not found")
}
return listener, nil
}
// NotifyBlockAdded notifies the notification manager that a block has been added to the DAG
func (nm *NotificationManager) NotifyBlockAdded(notification *appmessage.BlockAddedNotificationMessage) {
nm.RLock()
defer nm.RUnlock()
for _, listener := range nm.listeners {
if listener.onBlockAddedListener != nil {
select {
case listener.onBlockAddedNotificationChan <- notification:
case <-listener.closeChan:
continue
}
}
}
}
// NotifyChainChanged notifies the notification manager that the DAG's selected parent chain has changed
func (nm *NotificationManager) NotifyChainChanged(message *appmessage.ChainChangedNotificationMessage) {
nm.RLock()
defer nm.RUnlock()
for _, listener := range nm.listeners {
if listener.onChainChangedListener != nil {
select {
case listener.onChainChangedNotificationChan <- message:
case <-listener.closeChan:
continue
}
}
}
}
// NotifyFinalityConflict notifies the notification manager that there's a finality conflict in the DAG
func (nm *NotificationManager) NotifyFinalityConflict(message *appmessage.FinalityConflictNotificationMessage) {
nm.RLock()
defer nm.RUnlock()
for _, listener := range nm.listeners {
if listener.onFinalityConflictListener != nil {
select {
case listener.onFinalityConflictNotificationChan <- message:
case <-listener.closeChan:
continue
}
}
}
}
// NotifyFinalityConflictResolved notifies the notification manager that a finality conflict in the DAG has been resolved
func (nm *NotificationManager) NotifyFinalityConflictResolved(message *appmessage.FinalityConflictResolvedNotificationMessage) {
nm.RLock()
defer nm.RUnlock()
for _, listener := range nm.listeners {
if listener.onFinalityConflictResolvedListener != nil {
select {
case listener.onFinalityConflictResolvedNotificationChan <- message:
case <-listener.closeChan:
continue
}
}
}
}
func newNotificationListener() *NotificationListener {
return &NotificationListener{
onBlockAddedNotificationChan: make(chan *appmessage.BlockAddedNotificationMessage),
onChainChangedNotificationChan: make(chan *appmessage.ChainChangedNotificationMessage),
onFinalityConflictNotificationChan: make(chan *appmessage.FinalityConflictNotificationMessage),
onFinalityConflictResolvedNotificationChan: make(chan *appmessage.FinalityConflictResolvedNotificationMessage),
closeChan: make(chan struct{}, 1),
}
}
// SetOnBlockAddedListener sets the onBlockAddedListener handler for this listener
func (nl *NotificationListener) SetOnBlockAddedListener(onBlockAddedListener OnBlockAddedListener) {
nl.onBlockAddedListener = onBlockAddedListener
}
// SetOnChainChangedListener sets the onChainChangedListener handler for this listener
func (nl *NotificationListener) SetOnChainChangedListener(onChainChangedListener OnChainChangedListener) {
nl.onChainChangedListener = onChainChangedListener
}
// SetOnFinalityConflictListener sets the onFinalityConflictListener handler for this listener
func (nl *NotificationListener) SetOnFinalityConflictListener(onFinalityConflictListener OnFinalityConflictListener) {
nl.onFinalityConflictListener = onFinalityConflictListener
}
// SetOnFinalityConflictResolvedListener sets the onFinalityConflictResolvedListener handler for this listener
func (nl *NotificationListener) SetOnFinalityConflictResolvedListener(onFinalityConflictResolvedListener OnFinalityConflictResolvedListener) {
nl.onFinalityConflictResolvedListener = onFinalityConflictResolvedListener
}
// ProcessNextNotification waits until a notification arrives and processes it
func (nl *NotificationListener) ProcessNextNotification() error {
select {
case block := <-nl.onBlockAddedNotificationChan:
return nl.onBlockAddedListener(block)
case notification := <-nl.onChainChangedNotificationChan:
return nl.onChainChangedListener(notification)
case notification := <-nl.onFinalityConflictNotificationChan:
return nl.onFinalityConflictListener(notification)
case notification := <-nl.onFinalityConflictResolvedNotificationChan:
return nl.onFinalityConflictResolvedListener(notification)
case <-nl.closeChan:
return nil
}
}
func (nl *NotificationListener) close() {
nl.closeChan <- struct{}{}
}

View File

@@ -1,246 +0,0 @@
package rpccontext
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/pointers"
"math/big"
"strconv"
)
// BuildBlockVerboseData builds a BlockVerboseData from the given block.
// This method must be called with the DAG lock held for reads
func (ctx *Context) BuildBlockVerboseData(block *util.Block, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
hash := block.Hash()
params := ctx.DAG.Params
blockHeader := block.MsgBlock().Header
blockBlueScore, err := ctx.DAG.BlueScoreByBlockHash(hash)
if err != nil {
return nil, err
}
// Get the hashes for the next blocks unless there are none.
childHashes, err := ctx.DAG.ChildHashesByHash(hash)
if err != nil {
return nil, err
}
blockConfirmations, err := ctx.DAG.BlockConfirmationsByHashNoLock(hash)
if err != nil {
return nil, err
}
selectedParentHash, err := ctx.DAG.SelectedParentHash(hash)
if err != nil {
return nil, err
}
selectedParentHashStr := ""
if selectedParentHash != nil {
selectedParentHashStr = selectedParentHash.String()
}
isChainBlock, err := ctx.DAG.IsInSelectedParentChain(hash)
if err != nil {
return nil, err
}
acceptedBlockHashes, err := ctx.DAG.BluesByBlockHash(hash)
if err != nil {
return nil, err
}
result := &appmessage.BlockVerboseData{
Hash: hash.String(),
Version: blockHeader.Version,
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
HashMerkleRoot: blockHeader.HashMerkleRoot.String(),
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(),
UTXOCommitment: blockHeader.UTXOCommitment.String(),
ParentHashes: daghash.Strings(blockHeader.ParentHashes),
SelectedParentHash: selectedParentHashStr,
Nonce: blockHeader.Nonce,
Time: blockHeader.Timestamp.UnixMilliseconds(),
Confirmations: blockConfirmations,
BlueScore: blockBlueScore,
IsChainBlock: isChainBlock,
Size: int32(block.MsgBlock().SerializeSize()),
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
Difficulty: ctx.GetDifficultyRatio(blockHeader.Bits, params),
ChildHashes: daghash.Strings(childHashes),
AcceptedBlockHashes: daghash.Strings(acceptedBlockHashes),
}
transactions := block.Transactions()
txIDs := make([]string, len(transactions))
for i, tx := range transactions {
txIDs[i] = tx.ID().String()
}
result.TxIDs = txIDs
if includeTransactionVerboseData {
transactions := block.Transactions()
transactionVerboseData := make([]*appmessage.TransactionVerboseData, len(transactions))
for i, tx := range transactions {
data, err := ctx.buildTransactionVerboseData(tx.MsgTx(), tx.ID().String(),
&blockHeader, hash.String(), nil, false)
if err != nil {
return nil, err
}
transactionVerboseData[i] = data
}
result.TransactionVerboseData = transactionVerboseData
}
return result, nil
}
// GetDifficultyRatio returns the proof-of-work difficulty as a multiple of the
// minimum difficulty using the passed bits field from the header of a block.
func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) float64 {
// The minimum difficulty is the max possible proof-of-work limit bits
// converted back to a number. Note this is not the same as the proof of
// work limit directly because the block difficulty is encoded in a block
// with the compact form which loses precision.
target := util.CompactToBig(bits)
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
outString := difficulty.FloatString(8)
diff, err := strconv.ParseFloat(outString, 64)
if err != nil {
log.Errorf("Cannot get difficulty: %s", err)
return 0
}
return diff
}
func (ctx *Context) buildTransactionVerboseData(mtx *appmessage.MsgTx,
txID string, blockHeader *appmessage.BlockHeader, blockHash string,
acceptingBlock *daghash.Hash, isInMempool bool) (*appmessage.TransactionVerboseData, error) {
mtxHex, err := msgTxToHex(mtx)
if err != nil {
return nil, err
}
var payloadHash string
if mtx.PayloadHash != nil {
payloadHash = mtx.PayloadHash.String()
}
txReply := &appmessage.TransactionVerboseData{
Hex: mtxHex,
TxID: txID,
Hash: mtx.TxHash().String(),
Size: int32(mtx.SerializeSize()),
TransactionVerboseInputs: ctx.buildTransactionVerboseInputs(mtx),
TransactionVerboseOutputs: ctx.buildTransactionVerboseOutputs(mtx, nil),
Version: mtx.Version,
LockTime: mtx.LockTime,
SubnetworkID: mtx.SubnetworkID.String(),
Gas: mtx.Gas,
PayloadHash: payloadHash,
Payload: hex.EncodeToString(mtx.Payload),
}
if blockHeader != nil {
txReply.Time = uint64(blockHeader.Timestamp.UnixMilliseconds())
txReply.BlockTime = uint64(blockHeader.Timestamp.UnixMilliseconds())
txReply.BlockHash = blockHash
}
txReply.IsInMempool = isInMempool
if acceptingBlock != nil {
txReply.AcceptedBy = acceptingBlock.String()
}
return txReply, nil
}
// msgTxToHex serializes a transaction using the latest protocol version and
// returns a hex-encoded string of the result.
func msgTxToHex(msgTx *appmessage.MsgTx) (string, error) {
var buf bytes.Buffer
err := msgTx.KaspaEncode(&buf, 0)
if err != nil {
return "", err
}
return hex.EncodeToString(buf.Bytes()), nil
}
func (ctx *Context) buildTransactionVerboseInputs(mtx *appmessage.MsgTx) []*appmessage.TransactionVerboseInput {
inputs := make([]*appmessage.TransactionVerboseInput, len(mtx.TxIn))
for i, txIn := range mtx.TxIn {
// The disassembled string will contain [error] inline
// if the script doesn't fully parse, so ignore the
// error here.
disbuf, _ := txscript.DisasmString(txIn.SignatureScript)
input := &appmessage.TransactionVerboseInput{}
input.TxID = txIn.PreviousOutpoint.TxID.String()
input.OutputIndex = txIn.PreviousOutpoint.Index
input.Sequence = txIn.Sequence
input.ScriptSig = &appmessage.ScriptSig{
Asm: disbuf,
Hex: hex.EncodeToString(txIn.SignatureScript),
}
inputs[i] = input
}
return inputs
}
// buildTransactionVerboseOutputs returns a slice of JSON objects for the outputs of the passed
// transaction.
func (ctx *Context) buildTransactionVerboseOutputs(mtx *appmessage.MsgTx, filterAddrMap map[string]struct{}) []*appmessage.TransactionVerboseOutput {
outputs := make([]*appmessage.TransactionVerboseOutput, len(mtx.TxOut))
for i, v := range mtx.TxOut {
// The disassembled string will contain [error] inline if the
// script doesn't fully parse, so ignore the error here.
disbuf, _ := txscript.DisasmString(v.ScriptPubKey)
// Ignore the error here since an error means the script
// couldn't parse and there is no additional information about
// it anyways.
scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress(
v.ScriptPubKey, ctx.DAG.Params)
// Encode the addresses while checking if the address passes the
// filter when needed.
passesFilter := len(filterAddrMap) == 0
var encodedAddr string
if addr != nil {
encodedAddr = *pointers.String(addr.EncodeAddress())
// If the filter doesn't already pass, make it pass if
// the address exists in the filter.
if _, exists := filterAddrMap[encodedAddr]; exists {
passesFilter = true
}
}
if !passesFilter {
continue
}
output := &appmessage.TransactionVerboseOutput{}
output.Index = uint32(i)
output.Value = v.Value
output.ScriptPubKey = &appmessage.ScriptPubKeyResult{
Address: encodedAddr,
Asm: disbuf,
Hex: hex.EncodeToString(v.ScriptPubKey),
Type: scriptClass.String(),
}
outputs[i] = output
}
return outputs
}

View File

@@ -1,24 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/network"
)
// HandleAddPeer handles the respectively named RPC command
func HandleAddPeer(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
AddPeerRequest := request.(*appmessage.AddPeerRequestMessage)
address, err := network.NormalizeAddress(AddPeerRequest.Address, context.DAG.Params.DefaultPort)
if err != nil {
errorMessage := &appmessage.AddPeerResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse address: %s", err)
return errorMessage, nil
}
context.ConnectionManager.AddConnectionRequest(address, AddPeerRequest.IsPermanent)
response := appmessage.NewAddPeerResponseMessage()
return response, nil
}

View File

@@ -1,98 +0,0 @@
package rpchandlers
import (
"bufio"
"bytes"
"encoding/hex"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/subnetworkid"
)
// HandleGetBlock handles the respectively named RPC command
func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getBlockRequest := request.(*appmessage.GetBlockRequestMessage)
// Load the raw block bytes from the database.
hash, err := daghash.NewHashFromStr(getBlockRequest.Hash)
if err != nil {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Hash could not be parsed: %s", err)
return errorMessage, nil
}
context.DAG.RLock()
defer context.DAG.RUnlock()
if context.DAG.IsKnownInvalid(hash) {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s is known to be invalid", hash)
return errorMessage, nil
}
if context.DAG.IsKnownOrphan(hash) {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s is an orphan", hash)
return errorMessage, nil
}
block, err := context.DAG.BlockByHash(hash)
if err != nil {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s not found", hash)
return errorMessage, nil
}
blockBytes, err := block.Bytes()
if err != nil {
return nil, err
}
// Handle partial blocks
if getBlockRequest.SubnetworkID != "" {
requestSubnetworkID, err := subnetworkid.NewFromStr(getBlockRequest.SubnetworkID)
if err != nil {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("SubnetworkID could not be parsed: %s", err)
return errorMessage, nil
}
nodeSubnetworkID := context.Config.SubnetworkID
if requestSubnetworkID != nil {
if nodeSubnetworkID != nil {
if !nodeSubnetworkID.IsEqual(requestSubnetworkID) {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("subnetwork %s does not match this partial node",
getBlockRequest.SubnetworkID)
return errorMessage, nil
}
// nothing to do - partial node stores partial blocks
} else {
// Deserialize the block.
msgBlock := block.MsgBlock()
msgBlock.ConvertToPartial(requestSubnetworkID)
var b bytes.Buffer
err := msgBlock.Serialize(bufio.NewWriter(&b))
if err != nil {
return nil, err
}
blockBytes = b.Bytes()
}
}
}
response := appmessage.NewGetBlockResponseMessage()
if getBlockRequest.IncludeBlockHex {
response.BlockHex = hex.EncodeToString(blockBytes)
}
if getBlockRequest.IncludeBlockVerboseData {
blockVerboseData, err := context.BuildBlockVerboseData(block, getBlockRequest.IncludeTransactionVerboseData)
if err != nil {
return nil, err
}
response.BlockVerboseData = blockVerboseData
}
return response, nil
}

View File

@@ -1,13 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleGetBlockCount handles the respectively named RPC command
func HandleGetBlockCount(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
response := appmessage.NewGetBlockCountResponseMessage(context.DAG.BlockCount())
return response, nil
}

View File

@@ -1,23 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/daghash"
)
// HandleGetBlockDAGInfo handles the respectively named RPC command
func HandleGetBlockDAGInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
dag := context.DAG
params := dag.Params
response := appmessage.NewGetBlockDAGInfoResponseMessage()
response.NetworkName = params.Name
response.BlockCount = dag.BlockCount()
response.TipHashes = daghash.Strings(dag.TipHashes())
response.VirtualParentHashes = daghash.Strings(dag.VirtualParentHashes())
response.Difficulty = context.GetDifficultyRatio(dag.CurrentBits(), params)
response.PastMedianTime = dag.CalcPastMedianTime().UnixMilliseconds()
return response, nil
}

View File

@@ -1,86 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
)
// HandleGetBlockTemplate handles the respectively named RPC command
func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getBlockTemplateRequest := request.(*appmessage.GetBlockTemplateRequestMessage)
payAddress, err := util.DecodeAddress(getBlockTemplateRequest.PayAddress, context.DAG.Params.Prefix)
if err != nil {
errorMessage := &appmessage.GetBlockTemplateResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not decode address: %s", err)
return errorMessage, nil
}
// When a long poll ID was provided, this is a long poll request by the
// client to be notified when block template referenced by the ID should
// be replaced with a new one.
if getBlockTemplateRequest.LongPollID != "" {
return handleGetBlockTemplateLongPoll(context, getBlockTemplateRequest.LongPollID, payAddress)
}
// Protect concurrent access when updating block templates.
context.BlockTemplateState.Lock()
defer context.BlockTemplateState.Unlock()
// Get and return a block template. A new block template will be
// generated when the current best block has changed or the transactions
// in the memory pool have been updated and it has been at least five
// seconds since the last template was generated. Otherwise, the
// timestamp for the existing block template is updated (and possibly
// the difficulty on testnet per the consesus rules).
err = context.BlockTemplateState.Update(payAddress)
if err != nil {
return nil, err
}
return context.BlockTemplateState.Response()
}
// handleGetBlockTemplateLongPoll is a helper for handleGetBlockTemplateRequest
// which deals with handling long polling for block templates. When a caller
// sends a request with a long poll ID that was previously returned, a response
// is not sent until the caller should stop working on the previous block
// template in favor of the new one. In particular, this is the case when the
// old block template is no longer valid due to a solution already being found
// and added to the block DAG, or new transactions have shown up and some time
// has passed without finding a solution.
func handleGetBlockTemplateLongPoll(context *rpccontext.Context, longPollID string,
payAddress util.Address) (*appmessage.GetBlockTemplateResponseMessage, error) {
state := context.BlockTemplateState
result, longPollChan, err := state.BlockTemplateOrLongPollChan(longPollID, payAddress)
if err != nil {
return nil, err
}
if result != nil {
return result, nil
}
// Wait until signal received to send the reply.
<-longPollChan
// Get the lastest block template
state.Lock()
defer state.Unlock()
if err := state.Update(payAddress); err != nil {
return nil, err
}
// Include whether or not it is valid to submit work against the old
// block template depending on whether or not a solution has already
// been found and added to the block DAG.
result, err = state.Response()
if err != nil {
return nil, err
}
return result, nil
}

View File

@@ -1,118 +0,0 @@
package rpchandlers
import (
"encoding/hex"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
const (
// maxBlocksInGetBlocksResponse is the max amount of blocks that are
// allowed in a GetBlocksResult.
maxBlocksInGetBlocksResponse = 1000
)
// HandleGetBlocks handles the respectively named RPC command
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
var lowHash *daghash.Hash
if getBlocksRequest.LowHash != "" {
lowHash = &daghash.Hash{}
err := daghash.Decode(lowHash, getBlocksRequest.LowHash)
if err != nil {
errorMessage := &appmessage.GetBlocksResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse lowHash: %s", err)
return errorMessage, nil
}
}
context.DAG.RLock()
defer context.DAG.RUnlock()
// If lowHash is not in the DAG, there's nothing to do; return an error.
if lowHash != nil && !context.DAG.IsKnownBlock(lowHash) {
errorMessage := &appmessage.GetBlocksResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s not found in DAG", lowHash)
return errorMessage, nil
}
// Retrieve the block hashes.
blockHashes, err := context.DAG.BlockHashesFrom(lowHash, maxBlocksInGetBlocksResponse)
if err != nil {
return nil, err
}
// Convert the hashes to strings
hashes := make([]string, len(blockHashes))
for i, blockHash := range blockHashes {
hashes[i] = blockHash.String()
}
// Include more data if requested
var blockHexes []string
var blockVerboseData []*appmessage.BlockVerboseData
if getBlocksRequest.IncludeBlockHexes || getBlocksRequest.IncludeBlockVerboseData {
blockBytesSlice, err := hashesToBlockBytes(context, blockHashes)
if err != nil {
return nil, err
}
if getBlocksRequest.IncludeBlockHexes {
blockHexes = blockBytesToStrings(blockBytesSlice)
}
if getBlocksRequest.IncludeBlockVerboseData {
data, err := blockBytesToBlockVerboseResults(context, blockBytesSlice, getBlocksRequest.IncludeBlockVerboseData)
if err != nil {
return nil, err
}
blockVerboseData = data
}
}
response := appmessage.NewGetBlocksResponseMessage(hashes, blockHexes, blockVerboseData)
return response, nil
}
func hashesToBlockBytes(context *rpccontext.Context, hashes []*daghash.Hash) ([][]byte, error) {
blocks := make([][]byte, len(hashes))
for i, hash := range hashes {
block, err := context.DAG.BlockByHash(hash)
if err != nil {
return nil, err
}
blockBytes, err := block.Bytes()
if err != nil {
return nil, err
}
blocks[i] = blockBytes
}
return blocks, nil
}
func blockBytesToStrings(blockBytesSlice [][]byte) []string {
rawBlocks := make([]string, len(blockBytesSlice))
for i, blockBytes := range blockBytesSlice {
rawBlocks[i] = hex.EncodeToString(blockBytes)
}
return rawBlocks
}
func blockBytesToBlockVerboseResults(context *rpccontext.Context, blockBytesSlice [][]byte,
includeTransactionVerboseData bool) ([]*appmessage.BlockVerboseData, error) {
verboseBlocks := make([]*appmessage.BlockVerboseData, len(blockBytesSlice))
for i, blockBytes := range blockBytesSlice {
block, err := util.NewBlockFromBytes(blockBytes)
if err != nil {
return nil, err
}
getBlockVerboseResult, err := context.BuildBlockVerboseData(block, includeTransactionVerboseData)
if err != nil {
return nil, err
}
verboseBlocks[i] = getBlockVerboseResult
}
return verboseBlocks, nil
}

View File

@@ -1,106 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
)
const (
// maxBlocksInGetChainFromBlockResponse is the max amount of blocks that
// are allowed in a GetChainFromBlockResponse.
maxBlocksInGetChainFromBlockResponse = 1000
)
// HandleGetChainFromBlock handles the respectively named RPC command
func HandleGetChainFromBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getChainFromBlockRequest := request.(*appmessage.GetChainFromBlockRequestMessage)
if context.AcceptanceIndex == nil {
errorMessage := &appmessage.GetChainFromBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("The acceptance index must be " +
"enabled to get the selected parent chain " +
"(specify --acceptanceindex)")
return errorMessage, nil
}
var startHash *daghash.Hash
if getChainFromBlockRequest.StartHash != "" {
startHash = &daghash.Hash{}
err := daghash.Decode(startHash, getChainFromBlockRequest.StartHash)
if err != nil {
errorMessage := &appmessage.GetChainFromBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse startHash: %s", err)
return errorMessage, nil
}
}
context.DAG.RLock()
defer context.DAG.RUnlock()
// If startHash is not in the selected parent chain, there's nothing
// to do; return an error.
if startHash != nil && !context.DAG.IsInDAG(startHash) {
errorMessage := &appmessage.GetChainFromBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s not found in the DAG", startHash)
return errorMessage, nil
}
// Retrieve the selected parent chain.
removedChainHashes, addedChainHashes, err := context.DAG.SelectedParentChain(startHash)
if err != nil {
return nil, err
}
// Limit the amount of blocks in the response
if len(addedChainHashes) > maxBlocksInGetChainFromBlockResponse {
addedChainHashes = addedChainHashes[:maxBlocksInGetChainFromBlockResponse]
}
// Collect addedChainBlocks.
addedChainBlocks, err := context.CollectChainBlocks(addedChainHashes)
if err != nil {
errorMessage := &appmessage.GetChainFromBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not collect chain blocks: %s", err)
return errorMessage, nil
}
// Collect removedHashes.
removedHashes := make([]string, len(removedChainHashes))
for i, hash := range removedChainHashes {
removedHashes[i] = hash.String()
}
// If the user specified to include the blocks, collect them as well.
var blockVerboseData []*appmessage.BlockVerboseData
if getChainFromBlockRequest.IncludeBlockVerboseData {
data, err := hashesToBlockVerboseData(context, addedChainHashes)
if err != nil {
return nil, err
}
blockVerboseData = data
}
response := appmessage.NewGetChainFromBlockResponseMessage(removedHashes, addedChainBlocks, blockVerboseData)
return response, nil
}
// hashesToBlockVerboseData takes block hashes and returns their
// correspondent block verbose.
func hashesToBlockVerboseData(context *rpccontext.Context, hashes []*daghash.Hash) ([]*appmessage.BlockVerboseData, error) {
getBlockVerboseResults := make([]*appmessage.BlockVerboseData, 0, len(hashes))
for _, blockHash := range hashes {
block, err := context.DAG.BlockByHash(blockHash)
if err != nil {
return nil, errors.Errorf("could not retrieve block %s.", blockHash)
}
getBlockVerboseResult, err := context.BuildBlockVerboseData(block, false)
if err != nil {
return nil, errors.Wrapf(err, "could not build getBlockVerboseResult for block %s", blockHash)
}
getBlockVerboseResults = append(getBlockVerboseResults, getBlockVerboseResult)
}
return getBlockVerboseResults, nil
}

View File

@@ -1,31 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleGetConnectedPeerInfo handles the respectively named RPC command
func HandleGetConnectedPeerInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
peers := context.ProtocolManager.Peers()
ibdPeer := context.ProtocolManager.IBDPeer()
infos := make([]*appmessage.GetConnectedPeerInfoMessage, 0, len(peers))
for _, peer := range peers {
info := &appmessage.GetConnectedPeerInfoMessage{
ID: peer.ID().String(),
Address: peer.Address(),
LastPingDuration: peer.LastPingDuration().Milliseconds(),
SelectedTipHash: peer.SelectedTipHash().String(),
IsSyncNode: peer == ibdPeer,
IsOutbound: peer.IsOutbound(),
TimeOffset: peer.TimeOffset().Milliseconds(),
UserAgent: peer.UserAgent(),
AdvertisedProtocolVersion: peer.AdvertisedProtocolVersion(),
TimeConnected: peer.TimeConnected().Milliseconds(),
}
infos = append(infos, info)
}
response := appmessage.NewGetConnectedPeerInfoResponseMessage(infos)
return response, nil
}

View File

@@ -1,13 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleGetCurrentNetwork handles the respectively named RPC command
func HandleGetCurrentNetwork(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
response := appmessage.NewGetCurrentNetworkResponseMessage(context.DAG.Params.Net.String())
return response, nil
}

View File

@@ -1,29 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/daghash"
)
// HandleGetMempoolEntry handles the respectively named RPC command
func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getMempoolEntryRequest := request.(*appmessage.GetMempoolEntryRequestMessage)
txID, err := daghash.NewTxIDFromStr(getMempoolEntryRequest.TxID)
if err != nil {
errorMessage := &appmessage.GetMempoolEntryResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse txId: %s", err)
return errorMessage, nil
}
_, ok := context.Mempool.FetchTxDesc(txID)
if !ok {
errorMessage := &appmessage.GetMempoolEntryResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("transaction is not in the pool")
return errorMessage, nil
}
response := appmessage.NewGetMempoolEntryResponseMessage()
return response, nil
}

View File

@@ -1,21 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleGetPeerAddresses handles the respectively named RPC command
func HandleGetPeerAddresses(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
peersState, err := context.AddressManager.PeersStateForSerialization()
if err != nil {
return nil, err
}
addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(peersState.Addresses))
for i, address := range peersState.Addresses {
addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: string(address.Address)}
}
response := appmessage.NewGetPeerAddressesResponseMessage(addresses)
return response, nil
}

View File

@@ -1,13 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleGetSelectedTipHash handles the respectively named RPC command
func HandleGetSelectedTipHash(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
response := appmessage.NewGetSelectedTipHashResponseMessage(context.DAG.SelectedTipHash().String())
return response, nil
}

View File

@@ -1,34 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/subnetworkid"
)
// HandleGetSubnetwork handles the respectively named RPC command
func HandleGetSubnetwork(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getSubnetworkRequest := request.(*appmessage.GetSubnetworkRequestMessage)
subnetworkID, err := subnetworkid.NewFromStr(getSubnetworkRequest.SubnetworkID)
if err != nil {
errorMessage := &appmessage.GetSubnetworkResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse subnetworkID: %s", err)
return errorMessage, nil
}
var gasLimit uint64
if !subnetworkID.IsBuiltInOrNative() {
limit, err := context.DAG.GasLimit(subnetworkID)
if err != nil {
errorMessage := &appmessage.GetSubnetworkResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Subnetwork %s not found.", subnetworkID)
return errorMessage, nil
}
gasLimit = limit
}
response := appmessage.NewGetSubnetworkResponseMessage(gasLimit)
return response, nil
}

View File

@@ -1,7 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
)
var log, _ = logger.Get(logger.SubsystemTags.RPCS)

View File

@@ -1,21 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleNotifyBlockAdded handles the respectively named RPC command
func HandleNotifyBlockAdded(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
listener, err := context.NotificationManager.Listener(router)
if err != nil {
return nil, err
}
listener.SetOnBlockAddedListener(func(notification *appmessage.BlockAddedNotificationMessage) error {
return router.OutgoingRoute().Enqueue(notification)
})
response := appmessage.NewNotifyBlockAddedResponseMessage()
return response, nil
}

View File

@@ -1,27 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleNotifyChainChanged handles the respectively named RPC command
func HandleNotifyChainChanged(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
if context.AcceptanceIndex == nil {
errorMessage := appmessage.NewNotifyChainChangedResponseMessage()
errorMessage.Error = appmessage.RPCErrorf("Acceptance index is not available")
return errorMessage, nil
}
listener, err := context.NotificationManager.Listener(router)
if err != nil {
return nil, err
}
listener.SetOnChainChangedListener(func(message *appmessage.ChainChangedNotificationMessage) error {
return router.OutgoingRoute().Enqueue(message)
})
response := appmessage.NewNotifyChainChangedResponseMessage()
return response, nil
}

View File

@@ -1,24 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleNotifyFinalityConflicts handles the respectively named RPC command
func HandleNotifyFinalityConflicts(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
listener, err := context.NotificationManager.Listener(router)
if err != nil {
return nil, err
}
listener.SetOnFinalityConflictListener(func(notification *appmessage.FinalityConflictNotificationMessage) error {
return router.OutgoingRoute().Enqueue(notification)
})
listener.SetOnFinalityConflictResolvedListener(func(notification *appmessage.FinalityConflictResolvedNotificationMessage) error {
return router.OutgoingRoute().Enqueue(notification)
})
response := appmessage.NewNotifyFinalityConflictsResponseMessage()
return response, nil
}

View File

@@ -1,30 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/daghash"
)
// HandleResolveFinalityConflict handles the respectively named RPC command
func HandleResolveFinalityConflict(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
ResolveFinalityConflictRequest := request.(*appmessage.ResolveFinalityConflictRequestMessage)
finalityBlockHash, err := daghash.NewHashFromStr(ResolveFinalityConflictRequest.FinalityBlockHash)
if err != nil {
errorMessage := &appmessage.ResolveFinalityConflictResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse finalityBlockHash: %s", err)
return errorMessage, nil
}
err = context.DAG.ResolveFinalityConflict(finalityBlockHash)
if err != nil {
errorMessage := &appmessage.ResolveFinalityConflictResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not resolve finality conflict: %s", err)
return errorMessage, nil
}
response := appmessage.NewResolveFinalityConflictResponseMessage()
return response, nil
}

View File

@@ -1,41 +0,0 @@
package rpchandlers
import (
"encoding/hex"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
)
// HandleSubmitBlock handles the respectively named RPC command
func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage)
// Deserialize the submitted block.
serializedBlock, err := hex.DecodeString(submitBlockRequest.BlockHex)
if err != nil {
errorMessage := &appmessage.SubmitBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block hex could not be parsed: %s", err)
return errorMessage, nil
}
block, err := util.NewBlockFromBytes(serializedBlock)
if err != nil {
errorMessage := &appmessage.SubmitBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block decode failed: %s", err)
return errorMessage, nil
}
err = context.ProtocolManager.AddBlock(block, blockdag.BFDisallowDelay|blockdag.BFDisallowOrphans)
if err != nil {
errorMessage := &appmessage.SubmitBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block rejected. Reason: %s", err)
return errorMessage, nil
}
log.Infof("Accepted block %s via submitBlock", block.Hash())
response := appmessage.NewSubmitBlockResponseMessage()
return response, nil
}

View File

@@ -1,47 +0,0 @@
package rpchandlers
import (
"bytes"
"encoding/hex"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/mempool"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
// HandleSubmitTransaction handles the respectively named RPC command
func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
submitTransactionRequest := request.(*appmessage.SubmitTransactionRequestMessage)
serializedTx, err := hex.DecodeString(submitTransactionRequest.TransactionHex)
if err != nil {
errorMessage := &appmessage.SubmitTransactionResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Transaction hex could not be parsed: %s", err)
return errorMessage, nil
}
var msgTx appmessage.MsgTx
err = msgTx.Deserialize(bytes.NewReader(serializedTx))
if err != nil {
errorMessage := &appmessage.SubmitTransactionResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Transaction decode failed: %s", err)
return errorMessage, nil
}
tx := util.NewTx(&msgTx)
err = context.ProtocolManager.AddTransaction(tx)
if err != nil {
if !errors.As(err, &mempool.RuleError{}) {
return nil, err
}
log.Debugf("Rejected transaction %s: %s", tx.ID(), err)
errorMessage := &appmessage.SubmitTransactionResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Rejected transaction %s: %s", tx.ID(), err)
return errorMessage, nil
}
response := appmessage.NewSubmitTransactionResponseMessage(tx.ID().String())
return response, nil
}

154
blockdag/accept.go Normal file
View File

@@ -0,0 +1,154 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
func (dag *BlockDAG) addNodeToIndexWithInvalidAncestor(block *util.Block) error {
blockHeader := &block.MsgBlock().Header
newNode, _ := dag.newBlockNode(blockHeader, newBlockSet())
newNode.status = statusInvalidAncestor
dag.index.AddNode(newNode)
dbTx, err := dag.databaseContext.NewTx()
if err != nil {
return err
}
defer dbTx.RollbackUnlessClosed()
err = dag.index.flushToDB(dbTx)
if err != nil {
return err
}
return dbTx.Commit()
}
// maybeAcceptBlock potentially accepts a block into the block DAG. It
// performs several validation checks which depend on its position within
// the block DAG before adding it. The block is expected to have already
// gone through ProcessBlock before calling this function with it.
//
// The flags are also passed to checkBlockContext and connectToDAG. See
// their documentation for how the flags modify their behavior.
//
// This function MUST be called with the dagLock held (for writes).
func (dag *BlockDAG) maybeAcceptBlock(block *util.Block, flags BehaviorFlags) error {
parents, err := lookupParentNodes(block, dag)
if err != nil {
var ruleErr RuleError
if ok := errors.As(err, &ruleErr); ok && ruleErr.ErrorCode == ErrInvalidAncestorBlock {
err := dag.addNodeToIndexWithInvalidAncestor(block)
if err != nil {
return err
}
}
return err
}
// The block must pass all of the validation rules which depend on the
// position of the block within the block DAG.
err = dag.checkBlockContext(block, parents, flags)
if err != nil {
return err
}
// Create a new block node for the block and add it to the node index.
newNode, selectedParentAnticone := dag.newBlockNode(&block.MsgBlock().Header, parents)
newNode.status = statusDataStored
dag.index.AddNode(newNode)
// Insert the block into the database if it's not already there. Even
// though it is possible the block will ultimately fail to connect, it
// has already passed all proof-of-work and validity tests which means
// it would be prohibitively expensive for an attacker to fill up the
// disk with a bunch of blocks that fail to connect. This is necessary
// since it allows block download to be decoupled from the much more
// expensive connection logic. It also has some other nice properties
// such as making blocks that never become part of the DAG or
// blocks that fail to connect available for further analysis.
dbTx, err := dag.databaseContext.NewTx()
if err != nil {
return err
}
defer dbTx.RollbackUnlessClosed()
blockExists, err := dbaccess.HasBlock(dbTx, block.Hash())
if err != nil {
return err
}
if !blockExists {
err := storeBlock(dbTx, block)
if err != nil {
return err
}
}
err = dag.index.flushToDB(dbTx)
if err != nil {
return err
}
err = dbTx.Commit()
if err != nil {
return err
}
// Make sure that all the block's transactions are finalized
fastAdd := flags&BFFastAdd == BFFastAdd
bluestParent := parents.bluest()
if !fastAdd {
if err := dag.validateAllTxsFinalized(block, newNode, bluestParent); err != nil {
return err
}
}
// Connect the passed block to the DAG. This also handles validation of the
// transaction scripts.
chainUpdates, err := dag.addBlock(newNode, block, selectedParentAnticone, flags)
if err != nil {
return err
}
// Notify the caller that the new block was accepted into the block
// DAG. The caller would typically want to react by relaying the
// inventory to other peers.
dag.dagLock.Unlock()
dag.sendNotification(NTBlockAdded, &BlockAddedNotificationData{
Block: block,
WasUnorphaned: flags&BFWasUnorphaned != 0,
})
if len(chainUpdates.addedChainBlockHashes) > 0 {
dag.sendNotification(NTChainChanged, &ChainChangedNotificationData{
RemovedChainBlockHashes: chainUpdates.removedChainBlockHashes,
AddedChainBlockHashes: chainUpdates.addedChainBlockHashes,
})
}
dag.dagLock.Lock()
return nil
}
func lookupParentNodes(block *util.Block, dag *BlockDAG) (blockSet, error) {
header := block.MsgBlock().Header
parentHashes := header.ParentHashes
nodes := newBlockSet()
for _, parentHash := range parentHashes {
node, ok := dag.index.LookupNode(parentHash)
if !ok {
str := fmt.Sprintf("parent block %s is unknown", parentHash)
return nil, ruleError(ErrParentBlockUnknown, str)
} else if dag.index.NodeStatus(node).KnownInvalid() {
str := fmt.Sprintf("parent block %s is known to be invalid", parentHash)
return nil, ruleError(ErrInvalidAncestorBlock, str)
}
nodes.add(node)
}
return nodes, nil
}

107
blockdag/accept_test.go Normal file
View File

@@ -0,0 +1,107 @@
package blockdag
import (
"errors"
"path/filepath"
"testing"
"github.com/kaspanet/kaspad/dagconfig"
)
func TestMaybeAcceptBlockErrors(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", true, Config{
DAGParams: &dagconfig.SimnetParams,
})
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
dag.TestSetCoinbaseMaturity(0)
// Test rejecting the block if its parents are missing
orphanBlockFile := "blk_3B.dat"
loadedBlocks, err := LoadBlocks(filepath.Join("testdata/", orphanBlockFile))
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: "+
"Error loading file '%s': %s\n", orphanBlockFile, err)
}
block := loadedBlocks[0]
err = dag.maybeAcceptBlock(block, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Expected: %s, got: <nil>", ErrParentBlockUnknown)
}
var ruleErr RuleError
if ok := errors.As(err, &ruleErr); !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrParentBlockUnknown {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Unexpected error code. Want: %s, got: %s", ErrParentBlockUnknown, ruleErr.ErrorCode)
}
// Test rejecting the block if its parents are invalid
blocksFile := "blk_0_to_4.dat"
blocks, err := LoadBlocks(filepath.Join("testdata/", blocksFile))
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: "+
"Error loading file '%s': %s\n", blocksFile, err)
}
// Add a valid block and mark it as invalid
block1 := blocks[1]
isOrphan, isDelayed, err := dag.ProcessBlock(block1, BFNone)
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: Valid block unexpectedly returned an error: %s", err)
}
if isDelayed {
t.Fatalf("TestMaybeAcceptBlockErrors: block 1 is too far in the future")
}
if isOrphan {
t.Fatalf("TestMaybeAcceptBlockErrors: incorrectly returned block 1 is an orphan")
}
blockNode1, ok := dag.index.LookupNode(block1.Hash())
if !ok {
t.Fatalf("block %s does not exist in the DAG", block1.Hash())
}
dag.index.SetStatusFlags(blockNode1, statusValidateFailed)
block2 := blocks[2]
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Expected: %s, got: <nil>", ErrInvalidAncestorBlock)
}
if ok := errors.As(err, &ruleErr); !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrInvalidAncestorBlock {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Unexpected error. Want: %s, got: %s", ErrInvalidAncestorBlock, ruleErr.ErrorCode)
}
// Set block1's status back to valid for next tests
dag.index.UnsetStatusFlags(blockNode1, statusValidateFailed)
// Test rejecting the block due to bad context
originalBits := block2.MsgBlock().Header.Bits
block2.MsgBlock().Header.Bits = 0
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Expected: %s, got: <nil>", ErrUnexpectedDifficulty)
}
if ok := errors.As(err, &ruleErr); !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrUnexpectedDifficulty {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Unexpected error. Want: %s, got: %s", ErrUnexpectedDifficulty, ruleErr.ErrorCode)
}
// Set block2's bits back to valid for next tests
block2.MsgBlock().Header.Bits = originalBits
}

View File

@@ -3,7 +3,7 @@ package blockdag
import (
"testing"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
)

View File

@@ -5,14 +5,10 @@
package blockdag
import (
"fmt"
"github.com/kaspanet/kaspad/dbaccess"
"sync"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
)
@@ -61,18 +57,6 @@ func (bi *blockIndex) LookupNode(hash *daghash.Hash) (*blockNode, bool) {
return node, ok
}
func (bi *blockIndex) LookupNodes(hashes []*daghash.Hash) ([]*blockNode, error) {
blocks := make([]*blockNode, 0, len(hashes))
for _, hash := range hashes {
node, ok := bi.LookupNode(hash)
if !ok {
return nil, errors.Errorf("Couldn't find block with hash %s", hash)
}
blocks = append(blocks, node)
}
return blocks, nil
}
// AddNode adds the provided node to the block index and marks it as dirty.
// Duplicate entries are not checked so it is up to caller to avoid adding them.
//
@@ -92,23 +76,36 @@ func (bi *blockIndex) addNode(node *blockNode) {
bi.index[*node.hash] = node
}
// BlockNodeStatus provides concurrent-safe access to the status field of a node.
// NodeStatus provides concurrent-safe access to the status field of a node.
//
// This function is safe for concurrent access.
func (bi *blockIndex) BlockNodeStatus(node *blockNode) blockStatus {
func (bi *blockIndex) NodeStatus(node *blockNode) blockStatus {
bi.RLock()
defer bi.RUnlock()
status := node.status
return status
}
// SetBlockNodeStatus changes the status of a blockNode
// SetStatusFlags flips the provided status flags on the block node to on,
// regardless of whether they were on or off previously. This does not unset any
// flags currently on.
//
// This function is safe for concurrent access.
func (bi *blockIndex) SetBlockNodeStatus(node *blockNode, newStatus blockStatus) {
func (bi *blockIndex) SetStatusFlags(node *blockNode, flags blockStatus) {
bi.Lock()
defer bi.Unlock()
node.status = newStatus
node.status |= flags
bi.dirty[node] = struct{}{}
}
// UnsetStatusFlags flips the provided status flags on the block node to off,
// regardless of whether they were on or off previously.
//
// This function is safe for concurrent access.
func (bi *blockIndex) UnsetStatusFlags(node *blockNode, flags blockStatus) {
bi.Lock()
defer bi.Unlock()
node.status &^= flags
bi.dirty[node] = struct{}{}
}
@@ -137,42 +134,3 @@ func (bi *blockIndex) flushToDB(dbContext *dbaccess.TxContext) error {
func (bi *blockIndex) clearDirtyEntries() {
bi.dirty = make(map[*blockNode]struct{})
}
func (dag *BlockDAG) addNodeToIndexWithInvalidAncestor(block *util.Block) error {
blockHeader := &block.MsgBlock().Header
newNode, _ := dag.newBlockNode(blockHeader, newBlockSet())
newNode.status = statusInvalidAncestor
dag.index.AddNode(newNode)
dbTx, err := dag.databaseContext.NewTx()
if err != nil {
return err
}
defer dbTx.RollbackUnlessClosed()
err = dag.index.flushToDB(dbTx)
if err != nil {
return err
}
return dbTx.Commit()
}
func lookupParentNodes(block *util.Block, dag *BlockDAG) (blockSet, error) {
header := block.MsgBlock().Header
parentHashes := header.ParentHashes
nodes := newBlockSet()
for _, parentHash := range parentHashes {
node, ok := dag.index.LookupNode(parentHash)
if !ok {
str := fmt.Sprintf("parent block %s is unknown", parentHash)
return nil, ruleError(ErrParentBlockUnknown, str)
} else if dag.index.BlockNodeStatus(node).KnownInvalid() {
str := fmt.Sprintf("parent block %s is known to be invalid", parentHash)
return nil, ruleError(ErrInvalidAncestorBlock, str)
}
nodes.add(node)
}
return nodes, nil
}

View File

@@ -1,7 +1,7 @@
package blockdag
import (
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/mstime"
"testing"
)

109
blockdag/blocklocator.go Normal file
View File

@@ -0,0 +1,109 @@
package blockdag
import (
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
)
// BlockLocator is used to help locate a specific block. The algorithm for
// building the block locator is to add block hashes in reverse order on the
// block's selected parent chain until the desired stop block is reached.
// In order to keep the list of locator hashes to a reasonable number of entries,
// the step between each entry is doubled each loop iteration to exponentially
// decrease the number of hashes as a function of the distance from the block
// being located.
//
// For example, assume a selected parent chain with IDs as depicted below, and the
// stop block is genesis:
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
//
// The block locator for block 17 would be the hashes of blocks:
// [17 16 14 11 7 2 genesis]
type BlockLocator []*daghash.Hash
// BlockLocatorFromHashes returns a block locator from high and low hash.
// See BlockLocator for details on the algorithm used to create a block locator.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) BlockLocatorFromHashes(highHash, lowHash *daghash.Hash) (BlockLocator, error) {
dag.dagLock.RLock()
defer dag.dagLock.RUnlock()
highNode, ok := dag.index.LookupNode(highHash)
if !ok {
return nil, errors.Errorf("block %s is unknown", highHash)
}
lowNode, ok := dag.index.LookupNode(lowHash)
if !ok {
return nil, errors.Errorf("block %s is unknown", lowHash)
}
return dag.blockLocator(highNode, lowNode)
}
// blockLocator returns a block locator for the passed high and low nodes.
// See the BlockLocator type comments for more details.
//
// This function MUST be called with the DAG state lock held (for reads).
func (dag *BlockDAG) blockLocator(highNode, lowNode *blockNode) (BlockLocator, error) {
// We use the selected parent of the high node, so the
// block locator won't contain the high node.
highNode = highNode.selectedParent
node := highNode
step := uint64(1)
locator := make(BlockLocator, 0)
for node != nil {
locator = append(locator, node.hash)
// Nothing more to add once the low node has been added.
if node.blueScore <= lowNode.blueScore {
if node != lowNode {
return nil, errors.Errorf("highNode and lowNode are " +
"not in the same selected parent chain.")
}
break
}
// Calculate blueScore of previous node to include ensuring the
// final node is lowNode.
nextBlueScore := node.blueScore - step
if nextBlueScore < lowNode.blueScore {
nextBlueScore = lowNode.blueScore
}
// walk backwards through the nodes to the correct ancestor.
node = node.SelectedAncestor(nextBlueScore)
// Double the distance between included hashes.
step *= 2
}
return locator, nil
}
// FindNextLocatorBoundaries returns the lowest unknown block locator, hash
// and the highest known block locator hash. This is used to create the
// next block locator to find the highest shared known chain block with the
// sync peer.
//
// This function MUST be called with the DAG state lock held (for reads).
func (dag *BlockDAG) FindNextLocatorBoundaries(locator BlockLocator) (highHash, lowHash *daghash.Hash) {
// Find the most recent locator block hash in the DAG. In the case none of
// the hashes in the locator are in the DAG, fall back to the genesis block.
lowNode := dag.genesis
nextBlockLocatorIndex := int64(len(locator) - 1)
for i, hash := range locator {
node, ok := dag.index.LookupNode(hash)
if ok {
lowNode = node
nextBlockLocatorIndex = int64(i) - 1
break
}
}
if nextBlockLocatorIndex < 0 {
return nil, lowNode.hash
}
return locator[nextBlockLocatorIndex], lowNode.hash
}

View File

@@ -8,20 +8,20 @@ import (
"fmt"
"math"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util/daghash"
)
// blockStatus is representing the validation state of the block.
// blockStatus is a bit field representing the validation state of the block.
type blockStatus byte
const (
// statusDataStored indicates that the block's payload is stored on disk.
statusDataStored blockStatus = iota
statusDataStored blockStatus = 1 << iota
// statusValid indicates that the block has been fully validated.
statusValid
@@ -32,33 +32,12 @@ const (
// statusInvalidAncestor indicates that one of the block's ancestors has
// has failed validation, thus the block is also invalid.
statusInvalidAncestor
// statusUTXOPendingVerification indicates that the block is pending verification against its past UTXO-Set, either
// because it was not yet verified since the block was never in the selected parent chain, or if the
// block violates finality.
statusUTXOPendingVerification
// statusDisqualifiedFromChain indicates that the block is not eligible to be a selected parent.
statusDisqualifiedFromChain
)
var blockStatusToString = map[blockStatus]string{
statusDataStored: "statusDataStored",
statusValid: "statusValid",
statusValidateFailed: "statusValidateFailed",
statusInvalidAncestor: "statusInvalidAncestor",
statusUTXOPendingVerification: "statusUTXOPendingVerification",
statusDisqualifiedFromChain: "statusDisqualifiedFromChain",
}
func (status blockStatus) String() string {
return blockStatusToString[status]
}
// KnownValid returns whether the block is known to be valid. This will return
// false for a valid block that has not been fully validated yet.
func (status blockStatus) KnownValid() bool {
return status == statusValid
return status&statusValid != 0
}
// KnownInvalid returns whether the block is known to be invalid. This may be
@@ -66,7 +45,7 @@ func (status blockStatus) KnownValid() bool {
// invalid. This will return false for invalid blocks that have not been proven
// invalid yet.
func (status blockStatus) KnownInvalid() bool {
return status == statusValidateFailed || status == statusInvalidAncestor
return status&(statusValidateFailed|statusInvalidAncestor) != 0
}
// blockNode represents a block within the block DAG. The DAG is stored into
@@ -79,9 +58,6 @@ type blockNode struct {
// hundreds of thousands of these in memory, so a few extra bytes of
// padding adds up.
// dag is the blockDAG in which this node resides
dag *BlockDAG
// parents is the parent blocks for this node.
parents blockSet
@@ -92,12 +68,9 @@ type blockNode struct {
// children are all the blocks that refer to this block as a parent
children blockSet
// blues are all blue blocks in this block's worldview that are in its merge set
// blues are all blue blocks in this block's worldview that are in its selected parent anticone
blues []*blockNode
// reds are all red blocks in this block's worldview that are in its merge set
reds []*blockNode
// blueScore is the count of all the blue blocks in this block's past
blueScore uint64
@@ -108,7 +81,7 @@ type blockNode struct {
// hash is the double sha 256 of the block.
hash *daghash.Hash
// Some fields from block headers to aid in reconstructing headers
// Some fields from block headers to aid in reconstructing headers
// from memory. These must be treated as immutable and are intentionally
// ordered to avoid padding on 64-bit platforms.
version int32
@@ -121,18 +94,20 @@ type blockNode struct {
// status is a bitfield representing the validation state of the block. The
// status field, unlike the other fields, may be written to and so should
// only be accessed using the concurrent-safe BlockNodeStatus method on
// only be accessed using the concurrent-safe NodeStatus method on
// blockIndex once the node has been added to the global index.
status blockStatus
// isFinalized determines whether the node is below the finality point.
isFinalized bool
}
// newBlockNode returns a new block node for the given block header and parents, and the
// anticone of its selected parent (parent with highest blue score).
// selectedParentAnticone is used to update reachability data we store for future reachability queries.
// This function is NOT safe for concurrent access.
func (dag *BlockDAG) newBlockNode(blockHeader *appmessage.BlockHeader, parents blockSet) (node *blockNode, selectedParentAnticone []*blockNode) {
func (dag *BlockDAG) newBlockNode(blockHeader *domainmessage.BlockHeader, parents blockSet) (node *blockNode, selectedParentAnticone []*blockNode) {
node = &blockNode{
dag: dag,
parents: parents,
children: make(blockSet),
blueScore: math.MaxUint64, // Initialized to the max value to avoid collisions with the genesis block
@@ -185,9 +160,9 @@ func (node *blockNode) less(other *blockNode) bool {
// Header constructs a block header from the node and returns it.
//
// This function is safe for concurrent access.
func (node *blockNode) Header() *appmessage.BlockHeader {
func (node *blockNode) Header() *domainmessage.BlockHeader {
// No lock is needed because all accessed fields are immutable.
return &appmessage.BlockHeader{
return &domainmessage.BlockHeader{
Version: node.version,
ParentHashes: node.ParentHashes(),
HashMerkleRoot: node.hashMerkleRoot,
@@ -230,8 +205,8 @@ func (node *blockNode) RelativeAncestor(distance uint64) *blockNode {
// prior to, and including, the block node.
//
// This function is safe for concurrent access.
func (node *blockNode) PastMedianTime() mstime.Time {
window := blueBlockWindow(node, 2*node.dag.TimestampDeviationTolerance-1)
func (node *blockNode) PastMedianTime(dag *BlockDAG) mstime.Time {
window := blueBlockWindow(node, 2*dag.TimestampDeviationTolerance-1)
medianTimestamp, err := window.medianTimestamp()
if err != nil {
panic(fmt.Sprintf("blueBlockWindow: %s", err))
@@ -239,14 +214,6 @@ func (node *blockNode) PastMedianTime() mstime.Time {
return mstime.UnixMilliseconds(medianTimestamp)
}
func (node *blockNode) selectedParentMedianTime() mstime.Time {
medianTime := node.Header().Timestamp
if !node.isGenesis() {
medianTime = node.selectedParent.PastMedianTime()
}
return medianTime
}
func (node *blockNode) ParentHashes() []*daghash.Hash {
return node.parents.hashes()
}
@@ -256,8 +223,8 @@ func (node *blockNode) isGenesis() bool {
return len(node.parents) == 0
}
func (node *blockNode) finalityScore() uint64 {
return node.blueScore / node.dag.FinalityInterval()
func (node *blockNode) finalityScore(dag *BlockDAG) uint64 {
return node.blueScore / uint64(dag.FinalityInterval())
}
// String returns a string that contains the block hash.
@@ -268,111 +235,3 @@ func (node blockNode) String() string {
func (node *blockNode) time() mstime.Time {
return mstime.UnixMilliseconds(node.timestamp)
}
func (node *blockNode) blockAtDepth(depth uint64) *blockNode {
if node.blueScore <= depth { // to prevent overflow of requiredBlueScore
depth = node.blueScore
}
current := node
requiredBlueScore := node.blueScore - depth
for current.blueScore >= requiredBlueScore {
if current.isGenesis() {
return current
}
current = current.selectedParent
}
return current
}
func (node *blockNode) finalityPoint() *blockNode {
return node.blockAtDepth(node.dag.FinalityInterval())
}
func (node *blockNode) hasFinalityPointInOthersSelectedChain(other *blockNode) (bool, error) {
finalityPoint := node.finalityPoint()
return node.dag.isInSelectedParentChainOf(finalityPoint, other)
}
func (node *blockNode) nonBoundedMergeDepthViolatingBlues() (blockSet, error) {
nonBoundedMergeDepthViolatingBlues := newBlockSet()
for _, blueNode := range node.blues {
notViolatingFinality, err := node.hasFinalityPointInOthersSelectedChain(blueNode)
if err != nil {
return nil, err
}
if notViolatingFinality {
nonBoundedMergeDepthViolatingBlues.add(blueNode)
}
}
return nonBoundedMergeDepthViolatingBlues, nil
}
func (node *blockNode) checkBoundedMergeDepth() error {
nonBoundedMergeDepthViolatingBlues, err := node.nonBoundedMergeDepthViolatingBlues()
if err != nil {
return err
}
finalityPoint := node.finalityPoint()
for _, red := range node.reds {
doesRedHaveFinalityPointInPast, err := node.dag.isInPast(finalityPoint, red)
if err != nil {
return err
}
isRedInPastOfAnyNonFinalityViolatingBlue, err := node.dag.isInPastOfAny(red, nonBoundedMergeDepthViolatingBlues)
if err != nil {
return err
}
if !doesRedHaveFinalityPointInPast && !isRedInPastOfAnyNonFinalityViolatingBlue {
return ruleError(ErrViolatingBoundedMergeDepth, "block is violating bounded merge depth")
}
}
return nil
}
func (node *blockNode) isViolatingFinality() (bool, error) {
if node.isGenesis() {
return false, nil
}
if node.dag.virtual.less(node) {
isVirtualFinalityPointInNodesSelectedChain, err := node.dag.isInSelectedParentChainOf(
node.dag.virtual.finalityPoint(), node.selectedParent) // use node.selectedParent because node still doesn't have reachability data
if err != nil {
return false, err
}
if !isVirtualFinalityPointInNodesSelectedChain {
return true, nil
}
}
return false, nil
}
func (node *blockNode) checkMergeSizeLimit() error {
mergeSetSize := len(node.reds) + len(node.blues)
if mergeSetSize > mergeSetSizeLimit {
return ruleError(ErrViolatingMergeLimit,
fmt.Sprintf("The block merges %d blocks > %d merge set size limit", mergeSetSize, mergeSetSizeLimit))
}
return nil
}
func (node *blockNode) hasValidChildren() bool {
for child := range node.children {
if node.dag.index.BlockNodeStatus(child) == statusValid {
return true
}
}
return false
}

View File

@@ -1,10 +1,9 @@
package blockdag
import (
"testing"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
"testing"
)
// This test is to ensure the size BlueAnticoneSizesSize is serialized to the size of KType.

View File

@@ -36,7 +36,7 @@ func (bs blockSet) remove(node *blockNode) {
// clone clones thie block set
func (bs blockSet) clone() blockSet {
clone := make(blockSet, len(bs))
clone := newBlockSet()
for node := range bs {
clone.add(node)
}
@@ -104,48 +104,14 @@ func (bs blockSet) String() string {
func (bs blockSet) bluest() *blockNode {
var bluestNode *blockNode
var maxScore uint64
for node := range bs {
if bluestNode == nil || bluestNode.less(node) {
if bluestNode == nil ||
node.blueScore > maxScore ||
(node.blueScore == maxScore && daghash.Less(node.hash, bluestNode.hash)) {
bluestNode = node
maxScore = node.blueScore
}
}
return bluestNode
}
func (bs blockSet) isEqual(other blockSet) bool {
if len(bs) != len(other) {
return false
}
for node := range bs {
if !other.contains(node) {
return false
}
}
return true
}
func (bs blockSet) areAllIn(other blockSet) bool {
for node := range bs {
if !other.contains(node) {
return false
}
}
return true
}
// isOnlyGenesis returns true if the only block in this blockSet is the genesis block
func (bs blockSet) isOnlyGenesis() bool {
if len(bs) != 1 {
return false
}
for node := range bs {
if node.isGenesis() {
return true
}
}
return false
}

View File

@@ -243,68 +243,3 @@ func TestBlockSetUnion(t *testing.T) {
}
}
}
func TestBlockSetAreAllIn(t *testing.T) {
node1 := &blockNode{hash: &daghash.Hash{10}}
node2 := &blockNode{hash: &daghash.Hash{20}}
node3 := &blockNode{hash: &daghash.Hash{30}}
tests := []struct {
name string
set blockSet
other blockSet
expectedResult bool
}{
{
name: "two empty sets",
set: blockSetFromSlice(),
other: blockSetFromSlice(),
expectedResult: true,
},
{
name: "set empty, other full",
set: blockSetFromSlice(),
other: blockSetFromSlice(node1, node2, node3),
expectedResult: true,
},
{
name: "set full, other empty",
set: blockSetFromSlice(node1, node2, node3),
other: blockSetFromSlice(),
expectedResult: false,
},
{
name: "same node in both",
set: blockSetFromSlice(node1),
other: blockSetFromSlice(node1),
expectedResult: true,
},
{
name: "different node in both",
set: blockSetFromSlice(node1),
other: blockSetFromSlice(node2),
expectedResult: false,
},
{
name: "set is subset of other",
set: blockSetFromSlice(node1, node2),
other: blockSetFromSlice(node2, node1, node3),
expectedResult: true,
},
{
name: "other is subset of set",
set: blockSetFromSlice(node2, node1, node3),
other: blockSetFromSlice(node1, node2),
expectedResult: false,
},
}
for _, test := range tests {
result := test.set.areAllIn(test.other)
if result != test.expectedResult {
t.Errorf("blockSet.areAllIn: unexpected result in test '%s'. "+
"Expected: '%t', got: '%t'", test.name, test.expectedResult, result)
}
}
}

View File

@@ -1,13 +1,12 @@
package blockdag
import (
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
"reflect"
"testing"
"time"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
func TestBlueBlockWindow(t *testing.T) {
@@ -52,12 +51,12 @@ func TestBlueBlockWindow(t *testing.T) {
expectedWindowWithGenesisPadding: []string{"B", "A", "A", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"C", "D"},
parents: []string{"D", "C"},
id: "E",
expectedWindowWithGenesisPadding: []string{"D", "C", "B", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"C", "D"},
parents: []string{"D", "C"},
id: "F",
expectedWindowWithGenesisPadding: []string{"D", "C", "B", "A", "A", "A", "A", "A", "A", "A"},
},
@@ -118,13 +117,13 @@ func TestBlueBlockWindow(t *testing.T) {
block, err := PrepareBlockForTest(dag, parents.hashes(), nil)
if err != nil {
t.Fatalf("block %v got unexpected error from PrepareBlockForTest: %+v", blockData.id, err)
t.Fatalf("block %v got unexpected error from PrepareBlockForTest: %v", blockData.id, err)
}
utilBlock := util.NewBlock(block)
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
if err != nil {
t.Fatalf("dag.ProcessBlock got unexpected error for block %v: %+v", blockData.id, err)
t.Fatalf("dag.ProcessBlock got unexpected error for block %v: %v", blockData.id, err)
}
if isDelayed {
t.Fatalf("block %s "+

199
blockdag/coinbase.go Normal file
View File

@@ -0,0 +1,199 @@
package blockdag
import (
"bufio"
"bytes"
"encoding/binary"
"io"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/coinbasepayload"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/util/txsort"
"github.com/pkg/errors"
)
// compactFeeData is a specialized data type to store a compact list of fees
// inside a block.
// Every transaction gets a single uint64 value, stored as a plain binary list.
// The transactions are ordered the same way they are ordered inside the block, making it easy
// to traverse every transaction in a block and extract its fee.
//
// compactFeeFactory is used to create such a list.
// compactFeeIterator is used to iterate over such a list.
type compactFeeData []byte
func (cfd compactFeeData) Len() int {
return len(cfd) / 8
}
type compactFeeFactory struct {
buffer *bytes.Buffer
writer *bufio.Writer
}
func newCompactFeeFactory() *compactFeeFactory {
buffer := bytes.NewBuffer([]byte{})
return &compactFeeFactory{
buffer: buffer,
writer: bufio.NewWriter(buffer),
}
}
func (cfw *compactFeeFactory) add(txFee uint64) error {
return binary.Write(cfw.writer, binary.LittleEndian, txFee)
}
func (cfw *compactFeeFactory) data() (compactFeeData, error) {
err := cfw.writer.Flush()
return compactFeeData(cfw.buffer.Bytes()), err
}
type compactFeeIterator struct {
reader io.Reader
}
func (cfd compactFeeData) iterator() *compactFeeIterator {
return &compactFeeIterator{
reader: bufio.NewReader(bytes.NewBuffer(cfd)),
}
}
func (cfr *compactFeeIterator) next() (uint64, error) {
var txFee uint64
err := binary.Read(cfr.reader, binary.LittleEndian, &txFee)
return txFee, err
}
// The following functions relate to storing and retrieving fee data from the database
// getBluesFeeData returns the compactFeeData for all nodes's blues,
// used to calculate the fees this blockNode needs to pay
func (dag *BlockDAG) getBluesFeeData(node *blockNode) (map[daghash.Hash]compactFeeData, error) {
bluesFeeData := make(map[daghash.Hash]compactFeeData)
for _, blueBlock := range node.blues {
feeData, err := dbaccess.FetchFeeData(dag.databaseContext, blueBlock.hash)
if err != nil {
return nil, err
}
bluesFeeData[*blueBlock.hash] = feeData
}
return bluesFeeData, nil
}
// The following functions deal with building and validating the coinbase transaction
func (node *blockNode) validateCoinbaseTransaction(dag *BlockDAG, block *util.Block, txsAcceptanceData MultiBlockTxsAcceptanceData) error {
if node.isGenesis() {
return nil
}
blockCoinbaseTx := block.CoinbaseTransaction().MsgTx()
_, scriptPubKey, extraData, err := coinbasepayload.DeserializeCoinbasePayload(blockCoinbaseTx)
if errors.Is(err, coinbasepayload.ErrIncorrectScriptPubKeyLen) {
return ruleError(ErrBadCoinbaseTransaction, err.Error())
}
if err != nil {
return err
}
expectedCoinbaseTransaction, err := node.expectedCoinbaseTransaction(dag, txsAcceptanceData, scriptPubKey, extraData)
if err != nil {
return err
}
if !expectedCoinbaseTransaction.Hash().IsEqual(block.CoinbaseTransaction().Hash()) {
return ruleError(ErrBadCoinbaseTransaction, "Coinbase transaction is not built as expected")
}
return nil
}
// expectedCoinbaseTransaction returns the coinbase transaction for the current block
func (node *blockNode) expectedCoinbaseTransaction(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData, scriptPubKey []byte, extraData []byte) (*util.Tx, error) {
bluesFeeData, err := dag.getBluesFeeData(node)
if err != nil {
return nil, err
}
txIns := []*domainmessage.TxIn{}
txOuts := []*domainmessage.TxOut{}
for _, blue := range node.blues {
txOut, err := coinbaseOutputForBlueBlock(dag, blue, txsAcceptanceData, bluesFeeData)
if err != nil {
return nil, err
}
if txOut != nil {
txOuts = append(txOuts, txOut)
}
}
payload, err := coinbasepayload.SerializeCoinbasePayload(node.blueScore, scriptPubKey, extraData)
if err != nil {
return nil, err
}
coinbaseTx := domainmessage.NewSubnetworkMsgTx(domainmessage.TxVersion, txIns, txOuts, subnetworkid.SubnetworkIDCoinbase, 0, payload)
sortedCoinbaseTx := txsort.Sort(coinbaseTx)
return util.NewTx(sortedCoinbaseTx), nil
}
// coinbaseOutputForBlueBlock calculates the output that should go into the coinbase transaction of blueBlock
// If blueBlock gets no fee - returns nil for txOut
func coinbaseOutputForBlueBlock(dag *BlockDAG, blueBlock *blockNode,
txsAcceptanceData MultiBlockTxsAcceptanceData, feeData map[daghash.Hash]compactFeeData) (*domainmessage.TxOut, error) {
blockTxsAcceptanceData, ok := txsAcceptanceData.FindAcceptanceData(blueBlock.hash)
if !ok {
return nil, errors.Errorf("No txsAcceptanceData for block %s", blueBlock.hash)
}
blockFeeData, ok := feeData[*blueBlock.hash]
if !ok {
return nil, errors.Errorf("No feeData for block %s", blueBlock.hash)
}
if len(blockTxsAcceptanceData.TxAcceptanceData) != blockFeeData.Len() {
return nil, errors.Errorf(
"length of accepted transaction data(%d) and fee data(%d) is not equal for block %s",
len(blockTxsAcceptanceData.TxAcceptanceData), blockFeeData.Len(), blueBlock.hash)
}
totalFees := uint64(0)
feeIterator := blockFeeData.iterator()
for _, txAcceptanceData := range blockTxsAcceptanceData.TxAcceptanceData {
fee, err := feeIterator.next()
if err != nil {
return nil, errors.Errorf("Error retrieving fee from compactFeeData iterator: %s", err)
}
if txAcceptanceData.IsAccepted {
totalFees += fee
}
}
totalReward := CalcBlockSubsidy(blueBlock.blueScore, dag.Params) + totalFees
if totalReward == 0 {
return nil, nil
}
// the ScriptPubKey for the coinbase is parsed from the coinbase payload
_, scriptPubKey, _, err := coinbasepayload.DeserializeCoinbasePayload(blockTxsAcceptanceData.TxAcceptanceData[0].Tx.MsgTx())
if err != nil {
return nil, err
}
txOut := &domainmessage.TxOut{
Value: totalReward,
ScriptPubKey: scriptPubKey,
}
return txOut, nil
}

60
blockdag/coinbase_test.go Normal file
View File

@@ -0,0 +1,60 @@
package blockdag
import (
"io"
"reflect"
"testing"
)
func TestFeeAccumulators(t *testing.T) {
fees := []uint64{1, 2, 3, 4, 5, 6, 7, 0xffffffffffffffff}
factory := newCompactFeeFactory()
for _, fee := range fees {
err := factory.add(fee)
if err != nil {
t.Fatalf("Error writing %d as tx fee: %s", fee, err)
}
}
expectedData := compactFeeData{
1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0,
5, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255,
}
actualData, err := factory.data()
if err != nil {
t.Fatalf("Error getting bytes from writer: %s", err)
}
if !reflect.DeepEqual(expectedData, actualData) {
t.Errorf("Expected bytes: %v, but got: %v", expectedData, actualData)
}
iterator := actualData.iterator()
for i, expectedFee := range fees {
actualFee, err := iterator.next()
if err != nil {
t.Fatalf("Error getting fee for Tx#%d: %s", i, err)
}
if actualFee != expectedFee {
t.Errorf("Tx #%d: Expected fee: %d, but got %d", i, expectedFee, actualFee)
}
}
_, err = iterator.next()
if err == nil {
t.Fatal("No error from iterator.nextTxFee after done reading all transactions")
}
if err != io.EOF {
t.Fatalf("Error from iterator.nextTxFee after done reading all transactions is not io.EOF: %s", err)
}
}

View File

@@ -17,8 +17,8 @@ import (
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
@@ -78,7 +78,7 @@ func loadUTXOSet(filename string) (UTXOSet, error) {
if err != nil {
return nil, err
}
utxoSet.utxoCollection[appmessage.Outpoint{TxID: txID, Index: index}] = entry
utxoSet.utxoCollection[domainmessage.Outpoint{TxID: txID, Index: index}] = entry
}
return utxoSet, nil
@@ -103,6 +103,8 @@ func newTestDAG(params *dagconfig.Params) *BlockDAG {
TimestampDeviationTolerance: params.TimestampDeviationTolerance,
powMaxBits: util.BigToCompact(params.PowMax),
index: index,
warningCaches: newThresholdCaches(vbNumBits),
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
}
// Create a genesis block node and block index index populated with it
@@ -118,7 +120,7 @@ func newTestDAG(params *dagconfig.Params) *BlockDAG {
// provided fields populated and fake values for the other fields.
func newTestNode(dag *BlockDAG, parents blockSet, blockVersion int32, bits uint32, timestamp mstime.Time) *blockNode {
// Make up a header and create a block node from it.
header := &appmessage.BlockHeader{
header := &domainmessage.BlockHeader{
Version: blockVersion,
ParentHashes: parents.hashes(),
Bits: bits,
@@ -166,7 +168,7 @@ func checkRuleError(gotErr, wantErr error) error {
return nil
}
func prepareAndProcessBlockByParentMsgBlocks(t *testing.T, dag *BlockDAG, parents ...*appmessage.MsgBlock) *appmessage.MsgBlock {
func prepareAndProcessBlockByParentMsgBlocks(t *testing.T, dag *BlockDAG, parents ...*domainmessage.MsgBlock) *domainmessage.MsgBlock {
parentHashes := make([]*daghash.Hash, len(parents))
for i, parent := range parents {
parentHashes[i] = parent.BlockHash()
@@ -174,7 +176,7 @@ func prepareAndProcessBlockByParentMsgBlocks(t *testing.T, dag *BlockDAG, parent
return PrepareAndProcessBlockForTest(t, dag, parentHashes, nil)
}
func nodeByMsgBlock(t *testing.T, dag *BlockDAG, block *appmessage.MsgBlock) *blockNode {
func nodeByMsgBlock(t *testing.T, dag *BlockDAG, block *domainmessage.MsgBlock) *blockNode {
node, ok := dag.index.LookupNode(block.BlockHash())
if !ok {
t.Fatalf("couldn't find block node with hash %s", block.BlockHash())

2143
blockdag/dag.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -14,14 +14,15 @@ import (
"time"
"github.com/kaspanet/go-secp256k1"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/txscript"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/pkg/errors"
)
func TestBlockCount(t *testing.T) {
@@ -236,47 +237,25 @@ func TestIsKnownBlock(t *testing.T) {
// the returned SequenceLocks are correct for each test instance.
func TestCalcSequenceLock(t *testing.T) {
netParams := &dagconfig.SimnetParams
dag, teardownFunc, err := DAGSetup("TestCalcSequenceLock", true, Config{
DAGParams: netParams,
})
if err != nil {
t.Fatalf("Error in DAGSetup: %+v", err)
}
defer teardownFunc()
blockVersion := int32(0x10000000)
// Generate enough synthetic blocks for the rest of the test
dag := newTestDAG(netParams)
node := dag.selectedTip()
blockTime := node.Header().Timestamp
numBlocksToGenerate := 5
for i := 0; i < numBlocksToGenerate; i++ {
parents := blockSetFromSlice(node)
block, err := PrepareBlockForTest(dag, parents.hashes(), nil)
if err != nil {
t.Fatalf("block No. %d got unexpected error from PrepareBlockForTest: %+v", i, err)
}
block.Header.Timestamp = blockTime.Add(time.Second)
utilBlock := util.NewBlock(block)
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
if err != nil {
t.Fatalf("block No. %d got unexpected error from ProcessBlock: %+v", i, err)
}
if isOrphan || isDelayed {
t.Fatalf("Block No. %d is unexpectadly orphan: %t or delayed: %t", i, isOrphan, isDelayed)
}
var ok bool
node, ok = dag.index.LookupNode(block.BlockHash())
if !ok {
t.Errorf("Block No. %d not found in index after adding to dag", i)
}
blockTime = blockTime.Add(time.Second)
node = newTestNode(dag, blockSetFromSlice(node), blockVersion, 0, blockTime)
dag.index.AddNode(node)
dag.virtual.SetTips(blockSetFromSlice(node))
}
// Create a utxo view with a fake utxo for the inputs used in the
// transactions created below. This utxo is added such that it has an
// age of 4 blocks.
msgTx := appmessage.NewNativeMsgTx(appmessage.TxVersion, nil, []*appmessage.TxOut{{ScriptPubKey: nil, Value: 10}})
msgTx := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, nil, []*domainmessage.TxOut{{ScriptPubKey: nil, Value: 10}})
targetTx := util.NewTx(msgTx)
utxoSet := NewFullUTXOSet()
blueScore := uint64(numBlocksToGenerate) - 4
@@ -291,7 +270,7 @@ func TestCalcSequenceLock(t *testing.T) {
// that the sequence lock heights are always calculated from the same
// point of view that they were originally calculated from for a given
// utxo. That is to say, the height prior to it.
utxo := appmessage.Outpoint{
utxo := domainmessage.Outpoint{
TxID: *targetTx.ID(),
Index: 0,
}
@@ -300,19 +279,19 @@ func TestCalcSequenceLock(t *testing.T) {
// Obtain the past median time from the PoV of the input created above.
// The past median time for the input is the past median time from the PoV
// of the block *prior* to the one that included it.
medianTime := node.RelativeAncestor(5).PastMedianTime().UnixMilliseconds()
medianTime := node.RelativeAncestor(5).PastMedianTime(dag).UnixMilliseconds()
// The median time calculated from the PoV of the best block in the
// test DAG. For unconfirmed inputs, this value will be used since
// the MTP will be calculated from the PoV of the yet-to-be-mined
// block.
nextMedianTime := node.PastMedianTime().UnixMilliseconds()
nextMedianTime := node.PastMedianTime(dag).UnixMilliseconds()
nextBlockBlueScore := int32(numBlocksToGenerate) + 1
// Add an additional transaction which will serve as our unconfirmed
// output.
unConfTx := appmessage.NewNativeMsgTx(appmessage.TxVersion, nil, []*appmessage.TxOut{{ScriptPubKey: nil, Value: 5}})
unConfUtxo := appmessage.Outpoint{
unConfTx := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, nil, []*domainmessage.TxOut{{ScriptPubKey: nil, Value: 5}})
unConfUtxo := domainmessage.Outpoint{
TxID: *unConfTx.TxID(),
Index: 0,
}
@@ -324,8 +303,9 @@ func TestCalcSequenceLock(t *testing.T) {
tests := []struct {
name string
tx *appmessage.MsgTx
tx *domainmessage.MsgTx
utxoSet UTXOSet
mempool bool
want *SequenceLock
}{
// A transaction with a single input with max sequence number.
@@ -333,7 +313,7 @@ func TestCalcSequenceLock(t *testing.T) {
// should be disabled.
{
name: "single input, max sequence number",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{PreviousOutpoint: utxo, Sequence: appmessage.MaxTxInSequenceNum}}, nil),
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{PreviousOutpoint: utxo, Sequence: domainmessage.MaxTxInSequenceNum}}, nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: -1,
@@ -348,7 +328,7 @@ func TestCalcSequenceLock(t *testing.T) {
// the targeted block.
{
name: "single input, milliseconds lock time below time granularity",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{PreviousOutpoint: utxo, Sequence: LockTimeToSequence(true, 2)}}, nil),
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{PreviousOutpoint: utxo, Sequence: LockTimeToSequence(true, 2)}}, nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: medianTime - 1,
@@ -360,7 +340,7 @@ func TestCalcSequenceLock(t *testing.T) {
// milliseconds after the median past time of the DAG.
{
name: "single input, 1048575 milliseconds after median time",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{PreviousOutpoint: utxo, Sequence: LockTimeToSequence(true, 1048576)}}, nil),
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{PreviousOutpoint: utxo, Sequence: LockTimeToSequence(true, 1048576)}}, nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: medianTime + 1048575,
@@ -375,8 +355,8 @@ func TestCalcSequenceLock(t *testing.T) {
// latest lock that isn't disabled.
{
name: "multiple varied inputs",
tx: appmessage.NewNativeMsgTx(1,
[]*appmessage.TxIn{{
tx: domainmessage.NewNativeMsgTx(1,
[]*domainmessage.TxIn{{
PreviousOutpoint: utxo,
Sequence: LockTimeToSequence(true, 2621440),
}, {
@@ -385,12 +365,12 @@ func TestCalcSequenceLock(t *testing.T) {
}, {
PreviousOutpoint: utxo,
Sequence: LockTimeToSequence(false, 5) |
appmessage.SequenceLockTimeDisabled,
domainmessage.SequenceLockTimeDisabled,
}},
nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: medianTime + (5 << appmessage.SequenceLockTimeGranularity) - 1,
Milliseconds: medianTime + (5 << domainmessage.SequenceLockTimeGranularity) - 1,
BlockBlueScore: int64(prevUtxoBlueScore) + 3,
},
},
@@ -400,7 +380,7 @@ func TestCalcSequenceLock(t *testing.T) {
// height of 2 meaning it can be included at height 3.
{
name: "single input, lock-time in blocks",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{PreviousOutpoint: utxo, Sequence: LockTimeToSequence(false, 3)}}, nil),
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{PreviousOutpoint: utxo, Sequence: LockTimeToSequence(false, 3)}}, nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: -1,
@@ -412,7 +392,7 @@ func TestCalcSequenceLock(t *testing.T) {
// be the time further in the future.
{
name: "two inputs, lock-times in seconds",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{
PreviousOutpoint: utxo,
Sequence: LockTimeToSequence(true, 5242880),
}, {
@@ -421,7 +401,7 @@ func TestCalcSequenceLock(t *testing.T) {
}}, nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: medianTime + (10 << appmessage.SequenceLockTimeGranularity) - 1,
Milliseconds: medianTime + (10 << domainmessage.SequenceLockTimeGranularity) - 1,
BlockBlueScore: -1,
},
},
@@ -431,8 +411,8 @@ func TestCalcSequenceLock(t *testing.T) {
// indicating it can be included at height 11.
{
name: "two inputs, lock-times in blocks",
tx: appmessage.NewNativeMsgTx(1,
[]*appmessage.TxIn{{
tx: domainmessage.NewNativeMsgTx(1,
[]*domainmessage.TxIn{{
PreviousOutpoint: utxo,
Sequence: LockTimeToSequence(false, 1),
}, {
@@ -451,8 +431,8 @@ func TestCalcSequenceLock(t *testing.T) {
// further into the future for both inputs should be chosen.
{
name: "four inputs, two lock-times in time, two lock-times in blocks",
tx: appmessage.NewNativeMsgTx(1,
[]*appmessage.TxIn{{
tx: domainmessage.NewNativeMsgTx(1,
[]*domainmessage.TxIn{{
PreviousOutpoint: utxo,
Sequence: LockTimeToSequence(true, 2621440),
}, {
@@ -468,7 +448,7 @@ func TestCalcSequenceLock(t *testing.T) {
nil),
utxoSet: utxoSet,
want: &SequenceLock{
Milliseconds: medianTime + (13 << appmessage.SequenceLockTimeGranularity) - 1,
Milliseconds: medianTime + (13 << domainmessage.SequenceLockTimeGranularity) - 1,
BlockBlueScore: int64(prevUtxoBlueScore) + 8,
},
},
@@ -480,8 +460,9 @@ func TestCalcSequenceLock(t *testing.T) {
// after that.
{
name: "single input, unconfirmed, lock-time in blocks",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{PreviousOutpoint: unConfUtxo, Sequence: LockTimeToSequence(false, 2)}}, nil),
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{PreviousOutpoint: unConfUtxo, Sequence: LockTimeToSequence(false, 2)}}, nil),
utxoSet: utxoSet,
mempool: true,
want: &SequenceLock{
Milliseconds: -1,
BlockBlueScore: int64(nextBlockBlueScore) + 1,
@@ -492,8 +473,9 @@ func TestCalcSequenceLock(t *testing.T) {
// MTP of the *next* block.
{
name: "single input, unconfirmed, lock-time in milliseoncds",
tx: appmessage.NewNativeMsgTx(1, []*appmessage.TxIn{{PreviousOutpoint: unConfUtxo, Sequence: LockTimeToSequence(true, 1048576)}}, nil),
tx: domainmessage.NewNativeMsgTx(1, []*domainmessage.TxIn{{PreviousOutpoint: unConfUtxo, Sequence: LockTimeToSequence(true, 1048576)}}, nil),
utxoSet: utxoSet,
mempool: true,
want: &SequenceLock{
Milliseconds: nextMedianTime + 1048575,
BlockBlueScore: -1,
@@ -504,7 +486,7 @@ func TestCalcSequenceLock(t *testing.T) {
t.Logf("Running %v SequenceLock tests", len(tests))
for _, test := range tests {
utilTx := util.NewTx(test.tx)
seqLock, err := dag.CalcSequenceLock(utilTx, utxoSet)
seqLock, err := dag.CalcSequenceLock(utilTx, utxoSet, test.mempool)
if err != nil {
t.Fatalf("test '%s', unable to calc sequence lock: %v", test.name, err)
}
@@ -559,7 +541,7 @@ func TestCalcPastMedianTime(t *testing.T) {
}
for _, test := range tests {
millisecondsSinceGenesis := nodes[test.blockNumber].PastMedianTime().UnixMilliseconds() -
millisecondsSinceGenesis := nodes[test.blockNumber].PastMedianTime(dag).UnixMilliseconds() -
dag.genesis.Header().Timestamp.UnixMilliseconds()
if millisecondsSinceGenesis != test.expectedMillisecondsSinceGenesis {
@@ -691,7 +673,7 @@ func TestAcceptingInInit(t *testing.T) {
t.Fatalf("block %s does not exist in the DAG", testBlock.Hash())
}
if testNode.status != statusValid {
if testNode.status&statusValid == 0 {
t.Fatalf("testNode is unexpectedly invalid")
}
}
@@ -719,7 +701,7 @@ func TestConfirmations(t *testing.T) {
}
// Add a chain of blocks
chainBlocks := make([]*appmessage.MsgBlock, 5)
chainBlocks := make([]*domainmessage.MsgBlock, 5)
chainBlocks[0] = dag.Params.GenesisBlock
for i := uint32(1); i < 5; i++ {
chainBlocks[i] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[i-1])
@@ -739,7 +721,7 @@ func TestConfirmations(t *testing.T) {
}
}
branchingBlocks := make([]*appmessage.MsgBlock, 2)
branchingBlocks := make([]*domainmessage.MsgBlock, 2)
// Add two branching blocks
branchingBlocks[0] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[1])
branchingBlocks[1] = prepareAndProcessBlockByParentMsgBlocks(t, dag, branchingBlocks[0])
@@ -756,7 +738,7 @@ func TestConfirmations(t *testing.T) {
}
// Check that each of the tips has a 0 confirmations
tips := dag.tips
tips := dag.virtual.tips()
for tip := range tips {
tipConfirmations, err := dag.blockConfirmations(tip)
if err != nil {
@@ -812,7 +794,7 @@ func TestAcceptingBlock(t *testing.T) {
defer teardownFunc()
dag.TestSetCoinbaseMaturity(0)
acceptingBlockByMsgBlock := func(block *appmessage.MsgBlock) (*blockNode, error) {
acceptingBlockByMsgBlock := func(block *domainmessage.MsgBlock) (*blockNode, error) {
node := nodeByMsgBlock(t, dag, block)
return dag.acceptingBlock(node)
}
@@ -828,7 +810,7 @@ func TestAcceptingBlock(t *testing.T) {
}
numChainBlocks := uint32(10)
chainBlocks := make([]*appmessage.MsgBlock, numChainBlocks)
chainBlocks := make([]*domainmessage.MsgBlock, numChainBlocks)
chainBlocks[0] = dag.Params.GenesisBlock
for i := uint32(1); i <= numChainBlocks-1; i++ {
chainBlocks[i] = prepareAndProcessBlockByParentMsgBlocks(t, dag, chainBlocks[i-1])
@@ -925,6 +907,119 @@ func TestAcceptingBlock(t *testing.T) {
}
}
func TestFinalizeNodesBelowFinalityPoint(t *testing.T) {
testFinalizeNodesBelowFinalityPoint(t, true)
testFinalizeNodesBelowFinalityPoint(t, false)
}
func testFinalizeNodesBelowFinalityPoint(t *testing.T, deleteDiffData bool) {
params := dagconfig.SimnetParams
params.K = 1
dag, teardownFunc, err := DAGSetup("testFinalizeNodesBelowFinalityPoint", true, Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
blockVersion := int32(0x10000000)
blockTime := dag.genesis.Header().Timestamp
flushUTXODiffStore := func() {
dbTx, err := dag.databaseContext.NewTx()
if err != nil {
t.Fatalf("Failed to open database transaction: %s", err)
}
defer dbTx.RollbackUnlessClosed()
err = dag.utxoDiffStore.flushToDB(dbTx)
if err != nil {
t.Fatalf("Error flushing utxoDiffStore data to DB: %s", err)
}
dag.utxoDiffStore.clearDirtyEntries()
err = dbTx.Commit()
if err != nil {
t.Fatalf("Failed to commit database transaction: %s", err)
}
}
addNode := func(parent *blockNode) *blockNode {
blockTime = blockTime.Add(time.Second)
node := newTestNode(dag, blockSetFromSlice(parent), blockVersion, 0, blockTime)
node.updateParentsChildren()
dag.index.AddNode(node)
// Put dummy diff data in dag.utxoDiffStore
err := dag.utxoDiffStore.setBlockDiff(node, NewUTXODiff())
if err != nil {
t.Fatalf("setBlockDiff: %s", err)
}
flushUTXODiffStore()
return node
}
finalityInterval := dag.FinalityInterval()
nodes := make([]*blockNode, 0, finalityInterval)
currentNode := dag.genesis
nodes = append(nodes, currentNode)
for i := uint64(0); i <= finalityInterval*2; i++ {
currentNode = addNode(currentNode)
nodes = append(nodes, currentNode)
}
// Manually set the last finality point
dag.lastFinalityPoint = nodes[finalityInterval-1]
// Don't unload diffData
currentDifference := maxBlueScoreDifferenceToKeepLoaded
maxBlueScoreDifferenceToKeepLoaded = math.MaxUint64
defer func() { maxBlueScoreDifferenceToKeepLoaded = currentDifference }()
dag.finalizeNodesBelowFinalityPoint(deleteDiffData)
flushUTXODiffStore()
for _, node := range nodes[:finalityInterval-1] {
if !node.isFinalized {
t.Errorf("Node with blue score %d expected to be finalized", node.blueScore)
}
if _, ok := dag.utxoDiffStore.loaded[node]; deleteDiffData && ok {
t.Errorf("The diff data of node with blue score %d should have been unloaded if deleteDiffData is %T", node.blueScore, deleteDiffData)
} else if !deleteDiffData && !ok {
t.Errorf("The diff data of node with blue score %d shouldn't have been unloaded if deleteDiffData is %T", node.blueScore, deleteDiffData)
}
_, err := dag.utxoDiffStore.diffDataFromDB(node.hash)
exists := !dbaccess.IsNotFoundError(err)
if exists && err != nil {
t.Errorf("diffDataFromDB: %s", err)
continue
}
if deleteDiffData && exists {
t.Errorf("The diff data of node with blue score %d should have been deleted from the database if deleteDiffData is %T", node.blueScore, deleteDiffData)
continue
}
if !deleteDiffData && !exists {
t.Errorf("The diff data of node with blue score %d shouldn't have been deleted from the database if deleteDiffData is %T", node.blueScore, deleteDiffData)
continue
}
}
for _, node := range nodes[finalityInterval-1:] {
if node.isFinalized {
t.Errorf("Node with blue score %d wasn't expected to be finalized", node.blueScore)
}
if _, ok := dag.utxoDiffStore.loaded[node]; !ok {
t.Errorf("The diff data of node with blue score %d shouldn't have been unloaded", node.blueScore)
}
if diffData, err := dag.utxoDiffStore.diffDataFromDB(node.hash); err != nil {
t.Errorf("diffDataFromDB: %s", err)
} else if diffData == nil {
t.Errorf("The diff data of node with blue score %d shouldn't have been deleted from the database", node.blueScore)
}
}
}
func TestDAGIndexFailedStatus(t *testing.T) {
params := dagconfig.SimnetParams
dag, teardownFunc, err := DAGSetup("TestDAGIndexFailedStatus", true, Config{
@@ -935,17 +1030,11 @@ func TestDAGIndexFailedStatus(t *testing.T) {
}
defer teardownFunc()
// Create a block with non-finalized transaction so that it's flagged as invalid
coinbaseTx := appmessage.NewSubnetworkMsgTx(appmessage.TxVersion, []*appmessage.TxIn{},
[]*appmessage.TxOut{}, subnetworkid.SubnetworkIDCoinbase, 0, []byte{})
invalidTxIn := appmessage.NewTxIn(appmessage.NewOutpoint(dag.Params.GenesisBlock.Transactions[0].TxID(), 0), nil)
invalidTxIn.Sequence = 0
invalidTx := appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{invalidTxIn}, []*appmessage.TxOut{})
invalidTx.LockTime = math.MaxUint64
txs := []*util.Tx{util.NewTx(coinbaseTx), util.NewTx(invalidTx)}
invalidCbTx := domainmessage.NewSubnetworkMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{}, []*domainmessage.TxOut{}, subnetworkid.SubnetworkIDCoinbase, 0, []byte{})
txs := []*util.Tx{util.NewTx(invalidCbTx)}
hashMerkleRoot := BuildHashMerkleTreeStore(txs).Root()
invalidMsgBlock := appmessage.NewMsgBlock(
appmessage.NewBlockHeader(
invalidMsgBlock := domainmessage.NewMsgBlock(
domainmessage.NewBlockHeader(
1,
[]*daghash.Hash{params.GenesisHash}, hashMerkleRoot,
&daghash.Hash{},
@@ -953,8 +1042,7 @@ func TestDAGIndexFailedStatus(t *testing.T) {
dag.genesis.bits,
0),
)
invalidMsgBlock.AddTransaction(coinbaseTx)
invalidMsgBlock.AddTransaction(invalidTx)
invalidMsgBlock.AddTransaction(invalidCbTx)
invalidBlock := util.NewBlock(invalidMsgBlock)
isOrphan, isDelayed, err := dag.ProcessBlock(invalidBlock, BFNoPoWCheck)
@@ -974,17 +1062,16 @@ func TestDAGIndexFailedStatus(t *testing.T) {
if !ok {
t.Fatalf("invalidBlockNode wasn't added to the block index as expected")
}
if invalidBlockNode.status != statusValidateFailed {
if invalidBlockNode.status&statusValidateFailed != statusValidateFailed {
t.Fatalf("invalidBlockNode status to have %b flags raised (got: %b)", statusValidateFailed, invalidBlockNode.status)
}
invalidMsgBlockChild := appmessage.NewMsgBlock(
appmessage.NewBlockHeader(1, []*daghash.Hash{
invalidMsgBlockChild := domainmessage.NewMsgBlock(
domainmessage.NewBlockHeader(1, []*daghash.Hash{
invalidBlock.Hash(),
}, hashMerkleRoot, &daghash.Hash{}, &daghash.Hash{}, dag.genesis.bits, 0),
)
invalidMsgBlockChild.AddTransaction(coinbaseTx)
invalidMsgBlockChild.AddTransaction(invalidTx)
invalidMsgBlockChild.AddTransaction(invalidCbTx)
invalidBlockChild := util.NewBlock(invalidMsgBlockChild)
isOrphan, isDelayed, err = dag.ProcessBlock(invalidBlockChild, BFNoPoWCheck)
@@ -1004,17 +1091,16 @@ func TestDAGIndexFailedStatus(t *testing.T) {
if !ok {
t.Fatalf("invalidBlockChild wasn't added to the block index as expected")
}
if invalidBlockChildNode.status != statusInvalidAncestor {
if invalidBlockChildNode.status&statusInvalidAncestor != statusInvalidAncestor {
t.Fatalf("invalidBlockNode status to have %b flags raised (got %b)", statusInvalidAncestor, invalidBlockChildNode.status)
}
invalidMsgBlockGrandChild := appmessage.NewMsgBlock(
appmessage.NewBlockHeader(1, []*daghash.Hash{
invalidMsgBlockGrandChild := domainmessage.NewMsgBlock(
domainmessage.NewBlockHeader(1, []*daghash.Hash{
invalidBlockChild.Hash(),
}, hashMerkleRoot, &daghash.Hash{}, &daghash.Hash{}, dag.genesis.bits, 0),
)
invalidMsgBlockGrandChild.AddTransaction(coinbaseTx)
invalidMsgBlockGrandChild.AddTransaction(invalidTx)
invalidMsgBlockGrandChild.AddTransaction(invalidCbTx)
invalidBlockGrandChild := util.NewBlock(invalidMsgBlockGrandChild)
isOrphan, isDelayed, err = dag.ProcessBlock(invalidBlockGrandChild, BFNoPoWCheck)
@@ -1033,60 +1119,40 @@ func TestDAGIndexFailedStatus(t *testing.T) {
if !ok {
t.Fatalf("invalidBlockGrandChild wasn't added to the block index as expected")
}
if invalidBlockGrandChildNode.status != statusInvalidAncestor {
if invalidBlockGrandChildNode.status&statusInvalidAncestor != statusInvalidAncestor {
t.Fatalf("invalidBlockGrandChildNode status to have %b flags raised (got %b)", statusInvalidAncestor, invalidBlockGrandChildNode.status)
}
}
// testProcessBlockStatus submits the given block, and makes sure this block has got the expected status
func testProcessBlockStatus(
t *testing.T, testName string, dag *BlockDAG, block *appmessage.MsgBlock, expectedStatus blockStatus) {
isOrphan, isDelayed, err := dag.ProcessBlock(util.NewBlock(block), BFNoPoWCheck)
if err != nil {
t.Fatalf("%s: Error submitting block: %+v", testName, err)
func TestIsDAGCurrentMaxDiff(t *testing.T) {
netParams := []*dagconfig.Params{
&dagconfig.MainnetParams,
&dagconfig.TestnetParams,
&dagconfig.DevnetParams,
&dagconfig.RegressionNetParams,
&dagconfig.SimnetParams,
}
if isDelayed {
t.Fatalf("%s: ProcessBlock: block is too far in the future", testName)
}
if isOrphan {
t.Fatalf("%s: ProcessBlock: block got unexpectedly orphaned", testName)
}
node, ok := dag.index.LookupNode(block.BlockHash())
if !ok {
t.Fatalf("%s: Error locating block %s after processing it", testName, block.BlockHash())
}
actualStatus := dag.index.BlockNodeStatus(node)
if actualStatus != expectedStatus {
t.Errorf("%s: Expected block status: '%s' but got '%s'", testName, expectedStatus, actualStatus)
for _, params := range netParams {
if params.FinalityDuration < isDAGCurrentMaxDiff*params.TargetTimePerBlock {
t.Errorf("in %s, a DAG can be considered current even if it's below the finality point", params.Name)
}
}
}
func testProcessBlockRuleError(t *testing.T, testName string, dag *BlockDAG, block *appmessage.MsgBlock, expectedRuleErr error) {
func testProcessBlockRuleError(t *testing.T, dag *BlockDAG, block *domainmessage.MsgBlock, expectedRuleErr error) {
isOrphan, isDelayed, err := dag.ProcessBlock(util.NewBlock(block), BFNoPoWCheck)
err = checkRuleError(err, expectedRuleErr)
if err != nil {
t.Errorf("%s: checkRuleError: %s", err, testName)
t.Errorf("checkRuleError: %s", err)
}
if isDelayed {
t.Fatalf("%s: ProcessBlock: block is too far in the future", testName)
t.Fatalf("ProcessBlock: block " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("%s: ProcessBlock: block got unexpectedly orphaned", testName)
}
}
// makeNextSelectedTip plays with block's nonce until its hash is smaller than dag's selectedTip
// It is the callers responsibility to make sure block's blue score is equal to selected tip's
func makeNextSelectedTip(dag *BlockDAG, block *appmessage.MsgBlock) {
selectedTip := dag.selectedTip()
for daghash.Less(block.BlockHash(), selectedTip.hash) {
block.Header.Nonce++
t.Fatalf("ProcessBlock: block got unexpectedly orphaned")
}
}
@@ -1109,26 +1175,26 @@ func TestDoubleSpends(t *testing.T) {
if err != nil {
t.Fatalf("Failed to build signature script: %s", err)
}
txIn := &appmessage.TxIn{
PreviousOutpoint: appmessage.Outpoint{TxID: *cbTx.TxID(), Index: 0},
txIn := &domainmessage.TxIn{
PreviousOutpoint: domainmessage.Outpoint{TxID: *cbTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: appmessage.MaxTxInSequenceNum,
Sequence: domainmessage.MaxTxInSequenceNum,
}
txOut := &appmessage.TxOut{
txOut := &domainmessage.TxOut{
ScriptPubKey: OpTrueScript,
Value: uint64(1),
}
tx1 := appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn}, []*appmessage.TxOut{txOut})
tx1 := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn}, []*domainmessage.TxOut{txOut})
doubleSpendTxOut := &appmessage.TxOut{
doubleSpendTxOut := &domainmessage.TxOut{
ScriptPubKey: OpTrueScript,
Value: uint64(2),
}
doubleSpendTx1 := appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn}, []*appmessage.TxOut{doubleSpendTxOut})
doubleSpendTx1 := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn}, []*domainmessage.TxOut{doubleSpendTxOut})
blockWithTx1 := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{fundingBlock.BlockHash()}, []*appmessage.MsgTx{tx1})
blockWithTx1 := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{fundingBlock.BlockHash()}, []*domainmessage.MsgTx{tx1})
// Check that a block will be disqualified if it has a transaction that already exists in its past.
// Check that a block will be rejected if it has a transaction that already exists in its past.
anotherBlockWithTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{blockWithTx1.BlockHash()}, nil)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
@@ -1142,9 +1208,9 @@ func TestDoubleSpends(t *testing.T) {
}
anotherBlockWithTx1.Header.HashMerkleRoot = BuildHashMerkleTreeStore(anotherBlockWithTx1UtilTxs).Root()
testProcessBlockStatus(t, "anotherBlockWithTx1", dag, anotherBlockWithTx1, statusDisqualifiedFromChain)
testProcessBlockRuleError(t, dag, anotherBlockWithTx1, ruleError(ErrOverwriteTx, ""))
// Check that a block will be disqualified if it has a transaction that double spends
// Check that a block will be rejected if it has a transaction that double spends
// a transaction from its past.
blockWithDoubleSpendForTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{blockWithTx1.BlockHash()}, nil)
if err != nil {
@@ -1159,18 +1225,16 @@ func TestDoubleSpends(t *testing.T) {
}
blockWithDoubleSpendForTx1.Header.HashMerkleRoot = BuildHashMerkleTreeStore(blockWithDoubleSpendForTx1UtilTxs).Root()
testProcessBlockStatus(t, "blockWithDoubleSpendForTx1", dag, blockWithDoubleSpendForTx1, statusDisqualifiedFromChain)
testProcessBlockRuleError(t, dag, blockWithDoubleSpendForTx1, ruleError(ErrMissingTxOut, ""))
blockInAnticoneOfBlockWithTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{fundingBlock.BlockHash()}, []*appmessage.MsgTx{doubleSpendTx1})
blockInAnticoneOfBlockWithTx1, err := PrepareBlockForTest(dag, []*daghash.Hash{fundingBlock.BlockHash()}, []*domainmessage.MsgTx{doubleSpendTx1})
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
makeNextSelectedTip(dag, blockInAnticoneOfBlockWithTx1)
// Check that a block will not get disqualified if it has a transaction that double spends
// Check that a block will not get rejected if it has a transaction that double spends
// a transaction from its anticone.
testProcessBlockStatus(t, "blockInAnticoneOfBlockWithTx1", dag, blockInAnticoneOfBlockWithTx1, statusValid)
testProcessBlockRuleError(t, dag, blockInAnticoneOfBlockWithTx1, nil)
// Check that a block will be rejected if it has two transactions that spend the same UTXO.
blockWithDoubleSpendWithItself, err := PrepareBlockForTest(dag, []*daghash.Hash{fundingBlock.BlockHash()}, nil)
@@ -1186,8 +1250,7 @@ func TestDoubleSpends(t *testing.T) {
}
blockWithDoubleSpendWithItself.Header.HashMerkleRoot = BuildHashMerkleTreeStore(blockWithDoubleSpendWithItselfUtilTxs).Root()
testProcessBlockRuleError(t, "blockWithDoubleSpendWithItself", dag,
blockWithDoubleSpendWithItself, ruleError(ErrDoubleSpendInSameBlock, ""))
testProcessBlockRuleError(t, dag, blockWithDoubleSpendWithItself, ruleError(ErrDoubleSpendInSameBlock, ""))
// Check that a block will be rejected if it has the same transaction twice.
blockWithDuplicateTransaction, err := PrepareBlockForTest(dag, []*daghash.Hash{fundingBlock.BlockHash()}, nil)
@@ -1202,8 +1265,7 @@ func TestDoubleSpends(t *testing.T) {
blockWithDuplicateTransactionUtilTxs[i] = util.NewTx(tx)
}
blockWithDuplicateTransaction.Header.HashMerkleRoot = BuildHashMerkleTreeStore(blockWithDuplicateTransactionUtilTxs).Root()
testProcessBlockRuleError(t, "blockWithDuplicateTransaction", dag,
blockWithDuplicateTransaction, ruleError(ErrDuplicateTx, ""))
testProcessBlockRuleError(t, dag, blockWithDuplicateTransaction, ruleError(ErrDuplicateTx, ""))
}
func TestUTXOCommitment(t *testing.T) {
@@ -1220,7 +1282,7 @@ func TestUTXOCommitment(t *testing.T) {
resetExtraNonceForTest()
createTx := func(txToSpend *appmessage.MsgTx) *appmessage.MsgTx {
createTx := func(txToSpend *domainmessage.MsgTx) *domainmessage.MsgTx {
scriptPubKey, err := txscript.PayToScriptHashScript(OpTrueScript)
if err != nil {
t.Fatalf("TestUTXOCommitment: failed to build script pub key: %s", err)
@@ -1229,16 +1291,16 @@ func TestUTXOCommitment(t *testing.T) {
if err != nil {
t.Fatalf("TestUTXOCommitment: failed to build signature script: %s", err)
}
txIn := &appmessage.TxIn{
PreviousOutpoint: appmessage.Outpoint{TxID: *txToSpend.TxID(), Index: 0},
txIn := &domainmessage.TxIn{
PreviousOutpoint: domainmessage.Outpoint{TxID: *txToSpend.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: appmessage.MaxTxInSequenceNum,
Sequence: domainmessage.MaxTxInSequenceNum,
}
txOut := &appmessage.TxOut{
txOut := &domainmessage.TxOut{
ScriptPubKey: scriptPubKey,
Value: uint64(1),
}
return appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn}, []*appmessage.TxOut{txOut})
return domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn}, []*domainmessage.TxOut{txOut})
}
// Build the following DAG:
@@ -1254,12 +1316,12 @@ func TestUTXOCommitment(t *testing.T) {
// Block C:
txSpendBlockACoinbase := createTx(blockA.Transactions[0])
blockCTxs := []*appmessage.MsgTx{txSpendBlockACoinbase}
blockCTxs := []*domainmessage.MsgTx{txSpendBlockACoinbase}
blockC := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{blockA.BlockHash()}, blockCTxs)
// Block D:
txSpendTxInBlockC := createTx(txSpendBlockACoinbase)
blockDTxs := []*appmessage.MsgTx{txSpendTxInBlockC}
blockDTxs := []*domainmessage.MsgTx{txSpendTxInBlockC}
blockD := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{blockB.BlockHash(), blockC.BlockHash()}, blockDTxs)
// Get the pastUTXO of blockD
@@ -1327,7 +1389,7 @@ func TestPastUTXOMultiSet(t *testing.T) {
if !ok {
t.Fatalf("TestPastUTXOMultiSet: blockNode for blockC not found")
}
blockCSelectedParentMultiset, err := blockNodeC.selectedParentMultiset()
blockCSelectedParentMultiset, err := blockNodeC.selectedParentMultiset(dag)
if err != nil {
t.Fatalf("TestPastUTXOMultiSet: selectedParentMultiset unexpectedly failed: %s", err)
}
@@ -1340,7 +1402,7 @@ func TestPastUTXOMultiSet(t *testing.T) {
PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{blockC.BlockHash()}, nil)
// Get blockC's selectedParentMultiset again
blockCSelectedParentMultiSetAfterAnotherBlock, err := blockNodeC.selectedParentMultiset()
blockCSelectedParentMultiSetAfterAnotherBlock, err := blockNodeC.selectedParentMultiset(dag)
if err != nil {
t.Fatalf("TestPastUTXOMultiSet: selectedParentMultiset unexpectedly failed: %s", err)
}

View File

@@ -11,11 +11,11 @@ import (
"fmt"
"io"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/binaryserializer"
"github.com/kaspanet/kaspad/util/daghash"
@@ -49,29 +49,22 @@ func IsNotInDAGErr(err error) bool {
// keys will be iterated in an ascending order by the outpoint index.
var outpointIndexByteOrder = binary.BigEndian
func serializeOutpoint(w io.Writer, outpoint *appmessage.Outpoint) error {
func serializeOutpoint(w io.Writer, outpoint *domainmessage.Outpoint) error {
_, err := w.Write(outpoint.TxID[:])
if err != nil {
return err
}
var buf [4]byte
outpointIndexByteOrder.PutUint32(buf[:], outpoint.Index)
_, err = w.Write(buf[:])
if err != nil {
return errors.WithStack(err)
}
return nil
return binaryserializer.PutUint32(w, outpointIndexByteOrder, outpoint.Index)
}
var outpointSerializeSize = daghash.TxIDSize + 4
// deserializeOutpoint decodes an outpoint from the passed serialized byte
// slice into a new appmessage.Outpoint using a format that is suitable for long-
// slice into a new domainmessage.Outpoint using a format that is suitable for long-
// term storage. This format is described in detail above.
func deserializeOutpoint(r io.Reader) (*appmessage.Outpoint, error) {
outpoint := &appmessage.Outpoint{}
func deserializeOutpoint(r io.Reader) (*domainmessage.Outpoint, error) {
outpoint := &domainmessage.Outpoint{}
_, err := r.Read(outpoint.TxID[:])
if err != nil {
return nil, err
@@ -133,10 +126,9 @@ func updateUTXOSet(dbContext dbaccess.Context, virtualUTXODiff *UTXODiff) error
}
type dagState struct {
TipHashes []*daghash.Hash
VirtualParentsHashes []*daghash.Hash
ValidTipHashes []*daghash.Hash
LocalSubnetworkID *subnetworkid.SubnetworkID
TipHashes []*daghash.Hash
LastFinalityPoint *daghash.Hash
LocalSubnetworkID *subnetworkid.SubnetworkID
}
// serializeDAGState returns the serialization of the DAG state.
@@ -173,10 +165,9 @@ func saveDAGState(dbContext dbaccess.Context, state *dagState) error {
// genesis block and the node's local subnetwork id.
func (dag *BlockDAG) createDAGState(localSubnetworkID *subnetworkid.SubnetworkID) error {
return saveDAGState(dag.databaseContext, &dagState{
TipHashes: []*daghash.Hash{dag.Params.GenesisHash},
VirtualParentsHashes: []*daghash.Hash{dag.Params.GenesisHash},
ValidTipHashes: []*daghash.Hash{dag.Params.GenesisHash},
LocalSubnetworkID: localSubnetworkID,
TipHashes: []*daghash.Hash{dag.Params.GenesisHash},
LastFinalityPoint: dag.Params.GenesisHash,
LocalSubnetworkID: localSubnetworkID,
})
}
@@ -236,11 +227,20 @@ func (dag *BlockDAG) initDAGState() error {
}
log.Debugf("Applying the stored tips to the virtual block...")
err = dag.initTipsAndVirtualParents(dagState)
err = dag.initVirtualBlockTips(dagState)
if err != nil {
return err
}
log.Debugf("Setting the last finality point...")
var ok bool
dag.lastFinalityPoint, ok = dag.index.LookupNode(dagState.LastFinalityPoint)
if !ok {
return errors.Errorf("finality point block %s "+
"does not exist in the DAG", dagState.LastFinalityPoint)
}
dag.finalizeNodesBelowFinalityPoint(false)
log.Debugf("Processing unprocessed blockNodes...")
err = dag.processUnprocessedBlockNodes(unprocessedBlockNodes)
if err != nil {
@@ -350,28 +350,17 @@ func (dag *BlockDAG) initUTXOSet() (fullUTXOCollection utxoCollection, err error
return fullUTXOCollection, nil
}
func (dag *BlockDAG) initTipsAndVirtualParents(state *dagState) error {
tips, err := dag.index.LookupNodes(state.TipHashes)
if err != nil {
return errors.Wrapf(err, "Error loading tips")
func (dag *BlockDAG) initVirtualBlockTips(state *dagState) error {
tips := newBlockSet()
for _, tipHash := range state.TipHashes {
tip, ok := dag.index.LookupNode(tipHash)
if !ok {
return errors.Errorf("cannot find "+
"DAG tip %s in block index", state.TipHashes)
}
tips.add(tip)
}
dag.tips = blockSetFromSlice(tips...)
validTips, err := dag.index.LookupNodes(state.ValidTipHashes)
if err != nil {
return errors.Wrapf(err, "Error loading tips")
}
dag.validTips = blockSetFromSlice(validTips...)
virtualParents, err := dag.index.LookupNodes(state.VirtualParentsHashes)
if err != nil {
return errors.Wrapf(err, "Error loading tips")
}
dag.virtual.blockNode, _ = dag.newBlockNode(nil, blockSetFromSlice(virtualParents...))
// call updateSelectedParentSet with genesis as oldSelectedParent, so that the selectedParentSet is fully calculated
_ = dag.virtual.updateSelectedParentSet(dag.genesis)
dag.virtual.SetTips(tips)
return nil
}
@@ -421,14 +410,13 @@ func (dag *BlockDAG) processUnprocessedBlockNodes(unprocessedBlockNodes []*block
func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
buffer := bytes.NewReader(blockRow)
var header appmessage.BlockHeader
var header domainmessage.BlockHeader
err := header.Deserialize(buffer)
if err != nil {
return nil, err
}
node := &blockNode{
dag: dag,
hash: header.BlockHash(),
version: header.Version,
bits: header.Bits,
@@ -476,7 +464,7 @@ func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
return nil, err
}
bluesCount, err := appmessage.ReadVarInt(buffer)
bluesCount, err := domainmessage.ReadVarInt(buffer)
if err != nil {
return nil, err
}
@@ -495,26 +483,7 @@ func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
}
}
redsCount, err := appmessage.ReadVarInt(buffer)
if err != nil {
return nil, err
}
node.reds = make([]*blockNode, redsCount)
for i := uint64(0); i < redsCount; i++ {
hash := &daghash.Hash{}
if _, err := io.ReadFull(buffer, hash[:]); err != nil {
return nil, err
}
var ok bool
node.reds[i], ok = dag.index.LookupNode(hash)
if !ok {
return nil, errors.Errorf("block %s does not exist in the DAG", selectedParentHash)
}
}
bluesAnticoneSizesLen, err := appmessage.ReadVarInt(buffer)
bluesAnticoneSizesLen, err := domainmessage.ReadVarInt(buffer)
if err != nil {
return nil, err
}
@@ -558,7 +527,7 @@ func storeBlock(dbContext *dbaccess.TxContext, block *util.Block) error {
}
func serializeBlockNode(node *blockNode) ([]byte, error) {
w := bytes.NewBuffer(make([]byte, 0, appmessage.MaxBlockHeaderPayload+1))
w := bytes.NewBuffer(make([]byte, 0, domainmessage.MaxBlockHeaderPayload+1))
header := node.Header()
err := header.Serialize(w)
if err != nil {
@@ -585,7 +554,7 @@ func serializeBlockNode(node *blockNode) ([]byte, error) {
return nil, err
}
err = appmessage.WriteVarInt(w, uint64(len(node.blues)))
err = domainmessage.WriteVarInt(w, uint64(len(node.blues)))
if err != nil {
return nil, err
}
@@ -597,19 +566,7 @@ func serializeBlockNode(node *blockNode) ([]byte, error) {
}
}
err = appmessage.WriteVarInt(w, uint64(len(node.reds)))
if err != nil {
return nil, err
}
for _, red := range node.reds {
_, err = w.Write(red.hash[:])
if err != nil {
return nil, err
}
}
err = appmessage.WriteVarInt(w, uint64(len(node.bluesAnticoneSizes)))
err = domainmessage.WriteVarInt(w, uint64(len(node.bluesAnticoneSizes)))
if err != nil {
return nil, err
}
@@ -705,16 +662,3 @@ func (dag *BlockDAG) BlockHashesFrom(lowHash *daghash.Hash, limit int) ([]*dagha
return blockHashes, nil
}
func (dag *BlockDAG) fetchBlueBlocks(node *blockNode) ([]*util.Block, error) {
blueBlocks := make([]*util.Block, len(node.blues))
for i, blueBlockNode := range node.blues {
blueBlock, err := dag.fetchBlockByHash(blueBlockNode.hash)
if err != nil {
return nil, err
}
blueBlocks[i] = blueBlock
}
return blueBlocks, nil
}

View File

@@ -7,11 +7,10 @@ package blockdag
import (
"bytes"
"encoding/hex"
"github.com/pkg/errors"
"reflect"
"testing"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/util/daghash"
)
@@ -186,20 +185,18 @@ func TestDAGStateSerialization(t *testing.T) {
{
name: "genesis",
state: &dagState{
TipHashes: []*daghash.Hash{newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
ValidTipHashes: []*daghash.Hash{newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
VirtualParentsHashes: []*daghash.Hash{newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
TipHashes: []*daghash.Hash{newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
LastFinalityPoint: newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
},
serialized: []byte(`{"TipHashes":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],"VirtualParentsHashes":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],"ValidTipHashes":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],"LocalSubnetworkID":null}`),
serialized: []byte("{\"TipHashes\":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0],\"LocalSubnetworkID\":null}"),
},
{
name: "block 1",
state: &dagState{
TipHashes: []*daghash.Hash{newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
ValidTipHashes: []*daghash.Hash{newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
VirtualParentsHashes: []*daghash.Hash{newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
TipHashes: []*daghash.Hash{newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
LastFinalityPoint: newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
},
serialized: []byte(`{"TipHashes":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],"VirtualParentsHashes":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],"ValidTipHashes":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],"LocalSubnetworkID":null}`),
serialized: []byte("{\"TipHashes\":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0],\"LocalSubnetworkID\":null}"),
},
}

View File

@@ -15,7 +15,7 @@ import (
// block given its bluest parent.
func (dag *BlockDAG) requiredDifficulty(bluestParent *blockNode, newBlockTime mstime.Time) uint32 {
// Genesis block.
if dag.Params.DisableDifficultyAdjustment || bluestParent == nil || bluestParent.blueScore < dag.difficultyAdjustmentWindowSize+1 {
if bluestParent == nil || bluestParent.blueScore < dag.difficultyAdjustmentWindowSize+1 {
return dag.powMaxBits
}

View File

@@ -5,13 +5,12 @@
package blockdag
import (
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/mstime"
"math/big"
"testing"
"time"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/kaspanet/kaspad/util"
)
@@ -81,7 +80,7 @@ func TestCalcWork(t *testing.T) {
}
func TestDifficulty(t *testing.T) {
params := dagconfig.MainnetParams
params := dagconfig.SimnetParams
params.K = 1
params.DifficultyAdjustmentWindowSize = 264
dag, teardownFunc, err := DAGSetup("TestDifficulty", true, Config{
@@ -136,7 +135,7 @@ func TestDifficulty(t *testing.T) {
t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
}
}
nodeInThePast := addNode(blockSetFromSlice(tip), tip.PastMedianTime())
nodeInThePast := addNode(blockSetFromSlice(tip), tip.PastMedianTime(dag))
if nodeInThePast.bits != tip.bits {
t.Fatalf("The difficulty should only change when nodeInThePast is in the past of a block bluest parent")
}
@@ -158,7 +157,7 @@ func TestDifficulty(t *testing.T) {
// Increase block rate to increase difficulty
for i := uint64(0); i < dag.difficultyAdjustmentWindowSize; i++ {
tip = addNode(blockSetFromSlice(tip), tip.PastMedianTime())
tip = addNode(blockSetFromSlice(tip), tip.PastMedianTime(dag))
if compareBits(tip.bits, tip.parents.bluest().bits) > 0 {
t.Fatalf("Because we're increasing the block rate, the difficulty can't decrease")
}
@@ -204,7 +203,7 @@ func TestDifficulty(t *testing.T) {
redChainTip := splitNode
for i := 0; i < 10; i++ {
redChainTip = addNode(blockSetFromSlice(redChainTip), redChainTip.PastMedianTime())
redChainTip = addNode(blockSetFromSlice(redChainTip), redChainTip.PastMedianTime(dag))
}
tipWithRedPast := addNode(blockSetFromSlice(redChainTip, blueTip), zeroTime)
tipWithoutRedPast := addNode(blockSetFromSlice(blueTip), zeroTime)

View File

@@ -203,9 +203,6 @@ const (
// is also an ancestor of another parent
ErrInvalidParentsRelation
// ErrTooManyParents indicates that a block points to more then `MaxNumParentBlocks` parents
ErrTooManyParents
// ErrDelayedBlockIsNotAllowed indicates that a block with a delayed timestamp was
// submitted with BFDisallowDelay flag raised.
ErrDelayedBlockIsNotAllowed
@@ -213,20 +210,6 @@ const (
// ErrOrphanBlockIsNotAllowed indicates that an orphan block was submitted with
// BFDisallowOrphans flag raised.
ErrOrphanBlockIsNotAllowed
// ErrViolatingBoundedMergeDepth indicates that a block is violating finality from
// its own point of view
ErrViolatingBoundedMergeDepth
// ErrViolatingMergeLimit indicates that a block merges more than mergeLimit blocks
ErrViolatingMergeLimit
// ErrChainedTransactions indicates that a block contains a transaction that spends an output of a transaction
// In the same block
ErrChainedTransactions
// ErrSelectedParentDisqualifiedFromChain indicates that a block's selectedParent has the status DisqualifiedFromChain
ErrSelectedParentDisqualifiedFromChain
)
// Map of ErrorCode values back to their constant names for pretty printing.

View File

@@ -13,15 +13,158 @@ import (
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/testtools"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/txscript"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/mining"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
)
// TestFinality checks that the finality mechanism works as expected.
// This is how the flow goes:
// 1) We build a chain of params.FinalityInterval blocks and call its tip altChainTip.
// 2) We build another chain (let's call it mainChain) of 2 * params.FinalityInterval
// blocks, which points to genesis, and then we check that the block in that
// chain with height of params.FinalityInterval is marked as finality point (This is
// very predictable, because the blue score of each new block in a chain is the
// parents plus one).
// 3) We make a new child to block with height (2 * params.FinalityInterval - 1)
// in mainChain, and we check that connecting it to the DAG
// doesn't affect the last finality point.
// 4) We make a block that points to genesis, and check that it
// gets rejected because its blue score is lower then the last finality
// point.
// 5) We make a block that points to altChainTip, and check that it
// gets rejected because it doesn't have the last finality point in
// its selected parent chain.
func TestFinality(t *testing.T) {
params := dagconfig.SimnetParams
params.K = 1
params.FinalityDuration = 100 * params.TargetTimePerBlock
dag, teardownFunc, err := blockdag.DAGSetup("TestFinality", true, blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
buildNodeToDag := func(parentHashes []*daghash.Hash) (*util.Block, error) {
msgBlock, err := mining.PrepareBlockForTest(dag, parentHashes, nil, false)
if err != nil {
return nil, err
}
block := util.NewBlock(msgBlock)
isOrphan, isDelayed, err := dag.ProcessBlock(block, blockdag.BFNoPoWCheck)
if err != nil {
return nil, err
}
if isDelayed {
return nil, errors.Errorf("ProcessBlock: block " +
"is too far in the future")
}
if isOrphan {
return nil, errors.Errorf("ProcessBlock: unexpected returned orphan block")
}
return block, nil
}
genesis := util.NewBlock(params.GenesisBlock)
currentNode := genesis
// First we build a chain of params.FinalityInterval blocks for future use
for i := uint64(0); i < dag.FinalityInterval(); i++ {
currentNode, err = buildNodeToDag([]*daghash.Hash{currentNode.Hash()})
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
}
altChainTip := currentNode
// Now we build a new chain of 2 * params.FinalityInterval blocks, pointed to genesis, and
// we expect the block with height 1 * params.FinalityInterval to be the last finality point
currentNode = genesis
for i := uint64(0); i < dag.FinalityInterval(); i++ {
currentNode, err = buildNodeToDag([]*daghash.Hash{currentNode.Hash()})
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
}
expectedFinalityPoint := currentNode
for i := uint64(0); i < dag.FinalityInterval(); i++ {
currentNode, err = buildNodeToDag([]*daghash.Hash{currentNode.Hash()})
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
}
if !dag.LastFinalityPointHash().IsEqual(expectedFinalityPoint.Hash()) {
t.Errorf("TestFinality: dag.lastFinalityPoint expected to be %v but got %v", expectedFinalityPoint, dag.LastFinalityPointHash())
}
// Here we check that even if we create a parallel tip (a new tip with
// the same parents as the current one) with the same blue score as the
// current tip, it still won't affect the last finality point.
_, err = buildNodeToDag(currentNode.MsgBlock().Header.ParentHashes)
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
if !dag.LastFinalityPointHash().IsEqual(expectedFinalityPoint.Hash()) {
t.Errorf("TestFinality: dag.lastFinalityPoint was unexpectly changed")
}
// Here we check that a block with lower blue score than the last finality
// point will get rejected
fakeCoinbaseTx, err := dag.NextBlockCoinbaseTransaction(nil, nil)
if err != nil {
t.Errorf("NextBlockCoinbaseTransaction: %s", err)
}
merkleRoot := blockdag.BuildHashMerkleTreeStore([]*util.Tx{fakeCoinbaseTx}).Root()
beforeFinalityBlock := domainmessage.NewMsgBlock(&domainmessage.BlockHeader{
Version: 0x10000000,
ParentHashes: []*daghash.Hash{genesis.Hash()},
HashMerkleRoot: merkleRoot,
AcceptedIDMerkleRoot: &daghash.ZeroHash,
UTXOCommitment: &daghash.ZeroHash,
Timestamp: dag.SelectedTipHeader().Timestamp,
Bits: genesis.MsgBlock().Header.Bits,
})
beforeFinalityBlock.AddTransaction(fakeCoinbaseTx.MsgTx())
_, _, err = dag.ProcessBlock(util.NewBlock(beforeFinalityBlock), blockdag.BFNoPoWCheck)
if err == nil {
t.Errorf("TestFinality: buildNodeToDag expected an error but got <nil>")
}
var ruleErr blockdag.RuleError
if errors.As(err, &ruleErr) {
if ruleErr.ErrorCode != blockdag.ErrFinality {
t.Errorf("TestFinality: buildNodeToDag expected an error with code %v but instead got %v", blockdag.ErrFinality, ruleErr.ErrorCode)
}
} else {
t.Errorf("TestFinality: buildNodeToDag got unexpected error: %v", err)
}
// Here we check that a block that doesn't have the last finality point in
// its selected parent chain will get rejected
_, err = buildNodeToDag([]*daghash.Hash{altChainTip.Hash()})
if err == nil {
t.Errorf("TestFinality: buildNodeToDag expected an error but got <nil>")
}
if errors.As(err, &ruleErr) {
if ruleErr.ErrorCode != blockdag.ErrFinality {
t.Errorf("TestFinality: buildNodeToDag expected an error with code %v but instead got %v", blockdag.ErrFinality, ruleErr.ErrorCode)
}
} else {
t.Errorf("TestFinality: buildNodeToDag got unexpected error: %v", ruleErr)
}
}
// TestFinalityInterval tests that the finality interval is
// smaller then appmessage.MaxInvPerMsg, so when a peer receives
// smaller then domainmessage.MaxInvPerMsg, so when a peer receives
// a getblocks message it should always be able to send
// all the necessary invs.
func TestFinalityInterval(t *testing.T) {
@@ -29,6 +172,7 @@ func TestFinalityInterval(t *testing.T) {
&dagconfig.MainnetParams,
&dagconfig.TestnetParams,
&dagconfig.DevnetParams,
&dagconfig.RegressionNetParams,
&dagconfig.SimnetParams,
}
for _, params := range netParams {
@@ -41,8 +185,8 @@ func TestFinalityInterval(t *testing.T) {
}
defer teardownFunc()
if dag.FinalityInterval() > appmessage.MaxInvPerMsg {
t.Errorf("FinalityInterval in %s should be lower or equal to appmessage.MaxInvPerMsg", params.Name)
if dag.FinalityInterval() > domainmessage.MaxInvPerMsg {
t.Errorf("FinalityInterval in %s should be lower or equal to domainmessage.MaxInvPerMsg", params.Name)
}
}()
}
@@ -88,7 +232,7 @@ func TestChainedTransactions(t *testing.T) {
}
defer teardownFunc()
block1, err := blockdag.PrepareBlockForTest(dag, []*daghash.Hash{params.GenesisHash}, nil)
block1, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{params.GenesisHash}, nil, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -109,34 +253,34 @@ func TestChainedTransactions(t *testing.T) {
if err != nil {
t.Fatalf("Failed to build signature script: %s", err)
}
txIn := &appmessage.TxIn{
PreviousOutpoint: appmessage.Outpoint{TxID: *cbTx.TxID(), Index: 0},
txIn := &domainmessage.TxIn{
PreviousOutpoint: domainmessage.Outpoint{TxID: *cbTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: appmessage.MaxTxInSequenceNum,
Sequence: domainmessage.MaxTxInSequenceNum,
}
txOut := &appmessage.TxOut{
txOut := &domainmessage.TxOut{
ScriptPubKey: blockdag.OpTrueScript,
Value: uint64(1),
}
tx := appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn}, []*appmessage.TxOut{txOut})
tx := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn}, []*domainmessage.TxOut{txOut})
chainedTxIn := &appmessage.TxIn{
PreviousOutpoint: appmessage.Outpoint{TxID: *tx.TxID(), Index: 0},
chainedTxIn := &domainmessage.TxIn{
PreviousOutpoint: domainmessage.Outpoint{TxID: *tx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: appmessage.MaxTxInSequenceNum,
Sequence: domainmessage.MaxTxInSequenceNum,
}
scriptPubKey, err := txscript.PayToScriptHashScript(blockdag.OpTrueScript)
if err != nil {
t.Fatalf("Failed to build public key script: %s", err)
}
chainedTxOut := &appmessage.TxOut{
chainedTxOut := &domainmessage.TxOut{
ScriptPubKey: scriptPubKey,
Value: uint64(1),
}
chainedTx := appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{chainedTxIn}, []*appmessage.TxOut{chainedTxOut})
chainedTx := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{chainedTxIn}, []*domainmessage.TxOut{chainedTxOut})
block2, err := blockdag.PrepareBlockForTest(dag, []*daghash.Hash{block1.BlockHash()}, []*appmessage.MsgTx{tx})
block2, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{block1.BlockHash()}, []*domainmessage.MsgTx{tx}, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -156,8 +300,8 @@ func TestChainedTransactions(t *testing.T) {
} else {
var ruleErr blockdag.RuleError
if ok := errors.As(err, &ruleErr); ok {
if ruleErr.ErrorCode != blockdag.ErrChainedTransactions {
t.Errorf("ProcessBlock expected an %v error code but got %v", blockdag.ErrChainedTransactions, ruleErr.ErrorCode)
if ruleErr.ErrorCode != blockdag.ErrMissingTxOut {
t.Errorf("ProcessBlock expected an %v error code but got %v", blockdag.ErrMissingTxOut, ruleErr.ErrorCode)
}
} else {
t.Errorf("ProcessBlock expected a blockdag.RuleError but got %v", err)
@@ -171,18 +315,18 @@ func TestChainedTransactions(t *testing.T) {
t.Errorf("ProcessBlock: block2 got unexpectedly orphaned")
}
nonChainedTxIn := &appmessage.TxIn{
PreviousOutpoint: appmessage.Outpoint{TxID: *cbTx.TxID(), Index: 0},
nonChainedTxIn := &domainmessage.TxIn{
PreviousOutpoint: domainmessage.Outpoint{TxID: *cbTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: appmessage.MaxTxInSequenceNum,
Sequence: domainmessage.MaxTxInSequenceNum,
}
nonChainedTxOut := &appmessage.TxOut{
nonChainedTxOut := &domainmessage.TxOut{
ScriptPubKey: scriptPubKey,
Value: uint64(1),
}
nonChainedTx := appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{nonChainedTxIn}, []*appmessage.TxOut{nonChainedTxOut})
nonChainedTx := domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{nonChainedTxIn}, []*domainmessage.TxOut{nonChainedTxOut})
block3, err := blockdag.PrepareBlockForTest(dag, []*daghash.Hash{block1.BlockHash()}, []*appmessage.MsgTx{nonChainedTx})
block3, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{block1.BlockHash()}, []*domainmessage.MsgTx{nonChainedTx}, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -219,29 +363,29 @@ func TestOrderInDiffFromAcceptanceData(t *testing.T) {
createBlock := func(previousBlock *util.Block) *util.Block {
// Prepare a transaction that spends the previous block's coinbase transaction
var txs []*appmessage.MsgTx
var txs []*domainmessage.MsgTx
if !previousBlock.IsGenesis() {
previousCoinbaseTx := previousBlock.MsgBlock().Transactions[0]
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
t.Fatalf("TestOrderInDiffFromAcceptanceData: Failed to build signature script: %s", err)
}
txIn := &appmessage.TxIn{
PreviousOutpoint: appmessage.Outpoint{TxID: *previousCoinbaseTx.TxID(), Index: 0},
txIn := &domainmessage.TxIn{
PreviousOutpoint: domainmessage.Outpoint{TxID: *previousCoinbaseTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: appmessage.MaxTxInSequenceNum,
Sequence: domainmessage.MaxTxInSequenceNum,
}
txOut := &appmessage.TxOut{
txOut := &domainmessage.TxOut{
ScriptPubKey: blockdag.OpTrueScript,
Value: uint64(1),
}
txs = append(txs, appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn}, []*appmessage.TxOut{txOut}))
txs = append(txs, domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn}, []*domainmessage.TxOut{txOut}))
}
// Create the block
msgBlock, err := blockdag.PrepareBlockForTest(dag, []*daghash.Hash{previousBlock.Hash()}, txs)
msgBlock, err := mining.PrepareBlockForTest(dag, []*daghash.Hash{previousBlock.Hash()}, txs, false)
if err != nil {
t.Fatalf("TestOrderInDiffFromAcceptanceData: Failed to prepare block: %+v", err)
t.Fatalf("TestOrderInDiffFromAcceptanceData: Failed to prepare block: %s", err)
}
// Add the block to the DAG
@@ -294,9 +438,9 @@ func TestGasLimit(t *testing.T) {
t.Fatalf("could not register network: %s", err)
}
cbTxs := []*appmessage.MsgTx{}
cbTxs := []*domainmessage.MsgTx{}
for i := 0; i < 4; i++ {
fundsBlock, err := blockdag.PrepareBlockForTest(dag, dag.VirtualParentHashes(), nil)
fundsBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), nil, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -325,30 +469,30 @@ func TestGasLimit(t *testing.T) {
t.Fatalf("Failed to build public key script: %s", err)
}
tx1In := &appmessage.TxIn{
PreviousOutpoint: *appmessage.NewOutpoint(cbTxs[0].TxID(), 0),
Sequence: appmessage.MaxTxInSequenceNum,
tx1In := &domainmessage.TxIn{
PreviousOutpoint: *domainmessage.NewOutpoint(cbTxs[0].TxID(), 0),
Sequence: domainmessage.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
tx1Out := &appmessage.TxOut{
tx1Out := &domainmessage.TxOut{
Value: cbTxs[0].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
tx1 := appmessage.NewSubnetworkMsgTx(appmessage.TxVersion, []*appmessage.TxIn{tx1In}, []*appmessage.TxOut{tx1Out}, subnetworkID, 10000, []byte{})
tx1 := domainmessage.NewSubnetworkMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{tx1In}, []*domainmessage.TxOut{tx1Out}, subnetworkID, 10000, []byte{})
tx2In := &appmessage.TxIn{
PreviousOutpoint: *appmessage.NewOutpoint(cbTxs[1].TxID(), 0),
Sequence: appmessage.MaxTxInSequenceNum,
tx2In := &domainmessage.TxIn{
PreviousOutpoint: *domainmessage.NewOutpoint(cbTxs[1].TxID(), 0),
Sequence: domainmessage.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
tx2Out := &appmessage.TxOut{
tx2Out := &domainmessage.TxOut{
Value: cbTxs[1].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
tx2 := appmessage.NewSubnetworkMsgTx(appmessage.TxVersion, []*appmessage.TxIn{tx2In}, []*appmessage.TxOut{tx2Out}, subnetworkID, 10000, []byte{})
tx2 := domainmessage.NewSubnetworkMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{tx2In}, []*domainmessage.TxOut{tx2Out}, subnetworkID, 10000, []byte{})
// Here we check that we can't process a block that has transactions that exceed the gas limit
overLimitBlock, err := blockdag.PrepareBlockForTest(dag, dag.VirtualParentHashes(), []*appmessage.MsgTx{tx1, tx2})
overLimitBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*domainmessage.MsgTx{tx1, tx2}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -370,20 +514,20 @@ func TestGasLimit(t *testing.T) {
t.Fatalf("ProcessBlock: overLimitBlock got unexpectedly orphan")
}
overflowGasTxIn := &appmessage.TxIn{
PreviousOutpoint: *appmessage.NewOutpoint(cbTxs[2].TxID(), 0),
Sequence: appmessage.MaxTxInSequenceNum,
overflowGasTxIn := &domainmessage.TxIn{
PreviousOutpoint: *domainmessage.NewOutpoint(cbTxs[2].TxID(), 0),
Sequence: domainmessage.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
overflowGasTxOut := &appmessage.TxOut{
overflowGasTxOut := &domainmessage.TxOut{
Value: cbTxs[2].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
overflowGasTx := appmessage.NewSubnetworkMsgTx(appmessage.TxVersion, []*appmessage.TxIn{overflowGasTxIn}, []*appmessage.TxOut{overflowGasTxOut},
overflowGasTx := domainmessage.NewSubnetworkMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{overflowGasTxIn}, []*domainmessage.TxOut{overflowGasTxOut},
subnetworkID, math.MaxUint64, []byte{})
// Here we check that we can't process a block that its transactions' gas overflows uint64
overflowGasBlock, err := blockdag.PrepareBlockForTest(dag, dag.VirtualParentHashes(), []*appmessage.MsgTx{tx1, overflowGasTx})
overflowGasBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*domainmessage.MsgTx{tx1, overflowGasTx}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -405,19 +549,19 @@ func TestGasLimit(t *testing.T) {
}
nonExistentSubnetwork := &subnetworkid.SubnetworkID{123}
nonExistentSubnetworkTxIn := &appmessage.TxIn{
PreviousOutpoint: *appmessage.NewOutpoint(cbTxs[3].TxID(), 0),
Sequence: appmessage.MaxTxInSequenceNum,
nonExistentSubnetworkTxIn := &domainmessage.TxIn{
PreviousOutpoint: *domainmessage.NewOutpoint(cbTxs[3].TxID(), 0),
Sequence: domainmessage.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
nonExistentSubnetworkTxOut := &appmessage.TxOut{
nonExistentSubnetworkTxOut := &domainmessage.TxOut{
Value: cbTxs[3].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
nonExistentSubnetworkTx := appmessage.NewSubnetworkMsgTx(appmessage.TxVersion, []*appmessage.TxIn{nonExistentSubnetworkTxIn},
[]*appmessage.TxOut{nonExistentSubnetworkTxOut}, nonExistentSubnetwork, 1, []byte{})
nonExistentSubnetworkTx := domainmessage.NewSubnetworkMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{nonExistentSubnetworkTxIn},
[]*domainmessage.TxOut{nonExistentSubnetworkTxOut}, nonExistentSubnetwork, 1, []byte{})
nonExistentSubnetworkBlock, err := blockdag.PrepareBlockForTest(dag, dag.VirtualParentHashes(), []*appmessage.MsgTx{nonExistentSubnetworkTx, overflowGasTx})
nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*domainmessage.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
@@ -438,7 +582,7 @@ func TestGasLimit(t *testing.T) {
}
// Here we check that we can process a block with a transaction that doesn't exceed the gas limit
validBlock, err := blockdag.PrepareBlockForTest(dag, dag.VirtualParentHashes(), []*appmessage.MsgTx{tx1})
validBlock, err := mining.PrepareBlockForTest(dag, dag.TipHashes(), []*domainmessage.MsgTx{tx1}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}

View File

@@ -1,10 +1,9 @@
package blockdag
import (
"sort"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/pkg/errors"
"sort"
)
// ghostdag runs the GHOSTDAG protocol and updates newNode.blues,
@@ -40,97 +39,86 @@ func (dag *BlockDAG) ghostdag(newNode *blockNode) (selectedParentAnticone []*blo
})
for _, blueCandidate := range selectedParentAnticone {
isBlue, candidateAnticoneSize, candidateBluesAnticoneSizes, err := dag.checkBlueCandidate(newNode, blueCandidate)
if err != nil {
return nil, err
candidateBluesAnticoneSizes := make(map[*blockNode]dagconfig.KType)
var candidateAnticoneSize dagconfig.KType
possiblyBlue := true
// Iterate over all blocks in the blue set of newNode that are not in the past
// of blueCandidate, and check for each one of them if blueCandidate potentially
// enlarges their blue anticone to be over K, or that they enlarge the blue anticone
// of blueCandidate to be over K.
for chainBlock := newNode; possiblyBlue; chainBlock = chainBlock.selectedParent {
// If blueCandidate is in the future of chainBlock, it means
// that all remaining blues are in the past of chainBlock and thus
// in the past of blueCandidate. In this case we know for sure that
// the anticone of blueCandidate will not exceed K, and we can mark
// it as blue.
//
// newNode is always in the future of blueCandidate, so there's
// no point in checking it.
if chainBlock != newNode {
if isAncestorOfBlueCandidate, err := dag.isInPast(chainBlock, blueCandidate); err != nil {
return nil, err
} else if isAncestorOfBlueCandidate {
break
}
}
for _, block := range chainBlock.blues {
// Skip blocks that exist in the past of blueCandidate.
if isAncestorOfBlueCandidate, err := dag.isInPast(block, blueCandidate); err != nil {
return nil, err
} else if isAncestorOfBlueCandidate {
continue
}
candidateBluesAnticoneSizes[block], err = dag.blueAnticoneSize(block, newNode)
if err != nil {
return nil, err
}
candidateAnticoneSize++
if candidateAnticoneSize > dag.Params.K {
// k-cluster violation: The candidate's blue anticone exceeded k
possiblyBlue = false
break
}
if candidateBluesAnticoneSizes[block] == dag.Params.K {
// k-cluster violation: A block in candidate's blue anticone already
// has k blue blocks in its own anticone
possiblyBlue = false
break
}
// This is a sanity check that validates that a blue
// block's blue anticone is not already larger than K.
if candidateBluesAnticoneSizes[block] > dag.Params.K {
return nil, errors.New("found blue anticone size larger than k")
}
}
}
if isBlue {
if possiblyBlue {
// No k-cluster violation found, we can now set the candidate block as blue
newNode.blues = append(newNode.blues, blueCandidate)
newNode.bluesAnticoneSizes[blueCandidate] = candidateAnticoneSize
for blue, blueAnticoneSize := range candidateBluesAnticoneSizes {
newNode.bluesAnticoneSizes[blue] = blueAnticoneSize + 1
}
} else {
newNode.reds = append(newNode.reds, blueCandidate)
// The maximum length of node.blues can be K+1 because
// it contains the selected parent.
if dagconfig.KType(len(newNode.blues)) == dag.Params.K+1 {
break
}
}
}
newNode.blueScore = newNode.selectedParent.blueScore + uint64(len(newNode.blues))
return selectedParentAnticone, nil
}
func (dag *BlockDAG) checkBlueCandidate(newNode *blockNode, blueCandidate *blockNode) (
isBlue bool, candidateAnticoneSize dagconfig.KType, candidateBluesAnticoneSizes map[*blockNode]dagconfig.KType,
err error) {
// The maximum length of node.blues can be K+1 because
// it contains the selected parent.
if dagconfig.KType(len(newNode.blues)) == dag.Params.K+1 {
return false, 0, nil, nil
}
candidateBluesAnticoneSizes = make(map[*blockNode]dagconfig.KType)
// Iterate over all blocks in the blue set of newNode that are not in the past
// of blueCandidate, and check for each one of them if blueCandidate potentially
// enlarges their blue anticone to be over K, or that they enlarge the blue anticone
// of blueCandidate to be over K.
for chainBlock := newNode; ; chainBlock = chainBlock.selectedParent {
// If blueCandidate is in the future of chainBlock, it means
// that all remaining blues are in the past of chainBlock and thus
// in the past of blueCandidate. In this case we know for sure that
// the anticone of blueCandidate will not exceed K, and we can mark
// it as blue.
//
// newNode is always in the future of blueCandidate, so there's
// no point in checking it.
if chainBlock != newNode {
if isAncestorOfBlueCandidate, err := dag.isInPast(chainBlock, blueCandidate); err != nil {
return false, 0, nil, err
} else if isAncestorOfBlueCandidate {
break
}
}
for _, block := range chainBlock.blues {
// Skip blocks that exist in the past of blueCandidate.
if isAncestorOfBlueCandidate, err := dag.isInPast(block, blueCandidate); err != nil {
return false, 0, nil, err
} else if isAncestorOfBlueCandidate {
continue
}
candidateBluesAnticoneSizes[block], err = dag.blueAnticoneSize(block, newNode)
if err != nil {
return false, 0, nil, err
}
candidateAnticoneSize++
if candidateAnticoneSize > dag.Params.K {
// k-cluster violation: The candidate's blue anticone exceeded k
return false, 0, nil, nil
}
if candidateBluesAnticoneSizes[block] == dag.Params.K {
// k-cluster violation: A block in candidate's blue anticone already
// has k blue blocks in its own anticone
return false, 0, nil, nil
}
// This is a sanity check that validates that a blue
// block's blue anticone is not already larger than K.
if candidateBluesAnticoneSizes[block] > dag.Params.K {
return false, 0, nil, errors.New("found blue anticone size larger than k")
}
}
}
return true, candidateAnticoneSize, candidateBluesAnticoneSizes, nil
}
// selectedParentAnticone returns the blocks in the anticone of the selected parent of the given node.
// The function work as follows.
// We start by adding all parents of the node (other than the selected parent) to a process queue.

View File

@@ -1,35 +1,24 @@
package blockdag
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
type block struct {
ID string // id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
ExpectedScore uint64
ExpectedSelectedParent string
ExpectedBlues []string
ExpectedReds []string
Parents []string
}
type testData struct {
K dagconfig.KType
GenesisID string
ExpectedReds []string
Blocks []block
type testBlockData struct {
parents []string
id string // id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
expectedScore uint64
expectedSelectedParent string
expectedBlues []string
}
// TestGHOSTDAG iterates over several dag simulations, and checks
@@ -37,26 +26,158 @@ type testData struct {
// block are calculated as expected.
func TestGHOSTDAG(t *testing.T) {
dagParams := dagconfig.SimnetParams
err := filepath.Walk("./testdata/dags/", func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
var test testData
file, err := os.Open(path)
if err != nil {
t.Fatalf("TestGHOSTDAG: failed opening file: %s", path)
}
decoder := json.NewDecoder(file)
decoder.DisallowUnknownFields()
err = decoder.Decode(&test)
if err != nil {
t.Fatalf("TestGHOSTDAG: test: %s, failed decoding json: %v", info.Name(), err)
}
tests := []struct {
k dagconfig.KType
expectedReds []string
dagData []*testBlockData
}{
{
k: 3,
expectedReds: []string{"F", "G", "H", "I", "N", "P"},
dagData: []*testBlockData{
{
parents: []string{"A"},
id: "B",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"B"},
id: "C",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"A"},
id: "D",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"C", "D"},
id: "E",
expectedScore: 4,
expectedSelectedParent: "C",
expectedBlues: []string{"C", "D"},
},
{
parents: []string{"A"},
id: "F",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"F"},
id: "G",
expectedScore: 2,
expectedSelectedParent: "F",
expectedBlues: []string{"F"},
},
{
parents: []string{"A"},
id: "H",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "I",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"E", "G"},
id: "J",
expectedScore: 5,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"J"},
id: "K",
expectedScore: 6,
expectedSelectedParent: "J",
expectedBlues: []string{"J"},
},
{
parents: []string{"I", "K"},
id: "L",
expectedScore: 7,
expectedSelectedParent: "K",
expectedBlues: []string{"K"},
},
{
parents: []string{"L"},
id: "M",
expectedScore: 8,
expectedSelectedParent: "L",
expectedBlues: []string{"L"},
},
{
parents: []string{"M"},
id: "N",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "O",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "P",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "Q",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "R",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"R"},
id: "S",
expectedScore: 10,
expectedSelectedParent: "R",
expectedBlues: []string{"R"},
},
{
parents: []string{"N", "O", "P", "Q", "S"},
id: "T",
expectedScore: 13,
expectedSelectedParent: "S",
expectedBlues: []string{"S", "O", "Q"},
},
},
},
}
for i, test := range tests {
func() {
resetExtraNonceForTest()
dagParams.K = test.K
dag, teardownFunc, err := DAGSetup(fmt.Sprintf("TestGHOSTDAG %s", info.Name()), true, Config{
dagParams.K = test.k
dag, teardownFunc, err := DAGSetup(fmt.Sprintf("TestGHOSTDAG%d", i), true, Config{
DAGParams: &dagParams,
})
if err != nil {
@@ -67,33 +188,32 @@ func TestGHOSTDAG(t *testing.T) {
genesisNode := dag.genesis
blockByIDMap := make(map[string]*blockNode)
idByBlockMap := make(map[*blockNode]string)
blockByIDMap[test.GenesisID] = genesisNode
idByBlockMap[genesisNode] = test.GenesisID
blockByIDMap["A"] = genesisNode
idByBlockMap[genesisNode] = "A"
for _, blockData := range test.Blocks {
for _, blockData := range test.dagData {
parents := blockSet{}
for _, parentID := range blockData.Parents {
for _, parentID := range blockData.parents {
parent := blockByIDMap[parentID]
parents.add(parent)
}
block, err := PrepareBlockForTest(dag, parents.hashes(), nil)
if err != nil {
t.Fatalf("TestGHOSTDAG: block %s got unexpected error from PrepareBlockForTest: %+v", blockData.ID,
err)
t.Fatalf("TestGHOSTDAG: block %v got unexpected error from PrepareBlockForTest: %v", blockData.id, err)
}
utilBlock := util.NewBlock(block)
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
if err != nil {
t.Fatalf("TestGHOSTDAG: dag.ProcessBlock got unexpected error for block %s: %+v", blockData.ID, err)
t.Fatalf("TestGHOSTDAG: dag.ProcessBlock got unexpected error for block %v: %v", blockData.id, err)
}
if isDelayed {
t.Fatalf("TestGHOSTDAG: block %s "+
"is too far in the future", blockData.ID)
"is too far in the future", blockData.id)
}
if isOrphan {
t.Fatalf("TestGHOSTDAG: block %s was unexpectedly orphan", blockData.ID)
t.Fatalf("TestGHOSTDAG: block %v was unexpectedly orphan", blockData.id)
}
node, ok := dag.index.LookupNode(utilBlock.Hash())
@@ -101,38 +221,27 @@ func TestGHOSTDAG(t *testing.T) {
t.Fatalf("block %s does not exist in the DAG", utilBlock.Hash())
}
blockByIDMap[blockData.ID] = node
idByBlockMap[node] = blockData.ID
blockByIDMap[blockData.id] = node
idByBlockMap[node] = blockData.id
bluesIDs := make([]string, 0, len(node.blues))
redsIDs := make([]string, 0, len(node.reds))
for _, blue := range node.blues {
bluesIDs = append(bluesIDs, idByBlockMap[blue])
}
for _, red := range node.reds {
redsIDs = append(redsIDs, idByBlockMap[red])
}
selectedParentID := idByBlockMap[node.selectedParent]
fullDataStr := fmt.Sprintf("blues: %v, selectedParent: %v, score: %v",
bluesIDs, selectedParentID, node.blueScore)
if blockData.ExpectedScore != node.blueScore {
t.Errorf("Test %s: Block %s expected to have score %v but got %v (fulldata: %v)",
info.Name(), blockData.ID, blockData.ExpectedScore, node.blueScore, fullDataStr)
if blockData.expectedScore != node.blueScore {
t.Errorf("Test %d: Block %v expected to have score %v but got %v (fulldata: %v)",
i, blockData.id, blockData.expectedScore, node.blueScore, fullDataStr)
}
if blockData.ExpectedSelectedParent != selectedParentID {
t.Errorf("Test %s: Block %s expected to have selected parent %v but got %v (fulldata: %v)",
info.Name(), blockData.ID, blockData.ExpectedSelectedParent, selectedParentID, fullDataStr)
if blockData.expectedSelectedParent != selectedParentID {
t.Errorf("Test %d: Block %v expected to have selected parent %v but got %v (fulldata: %v)",
i, blockData.id, blockData.expectedSelectedParent, selectedParentID, fullDataStr)
}
if !reflect.DeepEqual(blockData.ExpectedBlues, bluesIDs) {
t.Errorf("Test %s: Block %s expected to have blues %v but got %v (fulldata: %v)",
info.Name(), blockData.ID, blockData.ExpectedBlues, bluesIDs, fullDataStr)
}
if !reflect.DeepEqual(blockData.ExpectedReds, redsIDs) {
t.Errorf("Test %s: Block %v expected to have reds %v but got %v (fulldata: %v)",
info.Name(), blockData.ID, blockData.ExpectedReds, redsIDs, fullDataStr)
if !reflect.DeepEqual(blockData.expectedBlues, bluesIDs) {
t.Errorf("Test %d: Block %v expected to have blues %v but got %v (fulldata: %v)",
i, blockData.id, blockData.expectedBlues, bluesIDs, fullDataStr)
}
}
@@ -142,7 +251,7 @@ func TestGHOSTDAG(t *testing.T) {
reds[id] = true
}
for tip := dag.virtual.blockNode; tip.selectedParent != nil; tip = tip.selectedParent {
for tip := &dag.virtual.blockNode; tip.selectedParent != nil; tip = tip.selectedParent {
tipID := idByBlockMap[tip]
delete(reds, tipID)
for _, blue := range tip.blues {
@@ -150,22 +259,16 @@ func TestGHOSTDAG(t *testing.T) {
delete(reds, blueID)
}
}
if !checkReds(test.ExpectedReds, reds) {
if !checkReds(test.expectedReds, reds) {
redsIDs := make([]string, 0, len(reds))
for id := range reds {
redsIDs = append(redsIDs, id)
}
sort.Strings(redsIDs)
sort.Strings(test.ExpectedReds)
t.Errorf("Test %s: Expected reds %v but got %v", info.Name(), test.ExpectedReds, redsIDs)
sort.Strings(test.expectedReds)
t.Errorf("Test %d: Expected reds %v but got %v", i, test.expectedReds, redsIDs)
}
}()
return nil
})
if err != nil {
t.Fatal(err)
}
}

View File

@@ -4,9 +4,9 @@ import (
"bytes"
"encoding/gob"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
@@ -113,7 +113,7 @@ func (idx *AcceptanceIndex) TxsAcceptanceData(blockHash *daghash.Hash) (blockdag
}
type serializableTxAcceptanceData struct {
MsgTx appmessage.MsgTx
MsgTx domainmessage.MsgTx
IsAccepted bool
}

View File

@@ -9,10 +9,10 @@ import (
"syscall"
"testing"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/domainmessage"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
@@ -21,19 +21,19 @@ import (
func TestAcceptanceIndexSerializationAndDeserialization(t *testing.T) {
// Create test data
hash, _ := daghash.NewHashFromStr("1111111111111111111111111111111111111111111111111111111111111111")
txIn1 := &appmessage.TxIn{SignatureScript: []byte{1}, PreviousOutpoint: appmessage.Outpoint{Index: 1}, Sequence: 0}
txIn2 := &appmessage.TxIn{SignatureScript: []byte{2}, PreviousOutpoint: appmessage.Outpoint{Index: 2}, Sequence: 0}
txOut1 := &appmessage.TxOut{ScriptPubKey: []byte{1}, Value: 10}
txOut2 := &appmessage.TxOut{ScriptPubKey: []byte{2}, Value: 20}
txIn1 := &domainmessage.TxIn{SignatureScript: []byte{1}, PreviousOutpoint: domainmessage.Outpoint{Index: 1}, Sequence: 0}
txIn2 := &domainmessage.TxIn{SignatureScript: []byte{2}, PreviousOutpoint: domainmessage.Outpoint{Index: 2}, Sequence: 0}
txOut1 := &domainmessage.TxOut{ScriptPubKey: []byte{1}, Value: 10}
txOut2 := &domainmessage.TxOut{ScriptPubKey: []byte{2}, Value: 20}
blockTxsAcceptanceData := blockdag.BlockTxsAcceptanceData{
BlockHash: *hash,
TxAcceptanceData: []blockdag.TxAcceptanceData{
{
Tx: util.NewTx(appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn1}, []*appmessage.TxOut{txOut1})),
Tx: util.NewTx(domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn1}, []*domainmessage.TxOut{txOut1})),
IsAccepted: true,
},
{
Tx: util.NewTx(appmessage.NewNativeMsgTx(appmessage.TxVersion, []*appmessage.TxIn{txIn2}, []*appmessage.TxOut{txOut2})),
Tx: util.NewTx(domainmessage.NewNativeMsgTx(domainmessage.TxVersion, []*domainmessage.TxIn{txIn2}, []*domainmessage.TxOut{txOut2})),
IsAccepted: false,
},
},
@@ -77,6 +77,7 @@ func TestAcceptanceIndexRecover(t *testing.T) {
testFiles := []string{
"blk_0_to_4.dat",
"blk_3B.dat",
}
var blocks []*util.Block
@@ -109,7 +110,7 @@ func TestAcceptanceIndexRecover(t *testing.T) {
db1DAG, teardown, err := blockdag.DAGSetup("", false, db1Config)
if err != nil {
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %+v", err)
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %v", err)
}
if teardown != nil {
defer teardown()
@@ -177,7 +178,7 @@ func TestAcceptanceIndexRecover(t *testing.T) {
db2DAG, teardown, err := blockdag.DAGSetup("", false, db2Config)
if err != nil {
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %+v", err)
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %v", err)
}
if teardown != nil {
defer teardown()

View File

@@ -8,8 +8,8 @@ Package indexers implements optional block DAG indexes.
package indexers
import (
"github.com/kaspanet/kaspad/domain/blockdag"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dbaccess"
"github.com/kaspanet/kaspad/util/daghash"
)

View File

@@ -5,7 +5,7 @@
package indexers
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/logger"
)
var log, _ = logger.Get(logger.SubsystemTags.INDX)

Some files were not shown because too many files have changed in this diff Show More