Skip to content

Commit

Permalink
Merge commit 'a60e1366a63bd05f0fa1199d6f15b21acc32f90e' as 'src/simpl…
Browse files Browse the repository at this point in the history
…icity'
  • Loading branch information
roconnor-blockstream committed Mar 10, 2023
2 parents eb83661 + a60e136 commit 0819459
Show file tree
Hide file tree
Showing 85 changed files with 35,454 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/simplicity/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
OBJS := bitstream.o dag.o deserialize.o eval.o frame.o jets.o jets-secp256k1.o rsort.o sha256.o type.o typeInference.o primitive/elements/env.o primitive/elements/exec.o primitive/elements/ops.o primitive/elements/jets.o primitive/elements/primitive.o
TEST_OBJS := test.o hashBlock.o schnorr0.o schnorr6.o primitive/elements/checkSigHashAllTx1.o

# From https://fastcompression.blogspot.com/2019/01/compiler-warnings.html
CWARN := -Werror -Wall -Wextra -Wcast-qual -Wcast-align -Wstrict-aliasing -Wpointer-arith -Winit-self -Wshadow -Wswitch-enum -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wfloat-equal -Wundef -Wconversion

ifneq ($(doCheck), 1)
CPPFLAGS := $(CPPFLAGS) -DNDEBUG
endif

CFLAGS := -I include

# libsecp256k1 is full of conversion warnings, so we compile jets-secp256k1.c separately.
jets-secp256k1.o: jets-secp256k1.c
$(CC) -c $(CFLAGS) $(CWARN) -Wno-conversion $(CPPFLAGS) -o $@ $<

primitive/elements/jets.o: primitive/elements/jets.c
$(CC) -c $(CFLAGS) $(CWARN) -Wno-switch-enum -Wswitch $(CPPFLAGS) -o $@ $<

%.o: %.c
$(CC) -c $(CFLAGS) $(CWARN) $(CPPFLAGS) -o $@ $<

libElementsSimplicity.a: $(OBJS)
ar rcs $@ $^

test: $(TEST_OBJS) libElementsSimplicity.a
$(CC) $^ -o $@ $(LDFLAGS)

install: libElementsSimplicity.a
mkdir -p $(out)/lib
cp $^ $(out)/lib/
cp -R include $(out)/include

check: test
./test

clean:
-rm -f test libElementsSimplicity.a $(TEST_OBJS) $(OBJS)

.PHONY: install check clean
230 changes: 230 additions & 0 deletions src/simplicity/bitstream.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
#include "bitstream.h"

#include <assert.h>
#include <limits.h>
#include <stdlib.h>

/* Closes a bitstream by consuming all remaining bits.
* Returns false if CHAR_BIT or more bits remain in the stream or if any remaining bits are non-zero.
*
* Precondition: NULL != stream
*/
bool closeBitstream(bitstream* stream) {
if (1 < stream->len) return false; /* If there is more than one byte remaining. */
if (1 == stream->len && /* If there is one byte remaining */
(0 == stream->offset || /* and either no bits have been consumed */
0 != (*stream->arr & (UCHAR_MAX >> stream->offset)) /* or any of the unconsumed bits are non-zero */
)) return false;
/* Otherwise there are either 0 bits remaining or there are between 1 and CHAR_BITS-1 bits remaining and they are all zero. */
*stream = (bitstream){0};
return true;
}

/* Fetches up to 31 bits from 'stream' as the 'n' least significant bits of return value.
* The 'n' bits are set from the MSB to the LSB.
* Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if not enough bits are available.
*
* Precondition: 0 <= n < 32
* NULL != stream
*/
int32_t readNBits(int n, bitstream* stream) {
assert(0 <= n && n < 32);

uint32_t result = 0;
while (CHAR_BIT <= stream->offset + n) {
if (!stream->len) return SIMPLICITY_ERR_BITSTREAM_EOF;
n -= CHAR_BIT - stream->offset;
result |= (uint32_t)(*stream->arr & (UCHAR_MAX >> stream->offset)) << n;
stream->arr++; stream->len--; stream->offset = 0;
}
/* stream->offset + n < CHAR_BIT */
if (n) {
if (!stream->len) return SIMPLICITY_ERR_BITSTREAM_EOF;
stream->offset += (unsigned char)n;
result |= (*stream->arr >> (CHAR_BIT - stream->offset)) & ((UCHAR_MAX >> (CHAR_BIT - n)));
}
return (int32_t)result;
}
/* Decode an encoded bitstring up to length 1.
* If successful returns the length of the bitstring and 'result' contains the decoded bits.
* The decoded bitstring is stored in the LSBs of 'result', with the LSB being the last bit decoded.
* Any remaining bits in 'result' are reset to 0.
* If the decoded bitstring would be too long 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned ('result' may be modified).
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned ('result' may be modified).
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned ('result' may be modified).
*
* Precondition: NULL != result
* NULL != stream
*/
static int32_t decodeUpto1Bit(int32_t* result, bitstream* stream) {
*result = read1Bit(stream);
if (*result <= 0) return *result;

*result = read1Bit(stream);
if (*result < 0) return *result;
if (0 != *result) return SIMPLICITY_ERR_DATA_OUT_OF_RANGE;

*result = read1Bit(stream);
if (*result < 0) return *result;
return 1;
}

