100 lines
2.4 KiB
Go
100 lines
2.4 KiB
Go
package merge_mining
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
|
"io"
|
|
)
|
|
|
|
type Tag struct {
|
|
NumberAuxiliaryChains uint32
|
|
Nonce uint32
|
|
RootHash types.Hash
|
|
}
|
|
|
|
// FromReader Decodes the merge mining tag located in coinbase transaction
|
|
// Format according to https://github.com/SChernykh/p2pool/blob/e6b8292d5b59692921af23613456674ccab4958b/docs/MERGE_MINING.MD
|
|
func (t *Tag) FromReader(reader utils.ReaderAndByteReader) error {
|
|
merkleTreeData, err := binary.ReadUvarint(reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k := uint32(merkleTreeData)
|
|
n := 1 + (k & 7)
|
|
|
|
t.NumberAuxiliaryChains = 1 + ((k >> 3) & ((1 << n) - 1))
|
|
t.Nonce = uint32(merkleTreeData >> (3 + n))
|
|
|
|
if _, err = io.ReadFull(reader, t.RootHash[:]); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Tag) MarshalBinary() (buf []byte, err error) {
|
|
nBits := uint32(1)
|
|
|
|
for (1<<nBits) >= t.NumberAuxiliaryChains && nBits < 8 {
|
|
nBits++
|
|
}
|
|
merkleTreeData := (uint64(nBits) - 1) | (uint64(t.NumberAuxiliaryChains-1) << 3) | (uint64(t.Nonce) << (3 + nBits))
|
|
|
|
buf = make([]byte, utils.UVarInt64Size(merkleTreeData)+types.HashSize)
|
|
n := binary.PutUvarint(buf, merkleTreeData)
|
|
copy(buf[n:], t.RootHash[:])
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
// GetAuxiliarySlot Gets the slot for an auxiliary chain
|
|
func GetAuxiliarySlot(id types.Hash, nonce, numberAuxiliaryChains uint32) (auxiliarySlot uint32) {
|
|
if numberAuxiliaryChains <= 1 {
|
|
return 0
|
|
}
|
|
|
|
// HashKeyMergeMineSlot HASH_KEY_MM_SLOT
|
|
const HashKeyMergeMineSlot = 'm'
|
|
|
|
var buf [types.HashSize + 4 + 1]byte
|
|
copy(buf[:], id[:])
|
|
binary.LittleEndian.PutUint32(buf[types.HashSize:], nonce)
|
|
buf[types.HashSize+4] = HashKeyMergeMineSlot
|
|
|
|
//todo: optimize sha256
|
|
result := sha256.Sum256(buf[:])
|
|
|
|
return binary.LittleEndian.Uint32(result[:]) % numberAuxiliaryChains
|
|
}
|
|
|
|
func FindAuxiliaryNonce(auxId []types.Hash, maxNonce uint32) (nonce uint32, ok bool) {
|
|
numberAuxiliaryChains := uint32(len(auxId))
|
|
|
|
if numberAuxiliaryChains <= 1 {
|
|
return 0, true
|
|
}
|
|
|
|
slots := make([]bool, numberAuxiliaryChains)
|
|
for i := uint32(0); ; i++ {
|
|
clear(slots)
|
|
|
|
var j uint32
|
|
for ; j < numberAuxiliaryChains; j++ {
|
|
k := GetAuxiliarySlot(auxId[j], i, numberAuxiliaryChains)
|
|
if slots[k] {
|
|
break
|
|
}
|
|
slots[k] = true
|
|
}
|
|
|
|
if j >= numberAuxiliaryChains {
|
|
return i, true
|
|
}
|
|
|
|
if i == maxNonce {
|
|
return 0, false
|
|
}
|
|
}
|
|
}
|