Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asset transfer #639

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open

asset transfer #639

wants to merge 23 commits into from

Conversation

adecaro
Copy link
Contributor

@adecaro adecaro commented May 27, 2024

This PR adapts the code in #437 to the current shape of the token-sdk. The code is mostly contribution of @HagarMeir .

To be addressed in another PR:

  • Unit tests
  • Resolve TODO
  • Additional integration tests

@adecaro adecaro force-pushed the f-447-bis branch 3 times, most recently from 33a1ca7 to 13f5dd3 Compare May 27, 2024 13:02
@adecaro adecaro mentioned this pull request May 27, 2024
@adecaro adecaro linked an issue May 27, 2024 that may be closed by this pull request
@adecaro adecaro self-assigned this May 27, 2024
@adecaro adecaro force-pushed the f-447-bis branch 2 times, most recently from bd56a9f to 6040977 Compare June 1, 2024 05:13
@adecaro adecaro force-pushed the f-447-bis branch 2 times, most recently from 321360b to f9d2870 Compare November 12, 2024 14:22
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>

func TestAssetTransferWithTwoNetworks(network *integration.Infrastructure) {
// give some time to the nodes to get the public parameters
time.Sleep(10 * time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this sleep is better after start

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have it in all tests, maybe we can replace it with a different check in another PR.

assert.Equal(me, pledgeInfo.Script.Recipient, "recipient is different [%s]!=[%s]", me, pledgeInfo.Script.Recipient)

// Store the pledge and send a notification back
_, err = context.RunView(pledge.NewAcceptPledgeIndoView(pledgeInfo))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Info*

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this?

Copy link
Contributor

@KElkhiyaoui KElkhiyaoui Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adecaro There is a typo in the name of the method.
NewAcceptPledgeInfoView instead of NewAcceptPledgeIndoView

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heheheh, good catch.

@@ -23,7 +24,7 @@ import (
var _ = Describe("DLog end to end", func() {
BeforeEach(func() { token.Drivers = append(token.Drivers, "dlog") })

for _, t := range integration2.AllTestTypes {
for _, t := range integration2.WebSocketNoReplicationOnly {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to revert this, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

switch {
case output.IsHTLC():
// check script details
script, err := output.HTLC()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could use just one interface for both, like

Script() ValidatableScript

that only contains the method validate. That would be maybe more extensible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just an example, one might perform more checks...

"github.com/hyperledger-labs/fabric-token-sdk/token/token"
)

// Claim contains the input information to claim a token
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe to give more context

claim a token that has already been pledged, but not yet claimed by someone else, reclaimed by the owner or  redeemed by the issuer of the network.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the comment to the View

assert.NoError(err, "failed to request claim")

return view2.AsResponder(context, session,
func(context view.Context) (interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could extract this to a view and re-use the logic in both pledge and fast pledge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just an example in an integration test.

}

func IssueActionMetadata(attributes map[string][]byte, opts *driver.IssueOptions) (map[string][]byte, error) {
var metadata *IssueMetadata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe to avoid nesting

if len(opts.Attributes) == 0 {
 return nil, nil
}

tokenID, hasTokenID := ...
network, hasNetwork := ...
if !hasTokenID || !hasNetwork {
 return nil, nil
}

marshalled, err := json.Marshal(&IssueMetadata{tokenID.(*token.ID), network.(string)})
if err != nil {return nil, err}

key := common.Hashable(marshalled).String()
attributes := map[string][]byte{
 key: marshalled
}

if proofOpt, ok := ...; ok {
 attributes[key + ProofOfClaimSuffix] = proofOpt.([]byte)
}
return attributes, nil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay

OriginNetwork string
}

func IssueActionMetadata(attributes map[string][]byte, opts *driver.IssueOptions) (map[string][]byte, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't use the attributes as an input, it's better to avoid passing it as a param for more clarity. Otherwise this implies that the attributes we output depend on the input attributes. Which would require that the different metadataGenerators be called in a specific order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attributes is used an input and output, you can have multiple functions manipulating these attributes

var metadata *IssueMetadata
var proof []byte
if len(opts.Attributes) != 0 {
tokenID, ok1 := opts.Attributes["github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/pledge/tokenID"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have these attributes as constants

return errors.Wrap(err, "failed constructing metadata key")
}

if out.IsRedeem() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make it a bit more clear to re-use the common parts:

if out.IsRedeem() {
 key = pledge.RedeemPledgeKey + key
} else {
 if !script.Sender.Equal(...) {...}
 key = pledge.MetadataReclaimKey + key
}
v, ok := ctx.TransferAction.GetMetadta()[key]
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay

Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
Signed-off-by: Angelo De Caro <[email protected]>
assert.NoError(err, "failed to retrieve pledged token during redeem")

// make sure token was in fact claimed in the other network
proof, err := pledge.RequestProofOfTokenWithMetadataExistence(context, rv.TokenID, rv.TMSID, script)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we change the name of the method to RequestProofOfExistenceOfTokenWithMetadata?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay

Signed-off-by: Angelo De Caro <[email protected]>
if owner.Type == htlc.ScriptType {
// Then, the first output must be compatible with this input.
if len(ctx.TransferAction.GetOutputs()) != 1 {
return errors.New("invalid transfer action: an htlc script only transfers the ownership of a token")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should also enforce that len(Inputs) == 1 since a claim and a reclaim are just ownership transfers.


tokenID, hasTokenID := opts.Attributes[TokenIDKey]
network, hasNetwork := opts.Attributes[NetworkKey]
if !hasTokenID && !hasNetwork {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we perform this check? It does not seem to have an impact on the output of the method.

return err
}
if time.Now().Before(script.Deadline) {
return errors.New("cannot reclaim pledge yet: wait for timeout to elapse.")
Copy link
Contributor

@KElkhiyaoui KElkhiyaoui Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's both reclaim and redeem. Shall we say cannot change the state of the pledge yet?

logger.Debugf("Is Mine [%s,%s,%s]? [%b] with [%s]", view2.Identity(tok.Owner.Raw), tok.Type, tok.Quantity, len(ids) != 0, ids)
return "", ids, len(ids) != 0
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does Issued implement. A comment will be helpful.

v, ok := ctx.TransferAction.GetMetadata()[pledge.MetadataKey+script.ID]
if !ok {
return errors.Errorf("empty metadata for pledge script with identifier %s", script.ID)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What information does this metadata convey?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going through the rest of the code, this seems to enforce to enforce the uniqueness of PledgeID.

}

func (t *Output) GetOwner() []byte {
if t.Owner != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be if ! t.IsRedeem()?

}, nil
}

func (p *StateQueryExecutor) Exist(tokenID *token.ID) ([]byte, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exists?

logger.Debugf("failed unmarshalling pledge script [%s]: [%s]", tok, err)
return nil
}
if script.Sender.IsNone() || script.Recipient.IsNone() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return an error here?


// IDExists scans the ledger for a pledge identifier, taking into account the timeout
// IDExists returns true, if entry identified by key (MetadataKey+pledgeID) is occupied.
func IDExists(ctx view.Context, pledgeID string, timeout time.Duration, opts ...token.ServiceOption) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method does not seem to be used elsewhere.

return &ReceiveTransactionView{}
}

func (f *ReceiveTransactionView) Call(context view.Context) (interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we use v or rtv instead of f?

if err != nil {
return errors.Errorf("input owner at index [%d] cannot be unmarshalled", index)
}
scriptInf := &htlc.ScriptInfo{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall move ScriptInfo under a common repo. Since this is a bit confusing.

if err := json.Unmarshal(token.Owner.OwnerInfo, scriptInf); err != nil {
return errors.Wrapf(err, "failed to unmarshal script info")
}
scriptSender, _, scriptIssuer, err := pledge.GetScriptSenderAndRecipient(owner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we call the method GetScriptStakeholders?

if err := session.Receive(recipientData); err != nil {
return nil, errors.Wrapf(err, "failed to receive recipient data")
}
//if err := tms.WalletManager().RegisterRecipientIdentity(recipientData); err != nil {
Copy link
Contributor

@KElkhiyaoui KElkhiyaoui Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we remove the commented code? Or is this a todo?

}
logger.Debugf("proof of existence in claim request is valid [%s]", err)

if !req.ClaimDeadline.After(time.Now()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we start with checking the time first?

return nil, errors.Wrapf(err, "failed storing pledge info")
}

// raw, err := a.info.Bytes()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this?

if err != nil {
return nil, errors.WithMessagef(err, "failed getting prover for [%s]", c.source)
}
return p.Exist(c.tokenID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exists?

}, nil
}

func (v *StateVerifier) VerifyProofExistence(proofRaw []byte, tokenID *token.ID, metadata []byte) error {
Copy link
Contributor

@KElkhiyaoui KElkhiyaoui Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about VerifyProofOfTokenExistence?

return nil
}

func (v *StateVerifier) VerifyProofNonExistence(proofRaw []byte, tokenID *token.ID, origin string, deadline time.Time) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about VerifyProofOfTokenNonExistence

@@ -88,6 +94,25 @@ func (t *Translator) CreateTransferActionMetadataKey(key string) (translator.Key
return createCompositeKey(TransferActionMetadataPrefix, []string{key})
}

func (t *Translator) CreateProofOfExistenceKey(tokenId *token.ID) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe CreateProofOfTokenExistenceKey

})
}

func (t *Translator) CreateProofOfMetadataExistenceKey(tokenID *token.ID, origin string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe CreateProofOfTokenMetadataExistenceKey

return createCompositeKey(ProofOfExistencePrefix, []string{id})
}

func (t *Translator) CreateProofOfNonExistenceKey(tokenID *token.ID, origin string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe CreateProofOfTokenMetadataNonExistenceKey

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce asset transfer between two fabric networks
3 participants