Skip to content
Snippets Groups Projects
Commit e306d4b0 authored by Hans Moog's avatar Hans Moog
Browse files

Feat: ledgerstate can be exported to a PNG + fixed bugs + ConflictSets

parent 0e119acc
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,7 @@ require (
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/dgraph-io/badger v1.6.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/emicklei/dot v0.10.1
github.com/ethereum/go-ethereum v1.9.3
github.com/gdamore/tcell v1.2.0
github.com/go-zeromq/zmq4 v0.5.0
......
package ledgerstate
import (
"encoding/binary"
"fmt"
"sync"
"github.com/iotaledger/goshimmer/packages/stringify"
"github.com/iotaledger/hive.go/objectstorage"
)
type ConflictSet struct {
id ConflictSetId
members map[RealityId]empty
storageKey []byte
ledgerState *LedgerState
membersMutex sync.RWMutex
}
func newConflictSet(id ConflictSetId) *ConflictSet {
result := &ConflictSet{
id: id,
members: make(map[RealityId]empty),
storageKey: make([]byte, conflictSetIdLength),
}
copy(result.storageKey, id[:])
return result
}
func (conflictSet *ConflictSet) AddMember(realityId RealityId) {
conflictSet.membersMutex.Lock()
conflictSet.members[realityId] = void
conflictSet.membersMutex.Unlock()
}
func (conflictSet *ConflictSet) String() string {
conflictSet.membersMutex.RLock()
defer conflictSet.membersMutex.RUnlock()
return stringify.Struct("ConflictSet",
stringify.StructField("id", conflictSet.id.String()),
stringify.StructField("members", conflictSet.members),
)
}
// region support object storage ///////////////////////////////////////////////////////////////////////////////////////
func (conflictSet *ConflictSet) GetStorageKey() []byte {
return conflictSet.storageKey
}
func (conflictSet *ConflictSet) Update(other objectstorage.StorableObject) {
fmt.Println("UPDATE")
}
func (conflictSet *ConflictSet) MarshalBinary() ([]byte, error) {
conflictSet.membersMutex.RLock()
offset := 0
membersCount := len(conflictSet.members)
result := make([]byte, 4+membersCount*realityIdLength)
binary.LittleEndian.PutUint32(result[offset:], uint32(membersCount))
offset += 4
for realityId := range conflictSet.members {
copy(result[offset:], realityId[:realityIdLength])
offset += realityIdLength
}
conflictSet.membersMutex.RUnlock()
return result, nil
}
func (conflictSet *ConflictSet) UnmarshalBinary(serializedObject []byte) error {
if err := conflictSet.id.UnmarshalBinary(conflictSet.storageKey); err != nil {
return err
}
if members, err := conflictSet.unmarshalMembers(serializedObject); err != nil {
return err
} else {
conflictSet.members = members
}
return nil
}
func (conflictSet *ConflictSet) unmarshalMembers(serializedConsumers []byte) (map[RealityId]empty, error) {
offset := 0
membersCount := int(binary.LittleEndian.Uint32(serializedConsumers[offset:]))
offset += 4
members := make(map[RealityId]empty, membersCount)
for i := 0; i < membersCount; i++ {
realityId := RealityId{}
if err := realityId.UnmarshalBinary(serializedConsumers[offset:]); err != nil {
return nil, err
}
offset += realityIdLength
members[realityId] = void
}
return members, nil
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
package ledgerstate
import (
"unicode/utf8"
"github.com/iotaledger/goshimmer/packages/stringify"
"golang.org/x/crypto/blake2b"
)
type ConflictSetId [conflictSetIdLength]byte
func NewConflictSetId(conflictSetBytes ...interface{}) (result ConflictSetId) {
switch len(conflictSetBytes) {
case 2:
transferHash, ok := conflictSetBytes[0].(TransferHash)
if !ok {
panic("expected first parameter of NewConflictSetId to be a TransferHash")
}
addressHash, ok := conflictSetBytes[0].(TransferHash)
if !ok {
panic("expected second parameter of NewConflictSetId to be a AddressHash")
}
fullConflictSetIdentifier := make([]byte, transferHashLength+addressHashLength)
copy(fullConflictSetIdentifier, transferHash[:])
copy(fullConflictSetIdentifier[transferHashLength:], addressHash[:])
result = blake2b.Sum256(fullConflictSetIdentifier)
case 1:
default:
panic("invalid parameter count when calling NewConflictSetId")
}
return
}
func (conflictSetId *ConflictSetId) UnmarshalBinary(data []byte) error {
copy(conflictSetId[:], data[:conflictSetIdLength])
return nil
}
func (conflictSetId ConflictSetId) String() string {
if utf8.Valid(conflictSetId[:]) {
return string(conflictSetId[:])
} else {
return stringify.SliceOfBytes(conflictSetId[:])
}
}
const conflictSetIdLength = 32
package ledgerstate
import (
"io/ioutil"
"os"
"os/exec"
"reflect"
"sort"
"strings"
"time"
"golang.org/x/crypto/blake2b"
"github.com/emicklei/dot"
"github.com/iotaledger/goshimmer/packages/errors"
"github.com/iotaledger/hive.go/objectstorage"
)
......@@ -16,6 +22,7 @@ type LedgerState struct {
transferOutputs *objectstorage.ObjectStorage
transferOutputBookings *objectstorage.ObjectStorage
realities *objectstorage.ObjectStorage
conflictSets *objectstorage.ObjectStorage
}
func NewLedgerState(storageId string) *LedgerState {
......@@ -24,6 +31,7 @@ func NewLedgerState(storageId string) *LedgerState {
transferOutputs: objectstorage.New(storageId+"TRANSFER_OUTPUTS", transferOutputFactory, objectstorage.CacheTime(1*time.Second)),
transferOutputBookings: objectstorage.New(storageId+"TRANSFER_OUTPUT_BOOKING", transferOutputBookingFactory, objectstorage.CacheTime(1*time.Second)),
realities: objectstorage.New(storageId+"REALITIES", realityFactory, objectstorage.CacheTime(1*time.Second)),
conflictSets: objectstorage.New(storageId+"CONFLICT_SETS", conflictSetFactory, objectstorage.CacheTime(1*time.Second)),
}
mainReality := newReality(MAIN_REALITY_ID)
......@@ -54,6 +62,26 @@ func (ledgerState *LedgerState) GetTransferOutput(transferOutputReference *Trans
}
}
func (ledgerState *LedgerState) ForEachConflictSet(callback func(object *objectstorage.CachedObject) bool) {
if err := ledgerState.conflictSets.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject) bool {
cachedObject.Get().(*ConflictSet).ledgerState = ledgerState
return callback(cachedObject)
}); err != nil {
panic(err)
}
}
func (ledgerState *LedgerState) ForEachReality(callback func(object *objectstorage.CachedObject) bool) {
if err := ledgerState.realities.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject) bool {
cachedObject.Get().(*Reality).ledgerState = ledgerState
return callback(cachedObject)
}); err != nil {
panic(err)
}
}
func (ledgerState *LedgerState) ForEachTransferOutput(callback func(object *objectstorage.CachedObject) bool, filters ...interface{}) {
prefixes, searchBookings := ledgerState.generateFilterPrefixes(filters)
if searchBookings {
......@@ -71,6 +99,8 @@ func (ledgerState *LedgerState) ForEachTransferOutput(callback func(object *obje
if len(prefixes) >= 1 {
for _, prefix := range prefixes {
if err := ledgerState.transferOutputs.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject) bool {
cachedObject.Get().(*TransferOutput).ledgerState = ledgerState
return callback(cachedObject)
}, prefix); err != nil {
panic(err)
......@@ -78,6 +108,8 @@ func (ledgerState *LedgerState) ForEachTransferOutput(callback func(object *obje
}
} else {
if err := ledgerState.transferOutputs.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject) bool {
cachedObject.Get().(*TransferOutput).ledgerState = ledgerState
return callback(cachedObject)
}); err != nil {
panic(err)
......@@ -124,6 +156,126 @@ func (ledgerState *LedgerState) BookTransfer(transfer *Transfer) error {
return nil
}
func (ledgerState *LedgerState) GenerateRealityVisualization(pngFilename string) error {
di := dot.NewGraph(dot.Directed)
realityNodes := make(map[RealityId]dot.Node)
var drawReality func(reality *Reality) dot.Node
drawReality = func(reality *Reality) dot.Node {
realityNode, exists := realityNodes[reality.GetId()]
if !exists {
if len(reality.parentRealities) > 1 {
realityNode = di.Node("AGGREGATED REALITY: " + strings.Trim(reality.GetId().String(), "\x00"))
} else {
realityNode = di.Node("REALITY: " + strings.Trim(reality.GetId().String(), "\x00"))
}
realityNode.Attr("style", "rounded")
realityNode.Attr("shape", "rect")
realityNodes[reality.GetId()] = realityNode
}
if !exists {
parentRealities := reality.GetParentRealities()
for _, cachedReality := range parentRealities {
cachedReality.Consume(func(object objectstorage.StorableObject) {
realityNode.Edge(drawReality(object.(*Reality)))
})
}
}
return realityNode
}
ledgerState.ForEachReality(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
drawReality(object.(*Reality))
})
return true
})
d1 := []byte(di.String())
if err := ioutil.WriteFile("_tmp.dot", d1, 0755); err != nil {
return err
}
_, err := exec.Command("dot", "-Tpng", "_tmp.dot", "-o", pngFilename).Output()
if err != nil {
return err
}
if err := os.Remove("_tmp.dot"); err != nil {
return err
}
return nil
}
func (ledgerState *LedgerState) GenerateVisualization() error {
di := dot.NewGraph(dot.Directed)
realityGraphs := make(map[RealityId]*dot.Graph)
getRealityGraph := func(realityId RealityId) *dot.Graph {
realityGraph, exists := realityGraphs[realityId]
if !exists {
realityGraph = di.Subgraph("REALITY: "+strings.Trim(realityId.String(), "\x00"), dot.ClusterOption{})
realityGraph.Attr("style", "filled")
realityGraph.Attr("color", "lightgrey")
realityGraph.Attr("fillcolor", "lightgrey")
realityGraphs[realityId] = realityGraph
}
return realityGraph
}
var drawTransferOutput func(transferOutput *TransferOutput) dot.Node
drawTransferOutput = func(transferOutput *TransferOutput) dot.Node {
transferOutputNode := getRealityGraph(transferOutput.GetRealityId()).Node(transferOutput.GetTransferHash().String() + "\n" + transferOutput.GetAddressHash().String())
transferOutputNode.Attr("style", "filled")
transferOutputNode.Attr("color", "white")
transferOutputNode.Attr("fillcolor", "white")
for transferHash, addresses := range transferOutput.GetConsumers() {
for _, addressHash := range addresses {
ledgerState.GetTransferOutput(NewTransferOutputReference(transferHash, addressHash)).Consume(func(object objectstorage.StorableObject) {
transferOutputNode.Edge(drawTransferOutput(object.(*TransferOutput)))
})
}
}
return transferOutputNode
}
ledgerState.ForEachTransferOutput(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
drawTransferOutput(object.(*TransferOutput))
})
return true
}, MAIN_REALITY_ID)
d1 := []byte(di.String())
if err := ioutil.WriteFile("_tmp.dot", d1, 0755); err != nil {
return err
}
_, err := exec.Command("dot", "-Tpng", "_tmp.dot", "-o", "viz.png").Output()
if err != nil {
return err
}
if err := os.Remove("_tmp.dot"); err != nil {
return err
}
return nil
}
func (ledgerState *LedgerState) MergeRealities(realityIds ...RealityId) *objectstorage.CachedObject {
switch len(realityIds) {
case 0:
......@@ -389,3 +541,12 @@ func realityFactory(key []byte) objectstorage.StorableObject {
return result
}
func conflictSetFactory(key []byte) objectstorage.StorableObject {
result := &ConflictSet{
storageKey: make([]byte, len(key)),
}
copy(result.storageKey, key)
return result
}
......@@ -6,6 +6,8 @@ import (
"testing"
"time"
"github.com/iotaledger/goshimmer/packages/utils"
"github.com/iotaledger/hive.go/objectstorage"
)
......@@ -15,9 +17,15 @@ var (
transferHash1 = NewTransferHash("TRANSFER1")
transferHash2 = NewTransferHash("TRANSFER2")
transferHash3 = NewTransferHash("TRANSFER3")
transferHash4 = NewTransferHash("TRANSFER4")
transferHash5 = NewTransferHash("TRANSFER5")
transferHash6 = NewTransferHash("TRANSFER6")
addressHash1 = NewAddressHash("ADDRESS1")
addressHash2 = NewAddressHash("ADDRESS2")
addressHash3 = NewAddressHash("ADDRESS3")
addressHash4 = NewAddressHash("ADDRESS4")
addressHash5 = NewAddressHash("ADDRESS5")
addressHash6 = NewAddressHash("ADDRESS6")
pendingReality = NewRealityId("PENDING")
)
......@@ -88,3 +96,161 @@ func Test(t *testing.T) {
return true
})
}
func generateRandomTransferHash() TransferHash {
return NewTransferHash(utils.RandomString(32))
}
func generateRandomAddressHash() AddressHash {
return NewAddressHash(utils.RandomString(32))
}
func initializeLedgerStateWithBalances(numberOfBalances int) (ledgerState *LedgerState, result []*TransferOutputReference) {
ledgerState = NewLedgerState("testLedger").Prune()
for i := 0; i < numberOfBalances; i++ {
transferHash := generateRandomTransferHash()
addressHash := generateRandomAddressHash()
ledgerState.AddTransferOutput(transferHash, addressHash, NewColoredBalance(iota, 1337))
result = append(result, NewTransferOutputReference(transferHash, addressHash))
}
return
}
func doubleSpend(ledgerState *LedgerState, transferOutputReference *TransferOutputReference) (result []*TransferOutputReference) {
for i := 0; i < 2; i++ {
result = append(result, spend(ledgerState, transferOutputReference))
}
return
}
func spend(ledgerState *LedgerState, transferOutputReferences ...*TransferOutputReference) (result *TransferOutputReference) {
transferHash := generateRandomTransferHash()
addressHash := generateRandomAddressHash()
transfer := NewTransfer(transferHash).AddOutput(
addressHash, NewColoredBalance(iota, uint64(len(transferOutputReferences))*1337),
)
for _, transferOutputReference := range transferOutputReferences {
transfer.AddInput(transferOutputReference)
}
if err := ledgerState.BookTransfer(transfer); err != nil {
panic(err)
}
result = NewTransferOutputReference(transferHash, addressHash)
return
}
func TestElevateAggregatedReality(t *testing.T) {
ledgerState, transferOutputs := initializeLedgerStateWithBalances(2)
// create 2 double spends
doubleSpentOutputs1 := doubleSpend(ledgerState, transferOutputs[0])
doubleSpentOutputs2 := doubleSpend(ledgerState, transferOutputs[1])
// send funds from one of the double spends further
spentInput := spend(ledgerState, doubleSpentOutputs1[1])
// aggregate further sent funds with other reality
spend(ledgerState, spentInput, doubleSpentOutputs2[0])
// double spend further spend to elevate aggregated reality
spend(ledgerState, doubleSpentOutputs1[1])
time.Sleep(1000 * time.Millisecond)
objectstorage.WaitForWritesToFlush()
if err := ledgerState.GenerateRealityVisualization("realities.png"); err != nil {
t.Error(err)
}
ledgerState.ForEachTransferOutput(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
fmt.Println(object.(*TransferOutput))
})
return true
}, MAIN_REALITY_ID)
}
func TestElevate(t *testing.T) {
ledgerState := NewLedgerState("testLedger").Prune().AddTransferOutput(
transferHash1, addressHash1, NewColoredBalance(eth, 1337), NewColoredBalance(iota, 1338),
)
// create first legit spend
if err := ledgerState.BookTransfer(NewTransfer(transferHash2).AddInput(
NewTransferOutputReference(transferHash1, addressHash1),
).AddOutput(
addressHash2, NewColoredBalance(iota, 1338),
).AddOutput(
addressHash2, NewColoredBalance(eth, 1337),
)); err != nil {
t.Error(err)
}
// send funds further
if err := ledgerState.BookTransfer(NewTransfer(transferHash3).AddInput(
NewTransferOutputReference(transferHash2, addressHash2),
).AddOutput(
addressHash4, NewColoredBalance(iota, 1338),
).AddOutput(
addressHash4, NewColoredBalance(eth, 1337),
)); err != nil {
t.Error(err)
}
if err := ledgerState.BookTransfer(NewTransfer(transferHash4).AddInput(
NewTransferOutputReference(transferHash2, addressHash2),
).AddOutput(
addressHash4, NewColoredBalance(iota, 1338),
).AddOutput(
addressHash4, NewColoredBalance(eth, 1337),
)); err != nil {
t.Error(err)
}
// aggregate realities
if err := ledgerState.BookTransfer(NewTransfer(transferHash6).AddInput(
NewTransferOutputReference(transferHash3, addressHash4),
).AddInput(
NewTransferOutputReference(transferHash4, addressHash4),
).AddOutput(
addressHash6, NewColoredBalance(iota, 2676),
).AddOutput(
addressHash6, NewColoredBalance(eth, 2674),
)); err != nil {
t.Error(err)
}
// create double spend for first transfer
if err := ledgerState.BookTransfer(NewTransfer(transferHash5).AddInput(
NewTransferOutputReference(transferHash1, addressHash1),
).AddOutput(
addressHash5, NewColoredBalance(iota, 1338),
).AddOutput(
addressHash5, NewColoredBalance(eth, 1337),
)); err != nil {
t.Error(err)
}
time.Sleep(1000 * time.Millisecond)
objectstorage.WaitForWritesToFlush()
ledgerState.ForEachTransferOutput(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
fmt.Println(object.(*TransferOutput))
})
return true
})
}
packages/ledgerstate/realities.png

