Skip to content

Commit 346d8d9

Browse files
committed
Implement FIXED32_LITTLE length operation
1 parent 61321db commit 346d8d9

File tree

5 files changed

+60
-2
lines changed

5 files changed

+60
-2
lines changed

go/ops.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ics23
33
import (
44
"bytes"
55
"crypto"
6+
"encoding/binary"
67

78
// adds sha256 capability to crypto.SHA256
89
_ "crypto/sha256"
@@ -158,11 +159,15 @@ func doLengthOp(lengthOp LengthOp, data []byte) ([]byte, error) {
158159
return nil, errors.Errorf("Data was %d bytes, not 64", len(data))
159160
}
160161
return data, nil
162+
case LengthOp_FIXED32_LITTLE:
163+
res := make([]byte, 4, 4+len(data))
164+
binary.LittleEndian.PutUint32(res[:4], uint32(len(data)))
165+
res = append(res, data...)
166+
return res, nil
161167
// TODO
162168
// case LengthOp_VAR_RLP:
163169
// case LengthOp_FIXED32_BIG:
164170
// case LengthOp_FIXED64_BIG:
165-
// case LengthOp_FIXED32_LITTLE:
166171
// case LengthOp_FIXED64_LITTLE:
167172
}
168173
return nil, errors.Errorf("Unsupported lengthop: %d", lengthOp)

go/ops_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ func TestLeafOp(t *testing.T) {
8686
// echo -n 04666f6f6420a48c2d4f67b9f80374938535285ed285819d8a5a8fc1fccd1e3244e437cf290d | xxd -r -p | sha256sum
8787
expected: fromHex("87e0483e8fb624aef2e2f7b13f4166cda485baa8e39f437c83d74c94bedb148f"),
8888
},
89+
"hash with length prefix (fixed 32-bit little-endian encoding)": {
90+
op: &LeafOp{
91+
Hash: HashOp_SHA256,
92+
Length: LengthOp_FIXED32_LITTLE,
93+
// no prehash
94+
},
95+
// echo -n food | xxs -ps
96+
// and manually compute length bytes
97+
key: []byte("food"), // 04000000666f6f64
98+
value: []byte("some longer text"), // 10000000736f6d65206c6f6e6765722074657874
99+
// echo -n 04000000666f6f6410000000736f6d65206c6f6e6765722074657874 | xxd -r -p | sha256sum
100+
expected: fromHex("c853652437be02501c674744bf2a2b45d92a0a9f29c4b1044010fb3e2d43a949"),
101+
},
89102
}
90103

91104
for name, tc := range cases {

js/src/ops.spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ describe("applyLeaf", () => {
8686
expect(applyLeaf(op, key, value)).toEqual(expected);
8787
});
8888

89+
it("hashes with length prefix (fixed 32-bit little-endian encoding)", () => {
90+
const op: ics23.ILeafOp = {
91+
hash: ics23.HashOp.SHA256,
92+
length: ics23.LengthOp.FIXED32_LITTLE
93+
};
94+
// echo -n food | xxd -ps
95+
const key = toAscii("food"); // 04000000666f6f64
96+
const value = toAscii("some longer text"); // 10000000736f6d65206c6f6e6765722074657874
97+
// echo -n 04000000666f6f6410000000736f6d65206c6f6e6765722074657874 | xxd -r -p | sha256sum
98+
const expected = fromHex(
99+
"c853652437be02501c674744bf2a2b45d92a0a9f29c4b1044010fb3e2d43a949"
100+
);
101+
expect(applyLeaf(op, key, value)).toEqual(expected);
102+
});
103+
89104
it("hashes with prehash and length prefix", () => {
90105
const op: ics23.ILeafOp = {
91106
hash: ics23.HashOp.SHA256,

js/src/ops.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,12 @@ function doLengthOp(lengthOp: ics23.LengthOp, data: Uint8Array): Uint8Array {
134134
throw new Error(`Length is ${data.length}, not 64 bytes`);
135135
}
136136
return data;
137+
case ics23.LengthOp.FIXED32_LITTLE:
138+
return new Uint8Array([...encodeFixed32LE(data.length), ...data]);
137139
// TODO
138140
// case LengthOp_VAR_RLP:
139141
// case LengthOp_FIXED32_BIG:
140142
// case LengthOp_FIXED64_BIG:
141-
// case LengthOp_FIXED32_LITTLE:
142143
// case LengthOp_FIXED64_LITTLE:
143144
}
144145
throw new Error(`Unsupported lengthop: ${lengthOp}`);
@@ -155,3 +156,15 @@ function encodeVarintProto(n: number): Uint8Array {
155156
enc = [...enc, l];
156157
return new Uint8Array(enc);
157158
}
159+
160+
function encodeFixed32LE(n: number): Uint8Array {
161+
const enc = new Uint8Array(4);
162+
let l = n;
163+
for (let i = enc.length; i > 0; i--) {
164+
/* tslint:disable */
165+
enc[Math.abs(i - enc.length)] = l % 256;
166+
/* tslint:enable */
167+
l = Math.floor(l / 256);
168+
}
169+
return enc;
170+
}

rust/src/ops.rs

+12
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ fn do_length(length: LengthOp, data: &[u8]) -> Result<Hash> {
5555
len.extend(data);
5656
return Ok(len);
5757
}
58+
LengthOp::Fixed32Little => {
59+
let mut len = (data.len() as u32).to_le_bytes().to_vec();
60+
len.extend(data);
61+
return Ok(len);
62+
}
5863
_ => bail!("Unsupported LengthOp {:?}", length),
5964
}
6065
// if we don't error above or return custom string, just return item untouched (common case)
@@ -125,6 +130,13 @@ mod tests {
125130
"proto prefix returned {}",
126131
hex::encode(&prefixed),
127132
);
133+
134+
let prefixed = do_length(LengthOp::Fixed32Little, b"food")?;
135+
ensure!(
136+
prefixed == hex::decode("04000000666f6f64")?,
137+
"proto prefix returned {}",
138+
hex::encode(&prefixed),
139+
);
128140
Ok(())
129141
}
130142

0 commit comments

Comments
 (0)