|  | 
| 7 | 7 | 	"crypto/ed25519" | 
| 8 | 8 | 	"fmt" | 
| 9 | 9 | 	"log" | 
|  | 10 | +	"math/big" | 
| 10 | 11 | 	"os/exec" | 
| 11 | 12 | 	"sort" | 
| 12 | 13 | 	"strings" | 
| @@ -61,6 +62,8 @@ type Account struct { | 
| 61 | 62 | 	AccountID      iotago.AccountID | 
| 62 | 63 | 	AccountAddress *iotago.AccountAddress | 
| 63 | 64 | 	BlockIssuerKey ed25519.PrivateKey | 
|  | 65 | +	AccountOutput  *iotago.AccountOutput | 
|  | 66 | +	OutputID       iotago.OutputID | 
| 64 | 67 | } | 
| 65 | 68 | 
 | 
| 66 | 69 | type DockerTestFramework struct { | 
| @@ -375,6 +378,8 @@ func (d *DockerTestFramework) CreateAccount(opts ...options.Option[builder.Accou | 
| 375 | 378 | 		AccountID:      accountID, | 
| 376 | 379 | 		AccountAddress: accountAddress, | 
| 377 | 380 | 		BlockIssuerKey: accPrivateKey, | 
|  | 381 | +		AccountOutput:  accountOutput, | 
|  | 382 | +		OutputID:       accOutputID, | 
| 378 | 383 | 	} | 
| 379 | 384 | } | 
| 380 | 385 | 
 | 
| @@ -425,6 +430,135 @@ func (d *DockerTestFramework) DelegateToValidator(from *Account, validator *Node | 
| 425 | 430 | 	return delegationOutput.StartEpoch | 
| 426 | 431 | } | 
| 427 | 432 | 
 | 
|  | 433 | +// AllotManaTo allots amount of mana from one account to another. | 
|  | 434 | +func (d *DockerTestFramework) AllotManaTo(from *Account, to *Account, manaToAllot iotago.Mana) { | 
|  | 435 | +	// requesting faucet funds for allotment | 
|  | 436 | +	ctx := context.TODO() | 
|  | 437 | +	fundsAddr, privateKey := d.getAddress(iotago.AddressEd25519) | 
|  | 438 | +	fundsOutputID, fundsUTXOOutput := d.RequestFaucetFunds(ctx, fundsAddr) | 
|  | 439 | +	fundsAddrSigner := iotago.NewInMemoryAddressSigner(iotago.NewAddressKeysForEd25519Address(fundsAddr.(*iotago.Ed25519Address), privateKey)) | 
|  | 440 | + | 
|  | 441 | +	clt := d.Node("V1").Client | 
|  | 442 | +	currentSlot := clt.LatestAPI().TimeProvider().SlotFromTime(time.Now()) | 
|  | 443 | +	apiForSlot := clt.APIForSlot(currentSlot) | 
|  | 444 | + | 
|  | 445 | +	basicOutput, ok := fundsUTXOOutput.(*iotago.BasicOutput) | 
|  | 446 | +	require.True(d.Testing, ok) | 
|  | 447 | + | 
|  | 448 | +	// Subtract stored mana from source outputs to fund Allotment. | 
|  | 449 | +	outputBuilder := builder.NewBasicOutputBuilderFromPrevious(basicOutput) | 
|  | 450 | +	actualAllottedMana := manaToAllot | 
|  | 451 | +	if manaToAllot >= basicOutput.StoredMana() { | 
|  | 452 | +		actualAllottedMana = basicOutput.StoredMana() | 
|  | 453 | +		outputBuilder.Mana(0) | 
|  | 454 | +	} else { | 
|  | 455 | +		outputBuilder.Mana(basicOutput.StoredMana() - manaToAllot) | 
|  | 456 | +	} | 
|  | 457 | + | 
|  | 458 | +	issuerResp, err := clt.BlockIssuance(ctx) | 
|  | 459 | +	require.NoError(d.Testing, err) | 
|  | 460 | + | 
|  | 461 | +	congestionResp, err := clt.Congestion(ctx, from.AccountAddress, lo.PanicOnErr(issuerResp.LatestCommitment.ID())) | 
|  | 462 | +	require.NoError(d.Testing, err) | 
|  | 463 | + | 
|  | 464 | +	signedTx, err := builder.NewTransactionBuilder(apiForSlot). | 
|  | 465 | +		AddInput(&builder.TxInput{ | 
|  | 466 | +			UnlockTarget: fundsAddr, | 
|  | 467 | +			InputID:      fundsOutputID, | 
|  | 468 | +			Input:        fundsUTXOOutput, | 
|  | 469 | +		}). | 
|  | 470 | +		IncreaseAllotment(to.AccountID, actualAllottedMana). | 
|  | 471 | +		AddOutput(basicOutput). | 
|  | 472 | +		SetCreationSlot(currentSlot). | 
|  | 473 | +		AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). | 
|  | 474 | +		WithTransactionCapabilities(iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything())). | 
|  | 475 | +		Build(fundsAddrSigner) | 
|  | 476 | +	require.NoError(d.Testing, err) | 
|  | 477 | + | 
|  | 478 | +	blkID := d.SubmitPayload(ctx, signedTx, wallet.NewEd25519Account(from.AccountID, from.BlockIssuerKey), congestionResp, issuerResp) | 
|  | 479 | + | 
|  | 480 | +	d.AwaitTransactionPayloadAccepted(ctx, blkID) | 
|  | 481 | +} | 
|  | 482 | + | 
|  | 483 | +func (d *DockerTestFramework) CreateNativeToken(from *Account) (updatedAccount *Account) { | 
|  | 484 | +	// requesting faucet funds for native token creation | 
|  | 485 | +	ctx := context.TODO() | 
|  | 486 | +	fundsAddr, privateKey := d.getAddress(iotago.AddressEd25519) | 
|  | 487 | +	fundsOutputID, fundsUTXOOutput := d.RequestFaucetFunds(ctx, fundsAddr) | 
|  | 488 | + | 
|  | 489 | +	mintedAmount := fundsUTXOOutput.BaseTokenAmount() | 
|  | 490 | + | 
|  | 491 | +	clt := d.Node("V1").Client | 
|  | 492 | +	currentSlot := clt.LatestAPI().TimeProvider().SlotFromTime(time.Now()) | 
|  | 493 | +	apiForSlot := clt.APIForSlot(currentSlot) | 
|  | 494 | + | 
|  | 495 | +	// increase foundry counter | 
|  | 496 | +	accTransitionOutput := builder.NewAccountOutputBuilderFromPrevious(from.AccountOutput). | 
|  | 497 | +		FoundriesToGenerate(1).MustBuild() | 
|  | 498 | + | 
|  | 499 | +	// build foundry output, consume all amount from the UTXO output | 
|  | 500 | +	foundryID, _ := iotago.FoundryIDFromAddressAndSerialNumberAndTokenScheme(from.AccountAddress, accTransitionOutput.FoundryCounter, iotago.TokenSchemeSimple) | 
|  | 501 | +	tokenScheme := &iotago.SimpleTokenScheme{ | 
|  | 502 | +		MintedTokens:  big.NewInt(int64(mintedAmount)), | 
|  | 503 | +		MaximumSupply: big.NewInt(int64(mintedAmount)), | 
|  | 504 | +		MeltedTokens:  big.NewInt(0), | 
|  | 505 | +	} | 
|  | 506 | + | 
|  | 507 | +	foundryOutput := builder.NewFoundryOutputBuilder(from.AccountAddress, tokenScheme, mintedAmount). | 
|  | 508 | +		NativeToken(&iotago.NativeTokenFeature{ | 
|  | 509 | +			ID:     foundryID, | 
|  | 510 | +			Amount: big.NewInt(int64(mintedAmount)), | 
|  | 511 | +		}). | 
|  | 512 | +		SerialNumber(accTransitionOutput.FoundryCounter).MustBuild() | 
|  | 513 | + | 
|  | 514 | +	// prepare transaction | 
|  | 515 | +	issuerResp, err := clt.BlockIssuance(ctx) | 
|  | 516 | +	require.NoError(d.Testing, err) | 
|  | 517 | + | 
|  | 518 | +	congestionResp, err := clt.Congestion(ctx, from.AccountAddress, lo.PanicOnErr(issuerResp.LatestCommitment.ID())) | 
|  | 519 | +	require.NoError(d.Testing, err) | 
|  | 520 | + | 
|  | 521 | +	signer := iotago.NewInMemoryAddressSigner(iotago.NewAddressKeysForEd25519Address(fundsAddr.(*iotago.Ed25519Address), privateKey), | 
|  | 522 | +		iotago.NewAddressKeysForEd25519Address(from.AccountOutput.UnlockConditionSet().Address().Address.(*iotago.Ed25519Address), from.BlockIssuerKey)) | 
|  | 523 | + | 
|  | 524 | +	signedTx, err := builder.NewTransactionBuilder(apiForSlot). | 
|  | 525 | +		AddInput(&builder.TxInput{ | 
|  | 526 | +			UnlockTarget: fundsAddr, | 
|  | 527 | +			InputID:      fundsOutputID, | 
|  | 528 | +			Input:        fundsUTXOOutput, | 
|  | 529 | +		}). | 
|  | 530 | +		AddInput(&builder.TxInput{ | 
|  | 531 | +			UnlockTarget: from.AccountOutput.UnlockConditionSet().Address().Address, | 
|  | 532 | +			InputID:      from.OutputID, | 
|  | 533 | +			Input:        from.AccountOutput, | 
|  | 534 | +		}). | 
|  | 535 | +		AddOutput(accTransitionOutput). | 
|  | 536 | +		AddOutput(foundryOutput). | 
|  | 537 | +		SetCreationSlot(currentSlot). | 
|  | 538 | +		AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: from.AccountID}). | 
|  | 539 | +		AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). | 
|  | 540 | +		WithTransactionCapabilities(iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything())). | 
|  | 541 | +		AllotAllMana(currentSlot, from.AccountID). | 
|  | 542 | +		Build(signer) | 
|  | 543 | +	require.NoError(d.Testing, err) | 
|  | 544 | + | 
|  | 545 | +	blkID := d.SubmitPayload(ctx, signedTx, wallet.NewEd25519Account(from.AccountID, from.BlockIssuerKey), congestionResp, issuerResp) | 
|  | 546 | + | 
|  | 547 | +	updatedAccount = &Account{ | 
|  | 548 | +		AccountID:      from.AccountID, | 
|  | 549 | +		AccountAddress: from.AccountAddress, | 
|  | 550 | +		BlockIssuerKey: from.BlockIssuerKey, | 
|  | 551 | +		AccountOutput:  accTransitionOutput, | 
|  | 552 | +		OutputID:       iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 0), | 
|  | 553 | +	} | 
|  | 554 | + | 
|  | 555 | +	d.AwaitTransactionPayloadAccepted(ctx, blkID) | 
|  | 556 | +	d.AssertIndexerAccountExists(updatedAccount) | 
|  | 557 | +	d.AssertIndexerFoundryExists(foundryID) | 
|  | 558 | + | 
|  | 559 | +	return updatedAccount | 
|  | 560 | +} | 
|  | 561 | + | 
| 428 | 562 | func (d *DockerTestFramework) CheckAccountStatus(ctx context.Context, blkID iotago.BlockID, txID iotago.TransactionID, creationOutputID iotago.OutputID, accountAddress *iotago.AccountAddress, checkIndexer ...bool) { | 
| 429 | 563 | 	// request by blockID if provided, otherwise use txID | 
| 430 | 564 | 	// we take the slot from the blockID in case the tx is created earlier than the block. | 
| @@ -469,6 +603,43 @@ func (d *DockerTestFramework) RequestFaucetFunds(ctx context.Context, receiveAdd | 
| 469 | 603 | 	return outputID, output | 
| 470 | 604 | } | 
| 471 | 605 | 
 | 
|  | 606 | +func (d *DockerTestFramework) AssertIndexerAccountExists(account *Account) { | 
|  | 607 | +	d.Eventually(func() error { | 
|  | 608 | +		ctx := context.TODO() | 
|  | 609 | +		indexerClt, err := d.Node("V1").Client.Indexer(ctx) | 
|  | 610 | +		if err != nil { | 
|  | 611 | +			return err | 
|  | 612 | +		} | 
|  | 613 | + | 
|  | 614 | +		outputID, output, _, err := indexerClt.Account(ctx, account.AccountAddress) | 
|  | 615 | +		if err != nil { | 
|  | 616 | +			return err | 
|  | 617 | +		} | 
|  | 618 | + | 
|  | 619 | +		require.EqualValues(d.Testing, account.OutputID, outputID) | 
|  | 620 | +		require.EqualValues(d.Testing, account.AccountOutput, output) | 
|  | 621 | + | 
|  | 622 | +		return nil | 
|  | 623 | +	}) | 
|  | 624 | +} | 
|  | 625 | + | 
|  | 626 | +func (d *DockerTestFramework) AssertIndexerFoundryExists(foundryID iotago.FoundryID) { | 
|  | 627 | +	d.Eventually(func() error { | 
|  | 628 | +		ctx := context.TODO() | 
|  | 629 | +		indexerClt, err := d.Node("V1").Client.Indexer(ctx) | 
|  | 630 | +		if err != nil { | 
|  | 631 | +			return err | 
|  | 632 | +		} | 
|  | 633 | + | 
|  | 634 | +		_, _, _, err = indexerClt.Foundry(ctx, foundryID) | 
|  | 635 | +		if err != nil { | 
|  | 636 | +			return err | 
|  | 637 | +		} | 
|  | 638 | + | 
|  | 639 | +		return nil | 
|  | 640 | +	}) | 
|  | 641 | +} | 
|  | 642 | + | 
| 472 | 643 | func (d *DockerTestFramework) AssertValidatorExists(accountAddr *iotago.AccountAddress) { | 
| 473 | 644 | 	d.Eventually(func() error { | 
| 474 | 645 | 		for _, node := range d.nodes { | 
|  | 
0 commit comments