44.5 KiB

......@@ -3,6 +3,7 @@ package ledgerstate
import (
"encoding/binary"
"fmt"
"sync"
"github.com/iotaledger/goshimmer/packages/errors"
......@@ -13,21 +14,27 @@ import (
type Reality struct {
id RealityId
parentRealities []RealityId
parentRealities map[RealityId]empty
storageKey []byte
ledgerState *LedgerState
parentRealitiesMutex sync.RWMutex
}
func newReality(id RealityId, parentRealities ...RealityId) *Reality {
result := &Reality{
id: id,
parentRealities: parentRealities,
parentRealities: make(map[RealityId]empty),
storageKey: make([]byte, len(id)),
}
copy(result.storageKey, id[:])
for _, parentRealityId := range parentRealities {
result.parentRealities[parentRealityId] = void
}
return result
}
......@@ -35,6 +42,24 @@ func (reality *Reality) GetId() RealityId {
return reality.id
}
func (reality *Reality) Elevate(oldParentRealityId RealityId, newParentRealityId RealityId) {
reality.parentRealitiesMutex.Lock()
fmt.Println(reality.id)
if len(reality.parentRealities) > 1 {
// aggregated reality
fmt.Println("AGGREGATED REALITY")
delete(reality.parentRealities, oldParentRealityId)
reality.parentRealities[newParentRealityId] = void
} else {
delete(reality.parentRealities, oldParentRealityId)
reality.parentRealities[newParentRealityId] = void
}
reality.parentRealitiesMutex.Unlock()
}
func (reality *Reality) DescendsFromReality(realityId RealityId) bool {
if reality.id == realityId {
return true
......@@ -56,15 +81,21 @@ func (reality *Reality) DescendsFromReality(realityId RealityId) bool {
func (reality *Reality) GetParentRealities() map[RealityId]*objectstorage.CachedObject {
parentRealities := make(map[RealityId]*objectstorage.CachedObject)
for _, parentRealityId := range reality.parentRealities {
reality.parentRealitiesMutex.RLock()
for parentRealityId := range reality.parentRealities {
loadedParentReality := reality.ledgerState.GetReality(parentRealityId)
if !loadedParentReality.Exists() {
reality.parentRealitiesMutex.RUnlock()
panic("could not load parent reality with id \"" + string(parentRealityId[:]) + "\"")
}
parentRealities[loadedParentReality.Get().(*Reality).GetId()] = loadedParentReality
}
reality.parentRealitiesMutex.RUnlock()
return parentRealities
}
......@@ -154,26 +185,32 @@ func (reality *Reality) elevateTransferOutput(transferOutputReference *TransferO
}
}
} else {
fmt.Println("ALREADY ELEVATED")
reality.ledgerState.GetReality(transferOutputToElevate.GetRealityId()).Consume(func(nestedReality objectstorage.StorableObject) {
nestedReality.(*Reality).Elevate(reality.id, newRealityId)
})
}
return nil
}
// Creates a new reality for consumers that have previously been booked in this reality.
func (reality *Reality) elevateTransferOutputConsumersToOwnReality(consumers map[TransferHash][]AddressHash) {
func (reality *Reality) elevateTransferOutputConsumersToOwnReality(consumers map[TransferHash][]AddressHash, conflictSet *objectstorage.CachedObject) {
for transferHash, addressHashes := range consumers {
var elevatedRealityId RealityId
copy(elevatedRealityId[:], transferHash[:])
reality.CreateReality(elevatedRealityId).Release()
conflictSet.Get().(*ConflictSet).AddMember(elevatedRealityId)
for _, addressHash := range addressHashes {
reality.elevateTransferOutput(NewTransferOutputReference(transferHash, addressHash), elevatedRealityId)
}
}
}
func (reality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transferHash TransferHash, outputs map[AddressHash][]*ColoredBalance) (conflicting bool, err error) {
func (reality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transferHash TransferHash, outputs map[AddressHash][]*ColoredBalance) (conflictSets objectstorage.CachedObjects, err error) {
conflictSets = make(objectstorage.CachedObjects, 0)
for _, input := range inputs {
consumedTransferOutput := input.Get().(*TransferOutput)
......@@ -184,11 +221,27 @@ func (reality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transf
return
}
if inputConflicting && len(consumersToElevate) >= 1 {
reality.elevateTransferOutputConsumersToOwnReality(consumersToElevate)
if inputConflicting {
conflictSetId := NewConflictSetId(consumedTransferOutput.GetTransferHash(), consumedTransferOutput.GetAddressHash())
var conflictSet *objectstorage.CachedObject
if len(consumersToElevate) >= 1 {
newConflictSet := newConflictSet(conflictSetId)
newConflictSet.ledgerState = reality.ledgerState
conflictSet = reality.ledgerState.conflictSets.Store(newConflictSet)
reality.elevateTransferOutputConsumersToOwnReality(consumersToElevate, conflictSet)
} else {
conflictSet, err = reality.ledgerState.conflictSets.Load(conflictSetId[:])
if err != nil {
return
}
conflictSet.Get().(*ConflictSet).ledgerState = reality.ledgerState
}
conflicting = conflicting || inputConflicting
conflictSets = append(conflictSets, conflictSet)
}
input.Store()
}
......@@ -201,15 +254,19 @@ func (reality *Reality) bookTransfer(transferHash TransferHash, inputs objectsto
return err
}
conflicting, err := reality.consumeInputs(inputs, transferHash, outputs)
conflictSets, err := reality.consumeInputs(inputs, transferHash, outputs)
if err != nil {
return err
}
if conflicting {
if len(conflictSets) >= 1 {
var targetRealityId RealityId
copy(targetRealityId[:], transferHash[:])
for _, conflictSet := range conflictSets {
conflictSet.Get().(*ConflictSet).AddMember(targetRealityId)
}
cachedTargetReality := reality.CreateReality(targetRealityId)
cachedTargetReality.Get().(*Reality).bookTransferOutputs(transferHash, outputs)
cachedTargetReality.Release()
......@@ -217,7 +274,7 @@ func (reality *Reality) bookTransfer(transferHash TransferHash, inputs objectsto
reality.bookTransferOutputs(transferHash, outputs)
}
// 5. release objects
conflictSets.Release()
inputs.Release()
return nil
......@@ -233,11 +290,26 @@ func (reality *Reality) bookTransferOutputs(transferHash TransferHash, transferO
}
}
func (reality *Reality) String() string {
return stringify.Struct("Reality",
func (reality *Reality) String() (result string) {
reality.parentRealitiesMutex.RLock()
parentRealities := make([]string, len(reality.parentRealities))
i := 0
for parentRealityId := range reality.parentRealities {
parentRealities[i] = parentRealityId.String()
i++
}
result = stringify.Struct("Reality",
stringify.StructField("id", reality.id.String()),
stringify.StructField("parentRealities", reality.parentRealities),
stringify.StructField("parentRealities", parentRealities),
)
reality.parentRealitiesMutex.RUnlock()
return
}
// region support object storage ///////////////////////////////////////////////////////////////////////////////////////
......@@ -247,23 +319,36 @@ func (reality *Reality) GetStorageKey() []byte {
}
func (reality *Reality) Update(other objectstorage.StorableObject) {
reality.parentRealitiesMutex.Lock()
if otherReality, ok := other.(*Reality); !ok {
reality.parentRealitiesMutex.Unlock()
panic("Update method expects a *TransferOutputBooking")
} else {
reality.parentRealities = otherReality.parentRealities
}
reality.parentRealitiesMutex.Unlock()
}
func (reality *Reality) MarshalBinary() ([]byte, error) {
reality.parentRealitiesMutex.RLock()
parentRealityCount := len(reality.parentRealities)
marshaledReality := make([]byte, 4+parentRealityCount*realityIdLength)
binary.LittleEndian.PutUint32(marshaledReality, uint32(parentRealityCount))
for i := 0; i < parentRealityCount; i++ {
copy(marshaledReality[4+i*realityIdLength:], reality.parentRealities[i][:])
i := 0
for parentRealityId := range reality.parentRealities {
copy(marshaledReality[4+i*realityIdLength:], parentRealityId[:])
i++
}
reality.parentRealitiesMutex.RUnlock()
return marshaledReality, nil
}
......@@ -272,12 +357,16 @@ func (reality *Reality) UnmarshalBinary(serializedObject []byte) error {
return err
}
reality.parentRealities = make(map[RealityId]empty)
parentRealityCount := int(binary.LittleEndian.Uint32(serializedObject))
parentRealities := make([]RealityId, parentRealityCount)
for i := 0; i < parentRealityCount; i++ {
if err := parentRealities[i].UnmarshalBinary(serializedObject[4+i*realityIdLength:]); err != nil {
var restoredRealityId RealityId
if err := restoredRealityId.UnmarshalBinary(serializedObject[4+i*realityIdLength:]); err != nil {
return err
}
reality.parentRealities[restoredRealityId] = void
}
return nil
......
package ledgerstate
import (
"github.com/iotaledger/goshimmer/packages/stringify"
)
type TransferOutputReference struct {
storageKey []byte
transferHash TransferHash
......@@ -17,3 +21,10 @@ func NewTransferOutputReference(transferHash TransferHash, addressHash AddressHa
func (transferOutputReference *TransferOutputReference) GetStorageKey() []byte {
return transferOutputReference.storageKey
}
func (transferOutputReference *TransferOutputReference) String() string {
return stringify.Struct("TransferOutputReference",
stringify.StructField("transferHash", transferOutputReference.transferHash),
stringify.StructField("addressHash", transferOutputReference.addressHash),
)
}
package ledgerstate
type empty struct{}
var void empty
......@@ -32,6 +32,8 @@ func Interface(value interface{}) string {
return Int(int(typeCastedValue.Uint()))
case reflect.Ptr:
return Interface(typeCastedValue.Interface())
case reflect.Struct:
return fmt.Sprint(value)
default:
panic("undefined reflect type: " + typeCastedValue.Kind().String())
}
......
package utils
import (
"math/rand"
"time"
"unsafe"
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
func RandomString(length int) string {
b := make([]byte, length)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := length-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return *(*string)(unsafe.Pointer(&b))
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment