From dc5d3787264fc24cdd9ccede0f2b4f6b125a07cd Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Mon, 21 Oct 2024 11:25:23 -0500 Subject: [PATCH] add builder signature to header 0.1 and 0.2 --- tagged-base64/tagged_base64_test.go | 156 ++++++++++++++-------------- types/headers.go | 12 +++ types/test-data/header0_1.json | 63 +++++------ types/types.go | 19 ++++ types/types_test.go | 12 ++- 5 files changed, 152 insertions(+), 110 deletions(-) diff --git a/tagged-base64/tagged_base64_test.go b/tagged-base64/tagged_base64_test.go index 8d8e0a3..5b3bc6c 100644 --- a/tagged-base64/tagged_base64_test.go +++ b/tagged-base64/tagged_base64_test.go @@ -25,93 +25,93 @@ func TestStringParse(t *testing.T) { // - Generated string can be parsed // - Accessors and parsed string match the supplied values func checkTb64(t *testing.T, tag string, value []byte) { - tb64, err := New(tag, value) - require.NoError(t, err) - str := tb64.String() + tb64, err := New(tag, value) + require.NoError(t, err) + str := tb64.String() - parsed, err := Parse(str) - require.NoError(t, err) + parsed, err := Parse(str) + require.NoError(t, err) - require.Equal(t, tb64, parsed) + require.Equal(t, tb64, parsed) - // Do we get back the tag we supplied? - require.Equal(t, parsed.Tag(), tag) + // Do we get back the tag we supplied? + require.Equal(t, parsed.Tag(), tag) - // Do we get back the binary value we supplied? - require.Equal(t, parsed.Value(), value) + // Do we get back the binary value we supplied? + require.Equal(t, parsed.Value(), value) } func TestParse(t *testing.T) { - // The empty string is not a valid TaggedBase64. - _, err := Parse("") - require.NotNil(t, err) - - // The tag is alphanumeric with hyphen and underscore. - // The value here is the base64 encoding of foobar, but - // the encoding doesn't include the required checksum. - _, err = Parse("-_~Zm9vYmFy") - require.NotNil(t, err) - - // An invalid tag should err. - _, err = Parse("&_~wA") - require.NotNil(t, err) - - // A null value is not allowed. - b64Null := BASE64.EncodeToString([]byte{}); - tagged := fmt.Sprintf("a~%s", b64Null) - _, err = Parse(tagged) - require.NotNil(t, err) - - // The tag can be empty, but the value cannot because the value - // includes the checksum. - _, err = Parse("~") - require.NotNil(t, err) - - checkTb64(t, "mytag", []byte("mytag")) - - // Only base64 characters are allowed in the tag. No restrictions on - // the value because it will get base64 encoded. - checkTb64( - t, - "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", - []byte("~Yeah, we can have spaces and odd stuff—😀 here. ¯⧵_(ツ)_/¯"), - ) - checkTb64( - t, - "", - []byte("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789~"), - ); - - // All the following have invalid characters in the tag. - _, err = New("~", []byte{}) - require.NotNil(t, err) - _, err = New("a~", []byte{}) - require.NotNil(t, err) - _, err = New("~b", []byte{}) - require.NotNil(t, err) - _, err = New("c~d", []byte{}) - require.NotNil(t, err) - _, err = New("e~f~", []byte{}) - require.NotNil(t, err) - _, err = New("g~h~i", []byte{}) - require.NotNil(t, err) - _, err = New("Oh, no!", []byte{}) - require.NotNil(t, err) - _, err = New("Σ", []byte{}) - require.NotNil(t, err) + // The empty string is not a valid TaggedBase64. + _, err := Parse("") + require.NotNil(t, err) + + // The tag is alphanumeric with hyphen and underscore. + // The value here is the base64 encoding of foobar, but + // the encoding doesn't include the required checksum. + _, err = Parse("-_~Zm9vYmFy") + require.NotNil(t, err) + + // An invalid tag should err. + _, err = Parse("&_~wA") + require.NotNil(t, err) + + // A null value is not allowed. + b64Null := BASE64.EncodeToString([]byte{}) + tagged := fmt.Sprintf("a~%s", b64Null) + _, err = Parse(tagged) + require.NotNil(t, err) + + // The tag can be empty, but the value cannot because the value + // includes the checksum. + _, err = Parse("~") + require.NotNil(t, err) + + checkTb64(t, "mytag", []byte("mytag")) + + // Only base64 characters are allowed in the tag. No restrictions on + // the value because it will get base64 encoded. + checkTb64( + t, + "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", + []byte("~Yeah, we can have spaces and odd stuff—😀 here. ¯⧵_(ツ)_/¯"), + ) + checkTb64( + t, + "", + []byte("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789~"), + ) + + // All the following have invalid characters in the tag. + _, err = New("~", []byte{}) + require.NotNil(t, err) + _, err = New("a~", []byte{}) + require.NotNil(t, err) + _, err = New("~b", []byte{}) + require.NotNil(t, err) + _, err = New("c~d", []byte{}) + require.NotNil(t, err) + _, err = New("e~f~", []byte{}) + require.NotNil(t, err) + _, err = New("g~h~i", []byte{}) + require.NotNil(t, err) + _, err = New("Oh, no!", []byte{}) + require.NotNil(t, err) + _, err = New("Σ", []byte{}) + require.NotNil(t, err) } func TestCompat(t *testing.T) { // A hard-coded example, to check compatibility with the original, Rust implementation. - tag := "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" - data := []byte("~Yeah, we can have spaces and odd stuff—😀 here. ¯⧵_(ツ)_/¯") - expected := "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789~flllYWgsIHdlIGNhbiBoYXZlIHNwYWNlcyBhbmQgb2RkIHN0dWZm4oCU8J-YgCBoZXJlLiDCr-KntV8o44OEKV8vwq_6" - - tb64, err := New(tag, data) - require.NoError(t, err) - s := tb64.String() - require.Equal(t, s, expected) - parsed, err := Parse(expected) - require.NoError(t, err) - require.Equal(t, tb64, parsed) + tag := "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" + data := []byte("~Yeah, we can have spaces and odd stuff—😀 here. ¯⧵_(ツ)_/¯") + expected := "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789~flllYWgsIHdlIGNhbiBoYXZlIHNwYWNlcyBhbmQgb2RkIHN0dWZm4oCU8J-YgCBoZXJlLiDCr-KntV8o44OEKV8vwq_6" + + tb64, err := New(tag, data) + require.NoError(t, err) + s := tb64.String() + require.Equal(t, s, expected) + parsed, err := Parse(expected) + require.NoError(t, err) + require.Equal(t, tb64, parsed) } diff --git a/types/headers.go b/types/headers.go index c7abb2e..b6eaafa 100644 --- a/types/headers.go +++ b/types/headers.go @@ -127,6 +127,7 @@ type Header struct { BlockMerkleTreeRoot *TaggedBase64 `json:"block_merkle_tree_root"` FeeMerkleTreeRoot *TaggedBase64 `json:"fee_merkle_tree_root"` FeeInfo *FeeInfo `json:"fee_info"` + BuilderSignature *BuilderSignature `json:"builder_signature"` } func (h *Header) Version() Version { @@ -152,6 +153,10 @@ func (h *Header) GetFeeMerkleTreeRoot() *TaggedBase64 { return h.FeeMerkleTreeRoot } +func (h *Header) GetBuilderSignature() *BuilderSignature { + return h.BuilderSignature +} + func (h *Header) UnmarshalJSON(b []byte) error { // Parse using pointers so we can distinguish between missing and default fields. type Dec struct { @@ -166,6 +171,7 @@ func (h *Header) UnmarshalJSON(b []byte) error { BlockMerkleTreeRoot **TaggedBase64 `json:"block_merkle_tree_root"` FeeMerkleTreeRoot **TaggedBase64 `json:"fee_merkle_tree_root"` FeeInfo **FeeInfo `json:"fee_info"` + BuilderSignature **BuilderSignature `json:"builder_signature"` } var dec Dec @@ -218,6 +224,11 @@ func (h *Header) UnmarshalJSON(b []byte) error { } h.FeeInfo = *dec.FeeInfo + if dec.BuilderSignature == nil { + return fmt.Errorf("Field builder_signature of type Header is required") + } + h.BuilderSignature = *dec.BuilderSignature + if dec.ChainConfig == nil { return fmt.Errorf("Field chain_info of type Header is required") } @@ -248,6 +259,7 @@ func (self *Header) Commit() Commitment { VarSizeField("block_merkle_tree_root", self.BlockMerkleTreeRoot.Value()). VarSizeField("fee_merkle_tree_root", self.FeeMerkleTreeRoot.Value()). Field("fee_info", self.FeeInfo.Commit()). + Field("builder_signature", self.BuilderSignature.Commit()). Finalize() } diff --git a/types/test-data/header0_1.json b/types/test-data/header0_1.json index 4d57aee..c82fa25 100644 --- a/types/test-data/header0_1.json +++ b/types/test-data/header0_1.json @@ -1,32 +1,37 @@ { + "chain_config": { "chain_config": { - "chain_config": { - "Left": { - "chain_id": "35353", - "max_block_size": "10240", - "base_fee": "0", - "fee_contract": "0x0000000000000000000000000000000000000000", - "fee_recipient": "0x0000000000000000000000000000000000000000" - } - } - }, - "height": 42, - "timestamp": 789, - "l1_head": 124, - "l1_finalized": { - "number": 123, - "timestamp": "0x456", - "hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" - }, - "payload_commitment": "HASH~1yS-KEtL3oDZDBJdsW51Pd7zywIiHesBZsTbpOzrxOfu", - "builder_commitment": "BUILDER_COMMITMENT~1yS-KEtL3oDZDBJdsW51Pd7zywIiHesBZsTbpOzrxOdZ", - "ns_table": { - "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - }, - "block_merkle_tree_root": "MERKLE_COMM~yB4_Aqa35_PoskgTpcCR1oVLh6BUdLHIs7erHKWi-usUAAAAAAAAAAEAAAAAAAAAJg", - "fee_merkle_tree_root": "MERKLE_COMM~VJ9z239aP9GZDrHp3VxwPd_0l28Hc5KEAB1pFeCIxhYgAAAAAAAAAAIAAAAAAAAAdA", - "fee_info": { - "account": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", - "amount": "0" + "Left": { + "chain_id": "35353", + "max_block_size": "10240", + "base_fee": "0", + "fee_contract": "0x0000000000000000000000000000000000000000", + "fee_recipient": "0x0000000000000000000000000000000000000000" + } } -} \ No newline at end of file + }, + "height": 42, + "timestamp": 789, + "l1_head": 124, + "l1_finalized": { + "number": 123, + "timestamp": "0x456", + "hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + }, + "payload_commitment": "HASH~1yS-KEtL3oDZDBJdsW51Pd7zywIiHesBZsTbpOzrxOfu", + "builder_commitment": "BUILDER_COMMITMENT~1yS-KEtL3oDZDBJdsW51Pd7zywIiHesBZsTbpOzrxOdZ", + "ns_table": { + "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "block_merkle_tree_root": "MERKLE_COMM~yB4_Aqa35_PoskgTpcCR1oVLh6BUdLHIs7erHKWi-usUAAAAAAAAAAEAAAAAAAAAJg", + "fee_merkle_tree_root": "MERKLE_COMM~VJ9z239aP9GZDrHp3VxwPd_0l28Hc5KEAB1pFeCIxhYgAAAAAAAAAAIAAAAAAAAAdA", + "fee_info": { + "account": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "amount": "0" + }, + "builder_signature": { + "r": "0x1f92bab6350d4f33e04f9e9278d89f644d0abea16d6f882e91f87bec4e0ba53d", + "s": "0x2067627270a89b06e7486c2c56fef0fee5f49a14b296a1cde580b0b40fa7430f", + "v": 27 + } +} diff --git a/types/types.go b/types/types.go index 3fc4a5d..4c0f557 100644 --- a/types/types.go +++ b/types/types.go @@ -353,6 +353,20 @@ func (self *FeeInfo) Commit() Commitment { Finalize() } +type BuilderSignature struct { + R common.Hash `json:"r"` + S common.Hash `json:"s"` + V uint64 `json:"v"` +} + +func (bs *BuilderSignature) Commit() Commitment { + return NewRawCommitmentBuilder("BUILDER_SIGNATURE"). + FixedSizeField("r", bs.R[:]). + FixedSizeField("s", bs.S[:]). + Uint64Field("v", bs.V). + Finalize() +} + // For some intricate reasons, rollups need to build a dummy header as a placeholder. However, an empty Header can be serialized // but can not be deserialized because of the checks. Thus we provide this dummy header as a workaround. func GetDummyHeader() Header { @@ -390,5 +404,10 @@ func GetDummyHeader() Header { PayloadCommitment: payloadCommitment, FeeInfo: &FeeInfo{Account: common.HexToAddress("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"), Amount: *NewU256().SetUint64(0).ToDecimal()}, L1Finalized: &blockInfo, + BuilderSignature: &BuilderSignature{ + R: common.HexToHash("0x1f92bab6350d4f33e04f9e9278d89f644d0abea16d6f882e91f87bec4e0ba53d"), + S: common.HexToHash("0x2067627270a89b06e7486c2c56fef0fee5f49a14b296a1cde580b0b40fa7430f"), + V: uint64(27), + }, } } diff --git a/types/types_test.go b/types/types_test.go index e14b7fc..9f54857 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -59,6 +59,11 @@ var ReferenceHeader Header = Header{ BlockMerkleTreeRoot: ReferenceBlockMerkleTreeRoot, FeeMerkleTreeRoot: ReferenceFeeMerkleTreeRoot, FeeInfo: &FeeInfo{Account: common.HexToAddress("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"), Amount: *NewU256().SetUint64(0).ToDecimal()}, + BuilderSignature: &BuilderSignature{ + R: common.HexToHash("0x1f92bab6350d4f33e04f9e9278d89f644d0abea16d6f882e91f87bec4e0ba53d"), + S: common.HexToHash("0x2067627270a89b06e7486c2c56fef0fee5f49a14b296a1cde580b0b40fa7430f"), + V: 27, + }, } var ReferenceTransaction Transaction = Transaction{ @@ -118,7 +123,8 @@ func TestEspressoTypesHeaderJson(t *testing.T) { }, "block_merkle_tree_root": "MERKLE_COMM~yB4_Aqa35_PoskgTpcCR1oVLh6BUdLHIs7erHKWi-usUAAAAAAAAAAEAAAAAAAAAJg", "fee_merkle_tree_root": "MERKLE_COMM~VJ9z239aP9GZDrHp3VxwPd_0l28Hc5KEAB1pFeCIxhYgAAAAAAAAAAIAAAAAAAAAdA", - "fee_info":{"account":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","amount":"0"} + "fee_info":{"account":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","amount":"0"}, + "builder_signature":{"r":"0x1f92bab6350d4f33e04f9e9278d89f644d0abea16d6f882e91f87bec4e0ba53d","s":"0x2067627270a89b06e7486c2c56fef0fee5f49a14b296a1cde580b0b40fa7430f","v":27} }`)) // Check encoding. @@ -135,7 +141,7 @@ func TestEspressoTypesHeaderJson(t *testing.T) { } require.Equal(t, decoded, ReferenceHeader) - CheckJsonRequiredFields[Header](t, data, "height", "timestamp", "l1_head", "payload_commitment", "builder_commitment", "block_merkle_tree_root", "fee_merkle_tree_root", "fee_info", "chain_config") + CheckJsonRequiredFields[Header](t, data, "height", "timestamp", "l1_head", "payload_commitment", "builder_commitment", "block_merkle_tree_root", "fee_merkle_tree_root", "fee_info", "builder_signature", "chain_config") } func TestEspressoTransactionJson(t *testing.T) { @@ -174,7 +180,7 @@ func TestEspressoTypesNsTable(t *testing.T) { } func TestEspressoTypesHeaderCommit(t *testing.T) { - require.Equal(t, ReferenceHeader.Commit(), Commitment{108, 66, 152, 60, 96, 143, 195, 17, 58, 161, 229, 97, 42, 65, 218, 52, 73, 30, 164, 118, 5, 26, 70, 222, 184, 71, 228, 1, 92, 215, 177, 152}) + require.Equal(t, ReferenceHeader.Commit(), Commitment{231, 29, 239, 71, 22, 101, 198, 223, 100, 115, 197, 203, 125, 6, 172, 149, 141, 226, 79, 254, 84, 28, 152, 122, 37, 222, 143, 27, 192, 81, 80, 57}) } func TestEspressoTypesTransaction(t *testing.T) {