Skip to content

Commit 56749b4

Browse files
Flamkiadecaro
authored andcommitted
feat(hashescrow): resolve recipient by preimage with dual hashes
Signed-off-by: Flamki <9833ayush@gmail.com>
1 parent 0f38be9 commit 56749b4

12 files changed

Lines changed: 371 additions & 290 deletions

File tree

token/core/fabtoken/v1/validator/validator_transfer.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,13 @@ func TransferHTLCValidate(c context.Context, ctx *Context) error {
211211

212212
// check metadata
213213
sigma := ctx.Signatures[i]
214-
metadataKey, err := hashescrow2.MetadataClaimKeyCheck(ctx.TransferAction, script, sigma)
214+
metadataKey, resolvedOwner, err := hashescrow2.MetadataClaimKeyCheck(ctx.TransferAction, script, sigma)
215215
if err != nil {
216216
return errors.WithMessagef(err, "failed to check hash escrow metadata")
217217
}
218+
if !identity.Identity(resolvedOwner).Equal(tok.Owner) {
219+
return errors.New("invalid transfer action: output owner does not match hash escrow recipient resolved from preimage")
220+
}
218221
ctx.CountMetadataKey(metadataKey)
219222
}
220223
}
@@ -259,11 +262,13 @@ func TransferHTLCValidate(c context.Context, ctx *Context) error {
259262
if err := script.Validate(); err != nil {
260263
return errors.WithMessagef(err, "hash escrow script invalid")
261264
}
262-
metadataKey, err := hashescrow2.MetadataLockKeyCheck(ctx.TransferAction, script)
265+
metadataKeys, err := hashescrow2.MetadataLockKeyCheck(ctx.TransferAction, script)
263266
if err != nil {
264267
return errors.WithMessagef(err, "failed to check hash escrow metadata")
265268
}
266-
ctx.CountMetadataKey(metadataKey)
269+
for _, metadataKey := range metadataKeys {
270+
ctx.CountMetadataKey(metadataKey)
271+
}
267272

268273
continue
269274
}

token/core/zkatdlog/nogh/v1/validator/validator_transfer.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,13 @@ func TransferHTLCValidate(c context.Context, ctx *Context) error {
201201
}
202202

203203
sigma := ctx.Signatures[i]
204-
metadataKey, err := hashescrow2.MetadataClaimKeyCheck(ctx.TransferAction, script, sigma)
204+
metadataKey, resolvedOwner, err := hashescrow2.MetadataClaimKeyCheck(ctx.TransferAction, script, sigma)
205205
if err != nil {
206206
return errors.WithMessagef(err, "failed to check hash escrow metadata")
207207
}
208+
if !identity.Identity(resolvedOwner).Equal(out.Owner) {
209+
return errors.New("invalid transfer action: output owner does not match hash escrow recipient resolved from preimage")
210+
}
208211
ctx.CountMetadataKey(metadataKey)
209212
}
210213
}
@@ -243,11 +246,13 @@ func TransferHTLCValidate(c context.Context, ctx *Context) error {
243246
if err := script.Validate(); err != nil {
244247
return errors.WithMessagef(err, "hash escrow script invalid")
245248
}
246-
metadataKey, err := hashescrow2.MetadataLockKeyCheck(ctx.TransferAction, script)
249+
metadataKeys, err := hashescrow2.MetadataLockKeyCheck(ctx.TransferAction, script)
247250
if err != nil {
248251
return errors.WithMessagef(err, "failed to check hash escrow metadata")
249252
}
250-
ctx.CountMetadataKey(metadataKey)
253+
for _, metadataKey := range metadataKeys {
254+
ctx.CountMetadataKey(metadataKey)
255+
}
251256

252257
continue
253258
}

