@@ -303,7 +303,7 @@ func (st *programState) runSendStatement(statement parser.SendStatement) ([]Post
303
303
return nil , err
304
304
}
305
305
st .CurrentAsset = * asset
306
- sentAmt , err := st .trySendingAll (statement .Source )
306
+ sentAmt , err := st .sendAll (statement .Source )
307
307
if err != nil {
308
308
return nil , err
309
309
}
@@ -325,22 +325,11 @@ func (st *programState) runSendStatement(statement parser.SendStatement) ([]Post
325
325
return nil , NegativeAmountErr {Amount : monetary .Amount }
326
326
}
327
327
328
- sentTotal , err : = st .trySending (statement .Source , monetaryAmt )
328
+ err = st .trySendingExact (statement .Source , monetaryAmt )
329
329
if err != nil {
330
330
return nil , err
331
331
}
332
332
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
-
344
333
// TODO simplify pointers
345
334
amt := big .Int (monetary .Amount )
346
335
err = st .receiveFrom (statement .Destination , & amt )
@@ -373,56 +362,52 @@ func (s *programState) getBalance(account string, asset string) *big.Int {
373
362
return assetBalance
374
363
}
375
364
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
+ }
382
370
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 ,
386
374
}
387
375
}
388
376
377
+ balance := s .getBalance (* account , s .CurrentAsset )
378
+
379
+ // we sent balance+overdraft
380
+ var sentAmt big.Int
381
+ sentAmt .Add (balance , ovedraft )
382
+
389
383
s .Senders = append (s .Senders , Sender {
390
- Name : name ,
391
- Monetary : & monetaryAmount ,
384
+ Name : * account ,
385
+ Monetary : & sentAmt ,
392
386
})
393
-
394
- return & monetaryAmount , nil
387
+ return & sentAmt , nil
395
388
}
396
389
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 ) {
398
392
switch source := source .(type ) {
399
393
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 ))
405
395
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
409
402
}
403
+ cap = bounded
410
404
}
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 )
421
406
422
407
case * parser.SourceInorder :
423
408
totalSent := big .NewInt (0 )
424
409
for _ , subSource := range source .Sources {
425
- sent , err := s .trySendingAll (subSource )
410
+ sent , err := s .sendAll (subSource )
426
411
if err != nil {
427
412
return nil , err
428
413
}
@@ -437,96 +422,97 @@ func (s *programState) trySendingAll(source parser.Source) (*big.Int, error) {
437
422
}
438
423
439
424
// We switch to the default sending evaluation for this subsource
440
- return s .trySending (source .From , * monetary )
425
+ return s .trySendingUpTo (source .From , * monetary )
441
426
442
427
case * parser.SourceAllotment :
443
428
return nil , InvalidAllotmentInSendAll {}
444
429
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
+ }
450
435
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 ,
455
447
}
448
+ }
449
+ return nil
450
+ }
456
451
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
+ }
461
460
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 )
463
467
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 )
467
473
}
474
+
475
+ s .Senders = append (s .Senders , Sender {
476
+ Name : * account ,
477
+ Monetary : & actuallySentAmt ,
478
+ })
479
+ return & actuallySentAmt , nil
468
480
}
469
481
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 ) {
471
485
switch source := source .(type ) {
472
486
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 ))
478
488
479
489
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
487
491
if source .Bounded != nil {
488
492
upTo , err := evaluateLitExpecting (s , * source .Bounded , expectMonetaryOfAsset (s .CurrentAsset ))
489
493
if err != nil {
490
494
return nil , err
491
495
}
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
506
497
}
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 )
514
499
515
500
case * parser.SourceInorder :
516
- sentTotal := big .NewInt (0 )
501
+ var totalLeft big.Int
502
+ totalLeft .Set (& amount )
517
503
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 )
521
505
if err != nil {
522
506
return nil , err
523
507
}
524
- sentTotal . Add ( sentTotal , sentAmt )
508
+ totalLeft . Sub ( & totalLeft , sentAmt )
525
509
}
526
- return sentTotal , nil
510
+
511
+ var sentAmt big.Int
512
+ sentAmt .Sub (& amount , & totalLeft )
513
+ return & sentAmt , nil
527
514
528
515
case * parser.SourceAllotment :
529
- receivedTotal := big .NewInt (0 )
530
516
var items []parser.AllotmentValue
531
517
for _ , i := range source .Items {
532
518
items = append (items , i .Allotment )
@@ -536,29 +522,20 @@ func (s *programState) trySending(source parser.Source, amount big.Int) (*big.In
536
522
return nil , err
537
523
}
538
524
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 ]))
541
526
if err != nil {
542
527
return nil , err
543
528
}
544
- receivedTotal .Add (receivedTotal , received )
545
529
}
546
- return receivedTotal , nil
530
+ return & amount , nil
547
531
548
532
case * parser.SourceCapped :
549
533
cap , err := evaluateLitExpecting (s , source .Cap , expectMonetaryOfAsset (s .CurrentAsset ))
550
534
if err != nil {
551
535
return nil , err
552
536
}
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 )
562
539
563
540
default :
564
541
utils.NonExhaustiveMatchPanic [any ](source )
0 commit comments