From 3dfacba9515b5ee46eb2fc9a329ddb6cdbb223b4 Mon Sep 17 00:00:00 2001 From: bill Date: Sun, 10 Dec 2023 22:20:32 +0800 Subject: [PATCH 01/12] host keccak replace crypto, and set request %8=0 --- op-program/client/io_wasm.go | 5 +- op-program/client/keccak256_wasm.go | 85 ++++++++++++++++++++++++++++ op-program/client/smoke_test_conf.go | 3 +- 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 op-program/client/keccak256_wasm.go diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 022cfaa12b620..60e814db089ff 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -7,7 +7,7 @@ import ( "encoding/binary" preimage "github.com/ethereum-optimism/optimism/op-preimage" - "github.com/ethereum/go-ethereum/crypto" + //"github.com/ethereum/go-ethereum/crypto" ) func NewOracleClientAndHintWriter() (preimage.Oracle, preimage.Hinter) { @@ -43,7 +43,8 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { // Integrity check // TODO: can use customized circuit to optimize if !_isPublic { - hash := crypto.Keccak256Hash(buf) + //hash := crypto.Keccak256Hash(buf) + hash := Keccak256Hash(buf) hash[0] = _key[0] require_bool(hash == _key) } diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go new file mode 100644 index 0000000000000..ab19b264e056e --- /dev/null +++ b/op-program/client/keccak256_wasm.go @@ -0,0 +1,85 @@ +package client + +import "encoding/binary" + +//go:wasmimport env keccak_new +//go:noescape +func keccak_new(uint64) + +//go:wasmimport env keccak_push +//go:noescape +func keccak_push(uint64) + +//go:wasmimport env keccak_finalize +//go:noescape +func keccak_finalize() uint64 + +func Keccak256Hash(data ...[]byte) (output [32]byte) { + dataBytes := make([]byte, 0) + for _, value := range data { + dataBytes = append(dataBytes, value...) + } + require_bool(len(dataBytes)%8 == 0) + input := ByteSliceToUint64Slice(dataBytes) + keccak_new(0) + for _, value := range input { + keccak_push(value) + } + result := make([]uint64, 0) + for i := 0; i < 4; i++ { + result = append(result, keccak_finalize()) + } + resultBytes := Uint64SliceToByteSlice(result) + for i := 0; i < 32; i++ { + output[i] = resultBytes[i] + } + return output +} + +func Uint64SliceToByteSlice(uint64Slice []uint64) []byte { + byteSlice := make([]byte, len(uint64Slice)*8) + for i, val := range uint64Slice { + binary.LittleEndian.PutUint64(byteSlice[i*8:], val) + } + return byteSlice +} + +func ByteSliceToUint64Slice(byteSlice []byte) []uint64 { + require_bool(len(byteSlice)%8 == 0) + uint64Slice := make([]uint64, len(byteSlice)/8) + for i := 0; i < len(byteSlice); i += 8 { + val := binary.LittleEndian.Uint64(byteSlice[i:]) + uint64Slice[i/8] = val + } + return uint64Slice +} + +/* +// for test + +func keccak256check(input []byte, output []byte) { + result := Keccak256Hash(input) + for i := 0; i < len(result); i++ { + if result[i] != output[i] { + require(1) + require(0) + } + } +} + +func main() { + input := make([]byte, 0) + emtpy_output := []byte{ + 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, + 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, + } + keccak256check(input, emtpy_output) + + input = []byte{102, 111, 111, 98, 97, 114, 97, 97} + short_output := []byte{ + 172, 132, 33, 155, 248, 181, 178, 245, 199, 105, 157, 164, 188, 53, 193, 25, 7, 35, 159, + 188, 30, 123, 91, 143, 30, 100, 188, 128, 172, 248, 137, 202, + } + keccak256check(input, short_output) +} +*/ diff --git a/op-program/client/smoke_test_conf.go b/op-program/client/smoke_test_conf.go index b4281ad580640..2646b7736c20b 100644 --- a/op-program/client/smoke_test_conf.go +++ b/op-program/client/smoke_test_conf.go @@ -3,4 +3,5 @@ package client // in smoke test we only run 10 steps -var maximumSteps = 10 +//var maximumSteps = 10 +var maximumSteps = 1 From ba45d5a9a1f5af498ef1371ed6858297c0ef6ef2 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 11 Dec 2023 09:29:06 +0800 Subject: [PATCH 02/12] update keccak --- op-program/client/keccak256_wasm.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index ab19b264e056e..92a6455746576 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -1,3 +1,6 @@ +//go:build js || wasm || wasip1 +// +build js wasm wasip1 + package client import "encoding/binary" From 922bdbf6041c0d0fa94297c83db73cce01754273 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 11 Dec 2023 13:59:13 +0800 Subject: [PATCH 03/12] tiny keccak --- op-program/client/io_wasm.go | 41 ++++++++++++++++++++++++++--- op-program/client/keccak256_wasm.go | 29 +++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 60e814db089ff..66059e5d73466 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -7,7 +7,7 @@ import ( "encoding/binary" preimage "github.com/ethereum-optimism/optimism/op-preimage" - //"github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto" ) func NewOracleClientAndHintWriter() (preimage.Oracle, preimage.Hinter) { @@ -23,6 +23,38 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { _key := key.PreimageKey() _, _isPublic := key.(preimage.LocalIndexKey) + size := wasm_input(0) + ssize := size / 8 + uint64Size := (size + 7) / 8 + buf := make([]byte, size) + bufU64 := make([]uint64, uint64Size) + for i := uint64(0); i < ssize; i++ { + bufU64[i] = wasm_input(0) + binary.BigEndian.PutUint64(buf[i*8:], bufU64[i]) + } + + if ssize*8 < size { + bufU64[uint64Size-1] = wasm_input(0) + var sv uint64 = 56 + for i := uint64(ssize * 8); i < size; i++ { + buf[i] = byte(bufU64[uint64Size-1] >> sv) + sv = sv - 8 + } + } + // Integrity check + // TODO: can use customized circuit to optimize + if !_isPublic { + hash := Keccak256HashInputU64(bufU64) + hash[0] = _key[0] + require_bool(hash == _key) + } + return buf +} + +func (o wasmHostIO) GetBak(key preimage.Key) []byte { + _key := key.PreimageKey() + _, _isPublic := key.(preimage.LocalIndexKey) + size := wasm_input(0) buf := make([]byte, size) @@ -43,8 +75,7 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { // Integrity check // TODO: can use customized circuit to optimize if !_isPublic { - //hash := crypto.Keccak256Hash(buf) - hash := Keccak256Hash(buf) + hash := crypto.Keccak256Hash(buf) hash[0] = _key[0] require_bool(hash == _key) } @@ -64,6 +95,10 @@ func wasm_input(isPublic uint32) uint64 //go:noescape func wasm_output(value uint64) +//go:wasmimport env wasm_log +//go:noescape +func wasm_log(value uint64) + //go:wasmimport env require //go:noescape func require(uint32) diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index 92a6455746576..a50131563340f 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -22,7 +22,6 @@ func Keccak256Hash(data ...[]byte) (output [32]byte) { for _, value := range data { dataBytes = append(dataBytes, value...) } - require_bool(len(dataBytes)%8 == 0) input := ByteSliceToUint64Slice(dataBytes) keccak_new(0) for _, value := range input { @@ -39,19 +38,41 @@ func Keccak256Hash(data ...[]byte) (output [32]byte) { return output } +func Keccak256HashInputU64(data ...[]uint64) (output [32]byte) { + keccak_new(0) + for _, value := range data { + for _, value2 := range value { + keccak_push(value2) + } + } + result := make([]uint64, 0) + for i := 0; i < 4; i++ { + result = append(result, keccak_finalize()) + } + resultBytes := Uint64SliceToByteSlice(result) + for i := 0; i < 32; i++ { + output[i] = resultBytes[i] + } + return output +} + func Uint64SliceToByteSlice(uint64Slice []uint64) []byte { byteSlice := make([]byte, len(uint64Slice)*8) for i, val := range uint64Slice { binary.LittleEndian.PutUint64(byteSlice[i*8:], val) + //binary.BigEndian.PutUint64(byteSlice[i*8:], val) } return byteSlice } func ByteSliceToUint64Slice(byteSlice []byte) []uint64 { - require_bool(len(byteSlice)%8 == 0) - uint64Slice := make([]uint64, len(byteSlice)/8) + uint64Slice := make([]uint64, (len(byteSlice)+7)/8) for i := 0; i < len(byteSlice); i += 8 { - val := binary.LittleEndian.Uint64(byteSlice[i:]) + end := i + 8 + if end > len(byteSlice) { + end = len(byteSlice) + } + val := binary.LittleEndian.Uint64(byteSlice[i:end]) uint64Slice[i/8] = val } return uint64Slice From 8181f5b76a462471acf94e8a24239dc8cd5d4736 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 11 Dec 2023 14:07:40 +0800 Subject: [PATCH 04/12] update --- op-program/client/io_wasm.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 66059e5d73466..86f13d409f2c5 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -46,6 +46,21 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { if !_isPublic { hash := Keccak256HashInputU64(bufU64) hash[0] = _key[0] + for _, val := range hash { + wasm_log(uint64(val)) + } + wasm_log(0) + wasm_log(0) + wasm_log(0) + wasm_log(0) + wasm_log(0) + wasm_log(0) + wasm_log(0) + wasm_log(0) + for _, val := range _key { + wasm_log(uint64(val)) + } + require_bool(hash == _key) } return buf From e09657452c4385156d3968f16f13e63a13867c69 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 11 Dec 2023 18:55:31 +0800 Subject: [PATCH 05/12] update --- op-program/client/io_wasm.go | 35 ++++++++++++++++++----------- op-program/client/keccak256_wasm.go | 8 ++++++- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 86f13d409f2c5..7560520087cdd 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -46,19 +46,19 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { if !_isPublic { hash := Keccak256HashInputU64(bufU64) hash[0] = _key[0] - for _, val := range hash { - wasm_log(uint64(val)) - } - wasm_log(0) - wasm_log(0) - wasm_log(0) - wasm_log(0) - wasm_log(0) - wasm_log(0) - wasm_log(0) - wasm_log(0) - for _, val := range _key { - wasm_log(uint64(val)) + if _key[1] != 79 { + print_log_flag() + for _, val := range buf { + wasm_log(uint64(val)) + } + print_log_flag() + for _, val := range hash { + wasm_log(uint64(val)) + } + print_log_flag() + for _, val := range _key { + wasm_log(uint64(val)) + } } require_bool(hash == _key) @@ -125,3 +125,12 @@ func require_bool(cond bool) { require(0) } } + +func print_log_flag() { + wasm_log(1) + wasm_log(1) + wasm_log(1) + wasm_log(0) + wasm_log(0) + wasm_log(0) +} diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index a50131563340f..59e9327f50a12 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -42,7 +42,7 @@ func Keccak256HashInputU64(data ...[]uint64) (output [32]byte) { keccak_new(0) for _, value := range data { for _, value2 := range value { - keccak_push(value2) + keccak_push(Uint64BigEndianToLittleEndian(value2)) } } result := make([]uint64, 0) @@ -56,6 +56,12 @@ func Keccak256HashInputU64(data ...[]uint64) (output [32]byte) { return output } +func Uint64BigEndianToLittleEndian(input uint64) uint64 { + byteSlice := make([]byte, 8) + binary.BigEndian.PutUint64(byteSlice, input) + return binary.LittleEndian.Uint64(byteSlice) +} + func Uint64SliceToByteSlice(uint64Slice []uint64) []byte { byteSlice := make([]byte, len(uint64Slice)*8) for i, val := range uint64Slice { From 7ddd14ee80ddde723473c192998bb0648a29cc0b Mon Sep 17 00:00:00 2001 From: bill Date: Sat, 30 Dec 2023 23:31:14 +0800 Subject: [PATCH 06/12] modify call host keccak256 --- op-program/client/io_wasm.go | 51 ++++--------- op-program/client/keccak256_wasm.go | 108 ++++++++++++++-------------- 2 files changed, 68 insertions(+), 91 deletions(-) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 7560520087cdd..1e5829add4cf2 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -19,54 +19,39 @@ func NewOracleClientAndHintWriter() (preimage.Oracle, preimage.Hinter) { type wasmHostIO struct { } -func (o wasmHostIO) Get(key preimage.Key) []byte { +func (o wasmHostIO) OldGet(key preimage.Key) []byte { _key := key.PreimageKey() _, _isPublic := key.(preimage.LocalIndexKey) size := wasm_input(0) - ssize := size / 8 - uint64Size := (size + 7) / 8 buf := make([]byte, size) - bufU64 := make([]uint64, uint64Size) + + ssize := size / 8 for i := uint64(0); i < ssize; i++ { - bufU64[i] = wasm_input(0) - binary.BigEndian.PutUint64(buf[i*8:], bufU64[i]) + data := wasm_input(0) + binary.BigEndian.PutUint64(buf[i*8:], data) } if ssize*8 < size { - bufU64[uint64Size-1] = wasm_input(0) + data := wasm_input(0) var sv uint64 = 56 for i := uint64(ssize * 8); i < size; i++ { - buf[i] = byte(bufU64[uint64Size-1] >> sv) + buf[i] = byte(data >> sv) sv = sv - 8 } } // Integrity check // TODO: can use customized circuit to optimize if !_isPublic { - hash := Keccak256HashInputU64(bufU64) + hash := crypto.Keccak256Hash(buf) + //hash := Keccak256Hash(buf) hash[0] = _key[0] - if _key[1] != 79 { - print_log_flag() - for _, val := range buf { - wasm_log(uint64(val)) - } - print_log_flag() - for _, val := range hash { - wasm_log(uint64(val)) - } - print_log_flag() - for _, val := range _key { - wasm_log(uint64(val)) - } - } - require_bool(hash == _key) } return buf } -func (o wasmHostIO) GetBak(key preimage.Key) []byte { +func (o wasmHostIO) Get(key preimage.Key) []byte { _key := key.PreimageKey() _, _isPublic := key.(preimage.LocalIndexKey) @@ -90,7 +75,8 @@ func (o wasmHostIO) GetBak(key preimage.Key) []byte { // Integrity check // TODO: can use customized circuit to optimize if !_isPublic { - hash := crypto.Keccak256Hash(buf) + //hash := crypto.Keccak256Hash(buf) + hash := Keccak256Hash(buf) hash[0] = _key[0] require_bool(hash == _key) } @@ -110,10 +96,6 @@ func wasm_input(isPublic uint32) uint64 //go:noescape func wasm_output(value uint64) -//go:wasmimport env wasm_log -//go:noescape -func wasm_log(value uint64) - //go:wasmimport env require //go:noescape func require(uint32) @@ -125,12 +107,3 @@ func require_bool(cond bool) { require(0) } } - -func print_log_flag() { - wasm_log(1) - wasm_log(1) - wasm_log(1) - wasm_log(0) - wasm_log(0) - wasm_log(0) -} diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index 59e9327f50a12..fee033f125ed7 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -17,71 +17,75 @@ func keccak_push(uint64) //go:noescape func keccak_finalize() uint64 -func Keccak256Hash(data ...[]byte) (output [32]byte) { - dataBytes := make([]byte, 0) - for _, value := range data { - dataBytes = append(dataBytes, value...) - } - input := ByteSliceToUint64Slice(dataBytes) - keccak_new(0) - for _, value := range input { - keccak_push(value) - } - result := make([]uint64, 0) - for i := 0; i < 4; i++ { - result = append(result, keccak_finalize()) - } - resultBytes := Uint64SliceToByteSlice(result) - for i := 0; i < 32; i++ { - output[i] = resultBytes[i] +type KeccakHasher struct { + data uint64 + byteIdx uint64 + bufSize uint64 +} + +func NewKeccakHasher() *KeccakHasher { + keccak_new(1) + return &KeccakHasher{ + data: 0, + byteIdx: 0, + bufSize: 0, } - return output } -func Keccak256HashInputU64(data ...[]uint64) (output [32]byte) { - keccak_new(0) - for _, value := range data { - for _, value2 := range value { - keccak_push(Uint64BigEndianToLittleEndian(value2)) +func (kh *KeccakHasher) UpdateByte(v byte) { + kh.data += uint64(v) << (kh.byteIdx * 8) + kh.byteIdx++ + if kh.byteIdx >= 8 { + keccak_push(kh.data) + kh.data = 0 + kh.byteIdx = 0 + kh.bufSize++ + + if kh.bufSize == 17 { + keccak_finalize() + keccak_finalize() + keccak_finalize() + keccak_finalize() + keccak_new(0) + kh.bufSize = 0 } } - result := make([]uint64, 0) - for i := 0; i < 4; i++ { - result = append(result, keccak_finalize()) - } - resultBytes := Uint64SliceToByteSlice(result) - for i := 0; i < 32; i++ { - output[i] = resultBytes[i] - } - return output } -func Uint64BigEndianToLittleEndian(input uint64) uint64 { - byteSlice := make([]byte, 8) - binary.BigEndian.PutUint64(byteSlice, input) - return binary.LittleEndian.Uint64(byteSlice) -} +func (kh *KeccakHasher) Finalize() [4]uint64 { + bytesToPad := 136 - kh.byteIdx - kh.bufSize*8 + if bytesToPad == 1 { + var result uint64 = 0x86 << 56 + keccak_push(kh.data + result) + } else { + kh.UpdateByte(1) + for i := 0; i < int(bytesToPad)-2; i++ { + kh.UpdateByte(0) + } + var result uint64 = 0x80 << 56 + keccak_push(kh.data + result) + } -func Uint64SliceToByteSlice(uint64Slice []uint64) []byte { - byteSlice := make([]byte, len(uint64Slice)*8) - for i, val := range uint64Slice { - binary.LittleEndian.PutUint64(byteSlice[i*8:], val) - //binary.BigEndian.PutUint64(byteSlice[i*8:], val) + return [4]uint64{ + keccak_finalize(), + keccak_finalize(), + keccak_finalize(), + keccak_finalize(), } - return byteSlice } -func ByteSliceToUint64Slice(byteSlice []byte) []uint64 { - uint64Slice := make([]uint64, (len(byteSlice)+7)/8) - for i := 0; i < len(byteSlice); i += 8 { - end := i + 8 - if end > len(byteSlice) { - end = len(byteSlice) +func Keccak256Hash(data ...[]byte) (output [32]byte) { + hasher := NewKeccakHasher() + for _, value := range data { + for _, byteValue := range value { + hasher.UpdateByte(byteValue) } - val := binary.LittleEndian.Uint64(byteSlice[i:end]) - uint64Slice[i/8] = val } - return uint64Slice + result := hasher.Finalize() + for i, val := range result { + binary.LittleEndian.PutUint64(output[i*8:], val) + } + return output } /* From 811f9f31bd06d4c9fc816f84b049733c581725a0 Mon Sep 17 00:00:00 2001 From: Po Date: Tue, 2 Jan 2024 16:46:07 +0100 Subject: [PATCH 07/12] fix keccak host function --- op-program/client/io_wasm.go | 2 +- op-program/client/keccak256_wasm.go | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 1e5829add4cf2..b10826e94b63b 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -75,7 +75,7 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { // Integrity check // TODO: can use customized circuit to optimize if !_isPublic { - //hash := crypto.Keccak256Hash(buf) + // hash := crypto.Keccak256Hash(buf) hash := Keccak256Hash(buf) hash[0] = _key[0] require_bool(hash == _key) diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index fee033f125ed7..7a985f6880c61 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -55,7 +55,7 @@ func (kh *KeccakHasher) UpdateByte(v byte) { func (kh *KeccakHasher) Finalize() [4]uint64 { bytesToPad := 136 - kh.byteIdx - kh.bufSize*8 if bytesToPad == 1 { - var result uint64 = 0x86 << 56 + var result uint64 = 0x81 << 56 keccak_push(kh.data + result) } else { kh.UpdateByte(1) @@ -63,7 +63,7 @@ func (kh *KeccakHasher) Finalize() [4]uint64 { kh.UpdateByte(0) } var result uint64 = 0x80 << 56 - keccak_push(kh.data + result) + keccak_push(kh.data ^ result) } return [4]uint64{ @@ -88,6 +88,20 @@ func Keccak256Hash(data ...[]byte) (output [32]byte) { return output } +func Keccak256HashSimple(data ...[]byte) (output [32]byte) { + hasher := NewKeccakHasher() + for _, value := range data { + for _, byteValue := range value { + hasher.UpdateByte(byteValue) + } + } + result := hasher.Finalize() + for i, val := range result { + binary.LittleEndian.PutUint64(output[i*8:], val) + } + return output +} + /* // for test From 508f9bd014fd0ec23ebd552e8a50ee949262b157 Mon Sep 17 00:00:00 2001 From: Po Date: Wed, 3 Jan 2024 08:39:23 +0100 Subject: [PATCH 08/12] opt keccak host circuit --- op-program/client/io_wasm.go | 17 ++++- op-program/client/keccak256_wasm.go | 106 ++++++++-------------------- 2 files changed, 45 insertions(+), 78 deletions(-) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index b10826e94b63b..1179715db750a 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -56,7 +56,14 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { _, _isPublic := key.(preimage.LocalIndexKey) size := wasm_input(0) - buf := make([]byte, size) + padding := size % 136 + if padding != 0 { + padding = 136 - padding + } else { + padding = 136 + } + + buf := make([]byte, size+padding) ssize := size / 8 for i := uint64(0); i < ssize; i++ { @@ -76,11 +83,11 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { // TODO: can use customized circuit to optimize if !_isPublic { // hash := crypto.Keccak256Hash(buf) - hash := Keccak256Hash(buf) + hash := Keccak256Hash(buf, size, padding) hash[0] = _key[0] require_bool(hash == _key) } - return buf + return buf[:size] } func (o wasmHostIO) Hint(v preimage.Hint) { @@ -100,6 +107,10 @@ func wasm_output(value uint64) //go:noescape func require(uint32) +//go:wasmimport env wasm_dbg +//go:noescape +func wasm_dbg(uint64) + func require_bool(cond bool) { if cond { require(1) diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index 7a985f6880c61..a8c7e5e6b1a5d 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -17,89 +17,45 @@ func keccak_push(uint64) //go:noescape func keccak_finalize() uint64 -type KeccakHasher struct { - data uint64 - byteIdx uint64 - bufSize uint64 -} - -func NewKeccakHasher() *KeccakHasher { - keccak_new(1) - return &KeccakHasher{ - data: 0, - byteIdx: 0, - bufSize: 0, - } -} +var hash [32]byte -func (kh *KeccakHasher) UpdateByte(v byte) { - kh.data += uint64(v) << (kh.byteIdx * 8) - kh.byteIdx++ - if kh.byteIdx >= 8 { - keccak_push(kh.data) - kh.data = 0 - kh.byteIdx = 0 - kh.bufSize++ - - if kh.bufSize == 17 { - keccak_finalize() - keccak_finalize() - keccak_finalize() - keccak_finalize() - keccak_new(0) - kh.bufSize = 0 - } - } -} - -func (kh *KeccakHasher) Finalize() [4]uint64 { - bytesToPad := 136 - kh.byteIdx - kh.bufSize*8 - if bytesToPad == 1 { - var result uint64 = 0x81 << 56 - keccak_push(kh.data + result) +func Keccak256Hash(data []byte, size uint64, padding uint64) [32]byte { + total_len := len(data) + if padding == 1 { + data[total_len-1] = 0x81 } else { - kh.UpdateByte(1) - for i := 0; i < int(bytesToPad)-2; i++ { - kh.UpdateByte(0) - } - var result uint64 = 0x80 << 56 - keccak_push(kh.data ^ result) + data[size] = 0x01 + data[total_len-1] = 0x80 } - return [4]uint64{ - keccak_finalize(), - keccak_finalize(), - keccak_finalize(), - keccak_finalize(), - } -} + var hash_0 uint64 + var hash_1 uint64 + var hash_2 uint64 + var hash_3 uint64 -func Keccak256Hash(data ...[]byte) (output [32]byte) { - hasher := NewKeccakHasher() - for _, value := range data { - for _, byteValue := range value { - hasher.UpdateByte(byteValue) - } - } - result := hasher.Finalize() - for i, val := range result { - binary.LittleEndian.PutUint64(output[i*8:], val) - } - return output -} + var val uint64 -func Keccak256HashSimple(data ...[]byte) (output [32]byte) { - hasher := NewKeccakHasher() - for _, value := range data { - for _, byteValue := range value { - hasher.UpdateByte(byteValue) + round := total_len / 136 + keccak_new(1) + for i := 0; i < round; i++ { + for j := 0; j < 17; j++ { + start := i*136 + j*8 + val = binary.LittleEndian.Uint64(data[start : start+8]) + keccak_push(val) } + hash_0 = keccak_finalize() + hash_1 = keccak_finalize() + hash_2 = keccak_finalize() + hash_3 = keccak_finalize() + keccak_new(0) } - result := hasher.Finalize() - for i, val := range result { - binary.LittleEndian.PutUint64(output[i*8:], val) - } - return output + + binary.LittleEndian.PutUint64(hash[:], hash_0) + binary.LittleEndian.PutUint64(hash[8:], hash_1) + binary.LittleEndian.PutUint64(hash[16:], hash_2) + binary.LittleEndian.PutUint64(hash[24:], hash_3) + + return hash } /* From df56ec4fc69f92efc6e1b27c84507fb38fa2a36e Mon Sep 17 00:00:00 2001 From: bill Date: Sun, 7 Jan 2024 17:23:34 +0800 Subject: [PATCH 09/12] modify keccak256 param, []byte to []uint64 --- op-program/client/io_wasm.go | 54 +++++++++++++++++++++++++++-- op-program/client/keccak256_wasm.go | 28 +++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index 1179715db750a..ad47c245c2fd6 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -44,14 +44,13 @@ func (o wasmHostIO) OldGet(key preimage.Key) []byte { // TODO: can use customized circuit to optimize if !_isPublic { hash := crypto.Keccak256Hash(buf) - //hash := Keccak256Hash(buf) hash[0] = _key[0] require_bool(hash == _key) } return buf } -func (o wasmHostIO) Get(key preimage.Key) []byte { +func (o wasmHostIO) Uint8Get(key preimage.Key) []byte { _key := key.PreimageKey() _, _isPublic := key.(preimage.LocalIndexKey) @@ -90,6 +89,57 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { return buf[:size] } +func (o wasmHostIO) Get(key preimage.Key) []byte { + _key := key.PreimageKey() + _, _isPublic := key.(preimage.LocalIndexKey) + + size := wasm_input(0) + padding := size % 136 + if padding != 0 { + padding = 136 - padding + } else { + padding = 136 + } + + totalLen := size + padding + totalUint64Len := totalLen / 8 + buf := make([]byte, totalLen) + bufUint64 := make([]uint64, totalUint64Len) + + ssize := size / 8 + for i := uint64(0); i < ssize; i++ { + bufUint64[i] = wasm_input(0) + binary.LittleEndian.PutUint64(buf[i*8:], bufUint64[i]) + } + + if ssize*8 < size { + data := wasm_input(0) + var sv uint64 = 56 + for i := uint64(ssize * 8); i < size; i++ { + buf[i] = byte(data >> sv) + sv = sv - 8 + } + } + if padding == 1 { + buf[totalLen-1] = 0x81 + } else { + buf[size] = 0x01 + buf[totalLen-1] = 0x80 + } + for i := ssize; i < totalUint64Len; i++ { + bufUint64[i] = binary.LittleEndian.Uint64(buf[i*8:]) + } + // Integrity check + // TODO: can use customized circuit to optimize + if !_isPublic { + // hash := crypto.Keccak256Hash(buf) + hash := Keccak256HashUint64(bufUint64) + hash[0] = _key[0] + require_bool(hash == _key) + } + return buf[:size] +} + func (o wasmHostIO) Hint(v preimage.Hint) { // do nothing return diff --git a/op-program/client/keccak256_wasm.go b/op-program/client/keccak256_wasm.go index a8c7e5e6b1a5d..189ed2b496f81 100644 --- a/op-program/client/keccak256_wasm.go +++ b/op-program/client/keccak256_wasm.go @@ -58,6 +58,34 @@ func Keccak256Hash(data []byte, size uint64, padding uint64) [32]byte { return hash } +func Keccak256HashUint64(data []uint64) [32]byte { + var hash [32]byte + var hash_0 uint64 + var hash_1 uint64 + var hash_2 uint64 + var hash_3 uint64 + + keccak_new(1) + round := len(data) / 17 + for i := 0; i < round; i++ { + for j := 0; j < 17; j++ { + keccak_push(data[i*17+j]) + } + hash_0 = keccak_finalize() + hash_1 = keccak_finalize() + hash_2 = keccak_finalize() + hash_3 = keccak_finalize() + keccak_new(0) + } + + binary.LittleEndian.PutUint64(hash[:], hash_0) + binary.LittleEndian.PutUint64(hash[8:], hash_1) + binary.LittleEndian.PutUint64(hash[16:], hash_2) + binary.LittleEndian.PutUint64(hash[24:], hash_3) + + return hash +} + /* // for test From 5074d7519f0476b3e8a1780bb42f57ed6776bb6d Mon Sep 17 00:00:00 2001 From: bill Date: Sun, 7 Jan 2024 22:10:20 +0800 Subject: [PATCH 10/12] BigEndian to LittleEndian --- op-preimage/oracle.go | 6 +- op-program/client/io_wasm.go | 139 +++++++++++++++++- .../zkWasm-emulator/wasi/wasi_exec_node.js | 2 +- 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/op-preimage/oracle.go b/op-preimage/oracle.go index 07b479f97ba26..662f51e93fca2 100644 --- a/op-preimage/oracle.go +++ b/op-preimage/oracle.go @@ -34,7 +34,7 @@ func (o *OracleClient) Get(key Key) []byte { } var length uint64 - if err := binary.Read(o.rw, binary.BigEndian, &length); err != nil { + if err := binary.Read(o.rw, binary.LittleEndian, &length); err != nil { panic(fmt.Errorf("failed to read pre-image length of key %s (%T) from pre-image oracle: %w", key, key, err)) } payload := make([]byte, length) @@ -72,7 +72,7 @@ func (o *OracleServer) NextPreimageRequest(getPreimage PreimageGetter) error { if PreimageFile != nil { // write length & data to preimages.bin - binary.Write(PreimageFile, binary.BigEndian, uint64(len(value))) + binary.Write(PreimageFile, binary.LittleEndian, uint64(len(value))) _, err := PreimageFile.Write(value) if err != nil { return fmt.Errorf("failed to dump pre-image binary file: %w", err) @@ -87,7 +87,7 @@ func (o *OracleServer) NextPreimageRequest(getPreimage PreimageGetter) error { } } - if err := binary.Write(o.rw, binary.BigEndian, uint64(len(value))); err != nil { + if err := binary.Write(o.rw, binary.LittleEndian, uint64(len(value))); err != nil { return fmt.Errorf("failed to write length-prefix %d: %w", len(value), err) } if len(value) == 0 { diff --git a/op-program/client/io_wasm.go b/op-program/client/io_wasm.go index ad47c245c2fd6..77a258f8ba5d6 100644 --- a/op-program/client/io_wasm.go +++ b/op-program/client/io_wasm.go @@ -89,7 +89,7 @@ func (o wasmHostIO) Uint8Get(key preimage.Key) []byte { return buf[:size] } -func (o wasmHostIO) Get(key preimage.Key) []byte { +func (o wasmHostIO) Uint64Get(key preimage.Key) []byte { _key := key.PreimageKey() _, _isPublic := key.(preimage.LocalIndexKey) @@ -100,24 +100,21 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { } else { padding = 136 } - totalLen := size + padding totalUint64Len := totalLen / 8 buf := make([]byte, totalLen) bufUint64 := make([]uint64, totalUint64Len) - ssize := size / 8 for i := uint64(0); i < ssize; i++ { bufUint64[i] = wasm_input(0) binary.LittleEndian.PutUint64(buf[i*8:], bufUint64[i]) } - if ssize*8 < size { data := wasm_input(0) - var sv uint64 = 56 + var sv uint64 = 0 for i := uint64(ssize * 8); i < size; i++ { buf[i] = byte(data >> sv) - sv = sv - 8 + sv = sv + 8 } } if padding == 1 { @@ -140,6 +137,136 @@ func (o wasmHostIO) Get(key preimage.Key) []byte { return buf[:size] } +func (o wasmHostIO) Keccak256Get(key preimage.Key) []byte { + _key := key.PreimageKey() + _, _isPublic := key.(preimage.LocalIndexKey) + + size := wasm_input(0) + padding := size % 136 + if padding != 0 { + padding = 136 - padding + } else { + padding = 136 + } + totalLen := size + padding + totalUint64Len := totalLen / 8 + buf := make([]byte, totalLen) + + keccak_new(1) + ssize := size / 8 + for i := uint64(0); i < ssize; i++ { + data := wasm_input(0) + binary.LittleEndian.PutUint64(buf[i*8:], data) + keccak_push(data) + if (i+1)%17 == 0 { + keccak_finalize() + keccak_finalize() + keccak_finalize() + keccak_finalize() + keccak_new(0) + } + } + if ssize*8 < size { + data := wasm_input(0) + var sv uint64 = 0 + for i := uint64(ssize * 8); i < size; i++ { + buf[i] = byte(data >> sv) + sv = sv + 8 + } + } + if padding == 1 { + buf[totalLen-1] = 0x81 + } else { + buf[size] = 0x01 + buf[totalLen-1] = 0x80 + } + for i := ssize; i < totalUint64Len; i++ { + keccak_push(binary.LittleEndian.Uint64(buf[i*8:])) + } + // Integrity check + // TODO: can use customized circuit to optimize + if !_isPublic { + // hash := crypto.Keccak256Hash(buf) + var hash [32]byte + hash_0 := keccak_finalize() + hash_1 := keccak_finalize() + hash_2 := keccak_finalize() + hash_3 := keccak_finalize() + binary.LittleEndian.PutUint64(hash[:], hash_0) + binary.LittleEndian.PutUint64(hash[8:], hash_1) + binary.LittleEndian.PutUint64(hash[16:], hash_2) + binary.LittleEndian.PutUint64(hash[24:], hash_3) + hash[0] = _key[0] + require_bool(hash == _key) + } + return buf[:size] +} + +func (o wasmHostIO) Get(key preimage.Key) []byte { + _key := key.PreimageKey() + _, _isPublic := key.(preimage.LocalIndexKey) + + size := wasm_input(0) + padding := size % 136 + if padding != 0 { + padding = 136 - padding + } else { + padding = 136 + } + totalLen := size + padding + totalUint64Len := totalLen / 8 + buf := make([]byte, totalLen) + + keccak_new(1) + ssize := size / 8 + for i := uint64(0); i < ssize; i++ { + data := wasm_input(0) + binary.LittleEndian.PutUint64(buf[i*8:], data) + keccak_push(data) + if (i+1)%17 == 0 { + keccak_finalize() + keccak_finalize() + keccak_finalize() + keccak_finalize() + keccak_new(0) + } + } + if ssize*8 < size { + data := wasm_input(0) + var sv uint64 = 0 + for i := uint64(ssize * 8); i < size; i++ { + buf[i] = byte(data >> sv) + sv = sv + 8 + } + } + if padding == 1 { + buf[totalLen-1] = 0x81 + } else { + buf[size] = 0x01 + buf[totalLen-1] = 0x80 + } + for i := ssize; i < totalUint64Len; i++ { + keccak_push(binary.LittleEndian.Uint64(buf[i*8:])) + } + // Integrity check + // TODO: can use customized circuit to optimize + if !_isPublic { + // hash := crypto.Keccak256Hash(buf) + var hash [32]byte + hash_0 := keccak_finalize() + hash_1 := keccak_finalize() + hash_2 := keccak_finalize() + hash_3 := keccak_finalize() + binary.LittleEndian.PutUint64(hash[:], hash_0) + binary.LittleEndian.PutUint64(hash[8:], hash_1) + binary.LittleEndian.PutUint64(hash[16:], hash_2) + binary.LittleEndian.PutUint64(hash[24:], hash_3) + hash[0] = _key[0] + require_bool(hash == _key) + } + return buf[:size] +} + func (o wasmHostIO) Hint(v preimage.Hint) { // do nothing return diff --git a/op-program/zkWasm-emulator/wasi/wasi_exec_node.js b/op-program/zkWasm-emulator/wasi/wasi_exec_node.js index bbbae38d23a15..c877573e875d5 100644 --- a/op-program/zkWasm-emulator/wasi/wasi_exec_node.js +++ b/op-program/zkWasm-emulator/wasi/wasi_exec_node.js @@ -20,7 +20,7 @@ import fs from "fs" const hostio = { env: { wasm_input: (ispulic) => { - let data = preimages.readBigInt64BE(cur) + let data = preimages.readBigInt64LE(cur) cur += 8 return data }, From 5343ef200d3e00a1969c4c6e0b15593985f3f5eb Mon Sep 17 00:00:00 2001 From: bill Date: Tue, 16 Jan 2024 00:01:32 +0800 Subject: [PATCH 11/12] preimage BigEndian to LittleEndian --- op-preimage/oracle.go | 8 +- op-preimage/oracle_wasm.go | 102 ++++++++++++++++++ .../zkWasm-emulator/wasi/wasi_exec_node.js | 1 + 3 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 op-preimage/oracle_wasm.go diff --git a/op-preimage/oracle.go b/op-preimage/oracle.go index 662f51e93fca2..be38079e07fc2 100644 --- a/op-preimage/oracle.go +++ b/op-preimage/oracle.go @@ -1,3 +1,5 @@ +//go:build !(js || wasm || wasip1) +// +build !wasm,!wasip1, !js package preimage import ( @@ -34,7 +36,7 @@ func (o *OracleClient) Get(key Key) []byte { } var length uint64 - if err := binary.Read(o.rw, binary.LittleEndian, &length); err != nil { + if err := binary.Read(o.rw, binary.BigEndian, &length); err != nil { panic(fmt.Errorf("failed to read pre-image length of key %s (%T) from pre-image oracle: %w", key, key, err)) } payload := make([]byte, length) @@ -72,7 +74,7 @@ func (o *OracleServer) NextPreimageRequest(getPreimage PreimageGetter) error { if PreimageFile != nil { // write length & data to preimages.bin - binary.Write(PreimageFile, binary.LittleEndian, uint64(len(value))) + binary.Write(PreimageFile, binary.BigEndian, uint64(len(value))) _, err := PreimageFile.Write(value) if err != nil { return fmt.Errorf("failed to dump pre-image binary file: %w", err) @@ -87,7 +89,7 @@ func (o *OracleServer) NextPreimageRequest(getPreimage PreimageGetter) error { } } - if err := binary.Write(o.rw, binary.LittleEndian, uint64(len(value))); err != nil { + if err := binary.Write(o.rw, binary.BigEndian, uint64(len(value))); err != nil { return fmt.Errorf("failed to write length-prefix %d: %w", len(value), err) } if len(value) == 0 { diff --git a/op-preimage/oracle_wasm.go b/op-preimage/oracle_wasm.go new file mode 100644 index 0000000000000..7111d043c8a3e --- /dev/null +++ b/op-preimage/oracle_wasm.go @@ -0,0 +1,102 @@ +//go:build js || wasm || wasip1 +// +build js wasm wasip1 +package preimage + +import ( + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "os" +) + +// OracleClient implements the Oracle by writing the pre-image key to the given stream, +// and reading back a length-prefixed value. +type OracleClient struct { + rw io.ReadWriter +} + +func NewOracleClient(rw io.ReadWriter) *OracleClient { + return &OracleClient{rw: rw} +} + +var _ Oracle = (*OracleClient)(nil) + +func min(x, y int) int { + if x < y { + return x + } + return y +} + +func (o *OracleClient) Get(key Key) []byte { + h := key.PreimageKey() + if _, err := o.rw.Write(h[:]); err != nil { + panic(fmt.Errorf("failed to write key %s (%T) to pre-image oracle: %w", key, key, err)) + } + + var length uint64 + if err := binary.Read(o.rw, binary.LittleEndian, &length); err != nil { + panic(fmt.Errorf("failed to read pre-image length of key %s (%T) from pre-image oracle: %w", key, key, err)) + } + payload := make([]byte, length) + if _, err := io.ReadFull(o.rw, payload); err != nil { + panic(fmt.Errorf("failed to read pre-image payload (length %d) of key %s (%T) from pre-image oracle: %w", length, key, key, err)) + } + return payload +} + +// OracleServer serves the pre-image requests of the OracleClient, implementing the same protocol as the onchain VM. +type OracleServer struct { + rw io.ReadWriter +} + +func NewOracleServer(rw io.ReadWriter) *OracleServer { + return &OracleServer{rw: rw} +} + +type PreimageGetter func(key [32]byte) ([]byte, error) + +var PreimageFile *os.File + +func (o *OracleServer) NextPreimageRequest(getPreimage PreimageGetter) error { + var key [32]byte + if _, err := io.ReadFull(o.rw, key[:]); err != nil { + if err == io.EOF { + return io.EOF + } + return fmt.Errorf("failed to read requested pre-image key: %w", err) + } + value, err := getPreimage(key) + if err != nil { + return fmt.Errorf("failed to serve pre-image %s request: %w", hex.EncodeToString(key[:]), err) + } + + if PreimageFile != nil { + // write length & data to preimages.bin + binary.Write(PreimageFile, binary.LittleEndian, uint64(len(value))) + _, err := PreimageFile.Write(value) + if err != nil { + return fmt.Errorf("failed to dump pre-image binary file: %w", err) + } + + // padding some zeros to make preimages length can be divided by 8 + if len(value)%8 != 0 { + _, err := PreimageFile.Write(make([]byte, 8-len(value)%8)) + if err != nil { + return fmt.Errorf("failed to dump pre-image binary file: %w", err) + } + } + } + + if err := binary.Write(o.rw, binary.LittleEndian, uint64(len(value))); err != nil { + return fmt.Errorf("failed to write length-prefix %d: %w", len(value), err) + } + if len(value) == 0 { + return nil + } + if _, err := o.rw.Write(value); err != nil { + return fmt.Errorf("failed to write pre-image value (%d long): %w", len(value), err) + } + return nil +} diff --git a/op-program/zkWasm-emulator/wasi/wasi_exec_node.js b/op-program/zkWasm-emulator/wasi/wasi_exec_node.js index c877573e875d5..23651142bd6d2 100644 --- a/op-program/zkWasm-emulator/wasi/wasi_exec_node.js +++ b/op-program/zkWasm-emulator/wasi/wasi_exec_node.js @@ -20,6 +20,7 @@ import fs from "fs" const hostio = { env: { wasm_input: (ispulic) => { + //let data = preimages.readBigInt64BE(cur) let data = preimages.readBigInt64LE(cur) cur += 8 return data From f26837eef57d4b17193b3e070f6f90b54c636077 Mon Sep 17 00:00:00 2001 From: bill Date: Tue, 16 Jan 2024 00:37:45 +0800 Subject: [PATCH 12/12] rlphash keccak256 --- op-geth/core/types/hashing.go | 2 + op-geth/core/types/hashing_wasm.go | 144 +++++++++++++++++++++++ op-geth/core/types/keccak256_wasm.go | 164 +++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 op-geth/core/types/hashing_wasm.go create mode 100644 op-geth/core/types/keccak256_wasm.go diff --git a/op-geth/core/types/hashing.go b/op-geth/core/types/hashing.go index fbdeaf0d0793c..9dd0f849b15e4 100644 --- a/op-geth/core/types/hashing.go +++ b/op-geth/core/types/hashing.go @@ -1,3 +1,5 @@ +//go:build !(js || wasm || wasip1) +// +build !wasm,!wasip1, !js // Copyright 2021 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/op-geth/core/types/hashing_wasm.go b/op-geth/core/types/hashing_wasm.go new file mode 100644 index 0000000000000..0d012a405c7c8 --- /dev/null +++ b/op-geth/core/types/hashing_wasm.go @@ -0,0 +1,144 @@ +//go:build js || wasm || wasip1 +// +build js wasm wasip1 +package types + +import ( + "bytes" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +// hasherPool holds LegacyKeccak256 hashers for rlpHash. +var hasherPool = sync.Pool{ + New: func() interface{} { return sha3.NewLegacyKeccak256() }, +} + +// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding. +var encodeBufferPool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, +} + +// rlpHash encodes x and hashes the encoded bytes. +func oldrlpHash(x interface{}) (h common.Hash) { + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + rlp.Encode(sha, x) + sha.Read(h[:]) + return h +} + +func checkrlpHash(x interface{}) (h common.Hash) { + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + hash := NewHashHelper() + rlp.Encode(hash, x) + hash.WriteTo(sha) + sha.Read(h[:]) + n := hash.Hash() + for i := 0; i < 32; i++ { + if h[i] != n[i] { + } + require_bool(h[i] == n[i]) + } + return n +} + +func rlpHash(x interface{}) (h common.Hash) { + hash := NewHashHelper() + rlp.Encode(hash, x) + n := hash.Hash() + return n +} + +// prefixedRlpHash writes the prefix into the hasher before rlp-encoding x. +// It's used for typed transactions. +func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) { + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + sha.Write([]byte{prefix}) + rlp.Encode(sha, x) + sha.Read(h[:]) + wasm_dbg(222) + return h +} + +// TrieHasher is the tool used to calculate the hash of derivable list. +// This is internal, do not use. +type TrieHasher interface { + Reset() + Update([]byte, []byte) error + Hash() common.Hash +} + +// DerivableList is the input to DeriveSha. +// It is implemented by the 'Transactions' and 'Receipts' types. +// This is internal, do not use these methods. +type DerivableList interface { + Len() int + EncodeIndex(int, *bytes.Buffer) +} + +func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte { + buf.Reset() + list.EncodeIndex(i, buf) + // It's really unfortunate that we need to do perform this copy. + // StackTrie holds onto the values until Hash is called, so the values + // written to it must not alias. + return common.CopyBytes(buf.Bytes()) +} + +// DeriveSha creates the tree hashes of transactions, receipts, and withdrawals in a block header. +func DeriveSha(list DerivableList, hasher TrieHasher) common.Hash { + hasher.Reset() + + valueBuf := encodeBufferPool.Get().(*bytes.Buffer) + defer encodeBufferPool.Put(valueBuf) + + // StackTrie requires values to be inserted in increasing hash order, which is not the + // order that `list` provides hashes in. This insertion sequence ensures that the + // order is correct. + // + // The error returned by hasher is omitted because hasher will produce an incorrect + // hash in case any error occurs. + var indexBuf []byte + for i := 1; i < list.Len() && i <= 0x7f; i++ { + indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) + value := encodeForDerive(list, i, valueBuf) + hasher.Update(indexBuf, value) + } + if list.Len() > 0 { + indexBuf = rlp.AppendUint64(indexBuf[:0], 0) + value := encodeForDerive(list, 0, valueBuf) + hasher.Update(indexBuf, value) + } + for i := 0x80; i < list.Len(); i++ { + indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) + value := encodeForDerive(list, i, valueBuf) + hasher.Update(indexBuf, value) + } + wasm_dbg(111) + return hasher.Hash() +} + +//go:wasmimport env wasm_dbg +//go:noescape +func wasm_dbg(uint64) + +//go:wasmimport env require +//go:noescape +func require(uint32) + +func require_bool(cond bool) { + if cond { + require(1) + } else { + require(0) + } +} diff --git a/op-geth/core/types/keccak256_wasm.go b/op-geth/core/types/keccak256_wasm.go new file mode 100644 index 0000000000000..cad9a1f86c9bb --- /dev/null +++ b/op-geth/core/types/keccak256_wasm.go @@ -0,0 +1,164 @@ +//go:build js || wasm || wasip1 +// +build js wasm wasip1 +package types + +import ( + "encoding/binary" + "io" +) + +//go:wasmimport env keccak_new +//go:noescape +func keccak_new(uint64) + +//go:wasmimport env keccak_push +//go:noescape +func keccak_push(uint64) + +//go:wasmimport env keccak_finalize +//go:noescape +func keccak_finalize() uint64 + +func NewHashHelper() *HashHelper { + return &HashHelper{} +} + +type HashHelper struct { + data []byte +} + +func (b *HashHelper) Write(p []byte) (n int, err error) { + b.data = append(b.data, p...) + return len(p), nil +} + +func (b *HashHelper) WriteTo(w io.Writer) (err error) { + w.Write(b.data) + return nil +} + +func (b *HashHelper) Hash() [32]byte { + size := uint64(len(b.data)) + padding := size % 136 + if padding != 0 { + padding = 136 - padding + } else { + padding = 136 + } + buf := make([]byte, size+padding) + //wasm_dbg(size) + wasm_dbg(size + padding) + copy(buf, b.data) + hash := Keccak256Hash(buf, size, padding) + return hash +} + +/* + *func (b HashHelper) PrintData() { + * for _, v := range b.data { + * wasm_dbg(v) + * } + * for i := 0; i < 136; i++ { + * wasm_dbg(v) + * } + *} + */ + +var hash [32]byte + +func Keccak256Hash(data []byte, size uint64, padding uint64) [32]byte { + total_len := len(data) + if padding == 1 { + data[total_len-1] = 0x81 + } else { + data[size] = 0x01 + data[total_len-1] = 0x80 + } + + var hash_0 uint64 + var hash_1 uint64 + var hash_2 uint64 + var hash_3 uint64 + + var val uint64 + + round := total_len / 136 + keccak_new(1) + for i := 0; i < round; i++ { + for j := 0; j < 17; j++ { + start := i*136 + j*8 + val = binary.LittleEndian.Uint64(data[start : start+8]) + keccak_push(val) + } + hash_0 = keccak_finalize() + hash_1 = keccak_finalize() + hash_2 = keccak_finalize() + hash_3 = keccak_finalize() + keccak_new(0) + } + + binary.LittleEndian.PutUint64(hash[:], hash_0) + binary.LittleEndian.PutUint64(hash[8:], hash_1) + binary.LittleEndian.PutUint64(hash[16:], hash_2) + binary.LittleEndian.PutUint64(hash[24:], hash_3) + + return hash +} + +func Keccak256HashUint64(data []uint64) [32]byte { + var hash [32]byte + var hash_0 uint64 + var hash_1 uint64 + var hash_2 uint64 + var hash_3 uint64 + + keccak_new(1) + round := len(data) / 17 + for i := 0; i < round; i++ { + for j := 0; j < 17; j++ { + keccak_push(data[i*17+j]) + } + hash_0 = keccak_finalize() + hash_1 = keccak_finalize() + hash_2 = keccak_finalize() + hash_3 = keccak_finalize() + keccak_new(0) + } + + binary.LittleEndian.PutUint64(hash[:], hash_0) + binary.LittleEndian.PutUint64(hash[8:], hash_1) + binary.LittleEndian.PutUint64(hash[16:], hash_2) + binary.LittleEndian.PutUint64(hash[24:], hash_3) + + return hash +} + +/* +// for test + +func keccak256check(input []byte, output []byte) { + result := Keccak256Hash(input) + for i := 0; i < len(result); i++ { + if result[i] != output[i] { + require(1) + require(0) + } + } +} + +func main() { + input := make([]byte, 0) + emtpy_output := []byte{ + 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, + 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, + } + keccak256check(input, emtpy_output) + + input = []byte{102, 111, 111, 98, 97, 114, 97, 97} + short_output := []byte{ + 172, 132, 33, 155, 248, 181, 178, 245, 199, 105, 157, 164, 188, 53, 193, 25, 7, 35, 159, + 188, 30, 123, 91, 143, 30, 100, 188, 128, 172, 248, 137, 202, + } + keccak256check(input, short_output) +} +*/