token/services/identity/interop/hashescrow/deserializer.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,14 @@ func (t *TypedIdentityDeserializer) DeserializeVerifier(ctx context.Context, typ
4646
if err != nil {
4747
return nil, errors.Errorf("failed to unmarshal TypedIdentity as a hash escrow script")
4848
}
49-
v := &hashescrow.Verifier{}
50-
v.Sender, err = t.deserializer.DeserializeVerifier(ctx, script.Sender)
51-
if err != nil {
52-
return nil, errors.Errorf("failed to unmarshal the identity of the sender in the hash escrow script")
49+
// Ensure sender and recipient identities are still syntactically valid and deserializable.
50+
if _, err = t.deserializer.DeserializeVerifier(ctx, script.Sender); err != nil {
51+
return nil, errors.Errorf("failed to deserialize the identity of the sender in the hash escrow script")
5352
}
54-
v.Recipient, err = t.deserializer.DeserializeVerifier(ctx, script.Recipient)
55-
if err != nil {
56-
return nil, errors.Errorf("failed to unmarshal the identity of the recipient in the hash escrow script")
53+
if _, err = t.deserializer.DeserializeVerifier(ctx, script.Recipient); err != nil {
54+
return nil, errors.Errorf("failed to deserialize the identity of the recipient in the hash escrow script")
5755
}
58-
v.HashInfo.Hash = script.HashInfo.Hash
59-
v.HashInfo.HashFunc = script.HashInfo.HashFunc
60-
v.HashInfo.HashEncoding = script.HashInfo.HashEncoding
56+
v := &hashescrow.Verifier{Script: script}
6157

6258
return v, nil
6359
}

token/services/identity/interop/hashescrow/deserializer_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ func mkScript(t *testing.T, sender, recipient []byte) []byte {
5252
s := &he.Script{
5353
Sender: sender,
5454
Recipient: recipient,
55-
HashInfo: he.HashInfo{Hash: []byte("h")},
55+
RecipientHashInfo: he.HashInfo{
56+
Hash: []byte("rh"),
57+
},
58+
SenderHashInfo: he.HashInfo{
59+
Hash: []byte("sh"),
60+
},
5661
}
5762
raw, err := json.Marshal(s)
5863
require.NoError(t, err)

token/services/identity/interop/hashescrow/validator.go

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,49 +45,57 @@ func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) (*hashescrow.Script,
4545
}
4646

4747
// MetadataClaimKeyCheck validates claim metadata and returns the matched metadata key.
48-
func MetadataClaimKeyCheck(action Action, script *hashescrow.Script, sig []byte) (string, error) {
48+
func MetadataClaimKeyCheck(action Action, script *hashescrow.Script, sig []byte) (string, []byte, error) {
4949
claim := &hashescrow.ClaimSignature{}
5050
if err := json.Unmarshal(sig, claim); err != nil {
51-
return "", errors.Wrapf(err, "failed unmarshalling claim signature [%s]", string(sig))
51+
return "", nil, errors.Wrapf(err, "failed unmarshalling claim signature [%s]", string(sig))
5252
}
53-
if len(claim.Preimage) == 0 || len(claim.ClaimantSignature) == 0 {
54-
return "", errors.New("expected a valid claim preImage and claimant signature")
53+
if len(claim.Preimage) == 0 {
54+
return "", nil, errors.New("expected a valid claim preImage")
55+
}
56+
resolvedOwner, image, err := script.ResolveRecipientForPreImage(claim.Preimage)
57+
if err != nil {
58+
return "", nil, errors.Wrap(err, "failed resolving recipient from preimage")
5559
}
5660

5761
metadata := action.GetMetadata()
5862
if len(metadata) == 0 {
59-
return "", errors.New("cannot find hash escrow pre-image, no metadata")
60-
}
61-
image, err := script.HashInfo.Image(claim.Preimage)
62-
if err != nil {
63-
return "", errors.Wrapf(err, "failed to compute image of [%x]", claim.Preimage)
63+
return "", nil, errors.New("cannot find hash escrow pre-image, no metadata")
6464
}
6565
key := hashescrow.ClaimKey(image)
6666
value, ok := metadata[key]
6767
if !ok {
68-
return "", errors.New("cannot find hash escrow pre-image, missing metadata entry")
68+
return "", nil, errors.New("cannot find hash escrow pre-image, missing metadata entry")
6969
}
7070
if !bytes.Equal(value, claim.Preimage) {
71-
return "", errors.Errorf("invalid action, cannot match hash escrow pre-image with metadata [%x]!=[%x]", value, claim.Preimage)
71+
return "", nil, errors.Errorf("invalid action, cannot match hash escrow pre-image with metadata [%x]!=[%x]", value, claim.Preimage)
7272
}
7373

74-
return key, nil
74+
return key, resolvedOwner, nil
7575
}
7676

7777
// MetadataLockKeyCheck validates lock metadata and returns the lock metadata key.
78-
func MetadataLockKeyCheck(action Action, script *hashescrow.Script) (string, error) {
78+
func MetadataLockKeyCheck(action Action, script *hashescrow.Script) ([]string, error) {
7979
metadata := action.GetMetadata()
8080
if len(metadata) == 0 {
81-
return "", errors.New("cannot find hash escrow lock, no metadata")
81+
return nil, errors.New("cannot find hash escrow lock, no metadata")
8282
}
83-
key := hashescrow.LockKey(script.HashInfo.Hash)
84-
value, ok := metadata[key]
83+
recipientKey := hashescrow.LockKey(script.RecipientHashInfo.Hash)
84+
recipientValue, ok := metadata[recipientKey]
85+
if !ok {
86+
return nil, errors.New("cannot find recipient hash escrow lock, missing metadata entry")
87+
}
88+
if !bytes.Equal(recipientValue, hashescrow.LockValue(script.RecipientHashInfo.Hash)) {
89+
return nil, errors.Errorf("invalid action, cannot match recipient hash escrow lock with metadata [%x]!=[%x]", recipientValue, script.RecipientHashInfo.Hash)
90+
}
91+
senderKey := hashescrow.LockKey(script.SenderHashInfo.Hash)
92+
senderValue, ok := metadata[senderKey]
8593
if !ok {
86-
return "", errors.New("cannot find hash escrow lock, missing metadata entry")
94+
return nil, errors.New("cannot find sender hash escrow lock, missing metadata entry")
8795
}
88-
if !bytes.Equal(value, hashescrow.LockValue(script.HashInfo.Hash)) {
89-
return "", errors.Errorf("invalid action, cannot match hash escrow lock with metadata [%x]!=[%x]", value, script.HashInfo.Hash)
96+
if !bytes.Equal(senderValue, hashescrow.LockValue(script.SenderHashInfo.Hash)) {
97+
return nil, errors.Errorf("invalid action, cannot match sender hash escrow lock with metadata [%x]!=[%x]", senderValue, script.SenderHashInfo.Hash)
9098
}
9199

92-
return key, nil
100+
return []string{recipientKey, senderKey}, nil
93101
}

token/services/identity/interop/hashescrow/validator_test.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ func (a *actionStub) GetMetadata() map[string][]byte {
2828

2929
func wrapScript(t *testing.T, sender, recipient []byte, hash []byte) ([]byte, *he.Script) {
3030
t.Helper()
31+
recipientHash := append([]byte{}, hash...)
32+
senderHash := append([]byte{}, hash...)
33+
senderHash = append(senderHash, byte('2'))
3134
script := &he.Script{
3235
Sender: sender,
3336
Recipient: recipient,
34-
HashInfo: he.HashInfo{
35-
Hash: hash,
37+
RecipientHashInfo: he.HashInfo{
38+
Hash: recipientHash,
39+
HashFunc: crypto.SHA256,
40+
HashEncoding: encoding.Base64,
41+
},
42+
SenderHashInfo: he.HashInfo{
43+
Hash: senderHash,
3644
HashFunc: crypto.SHA256,
3745
HashEncoding: encoding.Base64,
3846
},
@@ -74,43 +82,50 @@ func TestMetadataClaimKeyCheck(t *testing.T) {
7482
_, script := wrapScript(t, []byte("s"), []byte("r"), []byte("h"))
7583

7684
preimage := []byte("pre")
77-
image, err := script.HashInfo.Image(preimage)
85+
image, err := script.RecipientHashInfo.Image(preimage)
7886
require.NoError(t, err)
87+
script.RecipientHashInfo.Hash = image
7988
key := he.ClaimKey(image)
8089

8190
sigRaw, err := json.Marshal(&he.ClaimSignature{
82-
ClaimantSignature: []byte("sig"),
83-
Preimage: preimage,
91+
Preimage: preimage,
8492
})
8593
require.NoError(t, err)
8694

8795
act := &actionStub{md: map[string][]byte{key: preimage}}
88-
got, err := ihe.MetadataClaimKeyCheck(act, script, sigRaw)
96+
got, _, err := ihe.MetadataClaimKeyCheck(act, script, sigRaw)
8997
require.NoError(t, err)
9098
require.Equal(t, key, got)
9199

92100
act = &actionStub{md: map[string][]byte{}}
93-
_, err = ihe.MetadataClaimKeyCheck(act, script, sigRaw)
101+
_, _, err = ihe.MetadataClaimKeyCheck(act, script, sigRaw)
94102
require.Error(t, err)
95103

96-
_, err = ihe.MetadataClaimKeyCheck(act, script, []byte("bad-json"))
104+
_, _, err = ihe.MetadataClaimKeyCheck(act, script, []byte("bad-json"))
97105
require.Error(t, err)
98106
}
99107

100108
func TestMetadataLockKeyCheck(t *testing.T) {
101109
_, script := wrapScript(t, []byte("s"), []byte("r"), []byte("h"))
102-
key := he.LockKey(script.HashInfo.Hash)
110+
recipientKey := he.LockKey(script.RecipientHashInfo.Hash)
111+
senderKey := he.LockKey(script.SenderHashInfo.Hash)
103112

104-
act := &actionStub{md: map[string][]byte{key: he.LockValue(script.HashInfo.Hash)}}
113+
act := &actionStub{md: map[string][]byte{
114+
recipientKey: he.LockValue(script.RecipientHashInfo.Hash),
115+
senderKey: he.LockValue(script.SenderHashInfo.Hash),
116+
}}
105117
got, err := ihe.MetadataLockKeyCheck(act, script)
106118
require.NoError(t, err)
107-
require.Equal(t, key, got)
119+
require.ElementsMatch(t, []string{recipientKey, senderKey}, got)
108120

109121
act = &actionStub{md: map[string][]byte{}}
110122
_, err = ihe.MetadataLockKeyCheck(act, script)
111123
require.Error(t, err)
112124

113-
act = &actionStub{md: map[string][]byte{key: []byte("bad")}}
125+
act = &actionStub{md: map[string][]byte{
126+
recipientKey: []byte("bad"),
127+
senderKey: he.LockValue(script.SenderHashInfo.Hash),
128+
}}
114129
_, err = ihe.MetadataLockKeyCheck(act, script)
115130
require.Error(t, err)
116131
}

token/services/interop/hashescrow/script.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,56 @@ type HashInfo = htlc.HashInfo
2525
// Script contains the details of a hash-based escrow lock.
2626
// Either sender or recipient can claim by presenting a valid preimage and signature.
2727
type Script struct {
28-
Sender view.Identity
29-
Recipient view.Identity
30-
HashInfo HashInfo
28+
Sender view.Identity
29+
Recipient view.Identity
30+
RecipientHashInfo HashInfo
31+
SenderHashInfo HashInfo
3132
}
3233

3334
// Validate performs the following checks:
3435
// - sender must be set
3536
// - recipient must be set
36-
// - hash info must be valid
37+
// - both recipient and sender hash info must be valid
3738
func (s *Script) Validate() error {
3839
if s.Sender.IsNone() {
3940
return errors.New("sender not set")
4041
}
4142
if s.Recipient.IsNone() {
4243
return errors.New("recipient not set")
4344
}
44-
if err := s.HashInfo.Validate(); err != nil {
45-
return err
45+
if err := s.RecipientHashInfo.Validate(); err != nil {
46+
return errors.WithMessage(err, "recipient hash info invalid")
47+
}
48+
if err := s.SenderHashInfo.Validate(); err != nil {
49+
return errors.WithMessage(err, "sender hash info invalid")
4650
}
4751

4852
return nil
4953
}
5054

55+
// ResolveRecipientForPreImage resolves the output recipient from the provided pre-image.
56+
// If the pre-image matches the recipient hash, recipient receives the token.
57+
// If the pre-image matches the sender hash, sender receives the token.
58+
func (s *Script) ResolveRecipientForPreImage(preImage []byte) (view.Identity, []byte, error) {
59+
recipientImage, err := s.RecipientHashInfo.Image(preImage)
60+
if err != nil {
61+
return nil, nil, errors.WithMessage(err, "failed computing recipient hash image")
62+
}
63+
if err := s.RecipientHashInfo.Compare(recipientImage); err == nil {
64+
return s.Recipient, recipientImage, nil
65+
}
66+
67+
senderImage, err := s.SenderHashInfo.Image(preImage)
68+
if err != nil {
69+
return nil, nil, errors.WithMessage(err, "failed computing sender hash image")
70+
}
71+
if err := s.SenderHashInfo.Compare(senderImage); err == nil {
72+
return s.Sender, senderImage, nil
73+
}
74+
75+
return nil, nil, errors.New("preimage does not match any hashescrow hash")
76+
}
77+
5178
func (s *Script) FromBytes(raw []byte) error {
5279
return json.Unmarshal(raw, s)
5380
}

0 commit comments

Comments
 (0)