Skip to content

Commit

Permalink
fix: Compute EigenDA preimage hash using preimage length - refactors …
Browse files Browse the repository at this point in the history
…and reworks
  • Loading branch information
epociask committed Aug 14, 2024
1 parent 2e1e27f commit 5406645
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 127 deletions.
20 changes: 14 additions & 6 deletions arbitrator/prover/src/kzgbn254.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,18 @@ pub fn prove_kzg_preimage_bn254(
blob.to_polynomial(PolynomialFormat::InCoefficientForm)?;
let blob_commitment = kzg.commit(&blob_polynomial_evaluation_form)?;


let commitment_x_bigint: BigUint = blob_commitment.x.into();
let commitment_y_bigint: BigUint = blob_commitment.y.into();
let mut commitment_encoded_bytes = Vec::with_capacity(32);
append_left_padded_biguint_be(&mut commitment_encoded_bytes, &commitment_x_bigint);
append_left_padded_biguint_be(&mut commitment_encoded_bytes, &commitment_y_bigint);
let length_bigint: BigUint = blob.len().into();

let mut commitment_encoded_length_bytes = Vec::with_capacity(69);
append_left_padded_biguint_be(&mut commitment_encoded_length_bytes, &commitment_x_bigint);
append_left_padded_biguint_be(&mut commitment_encoded_length_bytes, &commitment_y_bigint);
append_left_padded_biguint_be(&mut commitment_encoded_length_bytes, &length_bigint);

let mut keccak256_hasher = Keccak256::new();
keccak256_hasher.update(&commitment_encoded_bytes);
keccak256_hasher.update(&commitment_encoded_length_bytes);
let commitment_hash: Bytes32 = keccak256_hasher.finalize().into();

ensure!(
Expand All @@ -68,6 +72,11 @@ pub fn prove_kzg_preimage_bn254(
offset,
);

let mut commitment_encoded_bytes = Vec::with_capacity(64);

append_left_padded_biguint_be(&mut commitment_encoded_bytes, &commitment_x_bigint);
append_left_padded_biguint_be(&mut commitment_encoded_bytes, &commitment_y_bigint);

let mut proving_offset = offset;
let length_usize = preimage.len() as u64;

Expand All @@ -81,8 +90,7 @@ pub fn prove_kzg_preimage_bn254(
proving_offset = 0;
}

// Y = ϕ(offset) --> evaluation point for computing quotient proof
// confirming if this is actually ok ?
// Y = ϕ(offset)
let proven_y_fr = blob_polynomial_evaluation_form
.get_at_index(proving_offset as usize / 32)
.ok_or_else(|| {
Expand Down
14 changes: 10 additions & 4 deletions arbitrator/prover/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,18 @@ pub fn hash_preimage(preimage: &[u8], ty: PreimageType) -> Result<[u8; 32]> {

let commitment_x_bigint: BigUint = blob_commitment.x.into();
let commitment_y_bigint: BigUint = blob_commitment.y.into();
let mut commitment_encoded_bytes = Vec::with_capacity(32);
append_left_padded_biguint_be(&mut commitment_encoded_bytes, &commitment_x_bigint);
append_left_padded_biguint_be(&mut commitment_encoded_bytes, &commitment_y_bigint);
let length_bigint: BigUint = blob.len().into();
// 32 bytes per each commitment coordinate (64 bytes)
// 25 bits for length considering 32mb blobs padded to nearest power of 2 (2^25)
// pad to 32 bits or 4 bytes so 68 bytes total
let mut commitment_length_encoded_bytes = Vec::with_capacity(68);
append_left_padded_biguint_be(&mut commitment_length_encoded_bytes, &commitment_x_bigint);
append_left_padded_biguint_be(&mut commitment_length_encoded_bytes, &commitment_y_bigint);
append_left_padded_biguint_be(&mut commitment_length_encoded_bytes, &length_bigint);


let mut keccak256_hasher = Keccak256::new();
keccak256_hasher.update(&commitment_encoded_bytes);
keccak256_hasher.update(&commitment_length_encoded_bytes);
let commitment_hash: [u8; 32] = keccak256_hasher.finalize().into();

Ok(commitment_hash)
Expand Down
2 changes: 1 addition & 1 deletion arbitrator/prover/test-cases/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func main() {
}
}
// EIGENDA COMMIT HASH
_, err = wavmio.ResolveTypedPreimage(arbutil.EigenDaPreimageType, common.HexToHash("13bbacb54f9aa9896af97156ca4dfc626e94031c5ed78fea68659e4ec9c9c55a"))
_, err = wavmio.ResolveTypedPreimage(arbutil.EigenDaPreimageType, common.HexToHash("12deeb4e7b4288fcab2d46116afdd708f606e2e44157d0c423a5a0d310f88483"))
if err != nil {
panic(fmt.Sprintf("failed to resolve eigenda preimage: %v", err))
}
Expand Down
2 changes: 1 addition & 1 deletion arbitrator/prover/test-cases/rust/src/bin/host-io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ fn main() {
for i in 0..5{
// test-files srs 011e229d75b13559dcb2d757ecae9b66fa579268e28e196789503322115c06e1
// mainnet srs 01605220b6928163676612ca50bbe5e0c595052876796dbedeae8ef597c9fdcf
let eigen_hash = hex!("13bbacb54f9aa9896af97156ca4dfc626e94031c5ed78fea68659e4ec9c9c55a");
let eigen_hash = hex!("12deeb4e7b4288fcab2d46116afdd708f606e2e44157d0c423a5a0d310f88483");
bytebuffer = Bytes32(eigen_hash);

let actual_len = wavm_read_eigen_da_hash_preimage(bytebuffer.0.as_mut_ptr(), i * 32);
Expand Down
4 changes: 1 addition & 3 deletions cmd/replay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ func (dasReader *EigenDAPreimageReader) QueryBlob(ctx context.Context, cert *eig
return nil, err
}

// since the preimage is in encoded co-efficient form, we need to decode it to get the actual blob
// i.e,polynomial -> FFT -> length decode -> inverse onec -> blob
decodedBlob, err := eigenda.DecodeiFFTBlob(preimage)
decodedBlob, err := eigenda.GenericDecodeBlob(preimage)
if err != nil {
println("Error decoding blob: ", err)
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion contracts
19 changes: 14 additions & 5 deletions eigenda/eigenda.go

Large diffs are not rendered by default.

42 changes: 5 additions & 37 deletions eigenda/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (c *EigenDAProxyClient) Put(ctx context.Context, data []byte) (*disperser.B
return &blobInfo, nil
}

func (c *EigenDAProxyClient) Get(ctx context.Context, blobInfo *DisperserBlobInfo, domainFilter string) ([]byte, error) {
func (c *EigenDAProxyClient) Get(ctx context.Context, blobInfo *DisperserBlobInfo) ([]byte, error) {
commitment, err := rlp.EncodeToBytes(blobInfo)
if err != nil {
return nil, fmt.Errorf("failed to encode blob info: %w", err)
Expand All @@ -46,46 +46,14 @@ func (c *EigenDAProxyClient) Get(ctx context.Context, blobInfo *DisperserBlobInf
// TODO: support more strict versioning
commitWithVersion := append([]byte{0x0}, commitment...)

data, err := c.client.GetData(ctx, commitWithVersion, StrToDomainType(domainFilter))
data, err := c.client.GetData(ctx, commitWithVersion)
if err != nil {
return nil, fmt.Errorf("failed to get data: %w", err)
}

return data, nil
}

// DomainType is a enumeration type for the different data domains for which a
// blob can exist between
type DomainType uint8

const (
BinaryDomain DomainType = iota
PolyDomain
UnknownDomain
)

func (d DomainType) String() string {
switch d {
case BinaryDomain:
return "binary"
case PolyDomain:
return "polynomial"
default:
return "unknown"
}
}

func StrToDomainType(s string) DomainType {
switch s {
case "binary":
return BinaryDomain
case "polynomial":
return PolyDomain
default:
return UnknownDomain
}
}

// TODO: Add support for custom http client option
type Config struct {
URL string
Expand All @@ -94,7 +62,7 @@ type Config struct {
// ProxyClient is an interface for communicating with the EigenDA proxy server
type ProxyClient interface {
Health() error
GetData(ctx context.Context, cert []byte, domain DomainType) ([]byte, error)
GetData(ctx context.Context, cert []byte) ([]byte, error)
SetData(ctx context.Context, b []byte) ([]byte, error)
}

Expand Down Expand Up @@ -135,8 +103,8 @@ func (c *client) Health() error {
}

// GetData fetches blob data associated with a DA certificate
func (c *client) GetData(ctx context.Context, comm []byte, domain DomainType) ([]byte, error) {
url := fmt.Sprintf("%s/get/0x%x?domain=%s&commitment_mode=simple", c.cfg.URL, comm, domain.String())
func (c *client) GetData(ctx context.Context, comm []byte) ([]byte, error) {
url := fmt.Sprintf("%s/get/0x%x?commitment_mode=simple", c.cfg.URL, comm)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
Expand Down
14 changes: 3 additions & 11 deletions eigenda/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import (
"context"
"encoding/binary"
"errors"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/offchainlabs/nitro/arbstate/daprovider"
Expand All @@ -23,7 +21,6 @@ type readerForEigenDA struct {
readerEigenDA EigenDAReader
}

const sequencerMsgOffset = 41

Check failure on line 24 in eigenda/reader.go

View workflow job for this annotation

GitHub Actions / Go Tests (race)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 24 in eigenda/reader.go

View workflow job for this annotation

GitHub Actions / Go Tests (long)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 24 in eigenda/reader.go

View workflow job for this annotation

GitHub Actions / Go Tests (defaults)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 24 in eigenda/reader.go

View workflow job for this annotation

GitHub Actions / Go Tests (stylus)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 24 in eigenda/reader.go

View workflow job for this annotation

GitHub Actions / Go Tests (challenge)

File is not `gofmt`-ed with `-s` (gofmt)
func (d *readerForEigenDA) IsValidHeaderByte(headerByte byte) bool {
return IsEigenDAMessageHeaderByte(headerByte)
Expand Down Expand Up @@ -67,7 +64,7 @@ func RecoverPayloadFromEigenDABatch(ctx context.Context,

if preimageRecoder != nil {
// iFFT the preimage data
preimage, err := EncodeBlob(data)
preimage, err := GenericEncodeBlob(data)
if err != nil {
return nil, err
}
Expand All @@ -79,17 +76,12 @@ func RecoverPayloadFromEigenDABatch(ctx context.Context,
// ParseSequencerMsg parses the inbox tx calldata into a structured EigenDABlobInfo
func ParseSequencerMsg(calldata []byte) (*EigenDABlobInfo, error) {

// this should never happen, but just in case
if len(calldata) < 4 {
return nil, errors.New("calldata is shorter than expected method signature length")
}

// TODO: Construct the ABI struct at node initialization
abi, err := abi.JSON(strings.NewReader(sequencerInboxABI))
if err != nil {
return nil, err
}

method, err := abi.MethodById(calldata[0:4])
method, err := sequencerInboxABI.MethodById(calldata[0:4])
if err != nil {
return nil, err
}
Expand Down
63 changes: 6 additions & 57 deletions eigenda/decoding.go → eigenda/serialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import (
"bytes"
"encoding/binary"
"fmt"
"math"

"github.com/Layr-Labs/eigenda/encoding"
"github.com/Layr-Labs/eigenda/encoding/fft"
"github.com/Layr-Labs/eigenda/encoding/rs"
"github.com/Layr-Labs/eigenda/encoding/utils/codec"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
Expand All @@ -18,61 +16,21 @@ import (
- https://github.com/Layr-Labs/eigenda/blob/44569ec461c9a1dd1191e7999a72e63bd1e7aba9/api/clients/codecs/ifft_codec.go#L27-L38
*/

func FFT(data []byte) ([]byte, error) {
dataFr, err := rs.ToFrArray(data)
if err != nil {
return nil, fmt.Errorf("error converting data to fr.Element: %w", err)
}
dataFrLen := uint64(len(dataFr))
dataFrLenPow2 := encoding.NextPowerOf2(dataFrLen)

if dataFrLenPow2 != dataFrLen {
return nil, fmt.Errorf("data length %d is not a power of 2", dataFrLen)
}

maxScale := uint8(math.Log2(float64(dataFrLenPow2)))

fs := fft.NewFFTSettings(maxScale)

dataFFTFr, err := fs.FFT(dataFr, false)
if err != nil {
return nil, fmt.Errorf("failed to perform FFT: %w", err)
}

return rs.ToByteArray(dataFFTFr, dataFrLenPow2*encoding.BYTES_PER_SYMBOL), nil
}

func DecodeiFFTBlob(data []byte) ([]byte, error) {
if len(data) == 0 {
return nil, fmt.Errorf("blob has length 0, meaning it is malformed")
}
var err error
data, err = FFT(data)
if err != nil {
return nil, fmt.Errorf("error FFTing data: %w", err)
}

return GenericDecodeBlob(data)
}

Check failure on line 19 in eigenda/serialize.go

View workflow job for this annotation

GitHub Actions / Go Tests (race)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 19 in eigenda/serialize.go

View workflow job for this annotation

GitHub Actions / Go Tests (long)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 19 in eigenda/serialize.go

View workflow job for this annotation

GitHub Actions / Go Tests (stylus)

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 19 in eigenda/serialize.go

View workflow job for this annotation

GitHub Actions / Go Tests (challenge)

File is not `gofmt`-ed with `-s` (gofmt)
func GenericDecodeBlob(data []byte) ([]byte, error) {
if len(data) <= 32 {
return nil, fmt.Errorf("data is not of length greater than 32 bytes: %d", len(data))
}

data, err := DecodeBlob(data)
data, err := decodeBlob(data)
if err != nil {
return nil, err
}

return data, nil
}

func DecodeBlob(data []byte) ([]byte, error) {
if len(data) < 32 {
return nil, fmt.Errorf("blob does not contain 32 header bytes, meaning it is malformed")
}

func decodeBlob(data []byte) ([]byte, error) {
length := binary.BigEndian.Uint32(data[2:6])

// decode raw data modulo bn254
Expand All @@ -93,14 +51,14 @@ func DecodeBlob(data []byte) ([]byte, error) {

}

func EncodeBlob(data []byte) ([]byte, error) {
func GenericEncodeBlob(data []byte) ([]byte, error) {
var err error
data, err = encodeBlob(data)
if err != nil {
return nil, fmt.Errorf("error encoding data: %w", err)
}

return IFFT(data)
return padPow2(data)
}

func encodeBlob(rawData []byte) ([]byte, error) {
Expand All @@ -122,7 +80,7 @@ func encodeBlob(rawData []byte) ([]byte, error) {
return encodedData, nil
}

func IFFT(data []byte) ([]byte, error) {
func padPow2(data []byte) ([]byte, error) {
// we now IFFT data regardless of the encoding type
// convert data to fr.Element
dataFr, err := rs.ToFrArray(data)
Expand All @@ -143,14 +101,5 @@ func IFFT(data []byte) ([]byte, error) {
}
}

maxScale := uint8(math.Log2(float64(dataFrLenPow2)))

// perform IFFT
fs := fft.NewFFTSettings(maxScale)
dataIFFTFr, err := fs.FFT(paddedDataFr, true)
if err != nil {
return nil, fmt.Errorf("failed to perform IFFT: %w", err)
}

return rs.ToByteArray(dataIFFTFr, dataFrLenPow2*encoding.BYTES_PER_SYMBOL), nil
return rs.ToByteArray(paddedDataFr, dataFrLenPow2*encoding.BYTES_PER_SYMBOL), nil
}
22 changes: 22 additions & 0 deletions eigenda/serialize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package eigenda

import "testing"


func Test_EncodeDecodeBlob(t *testing.T) {
rawBlob := []byte("optimistic nihilism")

encodedBlob, err := GenericEncodeBlob(rawBlob)
if err != nil {
t.Fatalf("failed to encode blob: %v", err)
}

decodedBlob, err := GenericDecodeBlob(encodedBlob)
if err != nil {
t.Fatalf("failed to decode blob: %v", err)
}

if string(decodedBlob) != string(rawBlob) {
t.Fatalf("decoded blob does not match raw blob")
}
}
4 changes: 3 additions & 1 deletion eigenda/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ type EigenDABlobInfo struct {
BlobVerificationProof BlobVerificationProof `json:"blobVerificationProof"`
}

/*
Unlike 4844 there's no need to inject a version byte into the 0th offset of the hash
*/
func (e *EigenDABlobInfo) PreimageHash() (*common.Hash, error) {
kzgCommit, err := e.SerializeCommitment()
if err != nil {
Expand All @@ -32,7 +35,6 @@ func (e *EigenDABlobInfo) PreimageHash() (*common.Hash, error) {
digest := append(kzgCommit, uint32ToBytes(e.BlobHeader.DataLength)...)

Check failure on line 35 in eigenda/types.go

View workflow job for this annotation

GitHub Actions / Go Tests (race)

appendAssign: append result not assigned to the same slice (gocritic)

Check failure on line 35 in eigenda/types.go

View workflow job for this annotation

GitHub Actions / Go Tests (long)

appendAssign: append result not assigned to the same slice (gocritic)

Check failure on line 35 in eigenda/types.go

View workflow job for this annotation

GitHub Actions / Go Tests (defaults)

appendAssign: append result not assigned to the same slice (gocritic)

Check failure on line 35 in eigenda/types.go

View workflow job for this annotation

GitHub Actions / Go Tests (stylus)

appendAssign: append result not assigned to the same slice (gocritic)

Check failure on line 35 in eigenda/types.go

View workflow job for this annotation

GitHub Actions / Go Tests (challenge)

appendAssign: append result not assigned to the same slice (gocritic)
shaDataHash.Write(digest)
dataHash := shaDataHash.Sum([]byte{})
dataHash[0] = 1

h := common.BytesToHash(dataHash)

Expand Down

0 comments on commit 5406645

Please sign in to comment.