Support p2pool hardfork in March
This commit is contained in:
parent
92b74f667f
commit
968b07ae0b
|
@ -736,7 +736,7 @@ func main() {
|
||||||
|
|
||||||
if buf, err := hex.DecodeString(string(s)); err == nil {
|
if buf, err := hex.DecodeString(string(s)); err == nil {
|
||||||
|
|
||||||
raw, _ = sidechain.NewShareFromExportedBytes(buf)
|
raw, _ = sidechain.NewShareFromExportedBytes(buf, sidechain.NetworkMainnet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ var previousId, _ = types.HashFromString("d59abce89ce8131eba025988d1ea372937f2ac
|
||||||
var detTxPriv, _ = types.HashFromString("10f3941fd50ca266d3350984004a804c887c36ec11620080fe0b7c4a2a208605")
|
var detTxPriv, _ = types.HashFromString("10f3941fd50ca266d3350984004a804c887c36ec11620080fe0b7c4a2a208605")
|
||||||
|
|
||||||
func TestDeterministic(t *testing.T) {
|
func TestDeterministic(t *testing.T) {
|
||||||
detTx := types.HashFromBytes(GetDeterministicTransactionPrivateKey(testAddress2, previousId).AsSlice())
|
detTx := types.HashFromBytes(GetDeterministicTransactionPrivateKey(types.Hash(testAddress2.SpendPublicKey().AsBytes()), previousId).AsSlice())
|
||||||
if detTx != detTxPriv {
|
if detTx != detTxPriv {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDeterministicTransactionPrivateKey(a Interface, prevId types.Hash) crypto.PrivateKey {
|
func GetDeterministicTransactionPrivateKey(seed types.Hash, prevId types.Hash) crypto.PrivateKey {
|
||||||
return p2poolcrypto.GetDeterministicTransactionPrivateKey(a.SpendPublicKey(), prevId)
|
return p2poolcrypto.GetDeterministicTransactionPrivateKey(seed, prevId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPublicKeyForSharedData(a Interface, sharedData crypto.PrivateKey) crypto.PublicKey {
|
func GetPublicKeyForSharedData(a Interface, sharedData crypto.PrivateKey) crypto.PublicKey {
|
||||||
|
|
|
@ -197,7 +197,9 @@ func (b *Block) HeaderBlob() []byte {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SideChainHashingBlob Same as MarshalBinary but with nonce set to 0
|
||||||
func (b *Block) SideChainHashingBlob() (buf []byte, err error) {
|
func (b *Block) SideChainHashingBlob() (buf []byte, err error) {
|
||||||
|
|
||||||
var txBuf []byte
|
var txBuf []byte
|
||||||
if txBuf, err = b.Coinbase.SideChainHashingBlob(); err != nil {
|
if txBuf, err = b.Coinbase.SideChainHashingBlob(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (a *Api) GetFailedRawBlock(id types.Hash) (b *sidechain.PoolBlock, err erro
|
||||||
} else {
|
} else {
|
||||||
data := make([]byte, len(buf)/2)
|
data := make([]byte, len(buf)/2)
|
||||||
_, _ = hex.Decode(data, buf)
|
_, _ = hex.Decode(data, buf)
|
||||||
return sidechain.NewShareFromExportedBytes(data)
|
return sidechain.NewShareFromExportedBytes(data, sidechain.NetworkMainnet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ func (a *Api) GetRawBlock(id types.Hash) (b *sidechain.PoolBlock, err error) {
|
||||||
} else {
|
} else {
|
||||||
data := make([]byte, len(buf)/2)
|
data := make([]byte, len(buf)/2)
|
||||||
_, _ = hex.Decode(data, buf)
|
_, _ = hex.Decode(data, buf)
|
||||||
return sidechain.NewShareFromExportedBytes(data)
|
return sidechain.NewShareFromExportedBytes(data, sidechain.NetworkMainnet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,16 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDeterministicTransactionPrivateKey(spendPublicKey crypto.PublicKey, previousMoneroId types.Hash) crypto.PrivateKey {
|
func CalculateTransactionPrivateKeySeed(main, side []byte) types.Hash {
|
||||||
|
return crypto.PooledKeccak256(
|
||||||
|
// domain
|
||||||
|
[]byte("tx_key_seed"), //TODO: check for null termination
|
||||||
|
main,
|
||||||
|
side,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDeterministicTransactionPrivateKey(seed types.Hash, previousMoneroId types.Hash) crypto.PrivateKey {
|
||||||
/*
|
/*
|
||||||
Current deterministic key issues
|
Current deterministic key issues
|
||||||
* This Deterministic private key changes too ofter, and does not fit full purpose (prevent knowledge of private keys on Coinbase without observing of sidechains).
|
* This Deterministic private key changes too ofter, and does not fit full purpose (prevent knowledge of private keys on Coinbase without observing of sidechains).
|
||||||
|
@ -20,11 +29,11 @@ func GetDeterministicTransactionPrivateKey(spendPublicKey crypto.PublicKey, prev
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var entropy [13 + crypto.PublicKeySize + types.HashSize + (int(unsafe.Sizeof(uint32(0))) /*pre-allocate uint32 for counter*/)]byte
|
var entropy [13 + types.HashSize + types.HashSize + (int(unsafe.Sizeof(uint32(0))) /*pre-allocate uint32 for counter*/)]byte
|
||||||
copy(entropy[:], []byte("tx_secret_key")) //domain
|
copy(entropy[:], []byte("tx_secret_key")) //domain
|
||||||
copy(entropy[13:], spendPublicKey.AsSlice())
|
copy(entropy[13:], seed[:])
|
||||||
copy(entropy[13+crypto.PublicKeySize:], previousMoneroId[:])
|
copy(entropy[13+types.HashSize:], previousMoneroId[:])
|
||||||
return crypto.PrivateKeyFromScalar(crypto.DeterministicScalar(entropy[:13+crypto.PublicKeySize+types.HashSize]))
|
return crypto.PrivateKeyFromScalar(crypto.DeterministicScalar(entropy[:13+types.HashSize+types.HashSize]))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ func (c *MainChain) HandleMainBlock(b *mainblock.Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleChainMain
|
// HandleChainMain
|
||||||
// deprecated
|
// Deprecated
|
||||||
func (c *MainChain) HandleChainMain(mainData *sidechain.ChainMain, extra []byte) {
|
func (c *MainChain) HandleChainMain(mainData *sidechain.ChainMain, extra []byte) {
|
||||||
func() {
|
func() {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
|
|
|
@ -367,6 +367,7 @@ func (c *Client) OnConnection() {
|
||||||
case MessageBlockResponse:
|
case MessageBlockResponse:
|
||||||
block := &sidechain.PoolBlock{
|
block := &sidechain.PoolBlock{
|
||||||
LocalTimestamp: uint64(time.Now().Unix()),
|
LocalTimestamp: uint64(time.Now().Unix()),
|
||||||
|
NetworkType: c.Owner.Consensus().NetworkType,
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedBlockId, ok := c.getNextBlockRequest()
|
expectedBlockId, ok := c.getNextBlockRequest()
|
||||||
|
@ -424,6 +425,7 @@ func (c *Client) OnConnection() {
|
||||||
case MessageBlockBroadcast, MessageBlockBroadcastCompact:
|
case MessageBlockBroadcast, MessageBlockBroadcastCompact:
|
||||||
block := &sidechain.PoolBlock{
|
block := &sidechain.PoolBlock{
|
||||||
LocalTimestamp: uint64(time.Now().Unix()),
|
LocalTimestamp: uint64(time.Now().Unix()),
|
||||||
|
NetworkType: c.Owner.Consensus().NetworkType,
|
||||||
}
|
}
|
||||||
var blockSize uint32
|
var blockSize uint32
|
||||||
if err := binary.Read(c, binary.LittleEndian, &blockSize); err != nil {
|
if err := binary.Read(c, binary.LittleEndian, &blockSize); err != nil {
|
||||||
|
|
|
@ -13,10 +13,10 @@ import (
|
||||||
const BlockSaveEpochSize = 32
|
const BlockSaveEpochSize = 32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BlockSaveOptionTemplate = 1 << 0
|
BlockSaveOptionTemplate = 1 << 0
|
||||||
BlockSaveOptionDeterministicPrivateKey = 1 << 1
|
//BlockSaveOptionDeterministicPrivateKey = 1 << 1
|
||||||
BlockSaveOptionDeterministicBlobs = 1 << 2
|
BlockSaveOptionDeterministicBlobs = 1 << 2
|
||||||
BlockSaveOptionUncles = 1 << 3
|
BlockSaveOptionUncles = 1 << 3
|
||||||
|
|
||||||
BlockSaveFieldSizeInBits = 8
|
BlockSaveFieldSizeInBits = 8
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ func (c *SideChain) saveBlock(block *PoolBlock) {
|
||||||
defer c.sidechainLock.RUnlock()
|
defer c.sidechainLock.RUnlock()
|
||||||
|
|
||||||
//only store keys when not deterministic
|
//only store keys when not deterministic
|
||||||
isDeterministicPrivateKey := c.isPoolBlockTransactionKeyIsDeterministic(block)
|
//isDeterministicPrivateKey := c.isPoolBlockTransactionKeyIsDeterministic(block)
|
||||||
|
|
||||||
calculatedOutputs := c.calculateOutputs(block)
|
calculatedOutputs := c.calculateOutputs(block)
|
||||||
calcBlob, _ := calculatedOutputs.MarshalBinary()
|
calcBlob, _ := calculatedOutputs.MarshalBinary()
|
||||||
|
@ -80,9 +80,9 @@ func (c *SideChain) saveBlock(block *PoolBlock) {
|
||||||
|
|
||||||
var blockFlags uint64
|
var blockFlags uint64
|
||||||
|
|
||||||
if isDeterministicPrivateKey {
|
/*if isDeterministicPrivateKey {
|
||||||
blockFlags |= BlockSaveOptionDeterministicPrivateKey
|
blockFlags |= BlockSaveOptionDeterministicPrivateKey
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if !storeBlob {
|
if !storeBlob {
|
||||||
blockFlags |= BlockSaveOptionDeterministicBlobs
|
blockFlags |= BlockSaveOptionDeterministicBlobs
|
||||||
|
@ -164,8 +164,8 @@ func (c *SideChain) saveBlock(block *PoolBlock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// private key, if needed
|
// private key, if needed
|
||||||
if (blockFlags & BlockSaveOptionDeterministicPrivateKey) == 0 {
|
if true /*(blockFlags & BlockSaveOptionDeterministicPrivateKey) == 0*/ {
|
||||||
blob = append(blob, block.Side.CoinbasePrivateKey[:]...)
|
blob = append(blob, block.Side.CoinbasePrivateKeySeed[:]...)
|
||||||
//public may be needed on invalid - TODO check
|
//public may be needed on invalid - TODO check
|
||||||
//blob = append(blob, block.CoinbaseExtra(SideCoinbasePublicKey)...)
|
//blob = append(blob, block.CoinbaseExtra(SideCoinbasePublicKey)...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,14 +61,14 @@ func (d *DerivationCache) GetEphemeralPublicKey(a address.Interface, txKeySlice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DerivationCache) GetDeterministicTransactionKey(a address.Interface, prevId types.Hash) *crypto.KeyPair {
|
func (d *DerivationCache) GetDeterministicTransactionKey(seed types.Hash, prevId types.Hash) *crypto.KeyPair {
|
||||||
var key deterministicTransactionCacheKey
|
var key deterministicTransactionCacheKey
|
||||||
copy(key[:], a.SpendPublicKey().AsSlice())
|
copy(key[:], seed[:])
|
||||||
copy(key[types.HashSize:], prevId[:])
|
copy(key[types.HashSize:], prevId[:])
|
||||||
|
|
||||||
deterministicKeyCache := d.deterministicKeyCache.Load()
|
deterministicKeyCache := d.deterministicKeyCache.Load()
|
||||||
if kp := deterministicKeyCache.Get(key); kp == nil {
|
if kp := deterministicKeyCache.Get(key); kp == nil {
|
||||||
data := crypto.NewKeyPairFromPrivate(address.GetDeterministicTransactionPrivateKey(a, prevId))
|
data := crypto.NewKeyPairFromPrivate(address.GetDeterministicTransactionPrivateKey(seed, prevId))
|
||||||
deterministicKeyCache.Set(key, data)
|
deterministicKeyCache.Set(key, data)
|
||||||
return data
|
return data
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero"
|
||||||
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||||
|
@ -131,7 +130,7 @@ func (c *Consensus) verify() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.MinimumDifficulty < SmallestMinimumDifficulty || c.MinimumDifficulty > LargestMinimumDifficulty {
|
if c.NetworkType == NetworkMainnet && c.MinimumDifficulty < SmallestMinimumDifficulty || c.MinimumDifficulty > LargestMinimumDifficulty {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,15 +151,16 @@ func (c *Consensus) verify() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Consensus) CalculateSideTemplateId(main *mainblock.Block, side *SideData) types.Hash {
|
func (c *Consensus) CalculateSideTemplateId(share *PoolBlock) types.Hash {
|
||||||
|
|
||||||
mainData, _ := main.SideChainHashingBlob()
|
mainData, _ := share.Main.SideChainHashingBlob()
|
||||||
sideData, _ := side.MarshalBinary()
|
sideData, _ := share.Side.MarshalBinary(share.ShareVersion())
|
||||||
|
|
||||||
return c.CalculateSideChainIdFromBlobs(mainData, sideData)
|
return c.CalculateSideChainIdFromBlobs(mainData, sideData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Consensus) CalculateSideChainIdFromBlobs(mainBlob, sideBlob []byte) types.Hash {
|
func (c *Consensus) CalculateSideChainIdFromBlobs(mainBlob, sideBlob []byte) types.Hash {
|
||||||
|
//TODO: handle extra nonce
|
||||||
return crypto.PooledKeccak256(mainBlob, sideBlob, c.id[:])
|
return crypto.PooledKeccak256(mainBlob, sideBlob, c.id[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,10 @@ import (
|
||||||
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||||
|
p2poolcrypto "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -27,6 +29,17 @@ const (
|
||||||
SideTemplateId = transaction.TxExtraTagMergeMining
|
SideTemplateId = transaction.TxExtraTagMergeMining
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ShareVersion int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ShareVersion_None ShareVersion = 0
|
||||||
|
ShareVersion_V1 ShareVersion = 1
|
||||||
|
ShareVersion_V2 ShareVersion = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const ShareVersion_V2MainNetTimestamp uint64 = 1679173200 // 2023-03-18 21:00 UTC
|
||||||
|
const ShareVersion_V2TestNetTimestamp uint64 = 1674507600 // 2023-01-23 21:00 UTC
|
||||||
|
|
||||||
type PoolBlock struct {
|
type PoolBlock struct {
|
||||||
Main mainblock.Block
|
Main mainblock.Block
|
||||||
|
|
||||||
|
@ -41,12 +54,16 @@ type PoolBlock struct {
|
||||||
WantBroadcast atomic.Bool
|
WantBroadcast atomic.Bool
|
||||||
Broadcasted atomic.Bool
|
Broadcasted atomic.Bool
|
||||||
|
|
||||||
|
NetworkType NetworkType
|
||||||
LocalTimestamp uint64
|
LocalTimestamp uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewShareFromExportedBytes TODO deprecate this in favor of standard serialized shares
|
// NewShareFromExportedBytes TODO deprecate this in favor of standard serialized shares
|
||||||
func NewShareFromExportedBytes(buf []byte) (*PoolBlock, error) {
|
// Deprecated
|
||||||
b := &PoolBlock{}
|
func NewShareFromExportedBytes(buf []byte, networkType NetworkType) (*PoolBlock, error) {
|
||||||
|
b := &PoolBlock{
|
||||||
|
NetworkType: networkType,
|
||||||
|
}
|
||||||
|
|
||||||
if len(buf) < 32 {
|
if len(buf) < 32 {
|
||||||
return nil, errors.New("invalid block data")
|
return nil, errors.New("invalid block data")
|
||||||
|
@ -141,7 +158,7 @@ func NewShareFromExportedBytes(buf []byte) (*PoolBlock, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = b.Side.UnmarshalBinary(sideData); err != nil {
|
if err = b.Side.UnmarshalBinary(sideData, b.ShareVersion()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +167,37 @@ func NewShareFromExportedBytes(buf []byte) (*PoolBlock, error) {
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *PoolBlock) ShareVersion() ShareVersion {
|
||||||
|
// P2Pool forks to v2 at 2023-03-18 21:00 UTC
|
||||||
|
// Different miners can have different timestamps,
|
||||||
|
// so a temporary mix of v1 and v2 blocks is allowed
|
||||||
|
switch b.NetworkType {
|
||||||
|
case NetworkInvalid:
|
||||||
|
log.Panicf("invalid network type for determining share version")
|
||||||
|
case NetworkMainnet:
|
||||||
|
if b.Main.Timestamp >= ShareVersion_V2MainNetTimestamp {
|
||||||
|
return ShareVersion_V2
|
||||||
|
}
|
||||||
|
case NetworkTestnet:
|
||||||
|
if b.Main.Timestamp >= ShareVersion_V2TestNetTimestamp {
|
||||||
|
return ShareVersion_V2
|
||||||
|
}
|
||||||
|
case NetworkStagenet:
|
||||||
|
return ShareVersion_V2
|
||||||
|
}
|
||||||
|
if b.Main.Timestamp >= ShareVersion_V2MainNetTimestamp {
|
||||||
|
return ShareVersion_V2
|
||||||
|
}
|
||||||
|
return ShareVersion_V1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PoolBlock) ShareVersionSignaling() ShareVersion {
|
||||||
|
if b.ShareVersion() == ShareVersion_V1 && ((binary.LittleEndian.Uint32(b.CoinbaseExtra(SideExtraNonce)))&0xFF100000 == 0xFF000000) {
|
||||||
|
return ShareVersion_V2
|
||||||
|
}
|
||||||
|
return ShareVersion_None
|
||||||
|
}
|
||||||
|
|
||||||
func (b *PoolBlock) CoinbaseExtra(tag CoinbaseExtraTag) []byte {
|
func (b *PoolBlock) CoinbaseExtra(tag CoinbaseExtraTag) []byte {
|
||||||
switch tag {
|
switch tag {
|
||||||
case SideExtraNonce:
|
case SideExtraNonce:
|
||||||
|
@ -271,7 +319,7 @@ func (b *PoolBlock) SideTemplateId(consensus *Consensus) types.Hash {
|
||||||
b.cache.lock.Lock()
|
b.cache.lock.Lock()
|
||||||
defer b.cache.lock.Unlock()
|
defer b.cache.lock.Unlock()
|
||||||
if b.cache.templateId == types.ZeroHash { //check again for race
|
if b.cache.templateId == types.ZeroHash { //check again for race
|
||||||
b.cache.templateId = consensus.CalculateSideTemplateId(&b.Main, &b.Side)
|
b.cache.templateId = consensus.CalculateSideTemplateId(b)
|
||||||
}
|
}
|
||||||
return b.cache.templateId
|
return b.cache.templateId
|
||||||
}
|
}
|
||||||
|
@ -311,7 +359,7 @@ func (b *PoolBlock) UnmarshalBinary(data []byte) error {
|
||||||
func (b *PoolBlock) MarshalBinary() ([]byte, error) {
|
func (b *PoolBlock) MarshalBinary() ([]byte, error) {
|
||||||
if mainData, err := b.Main.MarshalBinary(); err != nil {
|
if mainData, err := b.Main.MarshalBinary(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if sideData, err := b.Side.MarshalBinary(); err != nil {
|
} else if sideData, err := b.Side.MarshalBinary(b.ShareVersion()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
data := make([]byte, 0, len(mainData)+len(sideData))
|
data := make([]byte, 0, len(mainData)+len(sideData))
|
||||||
|
@ -324,7 +372,7 @@ func (b *PoolBlock) MarshalBinary() ([]byte, error) {
|
||||||
func (b *PoolBlock) MarshalBinaryFlags(pruned, compact bool) ([]byte, error) {
|
func (b *PoolBlock) MarshalBinaryFlags(pruned, compact bool) ([]byte, error) {
|
||||||
if mainData, err := b.Main.MarshalBinaryFlags(pruned, compact); err != nil {
|
if mainData, err := b.Main.MarshalBinaryFlags(pruned, compact); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if sideData, err := b.Side.MarshalBinary(); err != nil {
|
} else if sideData, err := b.Side.MarshalBinary(b.ShareVersion()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
data := make([]byte, 0, len(mainData)+len(sideData))
|
data := make([]byte, 0, len(mainData)+len(sideData))
|
||||||
|
@ -339,7 +387,7 @@ func (b *PoolBlock) FromReader(reader readerAndByteReader) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = b.Side.FromReader(reader); err != nil {
|
if err = b.Side.FromReader(reader, b.ShareVersion()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +400,7 @@ func (b *PoolBlock) FromCompactReader(reader readerAndByteReader) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = b.Side.FromReader(reader); err != nil {
|
if err = b.Side.FromReader(reader, b.ShareVersion()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +435,26 @@ func (b *PoolBlock) IsProofHigherThanDifficultyWithError(f mainblock.GetSeedByHe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *PoolBlock) GetPrivateKeySeed() types.Hash {
|
||||||
|
if b.ShareVersion() > ShareVersion_V1 {
|
||||||
|
return b.Side.CoinbasePrivateKeySeed
|
||||||
|
}
|
||||||
|
return types.Hash(b.Side.PublicSpendKey.AsBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PoolBlock) CalculateTransactionPrivateKeySeed() types.Hash {
|
||||||
|
if b.ShareVersion() > ShareVersion_V1 {
|
||||||
|
mainData, _ := b.Main.SideChainHashingBlob()
|
||||||
|
sideData, _ := b.Side.MarshalBinary(b.ShareVersion())
|
||||||
|
return p2poolcrypto.CalculateTransactionPrivateKeySeed(
|
||||||
|
mainData,
|
||||||
|
sideData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.Hash(b.Side.PublicSpendKey.AsBytes())
|
||||||
|
}
|
||||||
|
|
||||||
func (b *PoolBlock) GetAddress() *address.PackedAddress {
|
func (b *PoolBlock) GetAddress() *address.PackedAddress {
|
||||||
a := address.NewPackedAddressFromBytes(b.Side.PublicSpendKey, b.Side.PublicViewKey)
|
a := address.NewPackedAddressFromBytes(b.Side.PublicSpendKey, b.Side.PublicViewKey)
|
||||||
return &a
|
return &a
|
||||||
|
|
|
@ -32,7 +32,7 @@ func TestPoolBlockDecode(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := NewShareFromExportedBytes(contents)
|
block, err := NewShareFromExportedBytes(contents, NetworkMainnet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -57,14 +57,14 @@ func TestPoolBlockDecode(t *testing.T) {
|
||||||
|
|
||||||
b2, _ := block.Main.MarshalBinary()
|
b2, _ := block.Main.MarshalBinary()
|
||||||
side2 := &SideData{}
|
side2 := &SideData{}
|
||||||
if err = side2.UnmarshalBinary(b2); err != nil {
|
if err = side2.UnmarshalBinary(b2, block.ShareVersion()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log(block.SideTemplateId(ConsensusDefault).String())
|
t.Log(block.SideTemplateId(ConsensusDefault).String())
|
||||||
|
|
||||||
t.Log(block.Side.CoinbasePrivateKey.String())
|
t.Log(block.Side.CoinbasePrivateKey.String())
|
||||||
t.Log(address.GetDeterministicTransactionPrivateKey(block.GetAddress(), block.Main.PreviousId).String())
|
t.Log(address.GetDeterministicTransactionPrivateKey(block.GetPrivateKeySeed(), block.Main.PreviousId).String())
|
||||||
|
|
||||||
txId := block.Main.Coinbase.Id()
|
txId := block.Main.Coinbase.Id()
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,21 @@ package sidechain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
|
||||||
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/client"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/client"
|
||||||
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||||
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
|
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
"log"
|
"log"
|
||||||
|
"lukechampine.com/uint128"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -151,8 +154,11 @@ func (c *SideChain) fillPoolBlockTransactionParentIndices(block *PoolBlock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SideChain) isPoolBlockTransactionKeyIsDeterministic(block *PoolBlock) bool {
|
func (c *SideChain) isPoolBlockTransactionKeyIsDeterministic(block *PoolBlock) bool {
|
||||||
kP := c.derivationCache.GetDeterministicTransactionKey(block.GetAddress(), block.Main.PreviousId)
|
kP := c.derivationCache.GetDeterministicTransactionKey(block.GetPrivateKeySeed(), block.Main.PreviousId)
|
||||||
return bytes.Compare(block.CoinbaseExtra(SideCoinbasePublicKey), kP.PublicKey.AsSlice()) == 0 && bytes.Compare(block.Side.CoinbasePrivateKey[:], kP.PrivateKey.AsSlice()) == 0
|
if block.ShareVersion() > ShareVersion_V1 {
|
||||||
|
block.Side.CoinbasePrivateKey = kP.PrivateKey.AsBytes()
|
||||||
|
}
|
||||||
|
return bytes.Compare(block.CoinbaseExtra(SideCoinbasePublicKey), kP.PublicKey.AsSlice()) == 0 && bytes.Compare(kP.PrivateKey.AsSlice(), block.Side.CoinbasePrivateKey[:]) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SideChain) getSeedByHeightFunc() mainblock.GetSeedByHeightFunc {
|
func (c *SideChain) getSeedByHeightFunc() mainblock.GetSeedByHeightFunc {
|
||||||
|
@ -192,7 +198,7 @@ func (c *SideChain) AddPoolBlockExternal(block *PoolBlock) (missingBlocks []type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templateId := c.Consensus().CalculateSideTemplateId(&block.Main, &block.Side)
|
templateId := c.Consensus().CalculateSideTemplateId(block)
|
||||||
if templateId != block.SideTemplateId(c.Consensus()) {
|
if templateId != block.SideTemplateId(c.Consensus()) {
|
||||||
return nil, fmt.Errorf("invalid template id %s, expected %s", templateId.String(), block.SideTemplateId(c.Consensus()).String())
|
return nil, fmt.Errorf("invalid template id %s, expected %s", templateId.String(), block.SideTemplateId(c.Consensus()).String())
|
||||||
}
|
}
|
||||||
|
@ -384,7 +390,8 @@ func (c *SideChain) verifyBlock(block *PoolBlock) (verification error, invalid e
|
||||||
if block.Side.Parent != types.ZeroHash ||
|
if block.Side.Parent != types.ZeroHash ||
|
||||||
len(block.Side.Uncles) != 0 ||
|
len(block.Side.Uncles) != 0 ||
|
||||||
block.Side.Difficulty.Cmp64(c.Consensus().MinimumDifficulty) != 0 ||
|
block.Side.Difficulty.Cmp64(c.Consensus().MinimumDifficulty) != 0 ||
|
||||||
block.Side.CumulativeDifficulty.Cmp64(c.Consensus().MinimumDifficulty) != 0 {
|
block.Side.CumulativeDifficulty.Cmp64(c.Consensus().MinimumDifficulty) != 0 ||
|
||||||
|
(block.ShareVersion() > ShareVersion_V1 && block.Side.CoinbasePrivateKeySeed == types.ZeroHash) {
|
||||||
return nil, errors.New("genesis block has invalid parameters")
|
return nil, errors.New("genesis block has invalid parameters")
|
||||||
}
|
}
|
||||||
//this does not verify coinbase outputs, but that's fine
|
//this does not verify coinbase outputs, but that's fine
|
||||||
|
@ -417,6 +424,16 @@ func (c *SideChain) verifyBlock(block *PoolBlock) (verification error, invalid e
|
||||||
return nil, errors.New("parent is invalid")
|
return nil, errors.New("parent is invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if block.ShareVersion() > ShareVersion_V1 {
|
||||||
|
expectedSeed := parent.Side.CoinbasePrivateKeySeed
|
||||||
|
if parent.Main.PreviousId != block.Main.PreviousId {
|
||||||
|
expectedSeed = parent.CalculateTransactionPrivateKeySeed()
|
||||||
|
}
|
||||||
|
if block.Side.CoinbasePrivateKeySeed != expectedSeed {
|
||||||
|
return nil, fmt.Errorf("invalid tx key seed: expected %s, got %s", expectedSeed.String(), block.Side.CoinbasePrivateKeySeed.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expectedHeight := parent.Side.Height + 1
|
expectedHeight := parent.Side.Height + 1
|
||||||
if expectedHeight != block.Side.Height {
|
if expectedHeight != block.Side.Height {
|
||||||
return nil, fmt.Errorf("wrong height, expected %d", expectedHeight)
|
return nil, fmt.Errorf("wrong height, expected %d", expectedHeight)
|
||||||
|
@ -524,7 +541,7 @@ func (c *SideChain) verifyBlock(block *PoolBlock) (verification error, invalid e
|
||||||
return nil, fmt.Errorf("wrong difficulty, got %s, expected %s", block.Side.Difficulty.StringNumeric(), diff.StringNumeric())
|
return nil, fmt.Errorf("wrong difficulty, got %s, expected %s", block.Side.Difficulty.StringNumeric(), diff.StringNumeric())
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.sharesCache = c.getShares(block, c.sharesCache); len(c.sharesCache) == 0 {
|
if c.sharesCache, _ = c.getShares(block, c.sharesCache); len(c.sharesCache) == 0 {
|
||||||
return nil, errors.New("could not get outputs")
|
return nil, errors.New("could not get outputs")
|
||||||
} else if len(c.sharesCache) != len(block.Main.Coinbase.Outputs) {
|
} else if len(c.sharesCache) != len(block.Main.Coinbase.Outputs) {
|
||||||
return nil, fmt.Errorf("invalid number of outputs, got %d, expected %d", len(block.Main.Coinbase.Outputs), len(c.sharesCache))
|
return nil, fmt.Errorf("invalid number of outputs, got %d, expected %d", len(block.Main.Coinbase.Outputs), len(c.sharesCache))
|
||||||
|
@ -641,6 +658,11 @@ func (c *SideChain) updateChainTip(block *PoolBlock) {
|
||||||
|
|
||||||
tip := c.GetChainTip()
|
tip := c.GetChainTip()
|
||||||
|
|
||||||
|
if block == tip {
|
||||||
|
log.Printf("[SideChain] Trying to update chain tip to the same block again. Ignoring it.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if isLongerChain, isAlternative := c.isLongerChain(tip, block); isLongerChain {
|
if isLongerChain, isAlternative := c.isLongerChain(tip, block); isLongerChain {
|
||||||
if diff := c.getDifficulty(block); diff != types.ZeroDifficulty {
|
if diff := c.getDifficulty(block); diff != types.ZeroDifficulty {
|
||||||
c.chainTip.Store(block)
|
c.chainTip.Store(block)
|
||||||
|
@ -777,7 +799,7 @@ func (c *SideChain) getOutputs(block *PoolBlock) transaction.Outputs {
|
||||||
|
|
||||||
func (c *SideChain) calculateOutputs(block *PoolBlock) transaction.Outputs {
|
func (c *SideChain) calculateOutputs(block *PoolBlock) transaction.Outputs {
|
||||||
//TODO: buffer
|
//TODO: buffer
|
||||||
tmpShares := c.getShares(block, make(Shares, 0, c.Consensus().ChainWindowSize*2))
|
tmpShares, _ := c.getShares(block, make(Shares, 0, c.Consensus().ChainWindowSize*2))
|
||||||
tmpRewards := c.SplitReward(block.Main.Coinbase.TotalReward, tmpShares)
|
tmpRewards := c.SplitReward(block.Main.Coinbase.TotalReward, tmpShares)
|
||||||
|
|
||||||
if tmpShares == nil || tmpRewards == nil || len(tmpRewards) != len(tmpShares) {
|
if tmpShares == nil || tmpRewards == nil || len(tmpRewards) != len(tmpShares) {
|
||||||
|
@ -844,19 +866,54 @@ func (c *SideChain) SplitReward(reward uint64, shares Shares) (rewards []uint64)
|
||||||
return rewards
|
return rewards
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SideChain) getShares(tip *PoolBlock, preAllocatedShares Shares) Shares {
|
func (c *SideChain) getShares(tip *PoolBlock, preAllocatedShares Shares) (shares Shares, bottomHeight uint64) {
|
||||||
|
|
||||||
var blockDepth uint64
|
var blockDepth uint64
|
||||||
|
|
||||||
cur := tip
|
cur := tip
|
||||||
|
|
||||||
|
var mainchainDiff types.Difficulty
|
||||||
|
|
||||||
|
if tip.Side.Parent != types.ZeroHash {
|
||||||
|
mainchainDiff = c.server.GetDifficultyByHeight(tip.Main.Coinbase.GenHeight)
|
||||||
|
if mainchainDiff == types.ZeroDifficulty {
|
||||||
|
log.Printf("[SideChain] get_shares: couldn't get mainchain difficulty for height = %d", tip.Main.Coinbase.GenHeight)
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic PPLNS window starting from v2
|
||||||
|
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
|
||||||
|
sidechainVersion := tip.ShareVersion()
|
||||||
|
|
||||||
|
maxPplnsWeight := types.MaxDifficulty
|
||||||
|
|
||||||
|
if sidechainVersion > ShareVersion_V1 {
|
||||||
|
maxPplnsWeight = mainchainDiff.Mul64(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pplnsWeight types.Difficulty
|
||||||
|
|
||||||
|
sharesSet := make(map[address.PackedAddress]*Share, c.Consensus().ChainWindowSize*2)
|
||||||
|
|
||||||
|
insertSet := func(weight types.Difficulty, a *address.PackedAddress) {
|
||||||
|
if _, ok := sharesSet[*a]; ok {
|
||||||
|
sharesSet[*a].Weight = sharesSet[*a].Weight.Add(weight)
|
||||||
|
} else {
|
||||||
|
sharesSet[*a] = &Share{
|
||||||
|
Weight: weight,
|
||||||
|
Address: *a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
index := 0
|
index := 0
|
||||||
l := len(preAllocatedShares)
|
l := len(preAllocatedShares)
|
||||||
insert := func(weight types.Difficulty, a *address.PackedAddress) {
|
insertPreAllocated := func(share *Share) {
|
||||||
if index < l {
|
if index < l {
|
||||||
preAllocatedShares[index].Weight, preAllocatedShares[index].Address = weight, *a
|
preAllocatedShares[index].Weight, preAllocatedShares[index].Address = share.Weight, share.Address
|
||||||
} else {
|
} else {
|
||||||
preAllocatedShares = append(preAllocatedShares, &Share{Weight: weight, Address: *a})
|
preAllocatedShares = append(preAllocatedShares, share)
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
|
@ -867,7 +924,7 @@ func (c *SideChain) getShares(tip *PoolBlock, preAllocatedShares Shares) Shares
|
||||||
for _, uncleId := range cur.Side.Uncles {
|
for _, uncleId := range cur.Side.Uncles {
|
||||||
if uncle := c.getPoolBlockByTemplateId(uncleId); uncle == nil {
|
if uncle := c.getPoolBlockByTemplateId(uncleId); uncle == nil {
|
||||||
//cannot find uncles
|
//cannot find uncles
|
||||||
return nil
|
return nil, 0
|
||||||
} else {
|
} else {
|
||||||
// Skip uncles which are already out of PPLNS window
|
// Skip uncles which are already out of PPLNS window
|
||||||
if (tip.Side.Height - uncle.Side.Height) >= c.Consensus().ChainWindowSize {
|
if (tip.Side.Height - uncle.Side.Height) >= c.Consensus().ChainWindowSize {
|
||||||
|
@ -875,15 +932,31 @@ func (c *SideChain) getShares(tip *PoolBlock, preAllocatedShares Shares) Shares
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take some % of uncle's weight into this share
|
// Take some % of uncle's weight into this share
|
||||||
product := uncle.Side.Difficulty.Mul64(c.Consensus().UnclePenalty)
|
unclePenalty := uncle.Side.Difficulty.Mul64(c.Consensus().UnclePenalty).Div64(100)
|
||||||
unclePenalty := product.Div64(100)
|
uncleWeight := uncle.Side.Difficulty.Sub(unclePenalty)
|
||||||
|
newPplnsWeight := pplnsWeight.Add(uncleWeight)
|
||||||
|
|
||||||
|
// Skip uncles that push PPLNS weight above the limit
|
||||||
|
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
curWeight = curWeight.Add(unclePenalty)
|
curWeight = curWeight.Add(unclePenalty)
|
||||||
|
|
||||||
insert(uncle.Side.Difficulty.Sub(unclePenalty), uncle.GetAddress())
|
insertSet(uncleWeight, uncle.GetAddress())
|
||||||
|
|
||||||
|
pplnsWeight = newPplnsWeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(curWeight, cur.GetAddress())
|
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||||
|
insertSet(curWeight, cur.GetAddress())
|
||||||
|
|
||||||
|
pplnsWeight = pplnsWeight.Add(curWeight)
|
||||||
|
|
||||||
|
// One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty
|
||||||
|
if pplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
blockDepth++
|
blockDepth++
|
||||||
|
|
||||||
|
@ -899,29 +972,39 @@ func (c *SideChain) getShares(tip *PoolBlock, preAllocatedShares Shares) Shares
|
||||||
cur = c.getParent(cur)
|
cur = c.getParent(cur)
|
||||||
|
|
||||||
if cur == nil {
|
if cur == nil {
|
||||||
return nil
|
return nil, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shares := preAllocatedShares[:index]
|
bottomHeight = cur.Side.Height
|
||||||
|
|
||||||
|
for _, share := range sharesSet {
|
||||||
|
insertPreAllocated(share)
|
||||||
|
}
|
||||||
|
|
||||||
|
shares = preAllocatedShares[:index]
|
||||||
|
|
||||||
// Combine shares with the same wallet addresses
|
// Combine shares with the same wallet addresses
|
||||||
slices.SortFunc(shares, func(a *Share, b *Share) bool {
|
slices.SortFunc(shares, func(a *Share, b *Share) bool {
|
||||||
return a.Address.Compare(&b.Address) < 0
|
return a.Address.Compare(&b.Address) < 0
|
||||||
})
|
})
|
||||||
|
|
||||||
k := 0
|
n := len(shares)
|
||||||
for i := 1; i < len(shares); i++ {
|
|
||||||
if shares[i].Address.Compare(&shares[k].Address) == 0 {
|
//Shuffle shares
|
||||||
shares[k].Weight = shares[k].Weight.Add(shares[i].Weight)
|
if sidechainVersion > ShareVersion_V1 && n > 1 {
|
||||||
} else {
|
h := crypto.PooledKeccak256(tip.Side.CoinbasePrivateKeySeed[:])
|
||||||
k++
|
seed := binary.LittleEndian.Uint64(h[:])
|
||||||
shares[k].Address = shares[i].Address
|
|
||||||
shares[k].Weight = shares[i].Weight
|
for i := 0; i < (n - 1); i++ {
|
||||||
|
seed = utils.XorShift64Star(seed)
|
||||||
|
k := int(uint128.From64(seed).Mul64(uint64(n - 1)).Hi)
|
||||||
|
//swap
|
||||||
|
shares[i], shares[i+k] = shares[i+k], shares[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return shares[:k+1]
|
return shares, bottomHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
type DifficultyData struct {
|
type DifficultyData struct {
|
||||||
|
|
|
@ -9,14 +9,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type SideData struct {
|
type SideData struct {
|
||||||
PublicSpendKey crypto.PublicKeyBytes
|
PublicSpendKey crypto.PublicKeyBytes
|
||||||
PublicViewKey crypto.PublicKeyBytes
|
PublicViewKey crypto.PublicKeyBytes
|
||||||
|
CoinbasePrivateKeySeed types.Hash
|
||||||
|
// CoinbasePrivateKey filled either on decoding, or side chain filling
|
||||||
CoinbasePrivateKey crypto.PrivateKeyBytes
|
CoinbasePrivateKey crypto.PrivateKeyBytes
|
||||||
Parent types.Hash
|
Parent types.Hash
|
||||||
Uncles []types.Hash
|
Uncles []types.Hash
|
||||||
Height uint64
|
Height uint64
|
||||||
Difficulty types.Difficulty
|
Difficulty types.Difficulty
|
||||||
CumulativeDifficulty types.Difficulty
|
CumulativeDifficulty types.Difficulty
|
||||||
|
|
||||||
|
// ExtraBuffer available in ShareVersion_2 and above
|
||||||
|
ExtraBuffer struct {
|
||||||
|
SoftwareId uint32
|
||||||
|
Version uint32
|
||||||
|
RandomNumber uint32
|
||||||
|
SideChainExtraNonce uint32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type readerAndByteReader interface {
|
type readerAndByteReader interface {
|
||||||
|
@ -24,11 +34,11 @@ type readerAndByteReader interface {
|
||||||
io.ByteReader
|
io.ByteReader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SideData) MarshalBinary() (buf []byte, err error) {
|
func (b *SideData) MarshalBinary(version ShareVersion) (buf []byte, err error) {
|
||||||
buf = make([]byte, 0, types.HashSize+types.HashSize+types.HashSize+types.HashSize+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64)
|
buf = make([]byte, 0, types.HashSize+types.HashSize+types.HashSize+types.HashSize+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+binary.MaxVarintLen64+4*4)
|
||||||
buf = append(buf, b.PublicSpendKey[:]...)
|
buf = append(buf, b.PublicSpendKey[:]...)
|
||||||
buf = append(buf, b.PublicViewKey[:]...)
|
buf = append(buf, b.PublicViewKey[:]...)
|
||||||
buf = append(buf, b.CoinbasePrivateKey[:]...)
|
buf = append(buf, b.CoinbasePrivateKeySeed[:]...)
|
||||||
buf = append(buf, b.Parent[:]...)
|
buf = append(buf, b.Parent[:]...)
|
||||||
buf = binary.AppendUvarint(buf, uint64(len(b.Uncles)))
|
buf = binary.AppendUvarint(buf, uint64(len(b.Uncles)))
|
||||||
for _, uId := range b.Uncles {
|
for _, uId := range b.Uncles {
|
||||||
|
@ -39,11 +49,17 @@ func (b *SideData) MarshalBinary() (buf []byte, err error) {
|
||||||
buf = binary.AppendUvarint(buf, b.Difficulty.Hi)
|
buf = binary.AppendUvarint(buf, b.Difficulty.Hi)
|
||||||
buf = binary.AppendUvarint(buf, b.CumulativeDifficulty.Lo)
|
buf = binary.AppendUvarint(buf, b.CumulativeDifficulty.Lo)
|
||||||
buf = binary.AppendUvarint(buf, b.CumulativeDifficulty.Hi)
|
buf = binary.AppendUvarint(buf, b.CumulativeDifficulty.Hi)
|
||||||
|
if version > ShareVersion_V1 {
|
||||||
|
buf = binary.LittleEndian.AppendUint32(buf, b.ExtraBuffer.SoftwareId)
|
||||||
|
buf = binary.LittleEndian.AppendUint32(buf, b.ExtraBuffer.Version)
|
||||||
|
buf = binary.LittleEndian.AppendUint32(buf, b.ExtraBuffer.RandomNumber)
|
||||||
|
buf = binary.LittleEndian.AppendUint32(buf, b.ExtraBuffer.SideChainExtraNonce)
|
||||||
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SideData) FromReader(reader readerAndByteReader) (err error) {
|
func (b *SideData) FromReader(reader readerAndByteReader, version ShareVersion) (err error) {
|
||||||
var (
|
var (
|
||||||
uncleCount uint64
|
uncleCount uint64
|
||||||
uncleHash types.Hash
|
uncleHash types.Hash
|
||||||
|
@ -54,9 +70,14 @@ func (b *SideData) FromReader(reader readerAndByteReader) (err error) {
|
||||||
if _, err = io.ReadFull(reader, b.PublicViewKey[:]); err != nil {
|
if _, err = io.ReadFull(reader, b.PublicViewKey[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err = io.ReadFull(reader, b.CoinbasePrivateKey[:]); err != nil {
|
if _, err = io.ReadFull(reader, b.CoinbasePrivateKeySeed[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if version > ShareVersion_V1 {
|
||||||
|
//needs preprocessing
|
||||||
|
} else {
|
||||||
|
b.CoinbasePrivateKey = crypto.PrivateKeyBytes(b.CoinbasePrivateKeySeed)
|
||||||
|
}
|
||||||
if _, err = io.ReadFull(reader, b.Parent[:]); err != nil {
|
if _, err = io.ReadFull(reader, b.Parent[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -95,11 +116,25 @@ func (b *SideData) FromReader(reader readerAndByteReader) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if version > ShareVersion_V1 {
|
||||||
|
if err = binary.Read(reader, binary.LittleEndian, &b.ExtraBuffer.SoftwareId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Read(reader, binary.LittleEndian, &b.ExtraBuffer.Version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Read(reader, binary.LittleEndian, &b.ExtraBuffer.RandomNumber); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Read(reader, binary.LittleEndian, &b.ExtraBuffer.SideChainExtraNonce); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SideData) UnmarshalBinary(data []byte) error {
|
func (b *SideData) UnmarshalBinary(data []byte, version ShareVersion) error {
|
||||||
reader := bytes.NewReader(data)
|
reader := bytes.NewReader(data)
|
||||||
return b.FromReader(reader)
|
return b.FromReader(reader, version)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
const DifficultySize = 16
|
const DifficultySize = 16
|
||||||
|
|
||||||
var ZeroDifficulty = Difficulty(uint128.Zero)
|
var ZeroDifficulty = Difficulty(uint128.Zero)
|
||||||
|
var MaxDifficulty = Difficulty(uint128.Max)
|
||||||
|
|
||||||
type Difficulty uint128.Uint128
|
type Difficulty uint128.Uint128
|
||||||
|
|
||||||
|
|
8
utils/xorshift64star.go
Normal file
8
utils/xorshift64star.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
func XorShift64Star(x uint64) uint64 {
|
||||||
|
x ^= x >> 12
|
||||||
|
x ^= x << 25
|
||||||
|
x ^= x >> 27
|
||||||
|
return x * 0x2545F4914F6CDD1D
|
||||||
|
}
|
Loading…
Reference in a new issue