Skip to content

Commit 4aebfb0

Browse files
committed
fixed other edge cases
1 parent 8749650 commit 4aebfb0

File tree

3 files changed

+292
-145
lines changed

3 files changed

+292
-145
lines changed

interpreter/interpreter.go

Lines changed: 95 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func (st *programState) runSendStatement(statement parser.SendStatement) ([]Post
303303
return nil, err
304304
}
305305
st.CurrentAsset = *asset
306-
sentAmt, err := st.trySendingAll(statement.Source)
306+
sentAmt, err := st.sendAll(statement.Source)
307307
if err != nil {
308308
return nil, err
309309
}
@@ -325,22 +325,11 @@ func (st *programState) runSendStatement(statement parser.SendStatement) ([]Post
325325
return nil, NegativeAmountErr{Amount: monetary.Amount}
326326
}
327327

328-
sentTotal, err := st.trySending(statement.Source, monetaryAmt)
328+
err = st.trySendingExact(statement.Source, monetaryAmt)
329329
if err != nil {
330330
return nil, err
331331
}
332332

333-
// sentTotal < monetary.Amount
334-
if sentTotal.Cmp(&monetaryAmt) == -1 {
335-
var missing big.Int
336-
missing.Sub(&monetaryAmt, sentTotal)
337-
return nil, MissingFundsErr{
338-
Asset: string(monetary.Asset),
339-
Missing: missing,
340-
Sent: *sentTotal,
341-
}
342-
}
343-
344333
// TODO simplify pointers
345334
amt := big.Int(monetary.Amount)
346335
err = st.receiveFrom(statement.Destination, &amt)
@@ -373,56 +362,52 @@ func (s *programState) getBalance(account string, asset string) *big.Int {
373362
return assetBalance
374363
}
375364

