diff --git a/packages/mana/balance.go b/packages/mana/balance.go index ebf746872358ab76fb954099f266174feb291e73..49b8a313e79e5c4debd7a9de72a052bb75b82601 100644 --- a/packages/mana/balance.go +++ b/packages/mana/balance.go @@ -161,12 +161,17 @@ func (balance *Balance) applyTransfer(transfer *Transfer) { } // calculate mana gains - gainedMana, roundingError := balance.calculator.GenerateMana(transfer.movedCoins, transfer.spentTime-transfer.receivedTime) + var gainedMana uint64 + for _, input := range transfer.inputs { + generatedMana, _ := balance.calculator.GenerateMana(input.coinAmount, transfer.spentTime-input.receivedTime) + + gainedMana += generatedMana + } // store results balance.transferHistory.AddLast(&BalanceHistoryEntry{ transfer: transfer, balance: currentBalance + gainedMana - transfer.burnedMana, - accumulatedRoundingError: roundingError, + accumulatedRoundingError: 0, // TODO: remove }) } diff --git a/packages/mana/balance_test.go b/packages/mana/balance_test.go index 0a35b1e6b862d41bf21bd9ce5db4811175dc83e1..65e5a3d3f2d135d0cbdd17c5c775c9a816cd0d75 100644 --- a/packages/mana/balance_test.go +++ b/packages/mana/balance_test.go @@ -13,22 +13,28 @@ func TestBalance_CleanupTransferHistory(t *testing.T) { // fill transfer history balance1 := NewBalance(calculator) balance1.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 10, - receivedTime: 1000, - spentTime: 1700, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 1000, + }}, + spentTime: 1700, + burnedMana: 10, }) balance1.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 0, - receivedTime: 700, - spentTime: 1000, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 700, + }}, + spentTime: 1000, + burnedMana: 0, }) balance1.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 0, - receivedTime: 0, - spentTime: 700, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 0, + }}, + spentTime: 700, + burnedMana: 0, }) // cleanup transfer history @@ -56,31 +62,39 @@ func TestBalance_AddTransfer(t *testing.T) { // spend coins multiple times balance1 := NewBalance(calculator) balance1.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 10, - receivedTime: 1000, - spentTime: 1700, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 1000, + }}, + spentTime: 1700, + burnedMana: 10, }) balance1.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 0, - receivedTime: 700, - spentTime: 1000, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 700, + }}, + spentTime: 1000, + burnedMana: 0, }) balance1.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 0, - receivedTime: 0, - spentTime: 700, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 0, + }}, + spentTime: 700, + burnedMana: 0, }) // hold coins for the full time balance2 := NewBalance(calculator) balance2.BookTransfer(&Transfer{ - movedCoins: 1000, - burnedMana: 10, - receivedTime: 0, - spentTime: 1700, + inputs: []*Input{{ + coinAmount: 1000, + receivedTime: 0, + }}, + spentTime: 1700, + burnedMana: 10, }) // check result diff --git a/packages/mana/errors.go b/packages/mana/errors.go index e462aad43529f4c59f902abf4c35b79e75f1a54f..258f8407ab2c2c82426834bb698968581dbebb46 100644 --- a/packages/mana/errors.go +++ b/packages/mana/errors.go @@ -1 +1,9 @@ package mana + +import ( + "github.com/iotaledger/goshimmer/packages/errors" +) + +var ( + ErrUnmarshalFailed = errors.New("unmarshal failed") +) diff --git a/packages/mana/input.go b/packages/mana/input.go new file mode 100644 index 0000000000000000000000000000000000000000..c28916606ff75e80a0a338b31389870c4baf7c0e --- /dev/null +++ b/packages/mana/input.go @@ -0,0 +1,76 @@ +package mana + +import ( + "encoding/binary" + "fmt" + + "github.com/iotaledger/goshimmer/packages/marshal" + + "github.com/iotaledger/goshimmer/packages/errors" +) + +type Input struct { + coinAmount uint64 + receivedTime uint64 +} + +func (input *Input) MarshalBinary() (data []byte, err errors.IdentifiableError) { + data = inputSchema.Marshal(input) + + return +} + +func (input *Input) UnmarshalBinary(data []byte) (err errors.IdentifiableError) { + if len(data) < INPUT_TOTAL_MARSHALED_SIZE { + err = ErrUnmarshalFailed.Derive("byte sequence of marshaled input is not long enough") + } + + input.coinAmount = binary.BigEndian.Uint64(data[INPUT_COIN_AMOUNT_BYTE_OFFSET_START:INPUT_COIN_AMOUNT_BYTE_OFFSET_END]) + input.receivedTime = binary.BigEndian.Uint64(data[INPUT_RECEIVED_TIME_BYTE_OFFSET_START:INPUT_RECEIVED_TIME_BYTE_OFFSET_END]) + + return +} + +func (input *Input) GetCoinAmount() uint64 { + return input_getCoinAmount(input) +} + +func input_getCoinAmount(input interface{}) uint64 { + return input.(*Input).coinAmount +} + +func input_setCoinAmount(input interface{}, coinAmount uint64) { + input.(*Input).coinAmount = coinAmount +} + +func input_getReceivedTime(input interface{}) uint64 { + return input.(*Input).receivedTime +} + +func input_setReceivedTime(input interface{}, receivedTime uint64) { + input.(*Input).coinAmount = receivedTime +} + +var inputSchema = marshal.Schema( + marshal.Uint64(input_getCoinAmount, input_setCoinAmount), + marshal.Uint64(input_getReceivedTime, input_setReceivedTime), +) + +func init() { + fmt.Println((&Input{ + coinAmount: 10, + receivedTime: 20, + }).MarshalBinary()) +} + +const ( + INPUT_COIN_AMOUNT_BYTE_OFFSET_START = 0 + INPUT_COIN_AMOUNT_BYTE_OFFSET_LENGTH = 8 + INPUT_COIN_AMOUNT_BYTE_OFFSET_END = INPUT_COIN_AMOUNT_BYTE_OFFSET_START + INPUT_COIN_AMOUNT_BYTE_OFFSET_LENGTH + + INPUT_RECEIVED_TIME_BYTE_OFFSET_START = INPUT_COIN_AMOUNT_BYTE_OFFSET_END + INPUT_RECEIVED_TIME_BYTE_OFFSET_LENGTH = 8 + INPUT_RECEIVED_TIME_BYTE_OFFSET_END = INPUT_RECEIVED_TIME_BYTE_OFFSET_START + INPUT_RECEIVED_TIME_BYTE_OFFSET_LENGTH + + INPUT_TOTAL_MARSHALED_SIZE = INPUT_RECEIVED_TIME_BYTE_OFFSET_END +) diff --git a/packages/mana/input_test.go b/packages/mana/input_test.go new file mode 100644 index 0000000000000000000000000000000000000000..997f0223121cdf557e5e1bb89243e1cb3c9d0bc1 --- /dev/null +++ b/packages/mana/input_test.go @@ -0,0 +1,27 @@ +package mana + +import ( + "testing" +) + +func BenchmarkDirectReceiver(b *testing.B) { + input := &Input{ + coinAmount: 10, + receivedTime: 0, + } + + for i := 0; i < b.N; i++ { + input.GetCoinAmount() + } +} + +func BenchmarkManualReceiver(b *testing.B) { + input := &Input{ + coinAmount: 10, + receivedTime: 0, + } + + for i := 0; i < b.N; i++ { + input_getCoinAmount(input) + } +} diff --git a/packages/mana/transfer.go b/packages/mana/transfer.go index bcf42d744083af21c330547b26040a0e0ad9b567..212235a6a0030b65bded2bc141dc27d163beb4a2 100644 --- a/packages/mana/transfer.go +++ b/packages/mana/transfer.go @@ -1,8 +1,31 @@ package mana +import ( + "github.com/iotaledger/goshimmer/packages/errors" +) + type Transfer struct { - movedCoins uint64 - burnedMana uint64 - receivedTime uint64 - spentTime uint64 + inputs []*Input + spentTime uint64 + burnedMana uint64 +} + +func (transfer *Transfer) MarshalBinary() (data []byte, err errors.IdentifiableError) { + data = make([]byte, INPUT_TOTAL_MARSHALED_SIZE) + + return } + +func (transfer *Transfer) UnmarshalBinary(data []byte) (err errors.IdentifiableError) { + return +} + +const ( + TRANSFER_SPENT_TIME_BYTE_OFFSET_START = 0 + TRANSFER_SPENT_TIME_BYTE_OFFSET_LENGTH = 8 + TRANSFER_SPENT_TIME_BYTE_OFFSET_END = TRANSFER_SPENT_TIME_BYTE_OFFSET_START + TRANSFER_SPENT_TIME_BYTE_OFFSET_LENGTH + + TRANSFER_BURNED_MANA_BYTE_OFFSET_START = INPUT_COIN_AMOUNT_BYTE_OFFSET_END + TRANSFER_BURNED_MANA_BYTE_OFFSET_LENGTH = 8 + TRANSFER_BURNED_MANA_BYTE_OFFSET_END = INPUT_RECEIVED_TIME_BYTE_OFFSET_START + INPUT_RECEIVED_TIME_BYTE_OFFSET_LENGTH +) diff --git a/packages/marshal/encoder.go b/packages/marshal/encoder.go new file mode 100644 index 0000000000000000000000000000000000000000..86990dc67a1d18927a26d95eafaf326b9e092c93 --- /dev/null +++ b/packages/marshal/encoder.go @@ -0,0 +1,26 @@ +package marshal + +type Field interface { + Get(obj interface{}) interface{} + Marshal(obj interface{}, data []byte) []byte +} + +type encoder struct { + fields []Field +} + +func Schema(fields ...Field) *encoder { + return &encoder{ + fields: fields, + } +} + +func (encoder *encoder) Marshal(obj interface{}) []byte { + result := make([]byte, 0) + + for _, field := range encoder.fields { + result = append(result, field.Marshal(field.Get(obj), result)...) + } + + return result +} diff --git a/packages/marshal/uint64.go b/packages/marshal/uint64.go new file mode 100644 index 0000000000000000000000000000000000000000..5bdd83254b5e39f27ea028581a638c3cab0f1b7a --- /dev/null +++ b/packages/marshal/uint64.go @@ -0,0 +1,32 @@ +package marshal + +import ( + "encoding/binary" +) + +func Uint64(getter uint64Getter, setter uint64Setter) *uint64Field { + return &uint64Field{ + getter: getter, + setter: setter, + } +} + +type uint64Getter func(receiver interface{}) uint64 +type uint64Setter func(receiver interface{}, val uint64) + +type uint64Field struct { + getter uint64Getter + setter uint64Setter +} + +func (uint64Field *uint64Field) Marshal(obj interface{}, data []byte) (result []byte) { + result = make([]byte, 8) + + binary.BigEndian.PutUint64(result, obj.(uint64)) + + return +} + +func (uint64Field *uint64Field) Get(obj interface{}) interface{} { + return uint64Field.getter(obj) +}