2023-09-09 23:45:47 +00:00
|
|
|
|
package app
|
|
|
|
|
|
|
|
|
|
import (
|
2023-11-27 02:51:46 +00:00
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/hex"
|
2023-09-09 23:45:47 +00:00
|
|
|
|
"fmt"
|
2023-11-27 02:51:46 +00:00
|
|
|
|
"math/big"
|
2023-09-09 23:45:47 +00:00
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
2024-01-15 04:32:28 +00:00
|
|
|
|
"time"
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
|
"github.com/libp2p/go-libp2p/core/crypto"
|
|
|
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
|
|
|
"github.com/multiformats/go-multiaddr"
|
|
|
|
|
mn "github.com/multiformats/go-multiaddr/net"
|
2023-09-27 09:05:39 +00:00
|
|
|
|
"github.com/pkg/errors"
|
2023-11-27 02:51:46 +00:00
|
|
|
|
"golang.org/x/term"
|
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
"google.golang.org/grpc/connectivity"
|
|
|
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/node/config"
|
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
textColor = lipgloss.Color("#fff")
|
|
|
|
|
primaryColor = lipgloss.Color("#ff0070")
|
|
|
|
|
secondaryColor = lipgloss.Color("#ff5c00")
|
|
|
|
|
windowHeader = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
unselectedListStyle = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Width(28).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
navigatedListStyle = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Width(28).
|
|
|
|
|
Bold(true).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
selectedListStyle = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Background(primaryColor).
|
|
|
|
|
Width(28).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
statusBarStyle = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Background(primaryColor)
|
|
|
|
|
statusStyle = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Background(primaryColor).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
statusItemStyle = lipgloss.NewStyle().
|
|
|
|
|
Foreground(textColor).
|
|
|
|
|
Background(secondaryColor).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
docStyle = lipgloss.NewStyle().Padding(0)
|
|
|
|
|
border = lipgloss.Border{
|
|
|
|
|
Top: "─",
|
|
|
|
|
Bottom: "─",
|
|
|
|
|
Left: "│",
|
|
|
|
|
Right: "│",
|
|
|
|
|
TopLeft: "┌",
|
|
|
|
|
TopRight: "┐",
|
|
|
|
|
BottomLeft: "└",
|
|
|
|
|
BottomRight: "┘",
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type DBConsole struct {
|
2023-11-27 02:51:46 +00:00
|
|
|
|
nodeConfig *config.Config
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
func newDBConsole(nodeConfig *config.Config) (*DBConsole, error) {
|
2023-09-09 23:45:47 +00:00
|
|
|
|
return &DBConsole{
|
2023-11-27 02:51:46 +00:00
|
|
|
|
nodeConfig,
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
type model struct {
|
2024-01-15 04:32:28 +00:00
|
|
|
|
filters []string
|
|
|
|
|
cursor int
|
|
|
|
|
selectedFilter string
|
|
|
|
|
conn *grpc.ClientConn
|
|
|
|
|
client protobufs.NodeServiceClient
|
|
|
|
|
peerId string
|
|
|
|
|
errorMsg string
|
|
|
|
|
frame *protobufs.ClockFrame
|
|
|
|
|
frames []*protobufs.ClockFrame
|
|
|
|
|
frameIndex int
|
|
|
|
|
grpcWarn bool
|
|
|
|
|
committed bool
|
|
|
|
|
lastChecked int64
|
|
|
|
|
owned *big.Int
|
|
|
|
|
unconfirmedOwned *big.Int
|
2023-11-27 02:51:46 +00:00
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
func (m model) Init() tea.Cmd {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
2024-01-15 04:32:28 +00:00
|
|
|
|
if m.conn.GetState() == connectivity.Ready {
|
|
|
|
|
if m.lastChecked < (time.Now().UnixMilli() - 10_000) {
|
|
|
|
|
m.lastChecked = time.Now().UnixMilli()
|
2024-01-29 21:11:40 +00:00
|
|
|
|
|
|
|
|
|
tokenBalance, err := FetchTokenBalance(m.client)
|
2024-01-15 04:32:28 +00:00
|
|
|
|
if err == nil {
|
2024-01-29 21:11:40 +00:00
|
|
|
|
m.owned = tokenBalance.Owned
|
|
|
|
|
m.unconfirmedOwned = tokenBalance.UnconfirmedOwned
|
2024-01-15 04:32:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2024-01-15 04:32:28 +00:00
|
|
|
|
switch msg := msg.(type) {
|
2023-11-27 02:51:46 +00:00
|
|
|
|
case tea.KeyMsg:
|
|
|
|
|
switch msg.String() {
|
|
|
|
|
case "ctrl+c", "q":
|
|
|
|
|
return m, tea.Quit
|
|
|
|
|
case "up", "w":
|
|
|
|
|
if m.cursor > 0 {
|
|
|
|
|
m.cursor--
|
|
|
|
|
}
|
|
|
|
|
case "down", "s":
|
|
|
|
|
if m.cursor < len(m.filters)-1 {
|
|
|
|
|
m.cursor++
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
case "left", "a":
|
|
|
|
|
m.committed = false
|
|
|
|
|
m.errorMsg = ""
|
|
|
|
|
if m.frameIndex > 0 {
|
|
|
|
|
m.frameIndex--
|
|
|
|
|
if len(m.frames) != 0 && m.conn.GetState() == connectivity.Ready {
|
|
|
|
|
filter, _ := hex.DecodeString(m.selectedFilter)
|
|
|
|
|
selector, err := m.frames[m.frameIndex].GetSelector()
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err == nil && bytes.Equal(
|
|
|
|
|
frameInfo.ClockFrame.Output,
|
|
|
|
|
m.frames[m.frameIndex].Output,
|
|
|
|
|
) {
|
|
|
|
|
m.committed = true
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
} else {
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
Selector: selector.FillBytes(make([]byte, 32)),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = hex.EncodeToString(
|
2024-02-18 04:52:19 +00:00
|
|
|
|
selector.FillBytes(make([]byte, 32)),
|
2023-11-27 02:51:46 +00:00
|
|
|
|
) + ":" + err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
m.errorMsg = "Not currently connected to node, cannot query."
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
first := uint64(0)
|
|
|
|
|
if len(m.frames) != 0 {
|
|
|
|
|
first = m.frames[0].FrameNumber - 1
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if first == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
max := uint64(17)
|
|
|
|
|
if len(m.frames) != 0 {
|
|
|
|
|
max = first
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
min := max - 16
|
|
|
|
|
filter, _ := hex.DecodeString(m.selectedFilter)
|
|
|
|
|
frames, err := m.client.GetFrames(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFramesRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FromFrameNumber: min,
|
|
|
|
|
ToFrameNumber: max + 1,
|
|
|
|
|
IncludeCandidates: true,
|
|
|
|
|
},
|
|
|
|
|
)
|
2023-09-09 23:45:47 +00:00
|
|
|
|
if err != nil {
|
2023-11-27 02:51:46 +00:00
|
|
|
|
m.selectedFilter = ""
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if frames.TruncatedClockFrames != nil {
|
|
|
|
|
m.frames = frames.TruncatedClockFrames
|
|
|
|
|
m.frameIndex = len(m.frames) - 1
|
|
|
|
|
selector, err := m.frames[m.frameIndex].GetSelector()
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err == nil && bytes.Equal(
|
|
|
|
|
frameInfo.ClockFrame.Output,
|
|
|
|
|
m.frames[m.frameIndex].Output,
|
|
|
|
|
) {
|
|
|
|
|
m.committed = true
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
} else {
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
Selector: selector.FillBytes(make([]byte, 32)),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "right", "d":
|
|
|
|
|
m.committed = false
|
|
|
|
|
m.errorMsg = ""
|
|
|
|
|
if m.frameIndex < len(m.frames)-1 {
|
|
|
|
|
m.frameIndex++
|
|
|
|
|
if len(m.frames) != 0 && m.conn.GetState() == connectivity.Ready {
|
|
|
|
|
filter, _ := hex.DecodeString(m.selectedFilter)
|
|
|
|
|
selector, err := m.frames[m.frameIndex].GetSelector()
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err == nil && bytes.Equal(
|
|
|
|
|
frameInfo.ClockFrame.Output,
|
|
|
|
|
m.frames[m.frameIndex].Output,
|
|
|
|
|
) {
|
|
|
|
|
m.committed = true
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
} else {
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
Selector: selector.FillBytes(make([]byte, 32)),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = hex.EncodeToString(
|
2024-02-18 04:52:19 +00:00
|
|
|
|
selector.FillBytes(make([]byte, 32)),
|
2023-11-27 02:51:46 +00:00
|
|
|
|
) + ":" + err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
m.errorMsg = "Not currently connected to node, cannot query."
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
min := uint64(1)
|
|
|
|
|
if len(m.frames) != 0 {
|
|
|
|
|
min = m.frames[len(m.frames)-1].FrameNumber + 1
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
max := min + 16
|
|
|
|
|
filter, _ := hex.DecodeString(m.selectedFilter)
|
|
|
|
|
frames, err := m.client.GetFrames(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFramesRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FromFrameNumber: min,
|
|
|
|
|
ToFrameNumber: max,
|
|
|
|
|
IncludeCandidates: true,
|
|
|
|
|
},
|
2023-09-09 23:45:47 +00:00
|
|
|
|
)
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
m.selectedFilter = ""
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
2023-09-09 23:45:47 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if frames.TruncatedClockFrames != nil {
|
|
|
|
|
m.frames = frames.TruncatedClockFrames
|
|
|
|
|
m.frameIndex = 0
|
|
|
|
|
selector, err := m.frames[m.frameIndex].GetSelector()
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
2023-09-27 09:05:39 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err == nil && bytes.Equal(
|
|
|
|
|
frameInfo.ClockFrame.Output,
|
|
|
|
|
m.frames[m.frameIndex].Output,
|
|
|
|
|
) {
|
|
|
|
|
m.committed = true
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
} else {
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
Selector: selector.FillBytes(make([]byte, 32)),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-27 09:05:39 +00:00
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
case "enter", " ":
|
|
|
|
|
m.errorMsg = ""
|
|
|
|
|
m.frame = nil
|
|
|
|
|
m.committed = false
|
|
|
|
|
if m.conn.GetState() == connectivity.Ready {
|
|
|
|
|
if m.selectedFilter != m.filters[m.cursor] {
|
|
|
|
|
m.selectedFilter = m.filters[m.cursor]
|
|
|
|
|
m.frames = []*protobufs.ClockFrame{}
|
|
|
|
|
}
|
2023-09-27 09:05:39 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
min := uint64(1)
|
|
|
|
|
if len(m.frames) != 0 {
|
|
|
|
|
min = m.frames[len(m.frames)-1].FrameNumber + 1
|
|
|
|
|
}
|
2023-09-27 09:05:39 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
max := min + 16
|
|
|
|
|
filter, _ := hex.DecodeString(m.selectedFilter)
|
|
|
|
|
frames, err := m.client.GetFrames(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFramesRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FromFrameNumber: min,
|
|
|
|
|
ToFrameNumber: max,
|
|
|
|
|
IncludeCandidates: true,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.selectedFilter = ""
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
2023-09-27 09:05:39 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if frames.TruncatedClockFrames != nil {
|
|
|
|
|
m.frames = frames.TruncatedClockFrames
|
|
|
|
|
m.frameIndex = 0
|
|
|
|
|
selector, err := m.frames[m.frameIndex].GetSelector()
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err == nil && bytes.Equal(
|
|
|
|
|
frameInfo.ClockFrame.Output,
|
|
|
|
|
m.frames[m.frameIndex].Output,
|
|
|
|
|
) {
|
|
|
|
|
m.committed = true
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
} else {
|
|
|
|
|
frameInfo, err := m.client.GetFrameInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetFrameInfoRequest{
|
|
|
|
|
Filter: filter,
|
|
|
|
|
FrameNumber: m.frames[m.frameIndex].FrameNumber,
|
|
|
|
|
Selector: selector.FillBytes(make([]byte, 32)),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
m.errorMsg = err.Error()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
m.frame = frameInfo.ClockFrame
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
m.errorMsg = "Not currently connected to node, cannot query."
|
2023-09-27 09:05:39 +00:00
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m model) View() string {
|
|
|
|
|
physicalWidth, physicalHeight, _ := term.GetSize(int(os.Stdout.Fd()))
|
|
|
|
|
doc := strings.Builder{}
|
|
|
|
|
|
|
|
|
|
window := lipgloss.NewStyle().
|
|
|
|
|
Border(border, true).
|
|
|
|
|
BorderForeground(primaryColor).
|
|
|
|
|
Padding(0, 1)
|
|
|
|
|
|
|
|
|
|
list := []string{}
|
|
|
|
|
for i, item := range m.filters {
|
2024-01-03 07:31:42 +00:00
|
|
|
|
str := item[0:12] + ".." + item[len(item)-12:]
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if m.selectedFilter == item {
|
|
|
|
|
list = append(list, selectedListStyle.Render(str))
|
|
|
|
|
} else if i == m.cursor {
|
|
|
|
|
list = append(list, navigatedListStyle.Render(str))
|
|
|
|
|
} else {
|
|
|
|
|
list = append(list, unselectedListStyle.Render(str))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w := lipgloss.Width
|
|
|
|
|
|
|
|
|
|
statusKey := statusItemStyle.Render("STATUS")
|
|
|
|
|
info := statusStyle.Render("(Press Ctrl-C or Q to quit)")
|
|
|
|
|
onlineStatus := "gRPC Not Enabled, Please Configure"
|
|
|
|
|
if !m.grpcWarn {
|
|
|
|
|
switch m.conn.GetState() {
|
|
|
|
|
case connectivity.Connecting:
|
|
|
|
|
onlineStatus = "CONNECTING"
|
|
|
|
|
case connectivity.Idle:
|
|
|
|
|
onlineStatus = "IDLE"
|
|
|
|
|
case connectivity.Shutdown:
|
|
|
|
|
onlineStatus = "SHUTDOWN"
|
|
|
|
|
case connectivity.TransientFailure:
|
|
|
|
|
onlineStatus = "DISCONNECTED"
|
|
|
|
|
default:
|
|
|
|
|
onlineStatus = "CONNECTED"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 04:32:28 +00:00
|
|
|
|
ownedVal := statusItemStyle.Copy().
|
|
|
|
|
Render("Owned: " + m.owned.String())
|
|
|
|
|
if m.owned.Cmp(big.NewInt(-1)) == 0 {
|
|
|
|
|
ownedVal = statusItemStyle.Copy().
|
|
|
|
|
Render("")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unconfirmedOwnedVal := statusItemStyle.Copy().
|
|
|
|
|
Render("Unconfirmed: " + m.unconfirmedOwned.String())
|
|
|
|
|
if m.unconfirmedOwned.Cmp(big.NewInt(-1)) == 0 {
|
|
|
|
|
unconfirmedOwnedVal = statusItemStyle.Copy().
|
|
|
|
|
Render("")
|
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
peerIdVal := statusItemStyle.Render(m.peerId)
|
|
|
|
|
statusVal := statusBarStyle.Copy().
|
2024-01-15 04:32:28 +00:00
|
|
|
|
Width(physicalWidth-w(statusKey)-w(info)-w(peerIdVal)-w(ownedVal)-
|
|
|
|
|
w(unconfirmedOwnedVal)).
|
2023-11-27 02:51:46 +00:00
|
|
|
|
Padding(0, 1).
|
|
|
|
|
Render(onlineStatus)
|
|
|
|
|
|
|
|
|
|
bar := lipgloss.JoinHorizontal(lipgloss.Top,
|
|
|
|
|
statusKey,
|
|
|
|
|
statusVal,
|
|
|
|
|
info,
|
|
|
|
|
peerIdVal,
|
2024-01-15 04:32:28 +00:00
|
|
|
|
ownedVal,
|
|
|
|
|
unconfirmedOwnedVal,
|
2023-11-27 02:51:46 +00:00
|
|
|
|
)
|
2023-09-27 09:05:39 +00:00
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
explorerContent := ""
|
|
|
|
|
|
|
|
|
|
if m.errorMsg != "" {
|
|
|
|
|
explorerContent = m.errorMsg
|
|
|
|
|
} else if m.frame != nil {
|
|
|
|
|
selector, err := m.frame.GetSelector()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
committed := "Unconfirmed"
|
|
|
|
|
if m.committed {
|
|
|
|
|
committed = "Confirmed"
|
|
|
|
|
}
|
|
|
|
|
explorerContent = fmt.Sprintf(
|
|
|
|
|
"Frame %d (Selector: %x, %s):\n\tParent: %x\n\tVDF Proof: %x\n",
|
|
|
|
|
m.frame.FrameNumber,
|
2024-02-18 04:52:19 +00:00
|
|
|
|
selector.FillBytes(make([]byte, 32)),
|
2023-11-27 02:51:46 +00:00
|
|
|
|
committed,
|
|
|
|
|
m.frame.ParentSelector,
|
|
|
|
|
m.frame.Input[:516],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for i := 0; i < len(m.frame.Input[516:])/74; i++ {
|
|
|
|
|
commit := m.frame.Input[516+(i*74) : 516+((i+1)*74)]
|
|
|
|
|
explorerContent += fmt.Sprintf(
|
|
|
|
|
"\tCommitment %+x\n",
|
|
|
|
|
commit,
|
|
|
|
|
)
|
|
|
|
|
explorerContent += fmt.Sprintf(
|
|
|
|
|
"\t\tType: %s\n",
|
|
|
|
|
m.frame.AggregateProofs[i].InclusionCommitments[0].TypeUrl,
|
|
|
|
|
)
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
} else {
|
|
|
|
|
explorerContent = logoVersion(physicalWidth - 34)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
doc.WriteString(
|
|
|
|
|
lipgloss.JoinVertical(
|
|
|
|
|
lipgloss.Left,
|
|
|
|
|
lipgloss.JoinHorizontal(
|
|
|
|
|
lipgloss.Top,
|
|
|
|
|
lipgloss.JoinVertical(
|
|
|
|
|
lipgloss.Left,
|
|
|
|
|
windowHeader.Render("Filters (Up/Down, Enter)"),
|
|
|
|
|
window.Width(30).Height(physicalHeight-4).Render(lipgloss.JoinVertical(lipgloss.Left, list...)),
|
|
|
|
|
),
|
|
|
|
|
lipgloss.JoinVertical(
|
|
|
|
|
lipgloss.Left,
|
|
|
|
|
windowHeader.Render("Explorer (Left/Right)"),
|
|
|
|
|
window.Width(physicalWidth-34).Height(physicalHeight-4).Render(explorerContent),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
statusBarStyle.Width(physicalWidth).Render(bar),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if physicalWidth > 0 {
|
|
|
|
|
docStyle = docStyle.MaxWidth(physicalWidth)
|
|
|
|
|
docStyle = docStyle.MaxHeight(physicalHeight)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return docStyle.Render(doc.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func consoleModel(
|
|
|
|
|
conn *grpc.ClientConn,
|
|
|
|
|
nodeConfig *config.Config,
|
|
|
|
|
grpcWarn bool,
|
|
|
|
|
) model {
|
|
|
|
|
peerPrivKey, err := hex.DecodeString(nodeConfig.P2P.PeerPrivKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(errors.Wrap(err, "error unmarshaling peerkey"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
privKey, err := crypto.UnmarshalEd448PrivateKey(peerPrivKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(errors.Wrap(err, "error unmarshaling peerkey"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub := privKey.GetPublic()
|
|
|
|
|
id, err := peer.IDFromPublicKey(pub)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(errors.Wrap(err, "error getting peer id"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return model{
|
|
|
|
|
filters: []string{
|
|
|
|
|
hex.EncodeToString([]byte{
|
2024-10-12 18:55:17 +00:00
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
2023-11-27 02:51:46 +00:00
|
|
|
|
}),
|
|
|
|
|
},
|
2024-01-15 04:32:28 +00:00
|
|
|
|
cursor: 0,
|
|
|
|
|
conn: conn,
|
|
|
|
|
client: protobufs.NewNodeServiceClient(conn),
|
|
|
|
|
owned: big.NewInt(-1),
|
|
|
|
|
unconfirmedOwned: big.NewInt(-1),
|
|
|
|
|
peerId: id.String(),
|
|
|
|
|
grpcWarn: grpcWarn,
|
2023-11-27 02:51:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-29 21:11:40 +00:00
|
|
|
|
var defaultGrpcAddress = "localhost:8337"
|
|
|
|
|
|
|
|
|
|
// Connect to the node via GRPC
|
|
|
|
|
func ConnectToNode(nodeConfig *config.Config) (*grpc.ClientConn, error) {
|
|
|
|
|
addr := defaultGrpcAddress
|
|
|
|
|
if nodeConfig.ListenGRPCMultiaddr != "" {
|
|
|
|
|
ma, err := multiaddr.NewMultiaddr(nodeConfig.ListenGRPCMultiaddr)
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, addr, err = mn.DialArgs(ma)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-29 21:11:40 +00:00
|
|
|
|
return grpc.Dial(
|
2023-11-27 02:51:46 +00:00
|
|
|
|
addr,
|
|
|
|
|
grpc.WithTransportCredentials(
|
|
|
|
|
insecure.NewCredentials(),
|
|
|
|
|
),
|
|
|
|
|
grpc.WithDefaultCallOptions(
|
|
|
|
|
grpc.MaxCallSendMsgSize(600*1024*1024),
|
|
|
|
|
grpc.MaxCallRecvMsgSize(600*1024*1024),
|
|
|
|
|
),
|
|
|
|
|
)
|
2024-01-29 21:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TokenBalance struct {
|
|
|
|
|
Owned *big.Int
|
|
|
|
|
UnconfirmedOwned *big.Int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FetchTokenBalance(client protobufs.NodeServiceClient) (TokenBalance, error) {
|
|
|
|
|
info, err := client.GetTokenInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetTokenInfoRequest{},
|
|
|
|
|
)
|
2023-11-27 02:51:46 +00:00
|
|
|
|
if err != nil {
|
2024-01-29 21:11:40 +00:00
|
|
|
|
return TokenBalance{}, errors.Wrap(err, "error getting token info")
|
2023-11-27 02:51:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-14 21:41:40 +00:00
|
|
|
|
owned := new(big.Int).SetBytes(info.OwnedTokens)
|
2024-01-29 21:11:40 +00:00
|
|
|
|
|
|
|
|
|
return TokenBalance{
|
2024-10-14 21:41:40 +00:00
|
|
|
|
Owned: owned,
|
2024-01-29 21:11:40 +00:00
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-24 08:11:58 +00:00
|
|
|
|
func FetchNodeInfo(client protobufs.NodeServiceClient) (*protobufs.NodeInfoResponse, error) {
|
|
|
|
|
info, err := client.GetNodeInfo(
|
|
|
|
|
context.Background(),
|
|
|
|
|
&protobufs.GetNodeInfoRequest{},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "error getting node info")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return info, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-29 21:11:40 +00:00
|
|
|
|
// Runs the DB console
|
|
|
|
|
func (c *DBConsole) Run() {
|
|
|
|
|
conn, err := ConnectToNode(c.nodeConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
defer conn.Close()
|
2024-01-29 21:11:40 +00:00
|
|
|
|
|
|
|
|
|
grpcWarn := c.nodeConfig.ListenGRPCMultiaddr == ""
|
|
|
|
|
|
2023-11-27 02:51:46 +00:00
|
|
|
|
p := tea.NewProgram(consoleModel(conn, c.nodeConfig, grpcWarn))
|
|
|
|
|
if _, err := p.Run(); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func logoVersion(width int) string {
|
|
|
|
|
var out string
|
|
|
|
|
|
|
|
|
|
if width >= 83 {
|
2024-10-12 18:55:17 +00:00
|
|
|
|
out = "████████████████████████████████████████████████████████████████████████████████\n"
|
|
|
|
|
out += "████████████████████████████████████████████████████████████████████████████████\n"
|
|
|
|
|
out += "██████████████████████████████ ██████████████████████████████\n"
|
|
|
|
|
out += "█████████████████████████ █████████████████████████\n"
|
|
|
|
|
out += "█████████████████████ █████████████████████\n"
|
|
|
|
|
out += "██████████████████ ██████████████████\n"
|
|
|
|
|
out += "████████████████ ██████ ████████████████\n"
|
|
|
|
|
out += "██████████████ ████████████████████ ██████████████\n"
|
|
|
|
|
out += "█████████████ ████████████████████████████ ████████████\n"
|
|
|
|
|
out += "███████████ ██████████████████████████████████ ███████████\n"
|
|
|
|
|
out += "██████████ ██████████████████████████████████████ ██████████\n"
|
|
|
|
|
out += "█████████ ██████████████████████████████████████████ █████████\n"
|
|
|
|
|
out += "████████ ████████████████████████████████████████████ ████████\n"
|
|
|
|
|
out += "███████ ████████████████████ ████████████████████ ███████\n"
|
|
|
|
|
out += "██████ ███████████████████ ███████████████████ ██████\n"
|
|
|
|
|
out += "█████ ███████████████████ ███████████████████ █████\n"
|
|
|
|
|
out += "█████ ████████████████████ ████████████████████ █████\n"
|
|
|
|
|
out += "████ █████████████████████ █████████████████████ ████\n"
|
|
|
|
|
out += "████ ██████████████████████ ██████████████████████ ████\n"
|
|
|
|
|
out += "████ █████████████████████████ █████████████████████████ ████\n"
|
|
|
|
|
out += "████ ████████████████████████████████████████████████████████ ████\n"
|
|
|
|
|
out += "████ ████████████████████████████████████████████████████████ ████\n"
|
|
|
|
|
out += "████ ████████████████████ ████████████ ████████████████████ ████\n"
|
|
|
|
|
out += "████ ██████████████████ ███████████████████ ████\n"
|
|
|
|
|
out += "████ ████████████████ ████████████████ ████\n"
|
|
|
|
|
out += "████ ██████████████ ██ ██████████████ ████\n"
|
|
|
|
|
out += "█████ ████████████ ██████ ████████████ █████\n"
|
|
|
|
|
out += "█████ █████████ ██████████ █████████ █████\n"
|
|
|
|
|
out += "██████ ███████ █████████████ ███████ ██████\n"
|
|
|
|
|
out += "██████ ████████ █████████████████ ████████ ██████\n"
|
|
|
|
|
out += "███████ █████████ █████████████████████ ████████ ███████\n"
|
|
|
|
|
out += "████████ █████████████████████████████████ ████████████████\n"
|
|
|
|
|
out += "█████████ ██████████████████████████████████ ██████████████\n"
|
|
|
|
|
out += "██████████ ██████████████████████████████████ █████████████\n"
|
|
|
|
|
out += "████████████ ████████████████████████████████ ███████████\n"
|
|
|
|
|
out += "█████████████ ███████████████████████████████ █████████\n"
|
|
|
|
|
out += "███████████████ ████████████████ █████████ ███████\n"
|
|
|
|
|
out += "█████████████████ █████████ █████\n"
|
|
|
|
|
out += "████████████████████ █████████ ██████\n"
|
|
|
|
|
out += "███████████████████████ ██████████ ████████\n"
|
|
|
|
|
out += "███████████████████████████ ███████████████ ██████████\n"
|
|
|
|
|
out += "█████████████████████████████████ █████████████████████████████████\n"
|
|
|
|
|
out += "████████████████████████████████████████████████████████████████████████████████\n"
|
|
|
|
|
out += "████████████████████████████████████████████████████████████████████████████████\n"
|
2023-11-27 02:51:46 +00:00
|
|
|
|
out += " \n"
|
2024-10-12 18:55:17 +00:00
|
|
|
|
out += " Quilibrium Node - v" + config.GetVersionString() + " – Dusk\n"
|
2023-11-27 02:51:46 +00:00
|
|
|
|
out += " \n"
|
|
|
|
|
out += " DB Console\n"
|
|
|
|
|
} else {
|
2024-10-12 18:55:17 +00:00
|
|
|
|
out = "Quilibrium Node - v" + config.GetVersionString() + " – Dusk - DB Console\n"
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|
2023-11-27 02:51:46 +00:00
|
|
|
|
return out
|
2023-09-09 23:45:47 +00:00
|
|
|
|
}
|