mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-20 05:46:44 +00:00

* [NOD-172] Port EMCH from bchd * [NOD-172] Fix hdkeychain.TestErrors and add btcec.TestRecoverCompact * [NOD-172] Make ECMH immutable * [NOD-172] Fix gofmt errors * [NOD-172] Add TestMultiset_NewMultisetFromDataSlice and fix Point to be immutable * [NOD-172] Fix gofmt errors * [NOD-172] Add test for checking that the Union of a multiset and its inverse is zero * [NOD-179] Add ECMH Point to all UTXO-structs * [NOD-179] Fix utxo set tests * [NOD-179] Fix mempool tests * [NOD-179] Remove RemoveTxOuts * [NOD-179] Move serializeBlockUTXODiffData to the top of the file * [NOD-179] Fix serializeBlockUTXODiffData comment format * [NOD-179] Fix AddTx comment and name return values * [NOD-180] Validate utxo commitments * [NOD-179] Fix TestAcceptingBlock and TestConfirmations to not use the block hash as phantom break even * [NOD-180] Fix typo * [NOD-180] move most of the logic in calcUTXOCommitment to UTXOSet.WithTransactions * [NOD-180] Optionally return error when a transaction in WithTransactions is double spent * [NOD-180] Rename allowDoubleSpends to ignoreDoubleSpends
153 lines
4.6 KiB
Go
153 lines
4.6 KiB
Go
package btcec
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"math/big"
|
|
|
|
"github.com/daglabs/btcd/util/daghash"
|
|
)
|
|
|
|
// Multiset tracks the state of a multiset as used to calculate the ECMH
|
|
// (elliptic curve multiset hash) hash of an unordered set. The state is
|
|
// a point on the curve. New elements are hashed onto a point on the curve
|
|
// and then added to the current state. Hence elements can be added in any
|
|
// order and we can also remove elements to return to a prior hash.
|
|
type Multiset struct {
|
|
curve *KoblitzCurve
|
|
x *big.Int
|
|
y *big.Int
|
|
}
|
|
|
|
// NewMultiset returns an empty multiset. The hash of an empty set
|
|
// is the 32 byte value of zero.
|
|
func NewMultiset(curve *KoblitzCurve) *Multiset {
|
|
return &Multiset{curve: curve, x: big.NewInt(0), y: big.NewInt(0)}
|
|
}
|
|
|
|
// NewMultisetFromPoint initializes a new multiset with the given x, y
|
|
// coordinate.
|
|
func NewMultisetFromPoint(curve *KoblitzCurve, x, y *big.Int) *Multiset {
|
|
var copyX, copyY big.Int
|
|
if x != nil {
|
|
copyX.Set(x)
|
|
}
|
|
if y != nil {
|
|
copyY.Set(y)
|
|
}
|
|
return &Multiset{curve: curve, x: ©X, y: ©Y}
|
|
}
|
|
|
|
// NewMultisetFromDataSlice gets a curve and a slice of byte
|
|
// slices, creates an empty multiset, hashes each data and
|
|
// add it to the multiset, and return the resulting multiset.
|
|
func NewMultisetFromDataSlice(curve *KoblitzCurve, datas [][]byte) *Multiset {
|
|
ms := NewMultiset(curve)
|
|
for _, data := range datas {
|
|
x, y := hashToPoint(curve, data)
|
|
ms.addPoint(x, y)
|
|
}
|
|
return ms
|
|
}
|
|
|
|
// Clone returns a clone of this multiset.
|
|
func (ms *Multiset) Clone() *Multiset {
|
|
return NewMultisetFromPoint(ms.curve, ms.x, ms.y)
|
|
}
|
|
|
|
// Add hashes the data onto the curve and returns
|
|
// a multiset with the new resulting point.
|
|
func (ms *Multiset) Add(data []byte) *Multiset {
|
|
newMs := ms.Clone()
|
|
x, y := hashToPoint(ms.curve, data)
|
|
newMs.addPoint(x, y)
|
|
return newMs
|
|
}
|
|
|
|
func (ms *Multiset) addPoint(x, y *big.Int) {
|
|
ms.x, ms.y = ms.curve.Add(ms.x, ms.y, x, y)
|
|
}
|
|
|
|
// Remove hashes the data onto the curve, subtracts
|
|
// the point from the existing multiset, and returns
|
|
// a multiset with the new point. This function
|
|
// will execute regardless of whether or not the passed
|
|
// data was previously added to the set. Hence if you
|
|
// remove an element that was never added and also remove
|
|
// all the elements that were added, you will not get
|
|
// back to the point at infinity (empty set).
|
|
func (ms *Multiset) Remove(data []byte) *Multiset {
|
|
newMs := ms.Clone()
|
|
x, y := hashToPoint(ms.curve, data)
|
|
newMs.removePoint(x, y)
|
|
return newMs
|
|
}
|
|
|
|
func (ms *Multiset) removePoint(x, y *big.Int) {
|
|
y.Neg(y).Mod(y, ms.curve.P)
|
|
ms.x, ms.y = ms.curve.Add(ms.x, ms.y, x, y)
|
|
}
|
|
|
|
// Union will add the point of the passed multiset instance to the point
|
|
// of this multiset and will return a multiset with the resulting point.
|
|
func (ms *Multiset) Union(otherMultiset *Multiset) *Multiset {
|
|
newMs := ms.Clone()
|
|
otherMsCopy := otherMultiset.Clone()
|
|
newMs.addPoint(otherMsCopy.x, otherMsCopy.y)
|
|
return newMs
|
|
}
|
|
|
|
// Subtract will remove the point of the passed multiset instance from the point
|
|
// of this multiset and will return a multiset with the resulting point.
|
|
func (ms *Multiset) Subtract(otherMultiset *Multiset) *Multiset {
|
|
newMs := ms.Clone()
|
|
otherMsCopy := otherMultiset.Clone()
|
|
newMs.removePoint(otherMsCopy.x, otherMsCopy.y)
|
|
return newMs
|
|
}
|
|
|
|
// Hash serializes and returns the hash of the multiset. The hash of an empty
|
|
// set is the 32 byte value of zero. The hash of a non-empty multiset is the
|
|
// sha256 hash of the 32 byte x value concatenated with the 32 byte y value.
|
|
func (ms *Multiset) Hash() *daghash.Hash {
|
|
if ms.x.Sign() == 0 && ms.y.Sign() == 0 {
|
|
return &daghash.Hash{}
|
|
}
|
|
|
|
hash := sha256.Sum256(append(ms.x.Bytes(), ms.y.Bytes()...))
|
|
castHash := daghash.Hash(hash)
|
|
return &castHash
|
|
}
|
|
|
|
// Point returns a copy of the x and y coordinates of the current multiset state.
|
|
func (ms *Multiset) Point() (x *big.Int, y *big.Int) {
|
|
var copyX, copyY big.Int
|
|
copyX.Set(ms.x)
|
|
copyY.Set(ms.y)
|
|
return ©X, ©Y
|
|
}
|
|
|
|
// hashToPoint hashes the passed data into a point on the curve. The x value
|
|
// is sha256(n, sha256(data)) where n starts at zero. If the resulting x value
|
|
// is not in the field or x^3+7 is not quadratic residue then n is incremented
|
|
// and we try again. There is a 50% chance of success for any given iteration.
|
|
func hashToPoint(curve *KoblitzCurve, data []byte) (x *big.Int, y *big.Int) {
|
|
i := uint64(0)
|
|
var err error
|
|
h := sha256.Sum256(data)
|
|
n := make([]byte, 8)
|
|
for {
|
|
binary.LittleEndian.PutUint64(n, i)
|
|
h2 := sha256.Sum256(append(n, h[:]...))
|
|
|
|
x = new(big.Int).SetBytes(h2[:])
|
|
|
|
y, err = decompressPoint(curve, x, false)
|
|
if err == nil && x.Cmp(curve.N) < 0 {
|
|
break
|
|
}
|
|
i++
|
|
}
|
|
return x, y
|
|
}
|