Skip to content

Commit

Permalink
Merge commit '13f988a70653463000090b3377c532ad7d09f516' as 'src/simpl…
Browse files Browse the repository at this point in the history
…icity'
  • Loading branch information
roconnor-blockstream committed Mar 1, 2023
2 parents d45c79e + 13f988a commit 6393d96
Show file tree
Hide file tree
Showing 89 changed files with 35,603 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/simplicity/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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

ifneq ($(strip $(SINGLE_THREADED)),)
# SINGLE_THREADED is non-empty
CPPFLAGS := $(CPPFLAGS) -DSINGLE_THREADED
endif

CFLAGS := -I include

ifeq ($(strip $(SINGLE_THREADED)),)
# SINGLE_THREADED is empty
LDFLAGS := -pthread
endif

# 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
34 changes: 34 additions & 0 deletions src/simplicity/ascii.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
_Static_assert(
0x20 == ' ' && 0x40 == '@' && 0x60 == '`' &&
0x21 == '!' && 0x41 == 'A' && 0x61 == 'a' &&
0x22 == '"' && 0x42 == 'B' && 0x62 == 'b' &&
0x23 == '#' && 0x43 == 'C' && 0x63 == 'c' &&
0x24 == '$' && 0x44 == 'D' && 0x64 == 'd' &&
0x25 == '%' && 0x45 == 'E' && 0x65 == 'e' &&
0x26 == '&' && 0x46 == 'F' && 0x66 == 'f' &&
0x27 == '\'' && 0x47 == 'G' && 0x67 == 'g' &&
0x28 == '(' && 0x48 == 'H' && 0x68 == 'h' &&
0x29 == ')' && 0x49 == 'I' && 0x69 == 'i' &&
0x2a == '*' && 0x4a == 'J' && 0x6a == 'j' &&
0x2b == '+' && 0x4b == 'K' && 0x6b == 'k' &&
0x2c == ',' && 0x4c == 'L' && 0x6c == 'l' &&
0x2d == '-' && 0x4d == 'M' && 0x6d == 'm' &&
0x2e == '.' && 0x4e == 'N' && 0x6e == 'n' &&
0x2f == '/' && 0x4f == 'O' && 0x6f == 'o' &&
0x30 == '0' && 0x50 == 'P' && 0x70 == 'p' &&
0x31 == '1' && 0x51 == 'Q' && 0x71 == 'q' &&
0x32 == '2' && 0x52 == 'R' && 0x72 == 'r' &&
0x33 == '3' && 0x53 == 'S' && 0x73 == 's' &&
0x34 == '4' && 0x54 == 'T' && 0x74 == 't' &&
0x35 == '5' && 0x55 == 'U' && 0x75 == 'u' &&
0x36 == '6' && 0x56 == 'V' && 0x76 == 'v' &&
0x37 == '7' && 0x57 == 'W' && 0x77 == 'w' &&
0x38 == '8' && 0x58 == 'X' && 0x78 == 'x' &&
0x39 == '9' && 0x59 == 'Y' && 0x79 == 'y' &&
0x3a == ':' && 0x5a == 'Z' && 0x7a == 'z' &&
0x3b == ';' && 0x5b == '[' && 0x7b == '{' &&
0x3c == '<' && 0x5c == '\\' && 0x7c == '|' &&
0x3d == '=' && 0x5d == ']' && 0x7d == '}' &&
0x3e == '>' && 0x5e == '^' && 0x7e == '~' &&
0x3f == '?' && 0x5f == '_'
, "ASCII character set required");
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
Loading

0 comments on commit 6393d96

Please sign in to comment.