/* Decode an encoded number between 1 and 3 inclusive.
* When successful returns the decoded result.
* If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned.
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned.
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned.
*
* Precondition: NULL != stream
*/
static int32_t decodeUpto3(bitstream* stream) {
int32_t result;
int32_t len = decodeUpto1Bit(&result, stream);
if (len < 0) return len;
result |= 1 << len;
return result;
}

/* Decode an encoded bitstring up to length 3.
* If successful returns the length of the bitstring and 'result' contains the decoded bits.
* The decoded bitstring is stored in the LSBs of 'result', with the LSB being the last bit decoded.
* Any remaining bits in 'result' are reset to 0.
* If the decoded bitstring would be too long 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned ('result' may be modified).
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned ('result' may be modified).
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned ('result' may be modified).
*
* Precondition: NULL != result
* NULL != stream
*/
static int32_t decodeUpto3Bits(int32_t* result, bitstream* stream) {
int32_t bit = read1Bit(stream);
if (bit < 0) return bit;

*result = 0;
if (0 == bit) {
return 0;
} else {
int32_t n = decodeUpto3(stream);
if (0 <= n) {
*result = readNBits(n, stream);
if (*result < 0) return *result;
}
return n;
}
}

/* Decode an encoded number between 1 and 15 inclusive.
* When successful returns the decoded result.
* If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned.
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned.
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned.
*
* Precondition: NULL != stream
*/
static int32_t decodeUpto15(bitstream* stream) {
int32_t result;
int32_t len = decodeUpto3Bits(&result, stream);
if (len < 0) return len;
result |= 1 << len;
return result;
}

/* Decode an encoded bitstring up to length 15.
* If successful returns the length of the bitstring and 'result' contains the decoded bits.
* The decoded bitstring is stored in the LSBs of 'result', with the LSB being the last bit decoded.
* Any remaining bits in 'result' are reset to 0.
* If the decoded bitstring would be too long 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned ('result' may be modified).
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned ('result' may be modified).
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned ('result' may be modified).
*
* Precondition: NULL != result
* NULL != stream
*/
static int32_t decodeUpto15Bits(int32_t* result, bitstream* stream) {
int32_t bit = read1Bit(stream);
if (bit < 0) return bit;

*result = 0;
if (0 == bit) {
return 0;
} else {
int32_t n = decodeUpto15(stream);
if (0 <= n) {
*result = readNBits(n, stream);
if (*result < 0) return *result;
}
return n;
}
}

/* Decode an encoded number between 1 and 65535 inclusive.
* When successful returns the decoded result.
* If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned.
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned.
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned.
*
* Precondition: NULL != stream
*/
static int32_t decodeUpto65535(bitstream* stream) {
int32_t result;
int32_t len = decodeUpto15Bits(&result, stream);
if (len < 0) return len;
result |= 1 << len;
return result;
}

/* Decode an encoded number between 1 and 2^31 - 1 inclusive.
* When successful returns the decoded result.
* If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned.
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned.
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned.
*
* Precondition: NULL != stream
*/
int32_t decodeUptoMaxInt(bitstream* stream) {
int32_t bit = read1Bit(stream);
if (bit < 0) return bit;
if (0 == bit) {
return 1;
} else {
int32_t n = decodeUpto65535(stream);
if (n < 0) return n;
if (30 < n) return SIMPLICITY_ERR_DATA_OUT_OF_RANGE;
{
int32_t result = readNBits(n, stream);
if (result < 0) return result;
return ((1 << n) | result);
}
}
}