376-
func (s *programState) trySendingAccount(name string, amount big.Int) (*big.Int, error) {
377-
var monetaryAmount big.Int
378-
monetaryAmount.Set(&amount)
379-
380-
if name != "world" {
381-
balance := s.getBalance(name, s.CurrentAsset)
365+
func (s *programState) sendAllToAccount(accountLiteral parser.Literal, ovedraft *big.Int) (*big.Int, error) {
366+
account, err := evaluateLitExpecting(s, accountLiteral, expectAccount)
367+
if err != nil {
368+
return nil, err
369+
}
382370

383-
// monetary = min(balance, monetary)
384-
if balance.Cmp(&monetaryAmount) == -1 /* balance < monetary */ {
385-
monetaryAmount.Set(balance)
371+
if *account == "world" || ovedraft == nil {
372+
return nil, InvalidUnboundedInSendAll{
373+
Name: *account,
386374
}
387375
}
388376

377+
balance := s.getBalance(*account, s.CurrentAsset)
378+
379+
// we sent balance+overdraft
380+
var sentAmt big.Int
381+
sentAmt.Add(balance, ovedraft)
382+
389383
s.Senders = append(s.Senders, Sender{
390-
Name: name,
391-
Monetary: &monetaryAmount,
384+
Name: *account,
385+
Monetary: &sentAmt,
392386
})
393-
394-
return &monetaryAmount, nil
387+
return &sentAmt, nil
395388
}
396389

397-
func (s *programState) trySendingAll(source parser.Source) (*big.Int, error) {
390+
// Send as much as possible (and return the sent amt)
391+
func (s *programState) sendAll(source parser.Source) (*big.Int, error) {
398392
switch source := source.(type) {
399393
case *parser.SourceAccount:
400-
account, err := evaluateLitExpecting(s, source.Literal, expectAccount)
401-
if err != nil {
402-
return nil, err
403-
}
404-
name := string(*account)
394+
return s.sendAllToAccount(source.Literal, big.NewInt(0))
405395

406-
if name == "world" {
407-
return nil, InvalidUnboundedInSendAll{
408-
Name: name,
396+
case *parser.SourceOverdraft:
397+
var cap *big.Int
398+
if source.Bounded != nil {
399+
bounded, err := evaluateLitExpecting(s, *source.Bounded, expectMonetaryOfAsset(s.CurrentAsset))
400+
if err != nil {
401+
return nil, err
409402
}
403+
cap = bounded
410404
}
411-
412-
var balanceClone big.Int
413-
// TODO err empty balance?
414-
balance := s.getBalance(name, s.CurrentAsset)
415-
s.Senders = append(s.Senders, Sender{
416-
Name: name,
417-
Monetary: balanceClone.Set(balance),
418-
})
419-
420-
return &balanceClone, nil
405+
return s.sendAllToAccount(source.Address, cap)
421406

422407
case *parser.SourceInorder:
423408
totalSent := big.NewInt(0)
424409
for _, subSource := range source.Sources {
425-
sent, err := s.trySendingAll(subSource)
410+
sent, err := s.sendAll(subSource)
426411
if err != nil {
427412
return nil, err
428413
}
@@ -437,96 +422,97 @@ func (s *programState) trySendingAll(source parser.Source) (*big.Int, error) {
437422
}
438423

439424
// We switch to the default sending evaluation for this subsource
440-
return s.trySending(source.From, *monetary)
425+
return s.trySendingUpTo(source.From, *monetary)
441426

442427
case *parser.SourceAllotment:
443428
return nil, InvalidAllotmentInSendAll{}
444429

445-
case *parser.SourceOverdraft:
446-
account, err := evaluateLitExpecting(s, source.Address, expectAccount)
447-
if err != nil {
448-
return nil, err
449-
}
430+
default:
431+
utils.NonExhaustiveMatchPanic[error](source)
432+
return nil, nil
433+
}
434+
}
450435

451-
if source.Bounded == nil {
452-
return nil, InvalidUnboundedInSendAll{
453-
Name: *account,
454-
}
436+
// Fails if it doesn't manage to send exactly "amount"
437+
func (s *programState) trySendingExact(source parser.Source, amount big.Int) error {
438+
sentAmt, err := s.trySendingUpTo(source, amount)
439+
if err != nil {
440+
return err
441+
}
442+
if sentAmt.Cmp(&amount) != 0 {
443+
return MissingFundsErr{
444+
Asset: s.CurrentAsset,
445+
Needed: amount,
446+
Available: *sentAmt,
455447
}
448+
}
449+
return nil
450+
}
456451

457-
amount, err := evaluateLitExpecting(s, *source.Bounded, expectMonetaryOfAsset(s.CurrentAsset))
458-
if err != nil {
459-
return nil, err
460-
}
452+
func (s *programState) trySendingToAccount(accountLiteral parser.Literal, amount big.Int, overdraft *big.Int) (*big.Int, error) {
453+
account, err := evaluateLitExpecting(s, accountLiteral, expectAccount)
454+
if err != nil {
455+
return nil, err
456+
}
457+
if *account == "world" {
458+
overdraft = nil
459+
}
461460

462-
return s.trySendingAccount(*account, *amount)
461+
var actuallySentAmt big.Int
462+
if overdraft == nil {
463+
// unbounded overdraft: we send the required amount
464+
actuallySentAmt.Set(&amount)
465+
} else {
466+
balance := s.getBalance(*account, s.CurrentAsset)
463467

464-
default:
465-
utils.NonExhaustiveMatchPanic[error](source)
466-
return nil, nil
468+
// that's the amount we are allowed to send (balance + overdraft)
469+
var safeSendAmt big.Int
470+
safeSendAmt.Add(balance, overdraft)
471+
472+
actuallySentAmt = *utils.MinBigInt(&safeSendAmt, &amount)
467473
}
474+
475+
s.Senders = append(s.Senders, Sender{
476+
Name: *account,
477+
Monetary: &actuallySentAmt,
478+
})
479+
return &actuallySentAmt, nil
468480
}
469481

470-
func (s *programState) trySending(source parser.Source, amount big.Int) (*big.Int, error) {
482+
// Tries sending "amount" and returns the actually sent amt.
483+
// Doesn't fail (unless nested sources fail)
484+
func (s *programState) trySendingUpTo(source parser.Source, amount big.Int) (*big.Int, error) {
471485
switch source := source.(type) {
472486
case *parser.SourceAccount:
473-
account, err := evaluateLitExpecting(s, source.Literal, expectAccount)
474-
if err != nil {
475-
return nil, err
476-
}
477-
return s.trySendingAccount(string(*account), amount)
487+
return s.trySendingToAccount(source.Literal, amount, big.NewInt(0))
478488

479489
case *parser.SourceOverdraft:
480-
name, err := evaluateLitExpecting(s, source.Address, expectAccount)
481-
if err != nil {
482-
return nil, err
483-
}
484-
485-
balance := s.getBalance(*name, s.CurrentAsset)
486-
// "overdraft up to `source.Bounded`"
490+
var cap *big.Int
487491
if source.Bounded != nil {
488492
upTo, err := evaluateLitExpecting(s, *source.Bounded, expectMonetaryOfAsset(s.CurrentAsset))
489493
if err != nil {
490494
return nil, err
491495
}
492-
493-
var balancePlusOverdraft big.Int
494-
balancePlusOverdraft.Add(balance, upTo)
495-
// check that amount > balance + overdraft
496-
if amount.Cmp(&balancePlusOverdraft) == 1 {
497-
var missing big.Int
498-
499-
return nil, MissingFundsErr{
500-
Asset: s.CurrentAsset,
501-
Missing: *missing.Sub(&amount, &balancePlusOverdraft),
502-
Sent: balancePlusOverdraft,
503-
}
504-
}
505-
496+
cap = upTo
506497
}
507-
508-
s.Senders = append(s.Senders, Sender{
509-
Name: *name,
510-
Monetary: &amount,
511-
})
512-
513-
return &amount, nil
498+
return s.trySendingToAccount(source.Address, amount, cap)
514499

515500
case *parser.SourceInorder:
516-
sentTotal := big.NewInt(0)
501+
var totalLeft big.Int
502+
totalLeft.Set(&amount)
517503
for _, source := range source.Sources {
518-
var sendingMonetary big.Int
519-
sendingMonetary.Sub(&amount, sentTotal)
520-
sentAmt, err := s.trySending(source, sendingMonetary)
504+
sentAmt, err := s.trySendingUpTo(source, totalLeft)
521505
if err != nil {
522506
return nil, err
523507
}
524-
sentTotal.Add(sentTotal, sentAmt)
508+
totalLeft.Sub(&totalLeft, sentAmt)
525509
}
526-
return sentTotal, nil
510+
511+
var sentAmt big.Int
512+
sentAmt.Sub(&amount, &totalLeft)
513+
return &sentAmt, nil
527514

528515
case *parser.SourceAllotment:
529-
receivedTotal := big.NewInt(0)
530516
var items []parser.AllotmentValue
531517
for _, i := range source.Items {
532518
items = append(items, i.Allotment)
@@ -536,29 +522,20 @@ func (s *programState) trySending(source parser.Source, amount big.Int) (*big.In
536522
return nil, err
537523
}
538524
for i, allotmentItem := range source.Items {
539-
source := allotmentItem.From
540-
received, err := s.trySending(source, *big.NewInt(allot[i]))
525+
err := s.trySendingExact(allotmentItem.From, *big.NewInt(allot[i]))
541526
if err != nil {
542527
return nil, err
543528
}
544-
receivedTotal.Add(receivedTotal, received)
545529
}
546-
return receivedTotal, nil
530+
return &amount, nil
547531

548532
case *parser.SourceCapped:
549533
cap, err := evaluateLitExpecting(s, source.Cap, expectMonetaryOfAsset(s.CurrentAsset))
550534
if err != nil {
551535
return nil, err
552536
}
553-
554-
// TODO use utils.min
555-
var cappedAmount big.Int
556-
if amount.Cmp(cap) == -1 /* monetary < cap */ {
557-
cappedAmount.Set(&amount)
558-
} else {
559-
cappedAmount.Set(cap)
560-
}
561-
return s.trySending(source.From, cappedAmount)
537+
cappedAmount := utils.MinBigInt(&amount, cap)
538+
return s.trySendingUpTo(source.From, *cappedAmount)
562539

563540
default:
564541
utils.NonExhaustiveMatchPanic[any](source)

interpreter/interpreter_error.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import (
66
)
77

88
type MissingFundsErr struct {
9-
Asset string
10-
Missing big.Int
11-
Sent big.Int
9+
Asset string
10+
Needed big.Int
11+
Available big.Int
1212
}
1313

1414
func (e MissingFundsErr) Error() string {
15-
return fmt.Sprintf("Not enough funds. Missing [%s %s] (only [%s %s] available)", e.Asset, e.Missing.String(), e.Asset, e.Sent.String())
15+
return fmt.Sprintf("Not enough funds. Needed [%s %s] (only [%s %s] available)", e.Asset, e.Needed.String(), e.Asset, e.Available.String())
1616
}
1717

1818
type TypeError struct {

0 commit comments

Comments
 (0)