Skip to content

Commit

Permalink
feat(ipld): bindnode support for all voucher types
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed May 18, 2022
1 parent 0949d3e commit e49619e
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 40 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ require (
github.com/ipfs/go-unixfs v0.3.1
github.com/ipld/go-car v0.3.3
github.com/ipld/go-car/v2 v2.1.1
github.com/ipld/go-ipld-prime v0.16.1-0.20220517113833-fe6665aff0fc
github.com/ipld/go-ipld-prime v0.16.1-0.20220518055629-4ad1ac458a5b
github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c
github.com/jpillora/backoff v1.0.0
github.com/libp2p/go-libp2p v0.18.0
Expand Down
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -706,10 +706,9 @@ github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704n
github.com/ipld/go-ipld-prime v0.14.2/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0=
github.com/ipld/go-ipld-prime v0.14.3-0.20211207234443-319145880958/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0=
github.com/ipld/go-ipld-prime v0.14.4/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0=
github.com/ipld/go-ipld-prime v0.16.0 h1:RS5hhjB/mcpeEPJvfyj0qbOj/QL+/j05heZ0qa97dVo=
github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA=
github.com/ipld/go-ipld-prime v0.16.1-0.20220517113833-fe6665aff0fc h1:4QmD8a1ct70iZPl7flNdvqz1slUVv41ndmlhqo6+rq8=
github.com/ipld/go-ipld-prime v0.16.1-0.20220517113833-fe6665aff0fc/go.mod h1:/bZAYlzT7SJS4UV0al4q67xgKvenm5hKrPCa2wNGN1U=
github.com/ipld/go-ipld-prime v0.16.1-0.20220518055629-4ad1ac458a5b h1:1a8YoJ6YtPN515W+oQS8qnSYbm/oc8X/XnJNT6fnA40=
github.com/ipld/go-ipld-prime v0.16.1-0.20220518055629-4ad1ac458a5b/go.mod h1:/bZAYlzT7SJS4UV0al4q67xgKvenm5hKrPCa2wNGN1U=
github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5/go.mod h1:gcvzoEDBjwycpXt3LBE061wT9f46szXGHAmj9uoP6fU=
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73 h1:TsyATB2ZRRQGTwafJdgEUQkmjOExRV0DNokcihZxbnQ=
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY=
Expand Down
183 changes: 156 additions & 27 deletions retrievalmarket/impl/ipld_compat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package retrievalimpl_test

import (
"bytes"
"encoding/hex"
"testing"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/shared"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/ipld/go-ipld-prime/node/basicnode"
"github.com/ipld/go-ipld-prime/schema"
Expand All @@ -17,8 +22,6 @@ import (
)

func TestIpldCompat_DealResponse(t *testing.T) {
t.Skip()

compareDealResponse := func(t *testing.T, drExpected retrievalmarket.DealResponse, drActual retrievalmarket.DealResponse) {
assert.Equal(t, drExpected.ID, drActual.ID)
assert.Equal(t, drExpected.Message, drActual.Message)
Expand Down Expand Up @@ -69,10 +72,7 @@ func TestIpldCompat_DealResponse(t *testing.T) {
dagcbor.Encode(node.(schema.TypedNode).Representation(), &bindnodeBuf)
bindnodeBytes := bindnodeBuf.Bytes()

// compare byte lengths - we can't compare bytes because we're dealing with maps
// and cbor-gen sorts maps differently to dag-cbor, length + roundtrip gets us
// pretty close though
assert.Equal(t, len(originalBytes), len(bindnodeBytes))
compareCbor(t, originalBytes, bindnodeBytes)

// decode the new bytes to DealResponse with cbor-gen
var roundtripdr retrievalmarket.DealResponse
Expand All @@ -85,32 +85,18 @@ func TestIpldCompat_DealResponse(t *testing.T) {
}

func TestIpldCompat_DealProposal(t *testing.T) {
t.Skip()

acid, err := cid.Decode("bafy2bzaceashdsqgbnisdg76gdhupvkpop4br5rs3veuy4whuxagnoco6px6e")
assert.Nil(t, err)
aselector := selectorparse.CommonSelector_MatchChildren

compareDealProposal := func(t *testing.T, drExpected retrievalmarket.DealProposal, drActual retrievalmarket.DealProposal) {
// for cbor-gen we get the selector as a deferred bytes, but an ipld.Node for bindnode,
// so we make them both bytes and compare those
var selExpected []byte
if drExpected.Params.SelectorSpecified() {
var byts bytes.Buffer
assert.Nil(t, drExpected.Params.Selector.MarshalCBOR(&byts))
selExpected = byts.Bytes()
}
var selActual []byte
/* TODO:
if drActual.Params.Selector != nil {
byts, err := shared.NodeToBytes(drActual.Params.Selector)
assert.Nil(t, err)
selActual = byts
}
*/
assert.Equal(t, drExpected.PayloadCID, drActual.PayloadCID, "PayloadCID")
assert.Equal(t, drExpected.ID, drActual.ID, "ID")
assert.Equal(t, selExpected, selActual, "Selector")
if !drExpected.Selector.IsNull() || !drActual.Selector.IsNull() {
assert.True(t, ipld.DeepEqual(drExpected.Selector.Node, drActual.Selector.Node), "Selector")
}
assert.Equal(t, drExpected.Params.PieceCID, drActual.Params.PieceCID, "PieceCID")
compareBigInt(t, drExpected.Params.PricePerByte, drActual.Params.PricePerByte, "PricePerByte")
assert.Equal(t, drExpected.Params.PaymentInterval, drActual.Params.PaymentInterval, "PaymentInterval")
Expand Down Expand Up @@ -178,10 +164,7 @@ func TestIpldCompat_DealProposal(t *testing.T) {
dagcbor.Encode(node.(schema.TypedNode).Representation(), &bindnodeBuf)
bindnodeBytes := bindnodeBuf.Bytes()

// compare byte lengths - we can't compare bytes because we're dealing with maps
// and cbor-gen sorts maps differently to dag-cbor, length + roundtrip gets us
// pretty close though
assert.Equal(t, len(originalBytes), len(bindnodeBytes))
compareCbor(t, originalBytes, bindnodeBytes)

// decode the new bytes to DealProposal with cbor-gen
var roundtripFromBindnodeDr retrievalmarket.DealProposal
Expand All @@ -194,6 +177,128 @@ func TestIpldCompat_DealProposal(t *testing.T) {
}
}

func TestIpldCompat_DealPayment(t *testing.T) {
compareDealPayment := func(t *testing.T, dpExpected retrievalmarket.DealPayment, dpActual retrievalmarket.DealPayment) {
assert.Equal(t, dpExpected.ID, dpActual.ID, "ID")
assert.Equal(t, dpExpected.PaymentChannel.String(), dpActual.PaymentChannel.String(), "PaymentChannel")
assert.Equal(t, dpExpected.PaymentVoucher == nil, dpActual.PaymentVoucher == nil, "PaymentVoucher")
}

for _, testCase := range []struct {
label string
dp retrievalmarket.DealPayment
}{
// addresses can't be null, and they're not marked nullable or optional, so we have to insert them
{"empty", retrievalmarket.DealPayment{PaymentChannel: address.TestAddress}},
{"empty voucher", retrievalmarket.DealPayment{
ID: 1001,
PaymentChannel: address.TestAddress,
PaymentVoucher: &paych.SignedVoucher{
ChannelAddr: address.TestAddress2,
},
}},
{"mostly full voucher", retrievalmarket.DealPayment{
ID: 1001,
PaymentChannel: address.TestAddress,
PaymentVoucher: &paych.SignedVoucher{
ChannelAddr: address.TestAddress2,
TimeLockMin: abi.ChainEpoch(2222),
TimeLockMax: abi.ChainEpoch(333333),
SecretPreimage: []byte("1234567890abcdef"),
Extra: nil,
Lane: 100,
Nonce: 200,
Amount: big.MustFromString("12345678901234567891234567890123456789012345678901234567890"),
MinSettleHeight: abi.ChainEpoch(444444444),
Signature: nil,
},
}},
{"full", retrievalmarket.DealPayment{
ID: 1001,
PaymentChannel: address.TestAddress,
PaymentVoucher: &paych.SignedVoucher{
ChannelAddr: address.TestAddress2,
TimeLockMin: abi.ChainEpoch(2222),
TimeLockMax: abi.ChainEpoch(333333),
SecretPreimage: []byte("1234567890abcdef"),
Extra: &paych.ModVerifyParams{
Actor: address.TestAddress,
Method: abi.MethodNum(50),
Data: []byte("doo-bee-doo"),
},
Lane: 100,
Nonce: 200,
Amount: big.MustFromString("12345678901234567891234567890123456789012345678901234567890"),
MinSettleHeight: abi.ChainEpoch(444444444),
Signature: &crypto.Signature{
Type: crypto.SigTypeBLS,
Data: []byte("beep-boop-beep"),
},
},
}},
{"full secp256k1", retrievalmarket.DealPayment{
ID: 1001,
PaymentChannel: address.TestAddress,
PaymentVoucher: &paych.SignedVoucher{
ChannelAddr: address.TestAddress2,
TimeLockMin: abi.ChainEpoch(2222),
TimeLockMax: abi.ChainEpoch(333333),
SecretPreimage: []byte("1234567890abcdef"),
Extra: &paych.ModVerifyParams{
Actor: address.TestAddress,
Method: abi.MethodNum(50),
Data: []byte("doo-bee-doo"),
},
Lane: 100,
Nonce: 200,
Amount: big.MustFromString("12345678901234567891234567890123456789012345678901234567890"),
MinSettleHeight: abi.ChainEpoch(444444444),
Signature: &crypto.Signature{
Type: crypto.SigTypeSecp256k1,
Data: []byte("bop-bee-bop"),
},
},
}},
} {
t.Run(testCase.label, func(t *testing.T) {
// encode the DealPayment with cbor-gen to bytes
var originalBuf bytes.Buffer
assert.Nil(t, testCase.dp.MarshalCBOR(&originalBuf))
originalBytes := originalBuf.Bytes()

// decode the bytes to DealPayment with bindnode
nb := basicnode.Prototype.Any.NewBuilder()
assert.Nil(t, dagcbor.Decode(nb, &originalBuf))
node := nb.Build()
dpBindnodeIface, err := shared.FromNode(node, &retrievalmarket.DealPayment{})
assert.Nil(t, err)
dpBindnode, ok := dpBindnodeIface.(*retrievalmarket.DealPayment)
assert.True(t, ok)

// compare objects
compareDealPayment(t, testCase.dp, *dpBindnode)

// encode the new DealPayment with bindnode to bytes
node, err = shared.ToNode(dpBindnode)
assert.Nil(t, err)
var bindnodeBuf bytes.Buffer
dagcbor.Encode(node.(schema.TypedNode).Representation(), &bindnodeBuf)
bindnodeBytes := bindnodeBuf.Bytes()

compareCbor(t, originalBytes, bindnodeBytes)

// decode the new bytes to DealPayment with cbor-gen
var roundtripdp retrievalmarket.DealPayment
assert.Nil(t, roundtripdp.UnmarshalCBOR(&bindnodeBuf))

// compare objects
compareDealPayment(t, testCase.dp, *dpBindnode)
})
}
}

// this exists not because the encoding bytes are different but the unitialized
// form may be different in the empty case; functionally they should be the same
func compareBigInt(t *testing.T, expected big.Int, actual big.Int, msg string) {
// special case `nil` because it ends up being an empty bytes (0x40) which is
// big.Int(0) in a round-trip according to cbor-gen encoding
Expand All @@ -202,3 +307,27 @@ func compareBigInt(t *testing.T, expected big.Int, actual big.Int, msg string) {
}
assert.Equal(t, expected, actual, msg)
}

// needed because cbor-gen sorts maps differently, so we can't compare bytes
// so instead we round-trip them, untyped, through go-ipld-prime and compare the
// output bytes
func compareCbor(t *testing.T, cb1 []byte, cb2 []byte) {
assert.Equal(t, len(cb1), len(cb2))
rt := func(cb []byte) []byte {
na := basicnode.Prototype.Any.NewBuilder()
err := dagcbor.Decode(na, bytes.NewReader(cb))
assert.Nil(t, err)
n := na.Build()
var buf bytes.Buffer
err = dagcbor.Encode(n, &buf)
assert.Nil(t, err)
return buf.Bytes()
}
if !bytes.Equal(rt(cb1), rt(cb2)) {
t.Logf(
"Round-tripped node forms of CBOR are not equal:\n\tExpected: %s\n\tActual: %s",
hex.EncodeToString(cb1),
hex.EncodeToString(cb2))
assert.Fail(t, "decoded cbor different")
}
}
6 changes: 0 additions & 6 deletions retrievalmarket/retrieval_restart_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ var noOpDelay = testnodes.DelayFakeCommonNode{}
// broken and then restarted during deal data transfer for an ongoing deal, the data transfer will resume and the deal will
// complete successfully.
func TestBounceConnectionDealTransferOngoing(t *testing.T) {
// TODO(rvagg): skipped for now, flaky
t.Skip()

bgCtx := context.Background()
logger.SetLogLevel("restart_test", "debug")
//logger.SetLogLevel("dt-impl", "debug")
Expand Down Expand Up @@ -187,9 +184,6 @@ func TestBounceConnectionDealTransferOngoing(t *testing.T) {
// is broken and then restarted during unsealing, the data transfer will resume
// and the deal will complete successfully.
func TestBounceConnectionDealTransferUnsealing(t *testing.T) {
// TODO(rvagg): skipped for now, flaky
t.Skip()

bgCtx := context.Background()
//logger.SetLogLevel("dt-chanmon", "debug")
//logger.SetLogLevel("retrieval", "debug")
Expand Down
75 changes: 75 additions & 0 deletions retrievalmarket/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package retrievalmarket
import (
"errors"
"fmt"
"strings"

"github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime"
Expand Down Expand Up @@ -236,6 +237,20 @@ type Params struct {
UnsealPrice abi.TokenAmount
}

// BindnodeSchema returns the IPLD Schema for a serialized Params
func (p *Params) BindnodeSchema() string {
return `
type Params struct {
Selector nullable Any # can be nullable, but shared.SerializedNode takes care of that
PieceCID nullable &Any
PricePerByte Bytes # abi.TokenAmount
PaymentInterval Int
PaymentIntervalIncrease Int
UnsealPrice Bytes # abi.TokenAmount
}
`
}

func (p Params) SelectorSpecified() bool {
return !p.Selector.IsNull()
}
Expand Down Expand Up @@ -362,6 +377,18 @@ func (dp *DealProposal) Type() datatransfer.TypeIdentifier {
return "RetrievalDealProposal/1"
}

// BindnodeSchema returns the IPLD Schema for a serialized DealProposal
func (dp *DealProposal) BindnodeSchema() string {
return strings.Join([]string{
`type DealProposal struct {
PayloadCID &Any
ID Int # DealID
Params Params
}`,
(*Params)(nil).BindnodeSchema(),
}, "\n")
}

// DealProposalUndefined is an undefined deal proposal
var DealProposalUndefined = DealProposal{}

Expand All @@ -381,6 +408,18 @@ func (dr *DealResponse) Type() datatransfer.TypeIdentifier {
return "RetrievalDealResponse/1"
}

// BindnodeSchema returns the IPLD Schema for a serialized StorageDataTransferVoucher
func (dr *DealResponse) BindnodeSchema() string {
return `
type DealResponse struct {
Status Int
ID Int
PaymentOwed Bytes
Message String
}
`
}

// DealResponseUndefined is an undefined deal response
var DealResponseUndefined = DealResponse{}

Expand All @@ -391,6 +430,42 @@ type DealPayment struct {
PaymentVoucher *paych.SignedVoucher
}

// BindnodeSchema returns the IPLD Schema for a serialized DealPayment
func (p *DealPayment) BindnodeSchema() string {
return `
type DealPayment struct {
ID Int # DealID
PaymentChannel Bytes # address.Address
PaymentVoucher nullable SignedVoucher
}
type SignedVoucher struct {
ChannelAddr Bytes # addr.Address
TimeLockMin Int # abi.ChainEpoch
TimeLockMax Int # abi.ChainEpoch
SecretPreimage Bytes
Extra nullable ModVerifyParams
Lane Int
Nonce Int
Amount Bytes # big.Int
MinSettleHeight Int # abi.ChainEpoch
Merges [Merge]
Signature nullable Bytes # crypto.Signature
} representation tuple
type ModVerifyParams struct {
Actor Bytes # addr.Address
Method Int # abi.MethodNum
Data Bytes
} representation tuple
type Merge struct {
Lane Int
Nonce Int
} representation tuple
`
}

// Type method makes DealPayment usable as a voucher
func (dr *DealPayment) Type() datatransfer.TypeIdentifier {
return "RetrievalDealPayment/1"
Expand Down
Loading

0 comments on commit e49619e

Please sign in to comment.