Skip to content

Commit

Permalink
Merge branch 'develop' into info-resp-naming
Browse files Browse the repository at this point in the history
  • Loading branch information
jkrvivian committed Oct 18, 2023
2 parents 80edf3b + 0058a6f commit 29b96fc
Showing 1 changed file with 88 additions and 60 deletions.
148 changes: 88 additions & 60 deletions builder/transaction_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,42 +146,62 @@ func (b *TransactionBuilder) AllotRequiredManaAndStoreRemainingManaInOutput(targ
return b
}

minManalockedSlot := b.transaction.CreationSlot + 2*b.api.ProtocolParameters().MaxCommittableAge()
if storedManaOutputIndex >= len(b.transaction.Outputs) {
return setBuildError(ierrors.Errorf("given storedManaOutputIndex does not exist: %d", storedManaOutputIndex))
}

// check if the output is locked for a certain time to an account.
hasManalockCondition := func(output iotago.Output) (iotago.AccountID, bool) {
if !output.UnlockConditionSet().HasTimelockUntil(minManalockedSlot) {
return iotago.EmptyAccountID, false
}
// calculate the minimum required mana to issue the block
minRequiredMana, err := b.MinRequiredAllotedMana(b.api.ProtocolParameters().WorkScoreParameters(), rmc, blockIssuerAccountID)
if err != nil {
return setBuildError(ierrors.Wrap(err, "failed to calculate the minimum required mana to issue the block"))
}

unlockAddress := output.UnlockConditionSet().Address()
if unlockAddress == nil {
return iotago.EmptyAccountID, false
}
unboundManaInputsLeftoverBalance, err := b.calculateAvailableManaLeftover(targetSlot, minRequiredMana, blockIssuerAccountID)
if err != nil {
return setBuildError(err)
}

if unlockAddress.Address.Type() != iotago.AddressAccount {
return iotago.EmptyAccountID, false
}
//nolint:forcetypeassert // we can safely assume that this is an AccountAddress
accountAddress := unlockAddress.Address.(*iotago.AccountAddress)
// allot the mana to the block issuer account (we increase the value, so we don't interfere with the already alloted value)
b.IncreaseAllotment(blockIssuerAccountID, minRequiredMana)

// move the remaining mana to stored mana on the specified output index
switch output := b.transaction.Outputs[storedManaOutputIndex].(type) {
case *iotago.BasicOutput:
output.Mana += unboundManaInputsLeftoverBalance
case *iotago.AccountOutput:
output.Mana += unboundManaInputsLeftoverBalance
case *iotago.NFTOutput:
output.Mana += unboundManaInputsLeftoverBalance
default:
return setBuildError(ierrors.Wrapf(iotago.ErrUnknownOutputType, "output type %T does not support stored mana", output))
}

return b
}

