Skip to content

Instantly share code, notes, and snippets.

@anorth
Created March 12, 2020 21:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anorth/59ed0473f93af4ded421cc7745e803b5 to your computer and use it in GitHub Desktop.
Save anorth/59ed0473f93af4ded421cc7745e803b5 to your computer and use it in GitHub Desktop.
//+build cgo
package ffi
import (
"encoding/json"
"fmt"
"os"
"runtime"
"unsafe"
"github.com/filecoin-project/go-address"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/filecoin-project/specs-actors/actors/abi"
cid "github.com/ipfs/go-cid"
"github.com/pkg/errors"
)
// #cgo LDFLAGS: ${SRCDIR}/libfilecoin.a
// #cgo pkg-config: ${SRCDIR}/filecoin.pc
// #include "./filecoin.h"
import "C"
func goRegisteredPoStProof(p C.FFIRegisteredPoStProof) (abi.RegisteredProof, error) {
switch p {
case C.FFIRegisteredPoStProof_StackedDrg2KiBV1:
return abi.RegisteredProof_StackedDRG2KiBPoSt, nil
case C.FFIRegisteredPoStProof_StackedDrg8MiBV1:
return abi.RegisteredProof_StackedDRG8MiBPoSt, nil
case C.FFIRegisteredPoStProof_StackedDrg512MiBV1:
return abi.RegisteredProof_StackedDRG512MiBPoSt, nil
case C.FFIRegisteredPoStProof_StackedDrg32GiBV1:
return abi.RegisteredProof_StackedDRG32GiBPoSt, nil
default:
return 0, errors.Errorf("no mapping from C.FFIRegisteredPoStProof value available for: %v", p)
}
}
func cRegisteredPoStProof(p abi.RegisteredProof) (C.FFIRegisteredPoStProof, error) {
pp, err := p.RegisteredPoStProof()
if err != nil {
return 0, err
}
switch pp {
case abi.RegisteredProof_StackedDRG2KiBPoSt:
return C.FFIRegisteredPoStProof_StackedDrg2KiBV1, nil
case abi.RegisteredProof_StackedDRG8MiBPoSt:
return C.FFIRegisteredPoStProof_StackedDrg8MiBV1, nil
case abi.RegisteredProof_StackedDRG512MiBPoSt:
return C.FFIRegisteredPoStProof_StackedDrg512MiBV1, nil
case abi.RegisteredProof_StackedDRG32GiBPoSt:
return C.FFIRegisteredPoStProof_StackedDrg32GiBV1, nil
default:
return 0, errors.Errorf("no mapping to C.FFIRegisteredPoStProof value available for: %v", p)
}
}
func cRegisteredSealProof(p abi.RegisteredProof) (C.FFIRegisteredSealProof, error) {
pp, err := p.RegisteredSealProof()
if err != nil {
return 0, err
}
switch pp {
case abi.RegisteredProof_StackedDRG2KiBSeal:
return C.FFIRegisteredSealProof_StackedDrg2KiBV1, nil
case abi.RegisteredProof_StackedDRG8MiBSeal:
return C.FFIRegisteredSealProof_StackedDrg8MiBV1, nil
case abi.RegisteredProof_StackedDRG512MiBSeal:
return C.FFIRegisteredSealProof_StackedDrg512MiBV1, nil
case abi.RegisteredProof_StackedDRG32GiBSeal:
return C.FFIRegisteredSealProof_StackedDrg32GiBV1, nil
default:
return 0, errors.Errorf("no mapping to C.FFIRegisteredSealProof value available for: %v", p)
}
}
// VerifySeal returns true if the sealing operation from which its inputs were
// derived was valid, and false if not.
func VerifySeal(info abi.SealVerifyInfo) (bool, error) {
cProofType, err := cRegisteredSealProof(info.OnChain.RegisteredProof)
if err != nil {
return false, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
proverID, err := toProverID(info.Miner)
if err != nil {
return false, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
commD, err := commcid.CIDToDataCommitmentV1(info.UnsealedCID)
if err != nil {
return false, errors.Wrap(err, "failed to convert unsealed CID to CommD")
}
commR, err := commcid.CIDToReplicaCommitmentV1(info.OnChain.SealedCID)
if err != nil {
return false, errors.Wrap(err, "failed to convert unsealed CID to CommR")
}
commDCBytes := C.CBytes(from32ByteArray(to32ByteArray(commD)))
defer C.free(commDCBytes)
commRCBytes := C.CBytes(from32ByteArray(to32ByteArray(commR)))
defer C.free(commRCBytes)
proofCBytes := C.CBytes(info.OnChain.Proof)
defer C.free(proofCBytes)
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
ticketCBytes := C.CBytes(from32ByteArray(to32ByteArray(info.Randomness)))
defer C.free(ticketCBytes)
seedCBytes := C.CBytes(from32ByteArray(to32ByteArray(info.InteractiveRandomness)))
defer C.free(seedCBytes)
// a mutable pointer to a VerifySealResponse C-struct
resPtr := C.verify_seal(
cProofType,
(*[CommitmentBytesLen]C.uint8_t)(commRCBytes),
(*[CommitmentBytesLen]C.uint8_t)(commDCBytes),
(*[32]C.uint8_t)(proverIDCBytes),
(*[32]C.uint8_t)(ticketCBytes),
(*[32]C.uint8_t)(seedCBytes),
C.uint64_t(uint64(info.OnChain.SectorNumber)),
(*C.uint8_t)(proofCBytes),
C.size_t(len(info.OnChain.Proof)),
)
defer C.destroy_verify_seal_response(resPtr)
if resPtr.status_code != 0 {
return false, errors.New(C.GoString(resPtr.error_msg))
}
return bool(resPtr.is_valid), nil
}
// VerifyPoSt returns true if the PoSt-generation operation from which its
// inputs were derived was valid, and false if not.
func VerifyPoSt(info abi.PoStVerifyInfo) (bool, error) {
jvi, _ := json.MarshalIndent(info, "", " ")
fmt.Println(jvi)
psis := make([]publicSectorInfo, len(info.EligibleSectors))
for idx := range psis {
psis[idx] = publicSectorInfo{
PoStProofType: info.EligibleSectors[idx].RegisteredProof,
SealedCID: info.EligibleSectors[idx].SealedCID,
SectorNum: info.EligibleSectors[idx].SectorNumber,
}
}
sectorInfo := newSortedPublicSectorInfo(psis...)
proverID, err := toProverID(info.Prover)
if err != nil {
return false, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
cInfoPtr, cInfoSize, err := cPublicReplicaInfos(sectorInfo.Values())
if err != nil {
return false, errors.Wrap(err, "failed to create public replica info for FFI")
}
defer C.free(unsafe.Pointer(cInfoPtr))
randomnessCBytes := C.CBytes(from32ByteArray(to32ByteArray(info.Randomness)))
defer C.free(randomnessCBytes)
proofsPtr, proofsSize, cleanup, err := cPoStProofs(info.Proofs)
if err != nil {
return false, errors.Wrap(err, "failed to convert to array of C.FFIPoStProof for FFI")
}
defer cleanup()
winnersPtr, winnersSize := cCandidates(info.Candidates)
defer C.free(unsafe.Pointer(winnersPtr))
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
// a mutable pointer to a VerifyPoStResponse C-struct
resPtr := C.verify_post(
(*[32]C.uint8_t)(randomnessCBytes),
C.uint64_t(info.ChallengeCount),
cInfoPtr,
cInfoSize,
proofsPtr,
proofsSize,
winnersPtr,
winnersSize,
(*[32]C.uint8_t)(proverIDCBytes),
)
defer C.destroy_verify_post_response(resPtr)
if resPtr.status_code != 0 {
return false, errors.New(C.GoString(resPtr.error_msg))
}
return bool(resPtr.is_valid), nil
}
// GeneratePieceCommitment produces a piece commitment for the provided data
// stored at a given path.
func GeneratePieceCID(proofType abi.RegisteredProof, piecePath string, pieceSize abi.UnpaddedPieceSize) (cid.Cid, error) {
pieceFile, err := os.Open(piecePath)
if err != nil {
return cid.Undef, err
}
return GeneratePieceCIDFromFile(proofType, pieceFile, pieceSize)
}
// GenerateDataCommitment produces a commitment for the sector containing the
// provided pieces.
func GenerateUnsealedCID(proofType abi.RegisteredProof, pieces []abi.PieceInfo) (cid.Cid, error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return cid.Undef, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
cPiecesPtr, cPiecesLen, err := cPublicPieceInfo(pieces)
if err != nil {
return cid.Undef, errors.Wrap(err, "failed to create public piece info array for FFI")
}
defer C.free(unsafe.Pointer(cPiecesPtr))
resPtr := C.generate_data_commitment(cProofType, (*C.FFIPublicPieceInfo)(cPiecesPtr), cPiecesLen)
defer C.destroy_generate_data_commitment_response(resPtr)
if resPtr.status_code != 0 {
return cid.Undef, errors.New(C.GoString(resPtr.error_msg))
}
commD := goCommitment(&resPtr.comm_d[0])
return commcid.DataCommitmentV1ToCID(commD[:]), nil
}
// GeneratePieceCIDFromFile produces a piece CID for the provided data stored in
//a given file.
func GeneratePieceCIDFromFile(proofType abi.RegisteredProof, pieceFile *os.File, pieceSize abi.UnpaddedPieceSize) (cid.Cid, error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return cid.Undef, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
pieceFd := pieceFile.Fd()
resPtr := C.generate_piece_commitment(cProofType, C.int(pieceFd), C.uint64_t(pieceSize))
defer C.destroy_generate_piece_commitment_response(resPtr)
// Make sure our filedescriptor stays alive, stayin alive
runtime.KeepAlive(pieceFile)
if resPtr.status_code != 0 {
return cid.Undef, errors.New(C.GoString(resPtr.error_msg))
}
commD := goCommitment(&resPtr.comm_p[0])
return commcid.DataCommitmentV1ToCID(commD[:]), nil
}
// WriteWithAlignment
func WriteWithAlignment(
proofType abi.RegisteredProof,
pieceFile *os.File,
pieceBytes abi.UnpaddedPieceSize,
stagedSectorFile *os.File,
existingPieceSizes []abi.UnpaddedPieceSize,
) (leftAlignment, total abi.UnpaddedPieceSize, pieceCID cid.Cid, retErr error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return 0, 0, cid.Undef, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
pieceFd := pieceFile.Fd()
runtime.KeepAlive(pieceFile)
stagedSectorFd := stagedSectorFile.Fd()
runtime.KeepAlive(stagedSectorFile)
raw := make([]uint64, len(existingPieceSizes))
for idx := range existingPieceSizes {
raw[idx] = uint64(existingPieceSizes[idx])
}
ptr, len := cUint64s(raw)
defer C.free(unsafe.Pointer(ptr))
resPtr := C.write_with_alignment(
cProofType,
C.int(pieceFd),
C.uint64_t(uint64(pieceBytes)),
C.int(stagedSectorFd),
ptr,
len,
)
defer C.destroy_write_with_alignment_response(resPtr)
if resPtr.status_code != 0 {
return 0, 0, cid.Undef, errors.New(C.GoString(resPtr.error_msg))
}
commP := goCommitment(&resPtr.comm_p[0])
return abi.UnpaddedPieceSize(uint64(resPtr.left_alignment_unpadded)), abi.UnpaddedPieceSize(uint64(resPtr.total_write_unpadded)), commcid.PieceCommitmentV1ToCID(commP[:]), nil
}
// WriteWithoutAlignment
func WriteWithoutAlignment(
proofType abi.RegisteredProof,
pieceFile *os.File,
pieceBytes abi.UnpaddedPieceSize,
stagedSectorFile *os.File,
) (abi.UnpaddedPieceSize, cid.Cid, error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return 0, cid.Undef, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
pieceFd := pieceFile.Fd()
runtime.KeepAlive(pieceFile)
stagedSectorFd := stagedSectorFile.Fd()
runtime.KeepAlive(stagedSectorFile)
resPtr := C.write_without_alignment(
cProofType,
C.int(pieceFd),
C.uint64_t(pieceBytes),
C.int(stagedSectorFd),
)
defer C.destroy_write_without_alignment_response(resPtr)
if resPtr.status_code != 0 {
return 0, cid.Undef, errors.New(C.GoString(resPtr.error_msg))
}
commP := goCommitment(&resPtr.comm_p[0])
return abi.UnpaddedPieceSize(uint64(resPtr.total_write_unpadded)), commcid.PieceCommitmentV1ToCID(commP[:]), nil
}
// SealPreCommitPhase1
func SealPreCommitPhase1(
proofType abi.RegisteredProof,
cacheDirPath string,
stagedSectorPath string,
sealedSectorPath string,
sectorNum abi.SectorNumber,
minerID abi.ActorID,
ticket abi.SealRandomness,
pieces []abi.PieceInfo,
) (phase1Output []byte, err error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return nil, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
proverID, err := toProverID(minerID)
if err != nil {
return nil, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
cCacheDirPath := C.CString(cacheDirPath)
defer C.free(unsafe.Pointer(cCacheDirPath))
cStagedSectorPath := C.CString(stagedSectorPath)
defer C.free(unsafe.Pointer(cStagedSectorPath))
cSealedSectorPath := C.CString(sealedSectorPath)
defer C.free(unsafe.Pointer(cSealedSectorPath))
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
ticketCBytes := C.CBytes(from32ByteArray(to32ByteArray(ticket)))
defer C.free(ticketCBytes)
cPiecesPtr, cPiecesLen, err := cPublicPieceInfo(pieces)
if err != nil {
return nil, errors.Wrap(err, "could not convert to C.FFIPublicPieceInfo")
}
defer C.free(unsafe.Pointer(cPiecesPtr))
resPtr := C.seal_pre_commit_phase1(
cProofType,
cCacheDirPath,
cStagedSectorPath,
cSealedSectorPath,
C.uint64_t(uint64(sectorNum)),
(*[32]C.uint8_t)(proverIDCBytes),
(*[32]C.uint8_t)(ticketCBytes),
(*C.FFIPublicPieceInfo)(cPiecesPtr),
cPiecesLen,
)
defer C.destroy_seal_pre_commit_phase1_response(resPtr)
if resPtr.status_code != 0 {
return nil, errors.New(C.GoString(resPtr.error_msg))
}
return goBytes(resPtr.seal_pre_commit_phase1_output_ptr, resPtr.seal_pre_commit_phase1_output_len), nil
}
// SealPreCommitPhase2
func SealPreCommitPhase2(
phase1Output []byte,
cacheDirPath string,
sealedSectorPath string,
) (sealedCID cid.Cid, unsealedCID cid.Cid, err error) {
cCacheDirPath := C.CString(cacheDirPath)
defer C.free(unsafe.Pointer(cCacheDirPath))
cSealedSectorPath := C.CString(sealedSectorPath)
defer C.free(unsafe.Pointer(cSealedSectorPath))
phase1OutputCBytes := C.CBytes(phase1Output[:])
defer C.free(phase1OutputCBytes)
resPtr := C.seal_pre_commit_phase2(
(*C.uint8_t)(phase1OutputCBytes),
C.size_t(len(phase1Output)),
cCacheDirPath,
cSealedSectorPath,
)
defer C.destroy_seal_pre_commit_phase2_response(resPtr)
if resPtr.status_code != 0 {
return cid.Undef, cid.Undef, errors.New(C.GoString(resPtr.error_msg))
}
commR := goCommitment(&resPtr.comm_r[0])
commD := goCommitment(&resPtr.comm_d[0])
return commcid.ReplicaCommitmentV1ToCID(commR[:]), commcid.DataCommitmentV1ToCID(commD[:]), nil
}
// SealCommitPhase1
func SealCommitPhase1(
proofType abi.RegisteredProof,
sealedCID cid.Cid,
unsealedCID cid.Cid,
cacheDirPath string,
sealedSectorPath string,
sectorNum abi.SectorNumber,
minerID abi.ActorID,
ticket abi.SealRandomness,
seed abi.InteractiveSealRandomness,
pieces []abi.PieceInfo,
) (phase1Output []byte, err error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return nil, errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
proverID, err := toProverID(minerID)
if err != nil {
return nil, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
commR, err := commcid.CIDToReplicaCommitmentV1(sealedCID)
if err != nil {
return nil, errors.Wrap(err, "failed to compute CommR from sealed CID")
}
commD, err := commcid.CIDToDataCommitmentV1(unsealedCID)
if err != nil {
return nil, errors.Wrap(err, "failed to compute CommD from sealed CID")
}
commRCBytes := C.CBytes(from32ByteArray(to32ByteArray(commR)))
defer C.free(commRCBytes)
commDCBytes := C.CBytes(from32ByteArray(to32ByteArray(commD)))
defer C.free(commDCBytes)
cCacheDirPath := C.CString(cacheDirPath)
defer C.free(unsafe.Pointer(cCacheDirPath))
cSealedSectorPath := C.CString(sealedSectorPath)
defer C.free(unsafe.Pointer(cSealedSectorPath))
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
ticketCBytes := C.CBytes(from32ByteArray(to32ByteArray(ticket)))
defer C.free(ticketCBytes)
seedCBytes := C.CBytes(from32ByteArray(to32ByteArray(seed)))
defer C.free(seedCBytes)
cPiecesPtr, cPiecesLen, err := cPublicPieceInfo(pieces)
if err != nil {
return nil, errors.Wrap(err, "failed to create public piece info array for FFI")
}
defer C.free(unsafe.Pointer(cPiecesPtr))
resPtr := C.seal_commit_phase1(
cProofType,
(*[CommitmentBytesLen]C.uint8_t)(commRCBytes),
(*[CommitmentBytesLen]C.uint8_t)(commDCBytes),
cCacheDirPath,
cSealedSectorPath,
C.uint64_t(uint64(sectorNum)),
(*[32]C.uint8_t)(proverIDCBytes),
(*[32]C.uint8_t)(ticketCBytes),
(*[32]C.uint8_t)(seedCBytes),
(*C.FFIPublicPieceInfo)(cPiecesPtr),
cPiecesLen,
)
defer C.destroy_seal_commit_phase1_response(resPtr)
if resPtr.status_code != 0 {
return nil, errors.New(C.GoString(resPtr.error_msg))
}
return goBytes(resPtr.seal_commit_phase1_output_ptr, resPtr.seal_commit_phase1_output_len), nil
}
// SealCommitPhase2
func SealCommitPhase2(
phase1Output []byte,
sectorID abi.SectorNumber,
minerID abi.ActorID,
) ([]byte, error) {
proverID, err := toProverID(minerID)
if err != nil {
return nil, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
phase1OutputCBytes := C.CBytes(phase1Output)
defer C.free(phase1OutputCBytes)
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
resPtr := C.seal_commit_phase2(
(*C.uint8_t)(phase1OutputCBytes),
C.size_t(len(phase1Output)),
C.uint64_t(sectorID),
(*[32]C.uint8_t)(proverIDCBytes),
)
defer C.destroy_seal_commit_phase2_response(resPtr)
if resPtr.status_code != 0 {
return nil, errors.New(C.GoString(resPtr.error_msg))
}
return goBytes(resPtr.proof_ptr, resPtr.proof_len), nil
}
// Unseal
func Unseal(
proofType abi.RegisteredProof,
cacheDirPath string,
sealedSectorPath string,
unsealOutputPath string,
sectorNum abi.SectorNumber,
minerID abi.ActorID,
ticket abi.SealRandomness,
unsealedCID cid.Cid,
) error {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
proverID, err := toProverID(minerID)
if err != nil {
return errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
commD, err := commcid.CIDToDataCommitmentV1(unsealedCID)
if err != nil {
return errors.Wrap(err, "failed to compute CommD from unsealed CID")
}
cCacheDirPath := C.CString(cacheDirPath)
defer C.free(unsafe.Pointer(cCacheDirPath))
cSealedSectorPath := C.CString(sealedSectorPath)
defer C.free(unsafe.Pointer(cSealedSectorPath))
cUnsealOutputPath := C.CString(unsealOutputPath)
defer C.free(unsafe.Pointer(cUnsealOutputPath))
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
ticketCBytes := C.CBytes(from32ByteArray(to32ByteArray(ticket)))
defer C.free(ticketCBytes)
commDCBytes := C.CBytes(from32ByteArray(to32ByteArray(commD)))
defer C.free(commDCBytes)
resPtr := C.unseal(
cProofType,
cCacheDirPath,
cSealedSectorPath,
cUnsealOutputPath,
C.uint64_t(uint64(sectorNum)),
(*[32]C.uint8_t)(proverIDCBytes),
(*[32]C.uint8_t)(ticketCBytes),
(*[CommitmentBytesLen]C.uint8_t)(commDCBytes),
)
defer C.destroy_unseal_response(resPtr)
if resPtr.status_code != 0 {
return errors.New(C.GoString(resPtr.error_msg))
}
return nil
}
// UnsealRange
func UnsealRange(
proofType abi.RegisteredProof,
cacheDirPath string,
sealedSectorPath string,
unsealOutputPath string,
sectorNum abi.SectorNumber,
minerID abi.ActorID,
ticket abi.SealRandomness,
unsealedCID cid.Cid,
offset uint64,
len uint64,
) error {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
proverID, err := toProverID(minerID)
if err != nil {
return errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
commD, err := commcid.CIDToDataCommitmentV1(unsealedCID)
if err != nil {
return errors.Wrap(err, "failed to compute CommD from unsealed CID")
}
cCacheDirPath := C.CString(cacheDirPath)
defer C.free(unsafe.Pointer(cCacheDirPath))
cSealedSectorPath := C.CString(sealedSectorPath)
defer C.free(unsafe.Pointer(cSealedSectorPath))
cUnsealOutputPath := C.CString(unsealOutputPath)
defer C.free(unsafe.Pointer(cUnsealOutputPath))
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
ticketCBytes := C.CBytes(from32ByteArray(to32ByteArray(ticket)))
defer C.free(ticketCBytes)
commDCBytes := C.CBytes(from32ByteArray(to32ByteArray(commD)))
defer C.free(commDCBytes)
resPtr := C.unseal_range(
cProofType,
cCacheDirPath,
cSealedSectorPath,
cUnsealOutputPath,
C.uint64_t(uint64(sectorNum)),
(*[32]C.uint8_t)(proverIDCBytes),
(*[32]C.uint8_t)(ticketCBytes),
(*[CommitmentBytesLen]C.uint8_t)(commDCBytes),
C.uint64_t(offset),
C.uint64_t(len),
)
defer C.destroy_unseal_range_response(resPtr)
if resPtr.status_code != 0 {
return errors.New(C.GoString(resPtr.error_msg))
}
return nil
}
// FinalizeTicket creates an actual ticket from a partial ticket.
func FinalizeTicket(partialTicket abi.PartialTicket) ([32]byte, error) {
partialTicketCBytes := C.CBytes(from32ByteArray(to32ByteArray(partialTicket)))
defer C.free(partialTicketCBytes)
resPtr := C.finalize_ticket(
(*[32]C.uint8_t)(partialTicketCBytes),
)
defer C.destroy_finalize_ticket_response(resPtr)
if resPtr.status_code != 0 {
return [32]byte{}, errors.New(C.GoString(resPtr.error_msg))
}
return goCommitment(&resPtr.ticket[0]), nil
}
// GenerateCandidates
func GenerateCandidates(
minerID abi.ActorID,
randomness abi.PoStRandomness,
challengeCount uint64,
privateSectorInfo SortedPrivateSectorInfo,
) ([]PoStCandidateWithTicket, error) {
proverID, err := toProverID(minerID)
if err != nil {
return nil, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
randomessCBytes := C.CBytes(from32ByteArray(to32ByteArray(randomness)))
defer C.free(randomessCBytes)
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
replicasPtr, replicasSize, err := cPrivateReplicaInfos(privateSectorInfo.Values())
if err != nil {
return nil, errors.Wrap(err, "failed to create private replica array")
}
defer C.free(unsafe.Pointer(replicasPtr))
resPtr := C.generate_candidates(
(*[32]C.uint8_t)(randomessCBytes),
C.uint64_t(challengeCount),
replicasPtr,
replicasSize,
(*[32]C.uint8_t)(proverIDCBytes),
)
defer C.destroy_generate_candidates_response(resPtr)
if resPtr.status_code != 0 {
return nil, errors.New(C.GoString(resPtr.error_msg))
}
candidates, err := goCandidatesWithTickets(resPtr.candidates_ptr, resPtr.candidates_len)
if err != nil {
return nil, errors.Wrap(err, "failed to create candidates with tickets")
}
return candidates, nil
}
// GeneratePoSt
func GeneratePoSt(
minerID abi.ActorID,
privateSectorInfo SortedPrivateSectorInfo,
randomness abi.PoStRandomness,
winners []abi.PoStCandidate,
) ([]abi.PoStProof, error) {
proverID, err := toProverID(minerID)
if err != nil {
return nil, errors.Wrap(err, "failed to convert ActorID to prover id ([32]byte) for FFI")
}
replicasPtr, replicasSize, err := cPrivateReplicaInfos(privateSectorInfo.Values())
if err != nil {
return nil, errors.Wrap(err, "failed to create private replica info array for FFI")
}
defer C.free(unsafe.Pointer(replicasPtr))
randomnessCBytes := C.CBytes(from32ByteArray(to32ByteArray(randomness)))
defer C.free(randomnessCBytes)
winnersPtr, winnersSize := cCandidates(winners)
defer C.free(unsafe.Pointer(winnersPtr))
proverIDCBytes := C.CBytes(proverID[:])
defer C.free(proverIDCBytes)
resPtr := C.generate_post(
(*[32]C.uint8_t)(randomnessCBytes),
replicasPtr,
replicasSize,
winnersPtr,
winnersSize,
(*[32]C.uint8_t)(proverIDCBytes),
)
defer C.destroy_generate_post_response(resPtr)
if resPtr.status_code != 0 {
return nil, errors.New(C.GoString(resPtr.error_msg))
}
proofs, err := goPoStProofs(resPtr.proofs_ptr, resPtr.proofs_len)
sectorInfos := make([]abi.SectorInfo, len(privateSectorInfo.Values()))
for i, info := range privateSectorInfo.Values() {
proofType, err := info.PoStProofType.RegisteredPoStProof()
if err != nil {
panic(err)
}
sectorInfos[i] = abi.SectorInfo{
RegisteredProof: proofType,
SectorNumber: info.SectorNumber,
SealedCID: info.SealedCID,
}
}
info := abi.PoStVerifyInfo{
Randomness: randomness,
Candidates: winners,
Proofs: proofs,
EligibleSectors: sectorInfos,
Prover: minerID,
ChallengeCount: ElectionPostChallengeCount(uint64(len(sectorInfos)), 0),
}
ok, err := VerifyPoSt(info)
j, _ := json.MarshalIndent(info, "", " ")
if !ok {
panic(fmt.Sprintf("proofs we just created failed to verify: %s", j))
}
return proofs, err
}
func ElectionPostChallengeCount(sectors uint64, faults uint64) uint64 {
if sectors-faults == 0 {
return 0
}
// ceil(sectors / SectorChallengeRatioDiv)
return (sectors-faults-1)/25 + 1
}
// GetGPUDevices produces a slice of strings, each representing the name of a
// detected GPU device.
func GetGPUDevices() ([]string, error) {
resPtr := C.get_gpu_devices()
defer C.destroy_gpu_device_response(resPtr)
if resPtr.status_code != 0 {
return nil, errors.New(C.GoString(resPtr.error_msg))
}
devices := make([]string, resPtr.devices_len)
if resPtr.devices_ptr == nil || resPtr.devices_len == 0 {
return devices, nil
}
ptrs := (*[1 << 30]*C.char)(unsafe.Pointer(resPtr.devices_ptr))[:resPtr.devices_len:resPtr.devices_len]
for i := 0; i < int(resPtr.devices_len); i++ {
devices[i] = C.GoString(ptrs[i])
}
return devices, nil
}
// GetSealVersion
func GetSealVersion(
proofType abi.RegisteredProof,
) (string, error) {
cProofType, err := cRegisteredSealProof(proofType)
if err != nil {
return "", errors.Wrap(err, "failed to create registered seal proof type for FFI")
}
resPtr := C.get_seal_version(cProofType)
defer C.destroy_string_response(resPtr)
if resPtr.status_code != 0 {
return "", errors.New(C.GoString(resPtr.error_msg))
}
return C.GoString(resPtr.string_val), nil
}
// GetPoStVersion
func GetPoStVersion(
proofType abi.RegisteredProof,
) (string, error) {
cProofType, err := cRegisteredPoStProof(proofType)
if err != nil {
return "", errors.Wrap(err, "failed to create registered PoSt proof type for FFI")
}
resPtr := C.get_post_version(cProofType)
defer C.destroy_string_response(resPtr)
if resPtr.status_code != 0 {
return "", errors.New(C.GoString(resPtr.error_msg))
}
return C.GoString(resPtr.string_val), nil
}
// ClearCache
func ClearCache(
cacheDirPath string,
) error {
cCacheDirPath := C.CString(cacheDirPath)
defer C.free(unsafe.Pointer(cCacheDirPath))
resPtr := C.clear_cache(cCacheDirPath)
defer C.destroy_clear_cache_response(resPtr)
if resPtr.status_code != 0 {
return errors.New(C.GoString(resPtr.error_msg))
}
return nil
}
func cPublicReplicaInfos(src []publicSectorInfo) (*C.FFIPublicReplicaInfo, C.size_t, error) {
srcCSizeT := C.size_t(len(src))
// allocate array in C heap
cPublicReplicas := C.malloc(srcCSizeT * C.sizeof_FFIPublicReplicaInfo)
// create a Go slice backed by the C-array
xs := (*[1 << 30]C.FFIPublicReplicaInfo)(cPublicReplicas)
for i, v := range src {
commR, err := commcid.CIDToReplicaCommitmentV1(v.SealedCID)
if err != nil {
return (*C.FFIPublicReplicaInfo)(unsafe.Pointer(nil)), 0, errors.Wrap(err, "failed to transform sealed CID to CommR")
}
commRAry := to32ByteArray(commR)
cProofType, err := cRegisteredPoStProof(v.PoStProofType)
if err != nil {
return (*C.FFIPublicReplicaInfo)(unsafe.Pointer(nil)), 0, errors.Wrap(err, "failed to create registered PoSt proof type for FFI")
}
xs[i] = C.FFIPublicReplicaInfo{
comm_r: *(*[CommitmentBytesLen]C.uint8_t)(unsafe.Pointer(&commRAry)),
registered_proof: cProofType,
sector_id: C.uint64_t(v.SectorNum),
}
}
return (*C.FFIPublicReplicaInfo)(cPublicReplicas), srcCSizeT, nil
}
func cPublicPieceInfo(src []abi.PieceInfo) (*C.FFIPublicPieceInfo, C.size_t, error) {
srcCSizeT := C.size_t(len(src))
// allocate array in C heap
cPublicPieceInfos := C.malloc(srcCSizeT * C.sizeof_FFIPublicPieceInfo)
// create a Go slice backed by the C-array
xs := (*[1 << 30]C.FFIPublicPieceInfo)(cPublicPieceInfos)
for i, v := range src {
commP, err := commcid.CIDToPieceCommitmentV1(v.PieceCID)
if err != nil {
return (*C.FFIPublicPieceInfo)(unsafe.Pointer(nil)), 0, errors.Wrap(err, "failed to create CommP from PieceCID")
}
commPAry := to32ByteArray(commP)
xs[i] = C.FFIPublicPieceInfo{
num_bytes: C.uint64_t(v.Size.Unpadded()),
comm_p: *(*[CommitmentBytesLen]C.uint8_t)(unsafe.Pointer(&commPAry)),
}
}
return (*C.FFIPublicPieceInfo)(cPublicPieceInfos), srcCSizeT, nil
}
func cUint64s(src []uint64) (*C.uint64_t, C.size_t) {
srcCSizeT := C.size_t(len(src))
// allocate array in C heap
cUint64s := C.malloc(srcCSizeT * C.sizeof_uint64_t)
// create a Go slice backed by the C-array
pp := (*[1 << 30]C.uint64_t)(cUint64s)
for i, v := range src {
pp[i] = C.uint64_t(v)
}
return (*C.uint64_t)(cUint64s), srcCSizeT
}
func cCandidates(src []abi.PoStCandidate) (*C.FFICandidate, C.size_t) {
srcCSizeT := C.size_t(len(src))
// allocate array in C heap
cCandidates := C.malloc(srcCSizeT * C.sizeof_FFICandidate)
// create a Go slice backed by the C-array
pp := (*[1 << 30]C.FFICandidate)(cCandidates)
for i, v := range src {
pt := to32ByteArray(v.PartialTicket)
pp[i] = C.FFICandidate{
sector_id: C.uint64_t(uint64(v.SectorID.Number)),
partial_ticket: *(*[32]C.uint8_t)(unsafe.Pointer(&pt)),
ticket: *(*[32]C.uint8_t)(unsafe.Pointer(&pt)), // this field is ignored by verify_post and generate_post
sector_challenge_index: C.uint64_t(v.ChallengeIndex),
}
}
return (*C.FFICandidate)(cCandidates), srcCSizeT
}
func cPrivateReplicaInfos(src []PrivateSectorInfo) (*C.FFIPrivateReplicaInfo, C.size_t, error) {
srcCSizeT := C.size_t(len(src))
cPrivateReplicas := C.malloc(srcCSizeT * C.sizeof_FFIPrivateReplicaInfo)
pp := (*[1 << 30]C.FFIPrivateReplicaInfo)(cPrivateReplicas)
for i, v := range src {
commR, err := commcid.CIDToReplicaCommitmentV1(v.SectorInfo.SealedCID)
if err != nil {
return (*C.FFIPrivateReplicaInfo)(unsafe.Pointer(nil)), 0, errors.Wrap(err, "failed to transform sealed CID to CommR")
}
commRAry := to32ByteArray(commR)
proofType, err := cRegisteredPoStProof(v.PoStProofType)
if err != nil {
return (*C.FFIPrivateReplicaInfo)(unsafe.Pointer(nil)), 0, errors.Wrap(err, "failed to create PoSt proof type")
}
pp[i] = C.FFIPrivateReplicaInfo{
cache_dir_path: C.CString(v.CacheDirPath),
comm_r: *(*[CommitmentBytesLen]C.uint8_t)(unsafe.Pointer(&commRAry)),
replica_path: C.CString(v.SealedSectorPath),
sector_id: C.uint64_t(v.SectorInfo.SectorNumber),
registered_proof: proofType,
}
}
return (*C.FFIPrivateReplicaInfo)(cPrivateReplicas), srcCSizeT, nil
}
func cPoStProofs(src []abi.PoStProof) (*C.FFIPoStProof, C.size_t, func(), error) {
srcCSizeT := C.size_t(len(src))
cPoStProofs := C.malloc(srcCSizeT * C.sizeof_FFIPoStProof)
var ptrs []unsafe.Pointer
cleanup := func() {
C.free(cPoStProofs)
for idx := range ptrs {
C.free(ptrs[idx])
}
}
pp := (*[1 << 30]C.FFIPoStProof)(cPoStProofs)
for i, v := range src {
proofType, err := cRegisteredPoStProof(v.RegisteredProof)
if err != nil {
cleanup()
return (*C.FFIPoStProof)(unsafe.Pointer(nil)), 0, func() {}, errors.Wrap(err, "failed to create PoSt proof type")
}
proofBytes := C.CBytes(v.ProofBytes)
ptrs = append(ptrs, unsafe.Pointer(proofBytes))
pp[i] = C.FFIPoStProof{
proof_ptr: (*C.uint8_t)(proofBytes),
proof_len: C.size_t(len(v.ProofBytes)),
registered_proof: proofType,
}
}
return (*C.FFIPoStProof)(cPoStProofs), srcCSizeT, cleanup, nil
}
func goBytes(src *C.uint8_t, size C.size_t) []byte {
return C.GoBytes(unsafe.Pointer(src), C.int(size))
}
func goCandidatesWithTickets(src *C.FFICandidate, size C.size_t) ([]PoStCandidateWithTicket, error) {
candidates := make([]PoStCandidateWithTicket, size)
if src == nil || size == 0 {
return candidates, nil
}
ptrs := (*[1 << 30]C.FFICandidate)(unsafe.Pointer(src))[:size:size]
for i := 0; i < int(size); i++ {
candidates[i] = goCandidateWithTicket(ptrs[i])
}
return candidates, nil
}
func goCandidateWithTicket(src C.FFICandidate) PoStCandidateWithTicket {
p := goCommitment(&src.partial_ticket[0])
return PoStCandidateWithTicket{
Candidate: abi.PoStCandidate{
SectorID: abi.SectorID{
Miner: 0,
Number: abi.SectorNumber(uint64(src.sector_id)),
},
PartialTicket: p[:],
ChallengeIndex: int64(src.sector_challenge_index),
},
Ticket: goCommitment(&src.ticket[0]),
}
}
func goPoStProofs(src *C.FFIPoStProof, size C.size_t) ([]abi.PoStProof, error) {
proofs := make([]abi.PoStProof, size)
if src == nil || size == 0 {
return proofs, nil
}
ptrs := (*[1 << 30]C.FFIPoStProof)(unsafe.Pointer(src))[:size:size]
for i := 0; i < int(size); i++ {
proof, err := goPoStProof(ptrs[i])
if err != nil {
return nil, err
}
proofs[i] = proof
}
return proofs, nil
}
func goPoStProof(src C.FFIPoStProof) (abi.PoStProof, error) {
rp, err := goRegisteredPoStProof(src.registered_proof)
if err != nil {
return abi.PoStProof{}, err
}
return abi.PoStProof{
RegisteredProof: rp,
ProofBytes: C.GoBytes(unsafe.Pointer(src.proof_ptr), C.int(src.proof_len)),
}, nil
}
func goCommitment(src *C.uint8_t) [CommitmentBytesLen]byte {
return to32ByteArray(C.GoBytes(unsafe.Pointer(src), 32))
}
func to32ByteArray(in []byte) [32]byte {
var out [32]byte
copy(out[:], in)
return out
}
func from32ByteArray(in [32]byte) []byte {
return in[:]
}
func toProverID(minerID abi.ActorID) ([32]byte, error) {
maddr, err := address.NewIDAddress(uint64(minerID))
if err != nil {
return [32]byte{}, err
}
var proverID [32]byte
copy(proverID[:], maddr.Payload())
return proverID, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment