Initial commit
This commit is contained in:
commit
51aa60bbc6
|
@ -0,0 +1,7 @@
|
|||
target
|
||||
.env
|
||||
.vscode
|
||||
bin
|
||||
config.json
|
||||
node_modules
|
||||
./package-lock.json
|
|
@ -0,0 +1,4 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"messaging",
|
||||
]
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2020 Serum Foundation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,3 @@
|
|||
This adds a statically-sized on-chain messaging buffer. This can be used to transmit arbitrary byte messages on-chain
|
||||
|
||||
STILL WIP
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
channel=${1:-v1.3.17}
|
||||
installDir="$(dirname "$0")"/bin
|
||||
cacheDir=~/.cache/solana-bpf-sdk/"$channel"
|
||||
|
||||
echo "Installing $channel BPF SDK into $installDir"
|
||||
|
||||
set -x
|
||||
|
||||
if [[ ! -r "$cacheDir"/bpf-sdk.tar.bz2 ]]; then
|
||||
mkdir -p "$cacheDir"
|
||||
curl -L --retry 5 --retry-delay 2 -o "$cacheDir"/bpf-sdk.tar.bz2 \
|
||||
https://solana-sdk.s3.amazonaws.com/"$channel"/bpf-sdk.tar.bz2
|
||||
fi
|
||||
|
||||
rm -rf "$installDir"
|
||||
mkdir -p "$installDir"
|
||||
(
|
||||
cd "$installDir"
|
||||
tar jxf "$cacheDir"/bpf-sdk.tar.bz2
|
||||
)
|
||||
cat "$installDir"/bpf-sdk/version.txt
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
set -x
|
||||
|
||||
# Cargo.lock can cause older spl-token bindings to be generated? Move it out of
|
||||
# the way...
|
||||
mv -f Cargo.lock Cargo.lock.org
|
||||
|
||||
cargo run --manifest-path=utils/cgen/Cargo.toml
|
||||
exitcode=$?
|
||||
|
||||
mv -f Cargo.lock.org Cargo.lock
|
||||
|
||||
exit $exitcode
|
|
@ -0,0 +1,212 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
CALLER_PWD=$PWD
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: do.sh <action> <project> <action specific arguments>
|
||||
Supported actions:
|
||||
build
|
||||
build-lib
|
||||
clean
|
||||
clippy
|
||||
doc
|
||||
dump
|
||||
fmt
|
||||
test
|
||||
update
|
||||
Supported projects:
|
||||
all
|
||||
any directory containing a Cargo.toml file
|
||||
EOF
|
||||
}
|
||||
|
||||
sdkDir=bin/bpf-sdk
|
||||
profile=bpfel-unknown-unknown/release
|
||||
|
||||
readCargoVariable() {
|
||||
declare variable="$1"
|
||||
declare Cargo_toml="$2"
|
||||
|
||||
while read -r name equals value _; do
|
||||
if [[ $name = "$variable" && $equals = = ]]; then
|
||||
echo "${value//\"/}"
|
||||
return
|
||||
fi
|
||||
done < <(cat "$Cargo_toml")
|
||||
echo "Unable to locate $variable in $Cargo_toml" 1>&2
|
||||
}
|
||||
|
||||
perform_action() {
|
||||
set -e
|
||||
# Use relative path if arg starts with "."
|
||||
if [[ $2 == .* ]]; then
|
||||
projectDir="$CALLER_PWD"/$2
|
||||
else
|
||||
projectDir="$PWD"/$2
|
||||
fi
|
||||
targetDir="$PWD"/target
|
||||
features=
|
||||
|
||||
crateName="$(readCargoVariable name "$projectDir/Cargo.toml")"
|
||||
so_path="$targetDir/$profile"
|
||||
so_name="${crateName//\-/_}"
|
||||
so_name_unstripped="${so_name}_unstripped"
|
||||
|
||||
if [[ -f "$projectDir"/Xargo.toml ]]; then
|
||||
features="--features=program"
|
||||
fi
|
||||
case "$1" in
|
||||
build)
|
||||
if [[ -f "$projectDir"/Xargo.toml ]]; then
|
||||
echo "build $crateName ($projectDir)"
|
||||
"$sdkDir"/rust/build.sh "$projectDir"
|
||||
cp "$so_path/${so_name}.so" "$so_path/${so_name_unstripped}.so"
|
||||
"$sdkDir"/dependencies/llvm-native/bin/llvm-objcopy --strip-all "$so_path/${so_name}.so" "$so_path/${so_name}.so"
|
||||
else
|
||||
echo "$projectDir does not contain a program, skipping"
|
||||
fi
|
||||
;;
|
||||
build-lib)
|
||||
(
|
||||
cd "$projectDir"
|
||||
echo "build-lib $crateName ($projectDir)"
|
||||
export RUSTFLAGS="${@:3}"
|
||||
cargo build
|
||||
)
|
||||
;;
|
||||
clean)
|
||||
"$sdkDir"/rust/clean.sh "$projectDir"
|
||||
;;
|
||||
clippy)
|
||||
(
|
||||
cd "$projectDir"
|
||||
echo "clippy $crateName ($projectDir)"
|
||||
cargo +nightly clippy $features ${@:3}
|
||||
)
|
||||
;;
|
||||
doc)
|
||||
(
|
||||
cd "$projectDir"
|
||||
echo "generating docs $crateName ($projectDir)"
|
||||
cargo doc ${@:3}
|
||||
)
|
||||
;;
|
||||
dump)
|
||||
# Dump depends on tools that are not installed by default and must be installed manually
|
||||
# - readelf
|
||||
# - rustfilt
|
||||
if [[ -f "$projectDir"/Xargo.toml ]]; then
|
||||
if ! which rustfilt > /dev/null; then
|
||||
echo "Error: rustfilt not found. It can be installed by running: cargo install rustfilt"
|
||||
exit 1
|
||||
fi
|
||||
if ! which readelf > /dev/null; then
|
||||
if [[ $(uname) = Darwin ]]; then
|
||||
echo "Error: readelf not found. It can be installed by running: brew install binutils"
|
||||
else
|
||||
echo "Error: readelf not found."
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$CALLER_PWD"
|
||||
"$0" build "$2"
|
||||
)
|
||||
|
||||
echo "dump $crateName ($projectDir)"
|
||||
|
||||
so="$so_path/${so_name}.so"
|
||||
|
||||
if [[ ! -r "$so" ]]; then
|
||||
echo "Error: No dump created, cannot read $so"
|
||||
exit 1
|
||||
fi
|
||||
dump="$so_path/${so_name}_dump"
|
||||
(
|
||||
set -x
|
||||
ls -la "$so" > "${dump}_mangled.txt"
|
||||
readelf -aW "$so" >>"${dump}_mangled.txt"
|
||||
"$sdkDir/dependencies/llvm-native/bin/llvm-objdump" \
|
||||
-print-imm-hex \
|
||||
--source \
|
||||
--disassemble \
|
||||
"$so" \
|
||||
>> "${dump}_mangled.txt"
|
||||
sed s/://g <"${dump}_mangled.txt" | rustfilt >"${dump}.txt"
|
||||
)
|
||||
if [[ -f "$dump.txt" ]]; then
|
||||
echo "Created $dump.txt"
|
||||
else
|
||||
echo "Error: No dump created"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "$projectDir does not contain a program, skipping"
|
||||
fi
|
||||
;;
|
||||
fmt)
|
||||
(
|
||||
cd "$projectDir"
|
||||
echo "formatting $projectDir"
|
||||
cargo fmt ${@:3}
|
||||
)
|
||||
;;
|
||||
help)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
test)
|
||||
(
|
||||
cd "$projectDir"
|
||||
echo "test $projectDir"
|
||||
cargo test $features ${@:3}
|
||||
)
|
||||
;;
|
||||
update)
|
||||
./bpf-sdk-install.sh
|
||||
./do.sh clean all
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown command"
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
set -e
|
||||
if [[ $1 == "update" ]]; then
|
||||
perform_action "$1"
|
||||
exit
|
||||
else
|
||||
if [[ "$#" -lt 2 ]]; then
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
if [[ ! -d "$sdkDir" ]]; then
|
||||
./do.sh update
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $2 == "all" ]]; then
|
||||
# Perform operation on all projects
|
||||
for project in */program*; do
|
||||
if [[ -f "$project"/Cargo.toml ]]; then
|
||||
perform_action "$1" "${project%/}" ${@:3}
|
||||
else
|
||||
continue
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Perform operation on requested project
|
||||
if [[ -d $2/program ]]; then
|
||||
perform_action "$1" "$2/program" "${@:3}"
|
||||
else
|
||||
perform_action "$1" "$2" "${@:3}"
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
# Note: This crate must be built using do.sh
|
||||
|
||||
[package]
|
||||
name = "spl-packet"
|
||||
version = "0.1.0"
|
||||
description = "Basic packet sending protocol"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
program = ["solana-sdk/program"]
|
||||
default = ["solana-sdk/default"]
|
||||
|
||||
[dependencies]
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
solana-sdk = { version = "1.3.17", default-features = false, optional = true }
|
||||
thiserror = "1.0"
|
||||
num_enum = "0.5.1"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,107 @@
|
|||
//! Super basic messaging buffer with limited history
|
||||
use crate::error::MessageError;
|
||||
|
||||
use solana_sdk::{program_error::ProgramError, pubkey::Pubkey};
|
||||
|
||||
const MESSAGE_LENGTH: usize = 256;
|
||||
const MESSAGE_HISTORY: usize = 128;
|
||||
|
||||
/**
|
||||
* Basic on-chain message struct - defines layout of the message
|
||||
*/
|
||||
#[repr(packed)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Message {
|
||||
/// Key of the writer of the transaction
|
||||
pub writer: Pubkey,
|
||||
/// Number of bytes contained in the message
|
||||
pub length: u8,
|
||||
|
||||
/// Actual instruction data
|
||||
pub bytes: [u8; MESSAGE_LENGTH],
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent the on-chain state of a message buffer
|
||||
*/
|
||||
pub struct MessageBuffer {
|
||||
/// Index of the first message in the buffer
|
||||
pub queue_head: u32,
|
||||
|
||||
/// Array containing messages
|
||||
pub messages: [Message; MESSAGE_HISTORY],
|
||||
}
|
||||
|
||||
impl MessageBuffer {
|
||||
/// Appends a message to the queue, possibly overwriting an old one
|
||||
#[inline]
|
||||
pub fn append(&mut self, message: &Message) {
|
||||
let next_head = self.queue_head + 1;
|
||||
let next_head = next_head % MESSAGE_HISTORY as u32;
|
||||
|
||||
self.messages[next_head as usize] = *message;
|
||||
self.queue_head = next_head;
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageBuffer {
|
||||
/// Unpacks a buffer of
|
||||
pub fn unpack(input: &mut [u8]) -> Result<&mut MessageBuffer, ProgramError> {
|
||||
if input.len() != std::mem::size_of::<MessageBuffer>() {
|
||||
Err(MessageError::MessageQueueAccountWrongSize)?;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
std::mem::align_of::<MessageBuffer>(),
|
||||
std::mem::align_of::<u8>()
|
||||
);
|
||||
|
||||
let buffer = unsafe { &mut *(input.as_mut_ptr() as *mut MessageBuffer) };
|
||||
|
||||
if buffer.queue_head >= MESSAGE_HISTORY as u32 {
|
||||
Err(MessageError::MessageQueueBad)?;
|
||||
}
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Unpacks a
|
||||
pub fn unpack(input: &[u8]) -> Result<Message, ProgramError> {
|
||||
if input.len() > std::mem::size_of::<Message>() {
|
||||
Err(MessageError::MessageTooLarge)?;
|
||||
}
|
||||
if input.len() < std::mem::size_of::<Message>() {
|
||||
Err(MessageError::MessageTooSmall)?;
|
||||
}
|
||||
|
||||
assert_eq!(input.len(), std::mem::size_of::<Message>());
|
||||
|
||||
let (key, rest) = input.split_at(std::mem::size_of::<Pubkey>());
|
||||
let (length, rest) = rest.split_first().unwrap();
|
||||
|
||||
let writer = Pubkey::new(key);
|
||||
let length = *length;
|
||||
let mut bytes = [0; MESSAGE_LENGTH];
|
||||
bytes.copy_from_slice(rest);
|
||||
|
||||
if length as usize > MESSAGE_LENGTH {
|
||||
Err(MessageError::MessageLengthTooLarge)?;
|
||||
}
|
||||
|
||||
if bytes[length as usize..].iter().any(|b| *b != 0) {
|
||||
Err(MessageError::MessageLengthTooSmall)?;
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
Err(MessageError::MessageEmpty)?;
|
||||
}
|
||||
|
||||
Ok(Message {
|
||||
writer,
|
||||
length,
|
||||
bytes,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//! Program entrypoint
|
||||
|
||||
#![cfg(feature = "program")]
|
||||
#![cfg(not(feature = "no-entrypoint"))]
|
||||
|
||||
use crate::buffer::{Message, MessageBuffer};
|
||||
use crate::error::MessageError;
|
||||
use solana_sdk::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(_: &Pubkey, accounts: &[AccountInfo], instruction: &[u8]) -> ProgramResult {
|
||||
if accounts.len() == 0 {
|
||||
Err(MessageError::NoAccountsPassed)?
|
||||
}
|
||||
if accounts.len() == 1 {
|
||||
Err(MessageError::QueueNotPassed)?
|
||||
}
|
||||
if accounts.len() > 2 {
|
||||
Err(MessageError::ExtraAccountsPassed)?
|
||||
}
|
||||
let signer = &accounts[0];
|
||||
let queue = &accounts[1];
|
||||
if !signer.is_signer {
|
||||
Err(MessageError::SenderDidNotSign)?;
|
||||
}
|
||||
let message = Message::unpack(instruction)?;
|
||||
let mut queue_ref = queue.try_borrow_mut_data()?;
|
||||
let message_buffer = MessageBuffer::unpack(&mut *queue_ref)?;
|
||||
|
||||
message_buffer.append(&message);
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//! Error types
|
||||
|
||||
use num_derive::FromPrimitive;
|
||||
use solana_sdk::{decode_error::DecodeError, program_error::ProgramError};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that may be returned by the Token program.
|
||||
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
|
||||
pub enum MessageError {
|
||||
/// Message instruction is too large
|
||||
#[error("Instruction size too large")]
|
||||
MessageTooLarge,
|
||||
/// Message instruction is too small
|
||||
#[error("Instruction size too small")]
|
||||
MessageTooSmall,
|
||||
/// Message size is greater than max
|
||||
#[error("Message length too large for instruction")]
|
||||
MessageLengthTooLarge,
|
||||
/// Message size is too small (nonzero data contained after length ends)
|
||||
#[error("Message length too small")]
|
||||
MessageLengthTooSmall,
|
||||
/// Message contains no data
|
||||
#[error("Message is empty")]
|
||||
MessageEmpty,
|
||||
/// Message queue buffer invalid size
|
||||
#[error("Message queue account is the wrong size")]
|
||||
MessageQueueAccountWrongSize,
|
||||
/// Message queue head invalid
|
||||
#[error("Message queue head is invalid")]
|
||||
MessageQueueBad,
|
||||
/// Signer and queue accounts not passed
|
||||
#[error("Invocation expects [signer, queue] accounts to get passed")]
|
||||
NoAccountsPassed,
|
||||
/// Queue account not passed
|
||||
#[error("Queue account not passed")]
|
||||
QueueNotPassed,
|
||||
/// Extra accounts passed
|
||||
#[error("Extra accounts passed")]
|
||||
ExtraAccountsPassed,
|
||||
/// Sender did not sign
|
||||
#[error("Sender must have signed the transaction")]
|
||||
SenderDidNotSign,
|
||||
}
|
||||
|
||||
impl From<MessageError> for ProgramError {
|
||||
fn from(e: MessageError) -> Self {
|
||||
ProgramError::Custom(e as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DecodeError<T> for MessageError {
|
||||
fn type_of() -> &'static str {
|
||||
"MessageError"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
//! A basic message buffer with limited history for a sending bytes around
|
||||
|
||||
pub mod buffer;
|
||||
pub mod entrypoint;
|
||||
pub mod error;
|
||||
|
||||
// Export current solana-sdk types for downstream users who may also be building with a different
|
||||
// solana-sdk version
|
||||
pub use solana_sdk;
|
||||
|
||||
solana_sdk::declare_id!("24CJEtjU3sjzNhzS3NarwcQwbZv68Zbgh1Kh86vQFX2f");
|
Loading…
Reference in New Issue