diff --git a/go.mod b/go.mod index 71ec711860eff317cd6343f017f21914c3233269..0feb1bed4fd9ab291d1dfea8c3c85da034ca98e7 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0 github.com/gorilla/websocket v1.4.1 - github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675 + github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b github.com/iotaledger/iota.go v1.0.0-beta.14 github.com/kr/pretty v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect diff --git a/go.sum b/go.sum index 5b2da7adcd516be81f51396ba445b33c895cda68..cde30b4b760f323146f0a3998bf9693ef5c89032 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675 h1:rlN7zGXPTms6tg4OoD4rTbca71X/wffneMvScmrwMp8= -github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64= +github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b h1:fmFIWBJt73KvQpUNuhTWpdseHrKIgb1Hz7g0a6K4S0M= +github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= diff --git a/main.go b/main.go index 435ede0cf04ecdd83ce8f7fa7a435228807efb3f..d8238b11190c49df9954e5c274bf769276f84ea3 100644 --- a/main.go +++ b/main.go @@ -27,8 +27,6 @@ import ( ) func main() { - config.Init() - go http.ListenAndServe("localhost:6061", nil) // pprof Server for Debbuging Mutexes node.Run( diff --git a/packages/binary/datastructure/orderedmap/element.go b/packages/binary/datastructure/orderedmap/element.go new file mode 100644 index 0000000000000000000000000000000000000000..d0ee44053031339c6cd11d3723e5c0962d0b1ef4 --- /dev/null +++ b/packages/binary/datastructure/orderedmap/element.go @@ -0,0 +1,8 @@ +package orderedmap + +type Element struct { + key interface{} + value interface{} + prev *Element + next *Element +} diff --git a/packages/binary/datastructure/orderedmap/orderedmap.go b/packages/binary/datastructure/orderedmap/orderedmap.go new file mode 100644 index 0000000000000000000000000000000000000000..d92c1e8c3b67f1292d87e2e50f4d09d86c984f92 --- /dev/null +++ b/packages/binary/datastructure/orderedmap/orderedmap.go @@ -0,0 +1,117 @@ +package orderedmap + +import ( + "sync" +) + +type OrderedMap struct { + head *Element + tail *Element + dictionary map[interface{}]*Element + size int + mutex sync.RWMutex +} + +func New() *OrderedMap { + return &OrderedMap{ + dictionary: make(map[interface{}]*Element), + } +} + +func (orderedMap *OrderedMap) Get(key interface{}) (interface{}, bool) { + orderedMap.mutex.RLock() + defer orderedMap.mutex.RUnlock() + + if orderedMapElement, orderedMapElementExists := orderedMap.dictionary[key]; !orderedMapElementExists { + return nil, false + } else { + return orderedMapElement.value, true + } +} + +func (orderedMap *OrderedMap) Set(key interface{}, newValue interface{}) bool { + if oldValue, oldValueExists := orderedMap.Get(key); oldValueExists && oldValue == newValue { + return false + } + + orderedMap.mutex.Lock() + defer orderedMap.mutex.Unlock() + + if oldValue, oldValueExists := orderedMap.dictionary[key]; oldValueExists { + if oldValue.value == newValue { + return false + } + + oldValue.value = newValue + + return true + } + + newElement := &Element{ + key: key, + value: newValue, + } + + if orderedMap.head == nil { + orderedMap.head = newElement + } else { + orderedMap.tail.next = newElement + newElement.prev = orderedMap.tail + } + orderedMap.tail = newElement + orderedMap.size++ + + orderedMap.dictionary[key] = newElement + + return true +} + +func (orderedMap *OrderedMap) ForEach(consumer func(key, value interface{}) bool) { + currentEntry := orderedMap.head + + for currentEntry != nil { + if !consumer(currentEntry.key, currentEntry.value) { + return + } + + currentEntry = currentEntry.next + } +} + +func (orderedMap *OrderedMap) Delete(key interface{}) bool { + if _, valueExists := orderedMap.Get(key); !valueExists { + return false + } + + orderedMap.mutex.Lock() + defer orderedMap.mutex.Unlock() + + value, valueExists := orderedMap.dictionary[key] + if !valueExists { + return false + } + + delete(orderedMap.dictionary, key) + orderedMap.size-- + + if value.prev != nil { + value.prev.next = value.next + } else { + orderedMap.head = value.next + } + + if value.next != nil { + value.next.prev = value.prev + } else { + orderedMap.tail = value.prev + } + + return true +} + +func (orderedMap *OrderedMap) Size() int { + orderedMap.mutex.RLock() + defer orderedMap.mutex.RUnlock() + + return orderedMap.size +} diff --git a/packages/binary/datastructure/orderedmap/orderedmap_test.go b/packages/binary/datastructure/orderedmap/orderedmap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..00071715985f9a7a9047ca73ffb9e4a8a20331cf --- /dev/null +++ b/packages/binary/datastructure/orderedmap/orderedmap_test.go @@ -0,0 +1,30 @@ +package orderedmap + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOrderedMap_Size(t *testing.T) { + orderedMap := New() + + assert.Equal(t, 0, orderedMap.Size()) + + orderedMap.Set(1, 1) + + assert.Equal(t, 1, orderedMap.Size()) + + orderedMap.Set(3, 1) + orderedMap.Set(2, 1) + + assert.Equal(t, 3, orderedMap.Size()) + + orderedMap.Set(2, 2) + + assert.Equal(t, 3, orderedMap.Size()) + + orderedMap.Delete(2) + + assert.Equal(t, 2, orderedMap.Size()) +} diff --git a/packages/binary/marshalutil/generic_parser.go b/packages/binary/marshalutil/generic_parser.go new file mode 100644 index 0000000000000000000000000000000000000000..e8a7409c0fdd2fd6bc3b41d2387413a3f9c6a23c --- /dev/null +++ b/packages/binary/marshalutil/generic_parser.go @@ -0,0 +1,3 @@ +package marshalutil + +type GenericParser func(data []byte) (result interface{}, err error, consumedBytes int) diff --git a/packages/binary/marshalutil/marshalutil.go b/packages/binary/marshalutil/marshalutil.go index 7218e54db052aec58d59e42e85c74503551e5e94..45ab8ad2ce81d77dbc4910fe6fe74b83cd878dec 100644 --- a/packages/binary/marshalutil/marshalutil.go +++ b/packages/binary/marshalutil/marshalutil.go @@ -38,6 +38,22 @@ func New(args ...interface{}) *MarshalUtil { } } +func (util *MarshalUtil) Parse(parser GenericParser) (result interface{}, err error) { + result, err, readBytes := parser(util.bytes[util.readOffset:]) + + util.ReadSeek(util.readOffset + readBytes) + + return +} + +func (util *MarshalUtil) ReadOffset() int { + return util.readOffset +} + +func (util *MarshalUtil) WriteOffset() int { + return util.writeOffset +} + func (util *MarshalUtil) WriteSeek(offset int) { util.writeOffset = offset } diff --git a/packages/binary/marshalutil/marshalutil.uint32.go b/packages/binary/marshalutil/marshalutil.uint32.go new file mode 100644 index 0000000000000000000000000000000000000000..473e1d505100c96a5f156b0a42ce033bd6924313 --- /dev/null +++ b/packages/binary/marshalutil/marshalutil.uint32.go @@ -0,0 +1,26 @@ +package marshalutil + +import ( + "encoding/binary" +) + +const UINT32_SIZE = 4 + +func (util *MarshalUtil) WriteUint32(value uint32) { + writeEndOffset := util.expandWriteCapacity(UINT32_SIZE) + + binary.LittleEndian.PutUint32(util.bytes[util.writeOffset:writeEndOffset], value) + + util.WriteSeek(writeEndOffset) +} + +func (util *MarshalUtil) ReadUint32() (uint32, error) { + readEndOffset, err := util.checkReadCapacity(UINT32_SIZE) + if err != nil { + return 0, err + } + + defer util.ReadSeek(readEndOffset) + + return binary.LittleEndian.Uint32(util.bytes[util.readOffset:readEndOffset]), nil +} diff --git a/packages/binary/tangle/events.go b/packages/binary/tangle/events.go index 5fe03f48e19c5ff19a5936a8b16486d51a40a597..ebf537c9d1a761177779f9fc3660bcdd1a426362 100644 --- a/packages/binary/tangle/events.go +++ b/packages/binary/tangle/events.go @@ -8,6 +8,7 @@ import ( ) type Events struct { + // Get's called whenever a transaction TransactionAttached *events.Event TransactionSolid *events.Event MissingTransactionReceived *events.Event diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go index 185937043da051b0a1c8f1ea33db0a72726f48f8..4b3653ca8c0a7402b7b1651932bf11e569136abc 100644 --- a/packages/binary/tangle/tangle.go +++ b/packages/binary/tangle/tangle.go @@ -74,7 +74,7 @@ func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *tran return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])} } -// Retrieves the approvers of a transaction from the tangle. +// GetApprovers retrieves the approvers of a transaction from the tangle. func (tangle *Tangle) GetApprovers(transactionId transaction.Id) approver.CachedApprovers { approvers := make(approver.CachedApprovers, 0) tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool { diff --git a/packages/binary/valuetangle/address.go b/packages/binary/valuetangle/address.go new file mode 100644 index 0000000000000000000000000000000000000000..0d1119b324a20ff0edfc03564a01dead879c7695 --- /dev/null +++ b/packages/binary/valuetangle/address.go @@ -0,0 +1,23 @@ +package valuetangle + +type AddressVersion = byte + +type AddressDigest = []byte + +type Address [AddressLength]byte + +func NewAddress(bytes []byte) (address Address) { + copy(address[:], bytes) + + return +} + +func (address *Address) GetVersion() AddressVersion { + return address[0] +} + +func (address *Address) GetDigest() AddressDigest { + return address[1:] +} + +const AddressLength = 33 diff --git a/packages/binary/valuetangle/test/payload_test.go b/packages/binary/valuetangle/test/payload_test.go index 1666070110a4cd37b82b9a3ae49e3349027c3ce9..ffee05e890a65972cc842298627f35a3ba12cb8a 100644 --- a/packages/binary/valuetangle/test/payload_test.go +++ b/packages/binary/valuetangle/test/payload_test.go @@ -8,9 +8,22 @@ import ( ) func TestPayload(t *testing.T) { - transfer := valuetangle.NewTransfer() - fmt.Println(transfer.GetId()) + inputs := valuetangle.NewTransferInputs().Add( + valuetangle.NewTransferOutputId(valuetangle.NewAddress([]byte("test")), valuetangle.NewTransferId([]byte("test"))), + ).Add( + valuetangle.NewTransferOutputId(valuetangle.NewAddress([]byte("test")), valuetangle.NewTransferId([]byte("test1"))), + ) - payload := valuetangle.NewPayload(valuetangle.EmptyPayloadId, valuetangle.EmptyPayloadId, transfer) - fmt.Println(payload.GetId()) + transfer := valuetangle.NewTransfer(inputs) + bytes, err := transfer.MarshalBinary() + if err != nil { + t.Error(err) + + return + } + + var restoredTransfer valuetangle.Transfer + fmt.Println(restoredTransfer.UnmarshalBinary(bytes)) + fmt.Println(bytes, nil) + fmt.Println(restoredTransfer.MarshalBinary()) } diff --git a/packages/binary/valuetangle/transfer.go b/packages/binary/valuetangle/transfer.go index e9faad1e4a5ffe845de5491793e886d224e5be7c..63c99caadbbc72a248b2bd44204ecd86f631ffab 100644 --- a/packages/binary/valuetangle/transfer.go +++ b/packages/binary/valuetangle/transfer.go @@ -5,20 +5,25 @@ import ( "github.com/iotaledger/hive.go/objectstorage" "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" ) type Transfer struct { objectstorage.StorableObjectFlags - id *TransferId - bytes []byte + id *TransferId + inputs *TransferInputs + bytes []byte idMutex sync.RWMutex bytesMutex sync.RWMutex } -func NewTransfer() *Transfer { - return &Transfer{} +func NewTransfer(inputs *TransferInputs) *Transfer { + return &Transfer{ + inputs: inputs, + } } func FromStorage(key []byte) *Transfer { @@ -50,36 +55,51 @@ func (transfer *Transfer) GetId() TransferId { return *transfer.id } - // otherwise calculate the PayloadId - transfer.id = transfer.calculateId() - - return *transfer.id -} - -func (transfer *Transfer) calculateId() *TransferId { - bytes, _ := transfer.MarshalBinary() - - id := blake2b.Sum256(bytes) + // otherwise calculate the id + bytes, err := transfer.MarshalBinary() + if err != nil { + panic(err) + } + idBytes := blake2b.Sum256(bytes) + transferId := NewTransferId(idBytes[:]) - result := NewTransferId(id[:]) + // cache result for later calls + transfer.id = &transferId - return &result + return transferId } func (transfer *Transfer) Update(other objectstorage.StorableObject) { - panic("implement me") + panic("update forbidden") } func (transfer *Transfer) GetStorageKey() []byte { - panic("implement me") + id := transfer.GetId() + + return id[:] } func (transfer *Transfer) MarshalBinary() ([]byte, error) { - return nil, nil + marshalUtil := marshalutil.New() + + marshalUtil.WriteBytes(transfer.inputs.ToBytes()) + + return marshalUtil.Bytes(), nil } func (transfer *Transfer) UnmarshalBinary(data []byte) error { - panic("implement me") + marshalUtil := marshalutil.New(data) + + parseResult, err := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) { + return TransferInputsFromBytes(data) + }) + if err != nil { + return err + } + transfer.inputs = parseResult.(*TransferInputs) + + return nil + } // define contracts (ensure that the struct fulfills the corresponding interfaces diff --git a/packages/binary/valuetangle/transfer_inputs.go b/packages/binary/valuetangle/transfer_inputs.go new file mode 100644 index 0000000000000000000000000000000000000000..f83e22d152914ea7fd17f6817e67f47d6d4763e9 --- /dev/null +++ b/packages/binary/valuetangle/transfer_inputs.go @@ -0,0 +1,112 @@ +package valuetangle + +import ( + "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" +) + +type TransferInputs struct { + *orderedmap.OrderedMap +} + +func NewTransferInputs(transferOutputIds ...TransferOutputId) (inputs *TransferInputs) { + inputs = &TransferInputs{orderedmap.New()} + + for _, transferOutputId := range transferOutputIds { + inputs.Add(transferOutputId) + } + + return +} + +func TransferInputsFromBytes(bytes []byte) (inputs *TransferInputs, err error, consumedBytes int) { + inputs = NewTransferInputs() + + marshalUtil := marshalutil.New(bytes) + inputCount, err := marshalUtil.ReadUint32() + if err != nil { + return + } + + for i := uint32(0); i < inputCount; i++ { + addressBytes, readErr := marshalUtil.ReadBytes(AddressLength) + if readErr != nil { + err = readErr + + return + } + address := NewAddress(addressBytes) + + transferIdBytes, readErr := marshalUtil.ReadBytes(TransferIdLength) + if readErr != nil { + err = readErr + + return + } + transferId := NewTransferId(transferIdBytes) + + addressMap, addressExists := inputs.Get(address) + if !addressExists { + addressMap = orderedmap.New() + + inputs.Set(address, addressMap) + } + addressMap.(*orderedmap.OrderedMap).Set(transferId, NewTransferOutputId(address, transferId)) + } + + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (inputs *TransferInputs) Add(input TransferOutputId) *TransferInputs { + address := input.GetAddress() + transferId := input.GetTransferId() + + addressMap, addressExists := inputs.Get(address) + if !addressExists { + addressMap = orderedmap.New() + + inputs.Set(address, addressMap) + } + + addressMap.(*orderedmap.OrderedMap).Set(transferId, input) + + return inputs +} + +func (inputs *TransferInputs) ToBytes() (bytes []byte) { + marshalUtil := marshalutil.New() + + marshalUtil.WriteSeek(4) + var inputCounter uint32 + inputs.ForEach(func(transferOutputId TransferOutputId) { + marshalUtil.WriteBytes(transferOutputId.ToBytes()) + + inputCounter++ + }) + marshalUtil.WriteSeek(0) + marshalUtil.WriteUint32(inputCounter) + + return marshalUtil.Bytes() +} + +func (inputs *TransferInputs) ForEach(consumer func(transferOutputId TransferOutputId)) { + inputs.OrderedMap.ForEach(func(key, value interface{}) bool { + value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool { + consumer(value.(TransferOutputId)) + + return true + }) + + return true + }) +} + +func (inputs *TransferInputs) ForEachAddress(consumer func(address Address)) { + inputs.OrderedMap.ForEach(func(key, value interface{}) bool { + consumer(key.(Address)) + + return true + }) +} diff --git a/packages/binary/valuetangle/transferoutput_id.go b/packages/binary/valuetangle/transferoutput_id.go new file mode 100644 index 0000000000000000000000000000000000000000..f39c12b60db79cdb60b91032adde136a7f9c6c92 --- /dev/null +++ b/packages/binary/valuetangle/transferoutput_id.go @@ -0,0 +1,38 @@ +package valuetangle + +import ( + "github.com/mr-tron/base58" +) + +type TransferOutputId [TransferOutputIdLength]byte + +func NewTransferOutputId(address Address, transferId TransferId) (transferOutputId TransferOutputId) { + copy(transferOutputId[:AddressLength], address[:]) + copy(transferOutputId[AddressLength:], transferId[:]) + + return +} + +func TransferOutputIdFromBytes(bytes []byte) (transferOutputId TransferOutputId) { + copy(transferOutputId[:], bytes) + + return +} + +func (transferOutputId TransferOutputId) GetAddress() Address { + return NewAddress(transferOutputId[:AddressLength]) +} + +func (transferOutputId TransferOutputId) GetTransferId() TransferId { + return NewTransferId(transferOutputId[AddressLength:]) +} + +func (transferOutputId TransferOutputId) ToBytes() []byte { + return transferOutputId[:] +} + +func (transferOutputId TransferOutputId) String() string { + return base58.Encode(transferOutputId[:]) +} + +const TransferOutputIdLength = AddressLength + TransferIdLength