diff --git a/common/pseudorand.c b/common/pseudorand.c index c08e21849..79da275b4 100644 --- a/common/pseudorand.c +++ b/common/pseudorand.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -56,3 +57,31 @@ const struct siphash_seed *siphash_seed(void) return &siphashseed; } + + +void tal_arr_randomize_(void *arr, size_t elemsize) +{ + /* Easier arith. */ + char *carr = arr; + size_t n = tal_bytelen(arr) / elemsize; + + assert(tal_bytelen(arr) % elemsize == 0); + + /* From Wikipedia's Fischer-Yates shuffle article: + * + * for i from 0 to nāˆ’2 do + * j ā† random integer such that i ā‰¤ j < n + * exchange a[i] and a[j] + */ + if (n < 2) + return; + + for (size_t i = 0; i < n - 1; i++) { + size_t j = i + pseudorand(n - i); + char tmp[elemsize]; + + memcpy(tmp, carr + i * elemsize, elemsize); + memcpy(carr + i * elemsize, carr + j * elemsize, elemsize); + memcpy(carr + j * elemsize, tmp, elemsize); + } +} diff --git a/common/pseudorand.h b/common/pseudorand.h index 65272adda..a34220055 100644 --- a/common/pseudorand.h +++ b/common/pseudorand.h @@ -2,6 +2,7 @@ #define LIGHTNING_COMMON_PSEUDORAND_H #include "config.h" #include +#include /** * pseudorand - pseudo (guessable!) random number between 0 and max-1. @@ -24,4 +25,9 @@ double pseudorand_double(void); */ const struct siphash_seed *siphash_seed(void); +/* Shuffle a tal array of type type. */ +#define tal_arr_randomize(arr, type) \ + tal_arr_randomize_((arr), sizeof(type) + 0*sizeof(arr == (type *)NULL)) +void tal_arr_randomize_(void *arr, size_t elemsize); + #endif /* LIGHTNING_COMMON_PSEUDORAND_H */ diff --git a/common/test/run-tal_arr_randomize.c b/common/test/run-tal_arr_randomize.c new file mode 100644 index 000000000..b539a6d3c --- /dev/null +++ b/common/test/run-tal_arr_randomize.c @@ -0,0 +1,191 @@ +#include "config.h" +#include "../pseudorand.c" +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_div */ +struct amount_sat amount_sat_div(struct amount_sat sat UNNEEDED, u64 div UNNEEDED) +{ fprintf(stderr, "amount_sat_div called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_mul */ +bool amount_sat_mul(struct amount_sat *res UNNEEDED, struct amount_sat sat UNNEEDED, u64 mul UNNEEDED) +{ fprintf(stderr, "amount_sat_mul called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static bool any_equal(const char **arr, size_t n) +{ + for (size_t i = 0; i < n; i++) { + for (size_t j = i+1; j < n; j++) { + if (arr[i] == arr[j]) + return true; + } + } + return false; +} + +static bool one_of(const char **arr, size_t n, const char *word) +{ + for (size_t i = 0; i < n; i++) { + if (word == arr[i]) + return true; + } + return false; +} + +int main(int argc, char *argv[]) +{ + const char *words[] = { + "hello", + "world", + "this", + "is", + "a", + "pay_test", + "with", + "many", + "elements", + "so", + "the", + "chances", + "of", + "it", + "remaining", + "unshuffled", + "are", + "insignficant", + "and", + "we", + "expect", + "almost", + "every", + "word", + "to", + "be", + "in", + "some", + "different", + "place", + "after", + "shuffling." + }; + + common_setup(argv[0]); + + for (size_t i = 0; i < ARRAY_SIZE(words); i++) { + const char **arr = tal_arr(tmpctx, const char *, i); + memcpy(arr, words, tal_bytelen(arr)); + tal_arr_randomize(arr, const char *); + /* They should all point to distinct words */ + assert(!any_equal(arr, i)); + /* They should point to our expected words. */ + for (size_t j = 0; j < i; j++) + assert(one_of(words, i, arr[j])); + + /* It will actually shuffle! */ + if (i == ARRAY_SIZE(words) - 1) { + size_t num_same = 0; + for (size_t j = 0; j < i; j++) { + printf("%s ", arr[j]); + if (arr[j] == words[j]) + num_same++; + } + assert(num_same < i/2); + printf("\n%zu\n", num_same); + } + } + common_shutdown(); + + return 0; +}