return accountAddress.AccountID(), true
// AllotAllMana allots all available mana to the provided account, even if the alloted value is less than the minimum required mana value to issue the block.
func (b *TransactionBuilder) AllotAllMana(targetSlot iotago.SlotIndex, blockIssuerAccountID iotago.AccountID) *TransactionBuilder {
setBuildError := func(err error) *TransactionBuilder {
b.occurredBuildErr = err
return b
}

// calculate the available mana on input side
_, unboundManaInputs, accountBoundManaInputs, err := CalculateAvailableMana(b.api.ProtocolParameters(), b.inputs, targetSlot)
unboundManaInputsLeftoverBalance, err := b.calculateAvailableManaLeftover(targetSlot, 0, blockIssuerAccountID)
if err != nil {
return setBuildError(ierrors.Wrap(err, "failed to calculate the available mana on input side"))
return setBuildError(err)
}

// update the unbound mana balance
updateUnboundManaBalance := func(manaOut iotago.Mana) error {
if unboundManaInputs < manaOut {
return ierrors.New("not enough unbound mana available on the input side")
}
unboundManaInputs -= manaOut
// allot the mana to the block issuer account (we increase the value, so we don't interfere with the already alloted value)
b.IncreaseAllotment(blockIssuerAccountID, unboundManaInputsLeftoverBalance)

return nil
return b
}

func (b *TransactionBuilder) calculateAvailableManaLeftover(targetSlot iotago.SlotIndex, minRequiredMana iotago.Mana, blockIssuerAccountID iotago.AccountID) (iotago.Mana, error) {
// calculate the available mana on input side
_, unboundManaInputs, accountBoundManaInputs, err := CalculateAvailableMana(b.api.ProtocolParameters(), b.inputs, targetSlot)
if err != nil {
return 0, ierrors.Wrap(err, "failed to calculate the available mana on input side")
}

// update the account bound mana balances if they exist and/or the onbound mana balance
Expand All @@ -195,7 +215,12 @@ func (b *TransactionBuilder) AllotRequiredManaAndStoreRemainingManaInOutput(targ
accountBoundManaInputs[accountID] = 0

// subtract the remainder from the unbound mana
return updateUnboundManaBalance(accountBoundManaOut - accountBalance)
unboundManaInputs, err = safemath.SafeSub(unboundManaInputs, accountBoundManaOut-accountBalance)
if err != nil {
return ierrors.Wrapf(err, "not enough unbound mana on the input side for account %s while subtracting remainder", accountID.String())
}

return nil
}

// there is enough account bound mana for this account, subtract it from there
Expand All @@ -205,70 +230,73 @@ func (b *TransactionBuilder) AllotRequiredManaAndStoreRemainingManaInOutput(targ
}

// no account bound mana available for the given account, subtract it from the unbounded mana
return updateUnboundManaBalance(accountBoundManaOut)
}
unboundManaInputs, err = safemath.SafeSub(unboundManaInputs, accountBoundManaOut)
if err != nil {
return ierrors.Wrapf(err, "not enough unbound mana on the input side for account %s", accountID.String())
}

if storedManaOutputIndex >= len(b.transaction.Outputs) {
return setBuildError(ierrors.Errorf("given storedManaOutputIndex does not exist: %d", storedManaOutputIndex))
return nil
}

// subtract the stored mana on the outputs side
for _, o := range b.transaction.Outputs {
switch output := o.(type) {
case *iotago.AccountOutput:
// mana on account outputs is locked to this account
if err := updateUnboundAndAccountBoundManaBalances(output.AccountID, output.StoredMana()); err != nil {
return setBuildError(ierrors.Wrap(err, "failed to subtract the stored mana on the outputs side"))
if err = updateUnboundAndAccountBoundManaBalances(output.AccountID, output.StoredMana()); err != nil {
return 0, ierrors.Wrap(err, "failed to subtract the stored mana on the outputs side for account output")
}

default:
// check if the output locked mana to a certain account
if accountID, isManaLocked := hasManalockCondition(output); isManaLocked {
if err := updateUnboundAndAccountBoundManaBalances(accountID, output.StoredMana()); err != nil {
return setBuildError(ierrors.Wrap(err, "failed to subtract the stored mana on the outputs side"))
if accountID, isManaLocked := b.hasManalockCondition(output); isManaLocked {
if err = updateUnboundAndAccountBoundManaBalances(accountID, output.StoredMana()); err != nil {
return 0, ierrors.Wrap(err, "failed to subtract the stored mana on the outputs side, while checking locked mana")
}
} else {
if err := updateUnboundManaBalance(output.StoredMana()); err != nil {
return setBuildError(ierrors.Wrap(err, "failed to subtract the stored mana on the outputs side"))
unboundManaInputs, err = safemath.SafeSub(unboundManaInputs, output.StoredMana())
if err != nil {
return 0, ierrors.Wrap(err, "failed to subtract the stored mana on the outputs side")
}
}
}
}

// subtract the already alloted mana
for _, allotment := range b.transaction.Allotments {
if err := updateUnboundAndAccountBoundManaBalances(allotment.AccountID, allotment.Value); err != nil {
return setBuildError(ierrors.Wrap(err, "failed to subtract the already alloted mana"))
if err = updateUnboundAndAccountBoundManaBalances(allotment.AccountID, allotment.Value); err != nil {
return 0, ierrors.Wrap(err, "failed to subtract the already alloted mana")
}
}

// calculate the minimum required mana to issue the block
minRequiredMana, err := b.MinRequiredAllotedMana(b.api.ProtocolParameters().WorkScoreParameters(), rmc, blockIssuerAccountID)
if err != nil {
return setBuildError(ierrors.Wrap(err, "failed to calculate the minimum required mana to issue the block"))
// subtract the minimum required mana to issue the block
if err = updateUnboundAndAccountBoundManaBalances(blockIssuerAccountID, minRequiredMana); err != nil {
return 0, ierrors.Wrap(err, "failed to subtract the minimum required mana to issue the block")
}

// subtract the minimum required mana to issue the block
if err := updateUnboundAndAccountBoundManaBalances(blockIssuerAccountID, minRequiredMana); err != nil {
return setBuildError(ierrors.Wrap(err, "failed to subtract the minimum required mana to issue the block"))
return unboundManaInputs, nil
}

// hasManalockCondition checks if the output is locked for a certain time to an account.
func (b *TransactionBuilder) hasManalockCondition(output iotago.Output) (iotago.AccountID, bool) {
minManalockedSlot := b.transaction.CreationSlot + 2*b.api.ProtocolParameters().MaxCommittableAge()

if !output.UnlockConditionSet().HasTimelockUntil(minManalockedSlot) {
return iotago.EmptyAccountID, false
}

// allot the mana to the block issuer account (we increase the value, so we don't interfere with the already alloted value)
b.IncreaseAllotment(blockIssuerAccountID, minRequiredMana)
unlockAddress := output.UnlockConditionSet().Address()
if unlockAddress == nil {
return iotago.EmptyAccountID, false
}

// move the remaining mana to stored mana on the specified output index
switch output := b.transaction.Outputs[storedManaOutputIndex].(type) {
case *iotago.BasicOutput:
output.Mana += unboundManaInputs
case *iotago.AccountOutput:
output.Mana += unboundManaInputs
case *iotago.NFTOutput:
output.Mana += unboundManaInputs
default:
return setBuildError(ierrors.Wrapf(iotago.ErrUnknownOutputType, "output type %T does not support stored mana", output))
if unlockAddress.Address.Type() != iotago.AddressAccount {
return iotago.EmptyAccountID, false
}
//nolint:forcetypeassert // we can safely assume that this is an AccountAddress
accountAddress := unlockAddress.Address.(*iotago.AccountAddress)

return b
return accountAddress.AccountID(), true
}

// BuildAndSwapToBlockBuilder builds the transaction and then swaps to a BasicBlockBuilder with
Expand Down

0 comments on commit 29b96fc

Please sign in to comment.