/* Fills a 'bitstring' containing 'n' bits from 'stream'.
* Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if not enough bits are available.
* If successful, '*result' is set to a bitstring with 'n' bits read from 'stream' and 0 is returned.
*
* If an error is returned '*result' might be modified.
*
* Precondition: NULL != result
* n <= 2^31
* NULL != stream
*/
int32_t readBitstring(bitstring* result, size_t n, bitstream* stream) {
static_assert(0x8000u + 2*(CHAR_BIT - 1) <= SIZE_MAX, "size_t needs to be at least 32-bits");
assert(n <= 0x8000u);
size_t total_offset = n + stream->offset;
/* |= stream->len * CHAR_BIT < total_offset iff stream->len < (total_offset + (CHAR_BIT - 1)) / CHAR_BIT */
if (stream->len < (total_offset + (CHAR_BIT - 1)) / CHAR_BIT) return SIMPLICITY_ERR_BITSTREAM_EOF;
/* total_offset <= stream->len * CHAR_BIT */
*result = (bitstring)
{ .arr = stream->arr
, .offset = stream->offset
, .len = n
};
{
size_t delta = total_offset / CHAR_BIT;
stream->arr += delta; stream->len -= delta;
stream->offset = total_offset % CHAR_BIT;
/* Note that if 0 == stream->len then 0 == stream->offset. */
}
return 0;
}
78 changes: 78 additions & 0 deletions src/simplicity/bitstream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* This module provides functions for initializing and reading from a stream of bits from a 'FILE'. */
#ifndef SIMPLICITY_BITSTREAM_H
#define SIMPLICITY_BITSTREAM_H

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "bitstring.h"
#include "errorCodes.h"

/* :TODO: consider adding an 'invalid' state that can be set when parsing has failed and should not be resumed. */
/* Datatype representing a bit stream.
* Bits are streamed from MSB to LSB.
*
* Invariant: unsigned char arr[len]
* 0 <= offset < CHAR_BIT
* 0 == len implies 0 == offset
*/
typedef struct bitstream {
const unsigned char *arr; /* Underlying byte array */
size_t len; /* Length of arr (in bytes) */
unsigned char offset; /* Number of bits parsed from the beginning of arr */
} bitstream;

/* Initialize a bit stream, 'stream', from a given byte array.
* Precondition: unsigned char arr[len];
*/
static inline bitstream initializeBitstream(const unsigned char* arr, size_t len) {
return (bitstream){ .arr = arr, .len = len };
}

/* Closes a bitstream by consuming all remaining bits.
* Returns false if CHAR_BIT or more bits remain in the stream or if any remaining bits are non-zero.
*
* Precondition: NULL != stream
*/
bool closeBitstream(bitstream* stream);

/* Fetches up to 31 bits from 'stream' as the 'n' least significant bits of return value.
* The 'n' bits are set from the MSB to the LSB.
* Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if not enough bits are available.
*
* Precondition: 0 <= n < 32
* NULL != stream
*/
int32_t readNBits(int n, bitstream* stream);

/* Returns one bit from 'stream', 0 or 1.
* Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if no bits are available.
*
* Precondition: NULL != stream
*/
static inline int32_t read1Bit(bitstream* stream) {
return readNBits(1, stream);
}

/* Decode an encoded number between 1 and 2^31 - 1 inclusive.
* When successful returns the decoded result.
* If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned.
* If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned.
* If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned.
*
* Precondition: NULL != stream
*/
int32_t decodeUptoMaxInt(bitstream* stream);

/* Fills a 'bitstring' containing 'n' bits from 'stream'.
* Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if not enough bits are available.
* If successful, '*result' is set to a bitstring with 'n' bits read from 'stream' and 0 is returned.
*
* If an error is returned '*result' might be modified.
*
* Precondition: NULL != result
* n <= 2^31
* NULL != stream
*/
int32_t readBitstring(bitstring* result, size_t n, bitstream* stream);
#endif
35 changes: 35 additions & 0 deletions src/simplicity/bitstring.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* This modules defines a structure representing bit strings. */
#ifndef SIMPLICITY_BITSTRING_H
#define SIMPLICITY_BITSTRING_H

#include <assert.h>
#include <limits.h>
#include <stdbool.h>

/* Represents a bitstring of length 'len' bits using an array of unsigned char.
* The bit at index 'n', where 0 <= 'n' < 'len', is located at bit '1 << (CHAR_BIT - 1 - (offset + n) % CHAR_BIT)' of
* array element 'arr[(offset + n) / CHAR_BIT]'.
* Other bits in the array may be any value.
*
* Invariant: len <= 2^31
* offset + length <= SIZE_MAX
* 0 < len implies unsigned char arr[(offset + len - 1) / CHAR_BIT + 1];
*/
typedef struct bitstring {
const unsigned char* arr;
size_t len;
size_t offset;
} bitstring;

/* Return the nth bit from a bitstring.
*
* Precondition: NULL != s
* n < s->len;
*/
static inline bool getBit(const bitstring *s, size_t n) {
size_t total_offset = s->offset + n;
assert(n < s->len);
return 1 & (s->arr[total_offset / CHAR_BIT] >> (CHAR_BIT - 1 - (total_offset % CHAR_BIT)));
}

#endif
Loading

0 comments on commit 0819459

Please sign in to comment.