Update to consensus v3.6.0
This commit is contained in:
parent
4caa6b12bc
commit
0d1ebd3913
286
api/api_types.go
Normal file
286
api/api_types.go
Normal file
|
@ -0,0 +1,286 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/address"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/observer-cmd-utils/index"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
JSONEventSideBlock = "side_block"
|
||||
JSONEventFoundBlock = "found_block"
|
||||
JSONEventOrphanedBlock = "orphaned_block"
|
||||
JSONEventPayout = "payout"
|
||||
)
|
||||
|
||||
type JSONEvent struct {
|
||||
Type string `json:"type"`
|
||||
SideBlock *index.SideBlock `json:"side_block,omitempty"`
|
||||
FoundBlock *index.FoundBlock `json:"found_block,omitempty"`
|
||||
MainCoinbaseOutputs index.MainCoinbaseOutputs `json:"main_coinbase_outputs,omitempty"`
|
||||
Payout *index.Payout `json:"payout,omitempty"`
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
Version string `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Link string `json:"link"`
|
||||
CheckedTimestamp int64 `json:"-"`
|
||||
}
|
||||
|
||||
func (v VersionInfo) ShortVersion() p2pooltypes.SemanticVersion {
|
||||
parts := strings.Split(v.Version, ".")
|
||||
for len(parts) < 2 {
|
||||
parts = append(parts, "0")
|
||||
}
|
||||
for len(parts) > 2 {
|
||||
parts = parts[:len(parts)-1]
|
||||
}
|
||||
return p2pooltypes.SemanticVersionFromString(strings.Join(parts, "."))
|
||||
}
|
||||
|
||||
type ReleaseDataJson struct {
|
||||
TagName string `json:"tag_name"`
|
||||
TargetCommitish string `json:"target_commitish"`
|
||||
Name string `json:"name"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
}
|
||||
|
||||
type SideChainVersionEntry struct {
|
||||
Weight types.Difficulty `json:"weight"`
|
||||
Share float64 `json:"share"`
|
||||
Count int `json:"count"`
|
||||
SoftwareId p2pooltypes.SoftwareId `json:"software_id"`
|
||||
SoftwareVersion p2pooltypes.SoftwareVersion `json:"software_version"`
|
||||
SoftwareString string `json:"software_string"`
|
||||
}
|
||||
|
||||
type PoolInfoResult struct {
|
||||
SideChain PoolInfoResultSideChain `json:"sidechain"`
|
||||
MainChain PoolInfoResultMainChain `json:"mainchain"`
|
||||
Versions struct {
|
||||
P2Pool VersionInfo `json:"p2pool"`
|
||||
Monero VersionInfo `json:"monero"`
|
||||
} `json:"versions"`
|
||||
}
|
||||
|
||||
type PoolInfoResultSideChain struct {
|
||||
// Consensus Specifies the consensus parameters for the backing p2pool instance
|
||||
Consensus *sidechain.Consensus `json:"consensus"`
|
||||
|
||||
// LastBlock Last sidechain block on database
|
||||
LastBlock *index.SideBlock `json:"last_block"`
|
||||
|
||||
// SecondsSinceLastBlock
|
||||
// Prefer using max(0, time.Now().Unix()-int64(LastBlock .Timestamp)) instead
|
||||
SecondsSinceLastBlock int64 `json:"seconds_since_last_block"`
|
||||
|
||||
// LastFound Last sidechain block on database found and accepted on Monero network
|
||||
LastFound *index.FoundBlock `json:"last_found"`
|
||||
|
||||
Effort PoolInfoResultSideChainEffort `json:"effort"`
|
||||
Window PoolInfoResultSideChainWindow `json:"window"`
|
||||
|
||||
// Found Total count of found blocks in database
|
||||
Found uint64 `json:"found"`
|
||||
// Miners Total count of miners in database
|
||||
Miners uint64 `json:"miners"`
|
||||
|
||||
// Id Available on LastBlock .TemplateId
|
||||
// Deprecated
|
||||
Id types.Hash `json:"id"`
|
||||
|
||||
// Height Available on LastBlock .SideHeight
|
||||
// Deprecated
|
||||
Height uint64 `json:"height"`
|
||||
|
||||
// Version Available via sidechain.P2PoolShareVersion
|
||||
// Deprecated
|
||||
Version sidechain.ShareVersion `json:"version"`
|
||||
|
||||
// Difficulty Available on LastBlock .Difficulty
|
||||
// Deprecated
|
||||
Difficulty types.Difficulty `json:"difficulty"`
|
||||
|
||||
// CumulativeDifficulty Available on LastBlock .CumulativeDifficulty
|
||||
// Deprecated
|
||||
CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"`
|
||||
|
||||
// Timestamp Available on LastBlock .Timestamp
|
||||
// Deprecated
|
||||
Timestamp uint64 `json:"timestamp"`
|
||||
|
||||
// WindowSize Available on Window .Blocks
|
||||
// Deprecated
|
||||
WindowSize int `json:"window_size"`
|
||||
|
||||
// MaxWindowSize Available on Consensus
|
||||
// Deprecated
|
||||
MaxWindowSize int `json:"max_window_size"`
|
||||
|
||||
// BlockTime Available on Consensus
|
||||
// Deprecated
|
||||
BlockTime int `json:"block_time"`
|
||||
|
||||
// UnclePenalty Available on Consensus
|
||||
// Deprecated
|
||||
UnclePenalty int `json:"uncle_penalty"`
|
||||
}
|
||||
|
||||
type PoolInfoResultSideChainEffortLastEntry struct {
|
||||
Id types.Hash `json:"id"`
|
||||
Effort float64 `json:"effort"`
|
||||
}
|
||||
|
||||
type PoolInfoResultSideChainEffort struct {
|
||||
Current float64 `json:"current"`
|
||||
Average10 float64 `json:"average10"`
|
||||
Average50 float64 `json:"average"`
|
||||
Average200 float64 `json:"average200"`
|
||||
Last []PoolInfoResultSideChainEffortLastEntry `json:"last"`
|
||||
}
|
||||
type PoolInfoResultSideChainWindow struct {
|
||||
// Miners Unique miners found in window
|
||||
Miners int `json:"miners"`
|
||||
// Blocks total count of blocks in the window, including uncles
|
||||
Blocks int `json:"blocks"`
|
||||
Uncles int `json:"uncles"`
|
||||
|
||||
// Top TemplateId of the window tip
|
||||
Top types.Hash `json:"top"`
|
||||
// Bottom TemplateId of the last non-uncle block in the window
|
||||
Bottom types.Hash `json:"bottom"`
|
||||
|
||||
// BottomUncles TemplateId of the uncles included under the last block, if any
|
||||
BottomUncles []types.Hash `json:"bottom_uncles,omitempty"`
|
||||
|
||||
Weight types.Difficulty `json:"weight"`
|
||||
Versions []SideChainVersionEntry `json:"versions"`
|
||||
}
|
||||
|
||||
type PoolInfoResultMainChainConsensus struct {
|
||||
BlockTime uint64 `json:"block_time"`
|
||||
TransactionUnlockTime uint64 `json:"transaction_unlock_time"`
|
||||
MinerRewardUnlockTime uint64 `json:"miner_reward_unlock_time"`
|
||||
|
||||
// HardForkSupportedVersion
|
||||
HardForkSupportedVersion uint8 `json:"hard_fork_supported_version"`
|
||||
// HardForks HardFork information for Monero known hardfork by backing p2pool
|
||||
HardForks []monero.HardFork `json:"hard_forks,omitempty"`
|
||||
}
|
||||
|
||||
type PoolInfoResultMainChain struct {
|
||||
Consensus PoolInfoResultMainChainConsensus `json:"consensus"`
|
||||
Id types.Hash `json:"id"`
|
||||
CoinbaseId types.Hash `json:"coinbase_id"`
|
||||
Height uint64 `json:"height"`
|
||||
Difficulty types.Difficulty `json:"difficulty"`
|
||||
Reward uint64 `json:"reward"`
|
||||
BaseReward uint64 `json:"base_reward"`
|
||||
|
||||
NextDifficulty types.Difficulty `json:"next_difficulty"`
|
||||
|
||||
// BlockTime included in Consensus
|
||||
// Deprecated
|
||||
BlockTime int `json:"block_time"`
|
||||
}
|
||||
|
||||
type MinerInfoBlockData struct {
|
||||
ShareCount uint64 `json:"shares"`
|
||||
UncleCount uint64 `json:"uncles"`
|
||||
LastShareHeight uint64 `json:"last_height"`
|
||||
}
|
||||
|
||||
type MinerInfoResult struct {
|
||||
Id uint64 `json:"id"`
|
||||
Address *address.Address `json:"address"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Shares [index.InclusionCount]MinerInfoBlockData `json:"shares"`
|
||||
LastShareHeight uint64 `json:"last_share_height"`
|
||||
LastShareTimestamp uint64 `json:"last_share_timestamp"`
|
||||
}
|
||||
|
||||
type TransactionLookupResult struct {
|
||||
Id types.Hash `json:"id"`
|
||||
Inputs index.TransactionInputQueryResults `json:"inputs"`
|
||||
Outs []client.Output `json:"outs"`
|
||||
Match []index.TransactionInputQueryResultsMatch `json:"matches"`
|
||||
}
|
||||
|
||||
type P2PoolSideChainStateResult struct {
|
||||
TipHeight uint64 `json:"tip_height"`
|
||||
TipId types.Hash `json:"tip_id"`
|
||||
Chain []P2PoolBinaryBlockResult `json:"chain"`
|
||||
Uncles []P2PoolBinaryBlockResult `json:"uncles"`
|
||||
}
|
||||
|
||||
type P2PoolBinaryBlockResult struct {
|
||||
Version int `json:"version"`
|
||||
Blob types.Bytes `json:"blob"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type P2PoolSpecialBinaryBlockResult struct {
|
||||
Version int `json:"version"`
|
||||
Blob types.Bytes `json:"blob"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type P2PoolSideChainStatusResult struct {
|
||||
Synchronized bool `json:"synchronized"`
|
||||
Height uint64 `json:"tip_height"`
|
||||
Id types.Hash `json:"tip_id"`
|
||||
Difficulty types.Difficulty `json:"difficulty"`
|
||||
CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"`
|
||||
Blocks int `json:"blocks"`
|
||||
}
|
||||
|
||||
type P2PoolServerStatusResult struct {
|
||||
PeerId uint64 `json:"peer_id"`
|
||||
SoftwareId string `json:"software_id"`
|
||||
SoftwareVersion string `json:"software_version"`
|
||||
ProtocolVersion string `json:"protocol_version"`
|
||||
ListenPort uint16 `json:"listen_port"`
|
||||
}
|
||||
|
||||
type P2PoolServerPeerResult struct {
|
||||
PeerId uint64 `json:"peer_id"`
|
||||
Incoming bool `json:"incoming"`
|
||||
Address string `json:"address"`
|
||||
SoftwareId string `json:"software_id"`
|
||||
SoftwareVersion string `json:"software_version"`
|
||||
ProtocolVersion string `json:"protocol_version"`
|
||||
ConnectionTime uint64 `json:"connection_time"`
|
||||
ListenPort uint32 `json:"listen_port"`
|
||||
Latency uint64 `json:"latency"`
|
||||
}
|
||||
|
||||
type P2PoolConnectionCheckInformation[TipType any] struct {
|
||||
Address string `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
ListenPort uint16 `json:"listen_port"`
|
||||
PeerId uint64 `json:"peer_id"`
|
||||
SoftwareId string `json:"software_id"`
|
||||
SoftwareVersion string `json:"software_version"`
|
||||
ProtocolVersion string `json:"protocol_version"`
|
||||
ConnectionTime uint64 `json:"connection_time"`
|
||||
Latency uint64 `json:"latency"`
|
||||
LastActive uint64 `json:"last_active"`
|
||||
Incoming bool `json:"incoming"`
|
||||
BroadcastTime uint64 `json:"broadcast_time"`
|
||||
BroadcastHeight uint64 `json:"broadcast_height"`
|
||||
// Tip is a sidechain.PoolBlock
|
||||
Tip TipType `json:"tip,omitempty"`
|
||||
Closed bool `json:"closed"`
|
||||
AlreadyConnected bool `json:"already_connected"`
|
||||
HandshakeComplete bool `json:"handshake_complete"`
|
||||
Banned bool `json:"banned"`
|
||||
Error string `json:"error,omitempty"`
|
||||
BanError string `json:"ban_error,omitempty"`
|
||||
}
|
739
api/p2pool.go
Normal file
739
api/p2pool.go
Normal file
|
@ -0,0 +1,739 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/block"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
"github.com/hashicorp/golang-lru/v2"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type P2PoolApi struct {
|
||||
Host string
|
||||
Client *http.Client
|
||||
consensus atomic.Pointer[sidechain.Consensus]
|
||||
derivationCache sidechain.DerivationCacheInterface
|
||||
difficultyByHeightCache *lru.Cache[uint64, types.Difficulty]
|
||||
}
|
||||
|
||||
func NewP2PoolApi(host string) *P2PoolApi {
|
||||
cache, err := lru.New[uint64, types.Difficulty](1024)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &P2PoolApi{
|
||||
Host: host,
|
||||
Client: &http.Client{
|
||||
Timeout: time.Second * 15,
|
||||
},
|
||||
derivationCache: sidechain.NewDerivationLRUCache(),
|
||||
difficultyByHeightCache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) WaitSync() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("panicked")
|
||||
}
|
||||
}()
|
||||
status := p.Status()
|
||||
for ; p == nil || !p.Status().Synchronized; status = p.Status() {
|
||||
if p == nil {
|
||||
utils.Logf("API", "Not synchronized (nil), waiting five seconds")
|
||||
} else {
|
||||
utils.Logf("API", "Not synchronized (height %d, id %s, blocks %d), waiting five seconds", status.Height, status.Id, status.Blocks)
|
||||
}
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
utils.Logf("API", "SYNCHRONIZED (height %d, id %s, blocks %d)", status.Height, status.Id, status.Blocks)
|
||||
utils.Logf("API", "Consensus id = %s\n", p.Consensus().Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) WaitSyncStart() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("panicked")
|
||||
}
|
||||
}()
|
||||
status := p.Status()
|
||||
for ; p == nil || !p.Status().Synchronized; status = p.Status() {
|
||||
if p == nil {
|
||||
utils.Logf("API", "Not synchronized (nil), waiting one seconds")
|
||||
} else {
|
||||
utils.Logf("API", "Not synchronized (height %d, id %s, blocks %d)", status.Height, status.Id, status.Blocks)
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
if status.Synchronized {
|
||||
utils.Logf("API", "SYNCHRONIZED (height %d, id %s, blocks %d)", status.Height, status.Id, status.Blocks)
|
||||
}
|
||||
utils.Logf("API", "Consensus id = %s\n", p.Consensus().Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) InsertAlternate(b *sidechain.PoolBlock) {
|
||||
buf, _ := b.MarshalBinary()
|
||||
uri, _ := url.Parse(p.Host + "/archive/insert_alternate")
|
||||
response, err := p.Client.Do(&http.Request{
|
||||
Method: "POST",
|
||||
URL: uri,
|
||||
Body: io.NopCloser(bytes.NewReader(buf)),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) LightByMainId(id types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/light_block_by_main_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
b := &sidechain.PoolBlock{}
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &b); err != nil || b.ShareVersion() == sidechain.ShareVersion_None {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) LightByMainIdWithHint(id, templateIdHint types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/light_block_by_main_id/" + id.String() + "?templateIdHint=" + templateIdHint.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
b := &sidechain.PoolBlock{}
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &b); err != nil || b.ShareVersion() == sidechain.ShareVersion_None {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) ByMainId(id types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/block_by_main_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || result.Version == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil || int(b.ShareVersion()) != result.Version {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) ByMainIdWithHint(id, templateIdHint types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/block_by_main_id/" + id.String() + "?templateIdHint=" + templateIdHint.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || result.Version == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil || int(b.ShareVersion()) != result.Version {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) LightByTemplateId(id types.Hash) sidechain.UniquePoolBlockSlice {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/light_blocks_by_template_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result sidechain.UniquePoolBlockSlice
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) ByTemplateId(id types.Hash) *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/block_by_template_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
} else if result.Version == 0 {
|
||||
// Fallback into archive
|
||||
if response, err := p.Client.Get(p.Host + "/archive/blocks_by_template_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result []P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, r := range result {
|
||||
//Get first block that matches
|
||||
if r.Version == 0 {
|
||||
continue
|
||||
}
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil || int(b.ShareVersion()) != r.Version {
|
||||
continue
|
||||
}
|
||||
return b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) LightBySideHeight(height uint64) sidechain.UniquePoolBlockSlice {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/light_blocks_by_side_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result sidechain.UniquePoolBlockSlice
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) BySideHeight(height uint64) sidechain.UniquePoolBlockSlice {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/blocks_by_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result []P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
} else if len(result) == 0 {
|
||||
// Fallback into archive
|
||||
if response, err := p.Client.Get(p.Host + "/archive/blocks_by_side_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result []P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
results := make([]*sidechain.PoolBlock, 0, len(result))
|
||||
for _, r := range result {
|
||||
if r.Version == 0 {
|
||||
return nil
|
||||
}
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil
|
||||
}
|
||||
results = append(results, b)
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results := make([]*sidechain.PoolBlock, 0, len(result))
|
||||
for _, r := range result {
|
||||
if r.Version == 0 {
|
||||
return nil
|
||||
}
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil
|
||||
}
|
||||
results = append(results, b)
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) LightByMainHeight(height uint64) sidechain.UniquePoolBlockSlice {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/light_blocks_by_main_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result sidechain.UniquePoolBlockSlice
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) ByMainHeight(height uint64) sidechain.UniquePoolBlockSlice {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/blocks_by_main_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result []P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
results := make([]*sidechain.PoolBlock, 0, len(result))
|
||||
for _, r := range result {
|
||||
if r.Version == 0 {
|
||||
return nil
|
||||
}
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil
|
||||
}
|
||||
results = append(results, b)
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) DifficultyByHeight(height uint64) types.Difficulty {
|
||||
if v, ok := p.difficultyByHeightCache.Get(height); !ok {
|
||||
if diff := p.MainDifficultyByHeight(height); diff != types.ZeroDifficulty {
|
||||
p.difficultyByHeightCache.Add(height, diff)
|
||||
return diff
|
||||
}
|
||||
return types.ZeroDifficulty
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) SeedByHeight(height uint64) types.Hash {
|
||||
seedHeight := randomx.SeedHeight(height)
|
||||
if v := p.MainHeaderByHeight(seedHeight); v != nil {
|
||||
return v.Id
|
||||
}
|
||||
return types.ZeroHash
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) PeerList() []byte {
|
||||
if response, err := p.Client.Get(p.Host + "/server/peerlist"); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
buf, err := io.ReadAll(response.Body)
|
||||
if err == nil {
|
||||
return buf
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) ConnectionCheck(addrPort netip.AddrPort) *P2PoolConnectionCheckInformation[*sidechain.PoolBlock] {
|
||||
if response, err := p.Client.Get(p.Host + "/server/connection_check/" + addrPort.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result P2PoolConnectionCheckInformation[*sidechain.PoolBlock]
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) MinerData() *p2pooltypes.MinerData {
|
||||
if response, err := p.Client.Get(p.Host + "/mainchain/miner_data"); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result p2pooltypes.MinerData
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) MainTip() *block.Header {
|
||||
if response, err := p.Client.Get(p.Host + "/mainchain/tip"); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result block.Header
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) MainHeaderById(id types.Hash) *block.Header {
|
||||
if response, err := p.Client.Get(p.Host + "/mainchain/header_by_id/" + id.String()); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result block.Header
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) MainHeaderByHeight(height uint64) *block.Header {
|
||||
if response, err := p.Client.Get(p.Host + "/mainchain/header_by_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result block.Header
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) MainDifficultyByHeight(height uint64) types.Difficulty {
|
||||
if response, err := p.Client.Get(p.Host + "/mainchain/difficulty_by_height/" + strconv.FormatUint(height, 10)); err != nil {
|
||||
return types.ZeroDifficulty
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return types.ZeroDifficulty
|
||||
} else {
|
||||
var result types.Difficulty
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return types.ZeroDifficulty
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) StateFromTemplateId(id types.Hash) (chain, uncles sidechain.UniquePoolBlockSlice) {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/state/" + id.String()); err != nil {
|
||||
return nil, nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil, nil
|
||||
} else {
|
||||
var result P2PoolSideChainStateResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chain = make([]*sidechain.PoolBlock, 0, len(result.Chain))
|
||||
uncles = make([]*sidechain.PoolBlock, 0, len(result.Uncles))
|
||||
|
||||
for _, r := range result.Chain {
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
chain = append(chain, b)
|
||||
}
|
||||
|
||||
for _, r := range result.Uncles {
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
uncles = append(uncles, b)
|
||||
}
|
||||
|
||||
return chain, uncles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) WindowFromTemplateId(id types.Hash) (chain, uncles sidechain.UniquePoolBlockSlice) {
|
||||
if response, err := p.Client.Get(p.Host + "/archive/window_from_template_id/" + id.String()); err != nil {
|
||||
return nil, nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil, nil
|
||||
} else {
|
||||
var result P2PoolSideChainStateResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chain = make([]*sidechain.PoolBlock, 0, len(result.Chain))
|
||||
uncles = make([]*sidechain.PoolBlock, 0, len(result.Uncles))
|
||||
|
||||
for _, r := range result.Chain {
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
chain = append(chain, b)
|
||||
}
|
||||
|
||||
for _, r := range result.Uncles {
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
uncles = append(uncles, b)
|
||||
}
|
||||
|
||||
return chain, uncles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) StateFromTip() (chain, uncles sidechain.UniquePoolBlockSlice) {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/state/tip"); err != nil {
|
||||
return nil, nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil, nil
|
||||
} else {
|
||||
var result P2PoolSideChainStateResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chain = make([]*sidechain.PoolBlock, 0, len(result.Chain))
|
||||
uncles = make([]*sidechain.PoolBlock, 0, len(result.Uncles))
|
||||
|
||||
for _, r := range result.Chain {
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
chain = append(chain, b)
|
||||
}
|
||||
|
||||
for _, r := range result.Uncles {
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
uncles = append(uncles, b)
|
||||
}
|
||||
|
||||
return chain, uncles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) Tip() *sidechain.PoolBlock {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/tip"); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
var result P2PoolBinaryBlockResult
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, &result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if result.Version == 0 {
|
||||
return nil
|
||||
}
|
||||
b := &sidechain.PoolBlock{}
|
||||
if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) getConsensus() *sidechain.Consensus {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/consensus"); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
c, _ := sidechain.NewConsensusFromJSON(buf)
|
||||
|
||||
return c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) Consensus() *sidechain.Consensus {
|
||||
if c := p.consensus.Load(); c == nil {
|
||||
if c = p.getConsensus(); c != nil {
|
||||
p.consensus.Store(c)
|
||||
}
|
||||
return c
|
||||
} else {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2PoolApi) Status() *P2PoolSideChainStatusResult {
|
||||
if response, err := p.Client.Get(p.Host + "/sidechain/status"); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
if buf, err := io.ReadAll(response.Body); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
result := &P2PoolSideChainStatusResult{}
|
||||
|
||||
if err = utils.UnmarshalJSON(buf, result); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
12
go.mod
12
go.mod
|
@ -3,10 +3,10 @@ module git.gammaspectra.live/P2Pool/observer-cmd-utils
|
|||
go 1.22
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.3.0
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.6.0
|
||||
git.gammaspectra.live/P2Pool/sha3 v0.17.0
|
||||
github.com/floatdrop/lru v1.3.0
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/jxskiss/base62 v1.1.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc
|
||||
|
@ -14,15 +14,17 @@ require (
|
|||
|
||||
require (
|
||||
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20240405085108-e2f706cb5c00 // indirect
|
||||
git.gammaspectra.live/P2Pool/go-randomx v0.0.0-20221027085532-f46adfce03a7 // indirect
|
||||
git.gammaspectra.live/P2Pool/go-randomx/v2 v2.1.0 // indirect
|
||||
git.gammaspectra.live/P2Pool/monero-base58 v1.0.0 // indirect
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20230514082649-9c5f18cd5a71 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v1.0.0 // indirect
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/dolthub/swiss v0.2.2-0.20240312182618-f4b2babd2bc1 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/goccy/go-json => github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887
|
||||
|
||||
replace github.com/go-zeromq/zmq4 => git.gammaspectra.live/P2Pool/zmq4 v0.16.1-0.20240412221749-a35fa84ca9f4
|
||||
|
|
22
go.sum
22
go.sum
|
@ -1,41 +1,39 @@
|
|||
git.gammaspectra.live/P2Pool/consensus/v3 v3.3.0 h1:UmdTohNb/KuGJSwR4p0NOIzRaw/do7xRQ0rR9PpSzNQ=
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.3.0/go.mod h1:pI4X4sieSwKWicOkRZ90UZEQeZbvd2swzYjwKfGp46Y=
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.6.0 h1:ICTC2jzsnfoXbFlpFK0U3pEIX04GL4ncmQ6MW4Xsz7A=
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.6.0/go.mod h1:3mjTaEx2Hm9VUs0rZIjCAFV6fToNxnVDj616NEc9Y0I=
|
||||
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20240405085108-e2f706cb5c00 h1:mDQY337iKB+kle5RYWL5CoAz+3DmnkAh/B2XD8B+PFk=
|
||||
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20240405085108-e2f706cb5c00/go.mod h1:FZsrMWGucMP3SZamzrd7m562geIs5zp1O/9MGoiAKH0=
|
||||
git.gammaspectra.live/P2Pool/go-randomx v0.0.0-20221027085532-f46adfce03a7 h1:bzHDuu1IgETKqPBOlIdCE2LaZIJ+ZpROSprNn+fnzd8=
|
||||
git.gammaspectra.live/P2Pool/go-randomx v0.0.0-20221027085532-f46adfce03a7/go.mod h1:3kT0v4AMwT/OdorfH2gRWPwoOrUX/LV03HEeBsaXG1c=
|
||||
git.gammaspectra.live/P2Pool/go-randomx/v2 v2.1.0 h1:L1fV2XBYFmpFU+JKP/7fsgDm2Lfh9yFlS+800v+3OsM=
|
||||
git.gammaspectra.live/P2Pool/go-randomx/v2 v2.1.0/go.mod h1:vNmHlEIRAcU/bA85mxbUKEiBYtrtS4MVwozf29KmoHM=
|
||||
git.gammaspectra.live/P2Pool/monero-base58 v1.0.0 h1:s8LZxVNc93YEs2NCCNWZ7CKr8RbEb031y6Wkvhn+TS4=
|
||||
git.gammaspectra.live/P2Pool/monero-base58 v1.0.0/go.mod h1:WWEJy/AdWKxKAruvlKI82brw+DtVlePy0ct3ZiBlc68=
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20230514082649-9c5f18cd5a71 h1:MgeHHcF+GnCJBWMSzq8XAbc8p/UhNwFruEKCPPJ74YQ=
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20230514082649-9c5f18cd5a71/go.mod h1:KQaYHIxGXNHNMQELC7xGLu8xouwvP/dN7iGk681BXmk=
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v1.0.0 h1:tajr4QFSPrb8NtHmU14JaXdhr+z+0RbOBLIgUDI5Tow=
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v1.0.0/go.mod h1:S17NNidG5hxqaVLsSykKqDBg/hTPSzP0KcSwXfH8WIA=
|
||||
git.gammaspectra.live/P2Pool/sha3 v0.17.0 h1:CZpB466LPbNVQrUNjQTtQScGDc30xSMkZ6Bmw0W9VuM=
|
||||
git.gammaspectra.live/P2Pool/sha3 v0.17.0/go.mod h1:HmrrYa97BZTKklUk2n/wAY+wrY0gHhoSGRd2+lIqXq8=
|
||||
github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887 h1:P01nqSM+0b6zlPasOFYsqnQSP2dTRVZkanAnY9q/Bcc=
|
||||
github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
||||
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
||||
github.com/dolthub/swiss v0.2.2-0.20240312182618-f4b2babd2bc1 h1:F7u1ZVCidajlPuJJzdL5REPHfLO/O6xLQRUw/YMxrkM=
|
||||
github.com/dolthub/swiss v0.2.2-0.20240312182618-f4b2babd2bc1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8cUrh0=
|
||||
github.com/floatdrop/lru v1.3.0 h1:83abtaKjXcWrPmtzTAk2Ggq8DUKqI29YzrTrB8+vu0c=
|
||||
github.com/floatdrop/lru v1.3.0/go.mod h1:83zlXKA06Bm32JImNINCiTr0ldadvdAjUe5jSwIaw0s=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
||||
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
@ -27,7 +27,7 @@ type FoundBlock struct {
|
|||
|
||||
func (b *FoundBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error {
|
||||
if err := row.Scan(
|
||||
&b.MainBlock.Id, &b.MainBlock.Height, &b.MainBlock.Timestamp, &b.MainBlock.Reward, &b.MainBlock.CoinbaseId, &b.MainBlock.CoinbasePrivateKey, &b.MainBlock.Difficulty, &b.MainBlock.SideTemplateId,
|
||||
&b.MainBlock.Id, &b.MainBlock.Height, &b.MainBlock.Timestamp, &b.MainBlock.Reward, &b.MainBlock.CoinbaseId, &b.MainBlock.CoinbasePrivateKey, &b.MainBlock.Difficulty, &b.MainBlock.SideTemplateId, &b.MainBlock.RootHash,
|
||||
&b.SideHeight, &b.Miner, &b.UncleOf, &b.EffectiveHeight, &b.WindowDepth, &b.WindowOutputs, &b.TransactionCount, &b.Difficulty, &b.CumulativeDifficulty, &b.Inclusion,
|
||||
); err != nil {
|
||||
return err
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
"github.com/floatdrop/lru"
|
||||
"github.com/hashicorp/golang-lru/v2"
|
||||
"github.com/lib/pq"
|
||||
"io"
|
||||
"path"
|
||||
|
@ -31,7 +31,7 @@ type Index struct {
|
|||
getSeedByHeight block.GetSeedByHeightFunc
|
||||
getByTemplateId sidechain.GetByTemplateIdFunc
|
||||
derivationCache sidechain.DerivationCacheInterface
|
||||
blockCache *lru.LRU[types.Hash, *sidechain.PoolBlock]
|
||||
blockCache *lru.Cache[types.Hash, *sidechain.PoolBlock]
|
||||
|
||||
handle *sql.DB
|
||||
statements struct {
|
||||
|
@ -63,13 +63,17 @@ var dbSchema string
|
|||
var dbFunctions embed.FS
|
||||
|
||||
func OpenIndex(connStr string, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getSeedByHeight block.GetSeedByHeightFunc, getByTemplateId sidechain.GetByTemplateIdFunc) (index *Index, err error) {
|
||||
cache, err := lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
index = &Index{
|
||||
consensus: consensus,
|
||||
getDifficultyByHeight: difficultyByHeight,
|
||||
getSeedByHeight: getSeedByHeight,
|
||||
getByTemplateId: getByTemplateId,
|
||||
derivationCache: sidechain.NewDerivationLRUCache(),
|
||||
blockCache: lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4)),
|
||||
blockCache: cache,
|
||||
views: make(map[string]string),
|
||||
}
|
||||
if index.handle, err = sql.Open("postgres", connStr); err != nil {
|
||||
|
@ -208,7 +212,7 @@ func OpenIndex(connStr string, consensus *sidechain.Consensus, difficultyByHeigh
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if index.statements.InsertOrUpdateSideBlock, err = index.handle.Prepare("INSERT INTO side_blocks (" + SideBlockSelectFields + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) ON CONFLICT (main_id) DO UPDATE SET uncle_of = $7, effective_height = $8, inclusion = $21;"); err != nil {
|
||||
if index.statements.InsertOrUpdateSideBlock, err = index.handle.Prepare("INSERT INTO side_blocks (" + SideBlockSelectFields + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) ON CONFLICT (main_id) DO UPDATE SET uncle_of = $7, effective_height = $8, inclusion = $22;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -257,11 +261,11 @@ func (i *Index) GetSeedByHeight(height uint64) types.Hash {
|
|||
}
|
||||
|
||||
func (i *Index) GetByTemplateId(id types.Hash) *sidechain.PoolBlock {
|
||||
if v := i.blockCache.Get(id); v != nil {
|
||||
return *v
|
||||
if v, ok := i.blockCache.Get(id); ok && v != nil {
|
||||
return v
|
||||
} else {
|
||||
if b := i.getByTemplateId(id); b != nil {
|
||||
i.blockCache.Set(id, b)
|
||||
i.blockCache.Add(id, b)
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +274,7 @@ func (i *Index) GetByTemplateId(id types.Hash) *sidechain.PoolBlock {
|
|||
}
|
||||
|
||||
func (i *Index) CachePoolBlock(b *sidechain.PoolBlock) {
|
||||
i.blockCache.Set(b.SideTemplateId(i.consensus), b)
|
||||
i.blockCache.Add(b.SideTemplateId(i.consensus), b)
|
||||
}
|
||||
|
||||
func (i *Index) Consensus() *sidechain.Consensus {
|
||||
|
@ -714,7 +718,9 @@ func (i *Index) InsertOrUpdateSideBlock(b *SideBlock) error {
|
|||
if oldBlock := i.GetTipSideBlockByHeight(b.SideHeight); oldBlock != nil {
|
||||
if oldBlock.MainId != b.MainId {
|
||||
//conflict resolution, change other block status
|
||||
if oldBlock.TemplateId == oldBlock.TemplateId {
|
||||
if oldBlock.TemplateId == b.TemplateId {
|
||||
oldBlock.Inclusion = InclusionAlternateInVerifiedChain
|
||||
} else if oldBlock.RootHash != types.ZeroHash && oldBlock.RootHash == b.RootHash {
|
||||
oldBlock.Inclusion = InclusionAlternateInVerifiedChain
|
||||
} else {
|
||||
//mark as orphan if templates don't match. if uncle it will be adjusted later
|
||||
|
@ -738,6 +744,7 @@ func (i *Index) InsertOrUpdateSideBlock(b *SideBlock) error {
|
|||
&b.MainId,
|
||||
b.MainHeight,
|
||||
&b.TemplateId,
|
||||
&b.RootHash,
|
||||
b.SideHeight,
|
||||
parentId,
|
||||
b.Miner,
|
||||
|
@ -789,7 +796,7 @@ func (i *Index) InsertOrUpdateMainBlock(b *MainBlock) error {
|
|||
} else {
|
||||
defer tx.Rollback()
|
||||
if _, err := tx.Exec(
|
||||
"INSERT INTO main_blocks (id, height, timestamp, reward, coinbase_id, difficulty, metadata, side_template_id, coinbase_private_key) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9) ON CONFLICT (id) DO UPDATE SET metadata = $7, side_template_id = $8, coinbase_private_key = $9;",
|
||||
"INSERT INTO main_blocks (id, height, timestamp, reward, coinbase_id, difficulty, metadata, side_template_id, root_hash, coinbase_private_key) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9, $10) ON CONFLICT (id) DO UPDATE SET metadata = $7, side_template_id = $8, root_hash = $9, coinbase_private_key = $10;",
|
||||
b.Id[:],
|
||||
b.Height,
|
||||
b.Timestamp,
|
||||
|
@ -798,6 +805,7 @@ func (i *Index) InsertOrUpdateMainBlock(b *MainBlock) error {
|
|||
b.Difficulty,
|
||||
metadataJson,
|
||||
&b.SideTemplateId,
|
||||
&b.RootHash,
|
||||
&b.CoinbasePrivateKey,
|
||||
); err != nil {
|
||||
return err
|
||||
|
@ -1366,7 +1374,7 @@ func (i *Index) preProcessPoolBlock(b *sidechain.PoolBlock) error {
|
|||
var preAllocatedShares sidechain.Shares
|
||||
if len(b.Main.Coinbase.Outputs) == 0 {
|
||||
//cannot use SideTemplateId() as it might not be proper to calculate yet. fetch from coinbase only here
|
||||
if b2 := i.GetByTemplateId(types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId))); b2 != nil && len(b2.Main.Coinbase.Outputs) != 0 {
|
||||
if b2 := i.GetByTemplateId(b.FastSideTemplateId(i.Consensus())); b2 != nil && len(b2.Main.Coinbase.Outputs) != 0 {
|
||||
b.Main.Coinbase.Outputs = b2.Main.Coinbase.Outputs
|
||||
} else {
|
||||
preAllocatedShares = sidechain.PreAllocateShares(i.consensus.ChainWindowSize * 2)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
)
|
||||
|
||||
const MainBlockSelectFields = "id, height, timestamp, reward, coinbase_id, difficulty, metadata, side_template_id, coinbase_private_key"
|
||||
const MainBlockSelectFields = "id, height, timestamp, reward, coinbase_id, difficulty, metadata, side_template_id, root_hash, coinbase_private_key"
|
||||
|
||||
type MainBlock struct {
|
||||
Id types.Hash `json:"id"`
|
||||
|
@ -23,6 +23,8 @@ type MainBlock struct {
|
|||
// sidechain data for blocks we own
|
||||
// SideTemplateId can be NULL
|
||||
SideTemplateId types.Hash `json:"side_template_id,omitempty"`
|
||||
// RootHash can be NULL
|
||||
RootHash types.Hash `json:"root_hash,omitempty"`
|
||||
// CoinbasePrivateKey private key for coinbase outputs we own (all 0x00 = not known, but should have one)
|
||||
CoinbasePrivateKey crypto.PrivateKeyBytes `json:"coinbase_private_key,omitempty"`
|
||||
}
|
||||
|
@ -38,7 +40,7 @@ func (b *MainBlock) SetMetadata(key string, v any) {
|
|||
func (b *MainBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error {
|
||||
var metadataBuf []byte
|
||||
b.Metadata = make(map[string]any)
|
||||
if err := row.Scan(&b.Id, &b.Height, &b.Timestamp, &b.Reward, &b.CoinbaseId, &b.Difficulty, &metadataBuf, &b.SideTemplateId, &b.CoinbasePrivateKey); err != nil {
|
||||
if err := row.Scan(&b.Id, &b.Height, &b.Timestamp, &b.Reward, &b.CoinbaseId, &b.Difficulty, &metadataBuf, &b.SideTemplateId, &b.RootHash, &b.CoinbasePrivateKey); err != nil {
|
||||
return err
|
||||
} else if err = utils.UnmarshalJSON(metadataBuf, &b.Metadata); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
-- Upgrades
|
||||
|
||||
-- v0 to v1
|
||||
ALTER TABLE IF EXISTS side_blocks ADD COLUMN IF NOT EXISTS root_hash bytea DEFAULT NULL;
|
||||
ALTER TABLE IF EXISTS main_blocks ADD COLUMN IF NOT EXISTS root_hash bytea UNIQUE DEFAULT NULL;
|
||||
|
||||
-- Creation
|
||||
|
||||
CREATE TABLE IF NOT EXISTS miners (
|
||||
id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
alias varchar UNIQUE DEFAULT NULL,
|
||||
|
@ -22,6 +30,7 @@ CREATE TABLE IF NOT EXISTS side_blocks (
|
|||
main_height bigint NOT NULL, -- mainchain height
|
||||
|
||||
template_id bytea NOT NULL, -- sidechain template id. Note multiple blocks can exist per template id, see inclusion
|
||||
root_hash bytea DEFAULT NULL, -- root hash. Note multiple blocks can exist per root hash, see inclusion. Can be NULL
|
||||
side_height bigint NOT NULL, -- sidechain height
|
||||
parent_template_id bytea NOT NULL, -- previous sidechain template id
|
||||
|
||||
|
@ -60,6 +69,7 @@ CREATE TABLE IF NOT EXISTS side_blocks (
|
|||
|
||||
CREATE INDEX IF NOT EXISTS side_blocks_miner_idx ON side_blocks (miner);
|
||||
CREATE INDEX IF NOT EXISTS side_blocks_template_id_idx ON side_blocks (template_id);
|
||||
CREATE INDEX IF NOT EXISTS side_blocks_root_hash_idx ON side_blocks (root_hash);
|
||||
CREATE INDEX IF NOT EXISTS side_blocks_main_height_idx ON side_blocks (main_height);
|
||||
CREATE INDEX IF NOT EXISTS side_blocks_side_height_idx ON side_blocks (side_height);
|
||||
CREATE INDEX IF NOT EXISTS side_blocks_parent_template_id_idx ON side_blocks (parent_template_id);
|
||||
|
@ -83,6 +93,7 @@ CREATE TABLE IF NOT EXISTS main_blocks (
|
|||
metadata jsonb NOT NULL DEFAULT '{}', -- metadata such as pool ownership, links to other p2pool networks, and other interesting data
|
||||
-- sidechain data for blocks we own
|
||||
side_template_id bytea UNIQUE DEFAULT NULL,
|
||||
root_hash bytea UNIQUE DEFAULT NULL,
|
||||
coinbase_private_key bytea DEFAULT NULL -- private key for coinbase outputs (all 0x00 = not known, but should have one)
|
||||
|
||||
-- Cannot have non-unique constraints
|
||||
|
@ -141,7 +152,7 @@ CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
|||
-- Views
|
||||
CREATE EXTENSION IF NOT EXISTS pg_ivm;
|
||||
|
||||
SELECT create_immv('found_main_blocks_v4', $$
|
||||
SELECT create_immv('found_main_blocks_v5', $$
|
||||
SELECT
|
||||
m.id AS main_id,
|
||||
m.height AS main_height,
|
||||
|
@ -151,6 +162,7 @@ SELECT
|
|||
m.coinbase_private_key AS coinbase_private_key,
|
||||
m.difficulty AS main_difficulty,
|
||||
m.side_template_id AS template_id,
|
||||
m.root_hash AS root_hash,
|
||||
s.side_height AS side_height,
|
||||
s.miner AS miner,
|
||||
s.uncle_of AS uncle_of,
|
||||
|
@ -167,9 +179,9 @@ FROM
|
|||
(SELECT * FROM side_blocks) AS s ON s.main_id = m.id
|
||||
$$);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS found_main_blocks_main_id_idx ON found_main_blocks_v4 (main_id);
|
||||
CREATE INDEX IF NOT EXISTS found_main_blocks_miner_idx ON found_main_blocks_v4 (miner);
|
||||
CREATE INDEX IF NOT EXISTS found_main_blocks_side_height_idx ON found_main_blocks_v4 (side_height);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS found_main_blocks_main_id_idx ON found_main_blocks_v5 (main_id);
|
||||
CREATE INDEX IF NOT EXISTS found_main_blocks_miner_idx ON found_main_blocks_v5 (miner);
|
||||
CREATE INDEX IF NOT EXISTS found_main_blocks_side_height_idx ON found_main_blocks_v5 (side_height);
|
||||
|
||||
SELECT create_immv('payouts_v4', $$
|
||||
SELECT
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package index
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/address"
|
||||
|
@ -26,7 +25,7 @@ const (
|
|||
InclusionCount
|
||||
)
|
||||
|
||||
const SideBlockSelectFields = "main_id, main_height, template_id, side_height, parent_template_id, miner, uncle_of, effective_height, nonce, extra_nonce, timestamp, software_id, software_version, window_depth, window_outputs, transaction_count, difficulty, cumulative_difficulty, pow_difficulty, pow_hash, inclusion"
|
||||
const SideBlockSelectFields = "main_id, main_height, template_id, root_hash, side_height, parent_template_id, miner, uncle_of, effective_height, nonce, extra_nonce, timestamp, software_id, software_version, window_depth, window_outputs, transaction_count, difficulty, cumulative_difficulty, pow_difficulty, pow_hash, inclusion"
|
||||
|
||||
type SideBlock struct {
|
||||
// MainId mainchain id, on Monero network
|
||||
|
@ -35,7 +34,10 @@ type SideBlock struct {
|
|||
|
||||
// TemplateId -- sidechain template id. Note multiple blocks can exist per template id, see Inclusion
|
||||
TemplateId types.Hash `json:"template_id"`
|
||||
SideHeight uint64 `json:"side_height"`
|
||||
// RootHash -- root hash. Note multiple blocks can exist per root hash, see Inclusion
|
||||
RootHash types.Hash `json:"root_hash,omitempty"`
|
||||
|
||||
SideHeight uint64 `json:"side_height"`
|
||||
// ParentTemplateId previous sidechain template id
|
||||
ParentTemplateId types.Hash `json:"parent_template_id"`
|
||||
|
||||
|
@ -96,11 +98,12 @@ type SideBlockUncleEntry struct {
|
|||
func (b *SideBlock) FromPoolBlock(i *Index, block *sidechain.PoolBlock, getSeedByHeight mainblock.GetSeedByHeightFunc) error {
|
||||
b.MainId = block.MainId()
|
||||
b.TemplateId = block.SideTemplateId(i.consensus)
|
||||
b.RootHash = block.MergeMiningTag().RootHash
|
||||
|
||||
if b.MainId == types.ZeroHash {
|
||||
return errors.New("invalid main id")
|
||||
}
|
||||
if b.TemplateId == types.ZeroHash || bytes.Compare(b.TemplateId[:], block.CoinbaseExtra(sidechain.SideTemplateId)) != 0 {
|
||||
if b.TemplateId == types.ZeroHash || b.TemplateId != block.FastSideTemplateId(i.Consensus()) {
|
||||
return errors.New("invalid template id")
|
||||
}
|
||||
b.MainHeight = block.Main.Coinbase.GenHeight
|
||||
|
@ -164,7 +167,7 @@ func (b *SideBlock) IsOrphan() bool {
|
|||
}
|
||||
|
||||
func (b *SideBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error {
|
||||
if err := row.Scan(&b.MainId, &b.MainHeight, &b.TemplateId, &b.SideHeight, &b.ParentTemplateId, &b.Miner, &b.UncleOf, &b.EffectiveHeight, &b.Nonce, &b.ExtraNonce, &b.Timestamp, &b.SoftwareId, &b.SoftwareVersion, &b.WindowDepth, &b.WindowOutputs, &b.TransactionCount, &b.Difficulty, &b.CumulativeDifficulty, &b.PowDifficulty, &b.PowHash, &b.Inclusion); err != nil {
|
||||
if err := row.Scan(&b.MainId, &b.MainHeight, &b.TemplateId, &b.RootHash, &b.SideHeight, &b.ParentTemplateId, &b.Miner, &b.UncleOf, &b.EffectiveHeight, &b.Nonce, &b.ExtraNonce, &b.Timestamp, &b.SoftwareId, &b.SoftwareVersion, &b.WindowDepth, &b.WindowOutputs, &b.TransactionCount, &b.Difficulty, &b.CumulativeDifficulty, &b.PowDifficulty, &b.PowHash, &b.Inclusion); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -427,7 +427,7 @@ func CalculateOutputs(indexDb *Index, block *SideBlock, transactionOutputType ui
|
|||
}
|
||||
}()
|
||||
|
||||
utils.SplitWork(-2, n, func(workIndex uint64, workerIndex int) error {
|
||||
err := utils.SplitWork(-2, n, func(workIndex uint64, workerIndex int) error {
|
||||
output := transaction.Output{
|
||||
Index: workIndex,
|
||||
Type: txType,
|
||||
|
@ -441,7 +441,11 @@ func CalculateOutputs(indexDb *Index, block *SideBlock, transactionOutputType ui
|
|||
}, func(routines, routineIndex int) error {
|
||||
hashers = append(hashers, crypto.GetKeccak256Hasher())
|
||||
return nil
|
||||
}, nil)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
return outputs, bottomHeight
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/address"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
|
@ -171,7 +172,7 @@ type PoolInfoResultMainChainConsensus struct {
|
|||
// HardForkSupportedVersion
|
||||
HardForkSupportedVersion uint8 `json:"hard_fork_supported_version"`
|
||||
// HardForks HardFork information for Monero known hardfork by backing p2pool
|
||||
HardForks []sidechain.HardFork `json:"hard_forks,omitempty"`
|
||||
HardForks []monero.HardFork `json:"hard_forks,omitempty"`
|
||||
}
|
||||
|
||||
type PoolInfoResultMainChain struct {
|
|
@ -1,33 +1,36 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/merge_mining"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/block"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client/rpc/daemon"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
"git.gammaspectra.live/P2Pool/observer-cmd-utils/index"
|
||||
"github.com/floatdrop/lru"
|
||||
"github.com/hashicorp/golang-lru/v2"
|
||||
"math"
|
||||
)
|
||||
|
||||
// keep last few main blocks
|
||||
var mainBlockCache = lru.New[types.Hash, *block.Block](100)
|
||||
var mainBlockCache, _ = lru.New[types.Hash, *block.Block](100)
|
||||
|
||||
func GetMainBlock(id types.Hash, client *client.Client) (*block.Block, error) {
|
||||
if bb := mainBlockCache.Get(id); bb == nil {
|
||||
if bb, ok := mainBlockCache.Get(id); !ok {
|
||||
blockData, err := client.GetBlock(id, context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mainBlock := &block.Block{}
|
||||
err = mainBlock.UnmarshalBinary(blockData.Blob)
|
||||
err = mainBlock.UnmarshalBinary(blockData.Blob, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -35,10 +38,10 @@ func GetMainBlock(id types.Hash, client *client.Client) (*block.Block, error) {
|
|||
//should never happen from monero itself
|
||||
return nil, errors.New("outputs not filled")
|
||||
}
|
||||
mainBlockCache.Set(id, mainBlock)
|
||||
mainBlockCache.Add(id, mainBlock)
|
||||
return mainBlock, nil
|
||||
} else {
|
||||
return *bb, nil
|
||||
return bb, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +68,7 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
// Find possible candidates that match on main height, main previous id, header nonce, timestamp, and count of transactions to reduce full block lookups
|
||||
var candidates sidechain.UniquePoolBlockSlice
|
||||
for _, b := range getByMainHeight(h.Height) {
|
||||
if b.Main.PreviousId == h.PrevHash && b.Main.Coinbase.TotalReward == h.Reward && b.Main.Timestamp == uint64(h.Timestamp) && len(b.Main.Transactions) == int(h.NumTxes) {
|
||||
if b.Main.PreviousId == h.PrevHash && b.Main.Coinbase.AuxiliaryData.TotalReward == h.Reward && b.Main.Timestamp == uint64(h.Timestamp) && len(b.Main.Transactions) == int(h.NumTxes) {
|
||||
// candidate for checking
|
||||
candidates = append(candidates, b)
|
||||
}
|
||||
|
@ -90,7 +93,9 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
}
|
||||
return nil
|
||||
}
|
||||
var sideTemplateId types.Hash
|
||||
|
||||
var possibleId types.Hash
|
||||
|
||||
var extraNonce []byte
|
||||
|
||||
// try to fetch extra nonce if existing
|
||||
|
@ -104,14 +109,48 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
|
||||
// get a merge tag if existing
|
||||
{
|
||||
if t := mainBlock.Coinbase.Extra.GetTag(uint8(sidechain.SideTemplateId)); t != nil {
|
||||
if len(t.Data) == types.HashSize {
|
||||
sideTemplateId = types.HashFromBytes(t.Data)
|
||||
}
|
||||
mergeMineTag := mainBlock.Coinbase.Extra.GetTag(transaction.TxExtraTagMergeMining)
|
||||
if mergeMineTag != nil {
|
||||
func() {
|
||||
shareVersion := sidechain.P2PoolShareVersion(indexDb.Consensus(), mainBlock.Timestamp)
|
||||
if shareVersion < sidechain.ShareVersion_V3 {
|
||||
//TODO: this is to comply with non-standard p2pool serialization, see https://github.com/SChernykh/p2pool/issues/249
|
||||
if mergeMineTag.VarInt != types.HashSize {
|
||||
return
|
||||
}
|
||||
|
||||
if len(mergeMineTag.Data) != types.HashSize {
|
||||
return
|
||||
}
|
||||
|
||||
// sidechain id
|
||||
possibleId = types.HashFromBytes(mergeMineTag.Data)
|
||||
} else {
|
||||
// fallback for "old" non forked blocks
|
||||
if len(mergeMineTag.Data) == types.HashSize && mergeMineTag.VarInt == types.HashSize {
|
||||
// sidechain id
|
||||
possibleId = types.HashFromBytes(mergeMineTag.Data)
|
||||
return
|
||||
}
|
||||
|
||||
//properly decode merge mining tag
|
||||
mergeMineReader := bytes.NewReader(mergeMineTag.Data)
|
||||
var mergeMiningTag merge_mining.Tag
|
||||
if err := mergeMiningTag.FromReader(mergeMineReader); err != nil {
|
||||
return
|
||||
}
|
||||
if mergeMineReader.Len() != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
possibleId = mergeMiningTag.RootHash
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if sideTemplateId == types.ZeroHash || len(extraNonce) == 0 {
|
||||
if possibleId == types.ZeroHash || len(extraNonce) == 0 {
|
||||
utils.Logf("Monero", "checking block %s at height %d: not a p2pool block", h.Hash, h.Height)
|
||||
if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil {
|
||||
return err
|
||||
|
@ -119,7 +158,7 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
return nil
|
||||
}
|
||||
|
||||
if t := getByTemplateId(sideTemplateId); t != nil {
|
||||
if t := getByTemplateId(possibleId); t != nil {
|
||||
//found template
|
||||
if err := processBlock(t); err != nil {
|
||||
return fmt.Errorf("could not process template: %w", err)
|
||||
|
@ -175,12 +214,14 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
t.Depth.Store(math.MaxUint64)
|
||||
storeFunc(t)
|
||||
|
||||
indexMain.SideTemplateId = sideTemplateId
|
||||
indexMain.SideTemplateId = t.FastSideTemplateId(indexDb.Consensus())
|
||||
indexMain.RootHash = t.MergeMiningTag().RootHash
|
||||
|
||||
indexMain.CoinbasePrivateKey = t.Side.CoinbasePrivateKey
|
||||
if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.Logf("Monero", "INSERTED found block %s at height %d, template id %s", h.Hash, h.Height, sideTemplateId)
|
||||
utils.Logf("Monero", "INSERTED found block %s at height %d, template id %s", h.Hash, h.Height, indexMain.SideTemplateId)
|
||||
if err := FindAndInsertMainHeaderOutputs(indexMain, indexDb, client, difficultyByHeight, getByTemplateId, getByMainId, getByMainHeight, processBlock); err != nil {
|
||||
utils.Errorf("", "error inserting coinbase outputs: %s", err)
|
||||
}
|
||||
|
@ -206,19 +247,21 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
sideBlock, _, err = indexDb.GetSideBlockFromPoolBlock(newBlock, index.InclusionInVerifiedChain)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not process %s, at side height %d, template id %s: %w", types.HashFromBytes(t.CoinbaseExtra(sidechain.SideTemplateId)), t.Side.Height, sideTemplateId, err)
|
||||
return fmt.Errorf("could not process %s, at side height %d, template id %s: %w", t.FastSideTemplateId(indexDb.Consensus()), t.Side.Height, t.FastSideTemplateId(indexDb.Consensus()), err)
|
||||
}
|
||||
|
||||
if err := indexDb.InsertOrUpdateSideBlock(sideBlock); err != nil {
|
||||
return fmt.Errorf("error inserting %s, %s at %d: %s", sideBlock.TemplateId, sideBlock.MainId, sideBlock.SideHeight, err)
|
||||
}
|
||||
|
||||
indexMain.SideTemplateId = sideTemplateId
|
||||
indexMain.SideTemplateId = t.FastSideTemplateId(indexDb.Consensus())
|
||||
indexMain.RootHash = t.MergeMiningTag().RootHash
|
||||
|
||||
indexMain.CoinbasePrivateKey = newBlock.Side.CoinbasePrivateKey
|
||||
if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.Logf("", "INSERTED ALTERNATE found block %s at height %d, template id %s", h.Hash, h.Height, sideTemplateId)
|
||||
utils.Logf("", "INSERTED ALTERNATE found block %s at height %d, template id %s", h.Hash, h.Height, indexMain.SideTemplateId)
|
||||
if err := FindAndInsertMainHeaderOutputs(indexMain, indexDb, client, difficultyByHeight, getByTemplateId, getByMainId, getByMainHeight, processBlock); err != nil {
|
||||
utils.Errorf("", "error inserting coinbase outputs: %s", err)
|
||||
}
|
||||
|
@ -227,13 +270,13 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
}
|
||||
}
|
||||
|
||||
utils.Logf("", "checking block %s at height %d, template id %s: could not find matching template id", h.Hash, h.Height, sideTemplateId)
|
||||
utils.Logf("", "checking block %s at height %d, possible id %s: could not find matching possible id", h.Hash, h.Height, possibleId)
|
||||
|
||||
// could not find template, maybe it's other pool?
|
||||
|
||||
// fill template id for future reference
|
||||
// TODO: do this for all blocks
|
||||
indexMain.SetMetadata("merge_mining_tag", sideTemplateId)
|
||||
indexMain.SetMetadata("merge_mining_tag", possibleId)
|
||||
indexMain.SetMetadata("extra_nonce", binary.LittleEndian.Uint32(extraNonce))
|
||||
|
||||
if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil {
|
||||
|
|
|
@ -5,7 +5,9 @@ import (
|
|||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/merge_mining"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
|
@ -120,7 +122,6 @@ func init() {
|
|||
|
||||
func ProcessFullBlock(b *index.MainBlock, indexDb *index.Index) error {
|
||||
|
||||
var sideTemplateId types.Hash
|
||||
var extraNonce []byte
|
||||
mainBlock, err := GetMainBlock(b.Id, client.GetDefaultClient())
|
||||
if err != nil {
|
||||
|
@ -138,19 +139,55 @@ func ProcessFullBlock(b *index.MainBlock, indexDb *index.Index) error {
|
|||
}
|
||||
}
|
||||
|
||||
var possibleId types.Hash
|
||||
|
||||
// get a merge tag if existing
|
||||
{
|
||||
if t := mainBlock.Coinbase.Extra.GetTag(uint8(sidechain.SideTemplateId)); t != nil {
|
||||
if len(t.Data) == types.HashSize {
|
||||
sideTemplateId = types.HashFromBytes(t.Data)
|
||||
}
|
||||
mergeMineTag := mainBlock.Coinbase.Extra.GetTag(transaction.TxExtraTagMergeMining)
|
||||
if mergeMineTag != nil {
|
||||
func() {
|
||||
shareVersion := sidechain.P2PoolShareVersion(indexDb.Consensus(), mainBlock.Timestamp)
|
||||
if shareVersion < sidechain.ShareVersion_V3 {
|
||||
//TODO: this is to comply with non-standard p2pool serialization, see https://github.com/SChernykh/p2pool/issues/249
|
||||
if mergeMineTag.VarInt != types.HashSize {
|
||||
return
|
||||
}
|
||||
|
||||
if len(mergeMineTag.Data) != types.HashSize {
|
||||
return
|
||||
}
|
||||
|
||||
// sidechain id
|
||||
possibleId = types.HashFromBytes(mergeMineTag.Data)
|
||||
} else {
|
||||
// fallback for "old" non forked blocks
|
||||
if len(mergeMineTag.Data) == types.HashSize && mergeMineTag.VarInt == types.HashSize {
|
||||
// sidechain id
|
||||
possibleId = types.HashFromBytes(mergeMineTag.Data)
|
||||
return
|
||||
}
|
||||
|
||||
//properly decode merge mining tag
|
||||
mergeMineReader := bytes.NewReader(mergeMineTag.Data)
|
||||
var mergeMiningTag merge_mining.Tag
|
||||
if err := mergeMiningTag.FromReader(mergeMineReader); err != nil {
|
||||
return
|
||||
}
|
||||
if mergeMineReader.Len() != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
possibleId = mergeMiningTag.RootHash
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if sideTemplateId != types.ZeroHash && len(extraNonce) != 0 {
|
||||
b.SetMetadata("merge_mining_tag", sideTemplateId)
|
||||
if possibleId != types.ZeroHash && len(extraNonce) != 0 {
|
||||
b.SetMetadata("merge_mining_tag", possibleId)
|
||||
b.SetMetadata("extra_nonce", binary.LittleEndian.Uint32(extraNonce))
|
||||
utils.Logf("", "p2pool tags found template id %s, extra nonce %d", sideTemplateId, binary.LittleEndian.Uint32(extraNonce))
|
||||
utils.Logf("", "p2pool tags found possible id %s, extra nonce %d", possibleId, binary.LittleEndian.Uint32(extraNonce))
|
||||
}
|
||||
|
||||
const txInputThreshold = 4
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/address"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
"git.gammaspectra.live/P2Pool/observer-cmd-utils/api"
|
||||
"git.gammaspectra.live/P2Pool/observer-cmd-utils/index"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -228,8 +229,8 @@ func SendSideBlock(w *index.MinerWebHook, ts int64, source *address.Address, blo
|
|||
},
|
||||
})
|
||||
case index.WebHookCustom:
|
||||
return SendJsonPost(w, ts, source, &JSONEvent{
|
||||
Type: JSONEventSideBlock,
|
||||
return SendJsonPost(w, ts, source, &api.JSONEvent{
|
||||
Type: api.JSONEventSideBlock,
|
||||
SideBlock: block,
|
||||
})
|
||||
default:
|
||||
|
@ -244,8 +245,8 @@ func SendFoundBlock(w *index.MinerWebHook, ts int64, source *address.Address, bl
|
|||
|
||||
switch w.Type {
|
||||
case index.WebHookCustom:
|
||||
return SendJsonPost(w, ts, source, &JSONEvent{
|
||||
Type: JSONEventFoundBlock,
|
||||
return SendJsonPost(w, ts, source, &api.JSONEvent{
|
||||
Type: api.JSONEventFoundBlock,
|
||||
FoundBlock: block,
|
||||
MainCoinbaseOutputs: outputs,
|
||||
})
|
||||
|
@ -331,8 +332,8 @@ func SendPayout(w *index.MinerWebHook, ts int64, source *address.Address, payout
|
|||
},
|
||||
})
|
||||
case index.WebHookCustom:
|
||||
return SendJsonPost(w, ts, source, &JSONEvent{
|
||||
Type: JSONEventPayout,
|
||||
return SendJsonPost(w, ts, source, &api.JSONEvent{
|
||||
Type: api.JSONEventPayout,
|
||||
Payout: payout,
|
||||
})
|
||||
default:
|
||||
|
@ -347,8 +348,8 @@ func SendOrphanedBlock(w *index.MinerWebHook, ts int64, source *address.Address,
|
|||
|
||||
switch w.Type {
|
||||
case index.WebHookCustom:
|
||||
return SendJsonPost(w, ts, source, &JSONEvent{
|
||||
Type: JSONEventOrphanedBlock,
|
||||
return SendJsonPost(w, ts, source, &api.JSONEvent{
|
||||
Type: api.JSONEventOrphanedBlock,
|
||||
SideBlock: block,
|
||||
})
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue