feat: update blockchain image and contracts addresses are in labels (#214)

This commit is contained in:
Adam Uhlíř 2022-11-04 03:42:37 -07:00 committed by GitHub
parent 0cee4ae939
commit a7eb6c6d05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 3634 additions and 8247 deletions

View File

@ -84,6 +84,35 @@ There are some ways you can make this module better:
You can run the CLI while developing using `npm start -- <command> ...`.
### Local images
If you want to locally build the Bee Factory images, then edit the `./generator/scripts/.env` file with appropriate configuration
and then run the `./generator/scripts/build-environment.sh` script. This will build the images with the `ethersphere/bee-factory-*` tags.
Then simply run the `npm start -- <command>` with the appropriate version of Bee you have build.
The CLI should pickup local images prior trying to pull it from Docker Hub, so it will use your build ones.
### Updating Blockchain images
If Bee updated its smart contracts suite, then smart contracts needs to be updated in Bee Factory as well otherwise the spawned Bees might
have unpredictable behavior.
First of all you have to preprocess the smart contract's bytecodes. In Solidity the constructor parameters are passed to the contract by appending the parameters
ABI-encoded to the end of the bytecode (for more see for example [here](https://ethereum.stackexchange.com/questions/58866/how-does-a-contracts-constructor-work-and-load-input-values)).
So you have to make sure that the bytecodes that you update are stripped of these constructor parameters.
This is because these parameters are then later on appended during smart contract deployment.
If only internal smart contract's logic has changed and there is no change to the smart contract's constructor, then you have to only
update the bytecodes. You do it by pasting the bytecodes to appropriate files in `./generator/contracts/` folder.
If there are new smart contracts or their constructor's have changed then you have to also modify the deploy script which is present in
`./generator/migrations/1_initial.js` file. **Don't remove the logging that starts with `::CONTRACT:`!** This serves to extract the deployed
contracts addresses and apply them to the Docker image's label.
If you are adding new smart contract than also add new `::CONTRACT:` log line that has format of `::CONTRACT:<bee-option-name>:<smart-contract-address>`,
where the `bee-option-name` stands for the option name that customize the contract's address in Bee.
Last step is to bump the `BLOCKCHAIN_VERSION` version in `./generator/scripts/.env` file.
## Maintainers
- [auhau](https://github.com/auhau)

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
0x608060405234801561001057600080fd5b506040516104b83803806104b883398101604081905261002f9161007e565b600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001919091556002556100a1565b60008060408385031215610090578182fd5b505080516020909101519092909150565b610408806100b06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806398d5fdca1161005b57806398d5fdca146100dd578063a035b1fe146100f8578063dd4899321461010f578063f2fde38b1461011857600080fd5b8063710f2dd11461008d578063715018a6146100a25780638d6cc56d146100aa5780638da5cb5b146100bd575b600080fd5b6100a061009b366004610385565b61012b565b005b6100a061019a565b6100a06100b8366004610385565b61020e565b6000546040516001600160a01b0390911681526020015b60405180910390f35b600154600254604080519283526020830191909152016100d4565b61010160015481565b6040519081526020016100d4565b61010160025481565b6100a0610126366004610357565b61026d565b6000546001600160a01b0316331461015e5760405162461bcd60e51b81526004016101559061039d565b60405180910390fd5b60028190556040518181527f0f45948e42a1e34f851df8452200e744563dae2029a99b8178ff6530458bd3df906020015b60405180910390a150565b6000546001600160a01b031633146101c45760405162461bcd60e51b81526004016101559061039d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031633146102385760405162461bcd60e51b81526004016101559061039d565b60018190556040518181527fae46785019700e30375a5d7b4f91e32f8060ef085111f896ebf889450aa2ab5a9060200161018f565b6000546001600160a01b031633146102975760405162461bcd60e51b81526004016101559061039d565b6001600160a01b0381166102fc5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610155565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b600060208284031215610368578081fd5b81356001600160a01b038116811461037e578182fd5b9392505050565b600060208284031215610396578081fd5b5035919050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260408201526060019056fea264697066735822122069737c178b79be7973d71a433150ff2571d211a983a7f5a5ba0f7dba3d57fd7664736f6c63430008040033
608060405234801561001057600080fd5b506040516104b83803806104b883398101604081905261002f9161007e565b600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001919091556002556100a1565b60008060408385031215610090578182fd5b505080516020909101519092909150565b610408806100b06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806398d5fdca1161005b57806398d5fdca146100dd578063a035b1fe146100f8578063dd4899321461010f578063f2fde38b1461011857600080fd5b8063710f2dd11461008d578063715018a6146100a25780638d6cc56d146100aa5780638da5cb5b146100bd575b600080fd5b6100a061009b366004610385565b61012b565b005b6100a061019a565b6100a06100b8366004610385565b61020e565b6000546040516001600160a01b0390911681526020015b60405180910390f35b600154600254604080519283526020830191909152016100d4565b61010160015481565b6040519081526020016100d4565b61010160025481565b6100a0610126366004610357565b61026d565b6000546001600160a01b0316331461015e5760405162461bcd60e51b81526004016101559061039d565b60405180910390fd5b60028190556040518181527f0f45948e42a1e34f851df8452200e744563dae2029a99b8178ff6530458bd3df906020015b60405180910390a150565b6000546001600160a01b031633146101c45760405162461bcd60e51b81526004016101559061039d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031633146102385760405162461bcd60e51b81526004016101559061039d565b60018190556040518181527fae46785019700e30375a5d7b4f91e32f8060ef085111f896ebf889450aa2ab5a9060200161018f565b6000546001600160a01b031633146102975760405162461bcd60e51b81526004016101559061039d565b6001600160a01b0381166102fc5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610155565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b600060208284031215610368578081fd5b81356001600160a01b038116811461037e578182fd5b9392505050565b600060208284031215610396578081fd5b5035919050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260408201526060019056fea264697066735822122069737c178b79be7973d71a433150ff2571d211a983a7f5a5ba0f7dba3d57fd7664736f6c63430008040033

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,9 @@
const ERC20PresetMinterPauser = artifacts.require("ERC20PresetMinterPauser");
const ERC20PresetMinterPauser = artifacts.require('ERC20PresetMinterPauser')
const FS = require('fs')
const Path = require('path')
const NETWORK_ID = 4020
function prefixedAddressParamToByteCode(address) {
// the first 2 chars removal removes 0x prefix
return address.substring(2).toLowerCase().padStart(64, '0')
@ -13,7 +15,7 @@ function intToByteCode(intParam) {
function getSimpleSwapFactoryBin(tokenAddress) {
const binPath = Path.join(__dirname, '..', 'contracts', 'SimpleSwapFactory.bytecode')
const bin = FS.readFileSync(binPath, 'utf8').toString()
const bin = FS.readFileSync(binPath, 'utf8').toString().trim()
tokenAddress = prefixedAddressParamToByteCode(tokenAddress)
//add tokenaddress for param to the end of the bytecode
return bin + tokenAddress
@ -21,7 +23,7 @@ function getSimpleSwapFactoryBin(tokenAddress) {
function getPostageStampBin(tokenAddress) {
const binPath = Path.join(__dirname, '..', 'contracts', 'PostageStamp.bytecode')
const bin = FS.readFileSync(binPath, 'utf8').toString()
const bin = FS.readFileSync(binPath, 'utf8').toString().trim()
tokenAddress = prefixedAddressParamToByteCode(tokenAddress)
//add tokenaddress for param to the end of the bytecode
return bin + tokenAddress
@ -29,55 +31,85 @@ function getPostageStampBin(tokenAddress) {
function getPriceOracleBin(price, chequeValueDeduction) {
const binPath = Path.join(__dirname, '..', 'contracts', 'PriceOracle.bytecode')
const bin = FS.readFileSync(binPath, 'utf8').toString()
const bin = FS.readFileSync(binPath, 'utf8').toString().trim()
const priceAbi = intToByteCode(price)
const chequeValueAbi = intToByteCode(chequeValueDeduction)
//add tokenaddress for param to the end of the bytecode
return bin + priceAbi + chequeValueAbi
}
function getStakeRegistryBin(tokenAddress) {
const binPath = Path.join(__dirname, '..', 'contracts', 'StakeRegistry.bytecode')
const bin = FS.readFileSync(binPath, 'utf8').toString().trim()
tokenAddress = prefixedAddressParamToByteCode(tokenAddress)
const networkIdAbi = intToByteCode(NETWORK_ID)
//add tokenaddress and encoded network ID for param to the end of the bytecode
return bin + tokenAddress + networkIdAbi
}
function getRedistributionBin(stakingAddress, postageContractAddress) {
const binPath = Path.join(__dirname, '..', 'contracts', 'Redistribution.bytecode')
const bin = FS.readFileSync(binPath, 'utf8').toString().trim()
stakingAddress = prefixedAddressParamToByteCode(stakingAddress)
postageContractAddress = prefixedAddressParamToByteCode(postageContractAddress)
//add staking address and postage address for param to the end of the bytecode
return bin + stakingAddress + postageContractAddress
}
/** Returns back contract hash */
async function createContract(contractName, data, creatorAccount) {
async function createContract(contractName, data, creatorAccount, configName) {
const transaction = await web3.eth.sendTransaction({
data: data,
gasLimit: 6721975,
gasPrice: web3.utils.toWei('10', 'gwei'),
from: creatorAccount
from: creatorAccount,
})
if(!transaction.status) {
if (!transaction.status) {
console.error(`${contractName} contract creation Error`, error)
throw new Error(`Error happened at creating ${contractName} contract creation`)
}
console.log(`${contractName} contract creation was successful!\n`
+ `\tTransaction ID: ${transaction.transactionHash}\n`
+ `\tContract ID: ${transaction.contractAddress}`)
console.log(
`${contractName} contract creation was successful!\n` +
`\tTransaction ID: ${transaction.transactionHash}\n` +
`\tContract ID: ${transaction.contractAddress}`,
)
console.log(`::CONTRACT:${configName}:${transaction.contractAddress}\n`)
return transaction.contractAddress
}
async function createSimpleSwapFactoryContract(erc20ContractAddress, creatorAccount) {
return createContract('SimpleSwapFactory', getSimpleSwapFactoryBin(erc20ContractAddress), creatorAccount)
}
async function createPostageStampContract(erc20ContractAddress, creatorAccount) {
return createContract('PostageStamp', getPostageStampBin(erc20ContractAddress), creatorAccount)
}
/**
*
* @param {number} price current price in PLUR per accounting unit
* @param {number} chequeValueDeduction value deducted from first received cheque from a peer in PLUR
* @param {string} creatorAccount
*/
async function createPriceOracleContract(price, chequeValueDeduction, creatorAccount) {
return createContract('PriceOracle', getPriceOracleBin(price, chequeValueDeduction), creatorAccount)
}
module.exports = function (deployer, network, accounts) {
deployer.deploy(ERC20PresetMinterPauser, "Swarm Token", "BZZ").then(async () => {
await createSimpleSwapFactoryContract(ERC20PresetMinterPauser.address, accounts[0])
await createPostageStampContract(ERC20PresetMinterPauser.address, accounts[0])
await createPriceOracleContract(100000, 1, accounts[0])
});
};
const creatorAccount = accounts[0]
deployer.deploy(ERC20PresetMinterPauser, 'Swarm Token', 'BZZ').then(async () => {
await createContract('PriceOracle', getPriceOracleBin(100000, 100), creatorAccount, 'price-oracle-address')
await createContract(
'SimpleSwapFactory',
getSimpleSwapFactoryBin(ERC20PresetMinterPauser.address),
creatorAccount,
'swap-factory-address',
)
const postageStampAddress = await createContract(
'PostageStamp',
getPostageStampBin(ERC20PresetMinterPauser.address),
creatorAccount,
'postage-stamp-address',
)
const stakeRegistryAddress = await createContract(
'StakeRegistry',
getStakeRegistryBin(ERC20PresetMinterPauser.address, accounts[0]),
creatorAccount,
'staking-address',
)
await createContract(
'Redistribution',
getRedistributionBin(stakeRegistryAddress, postageStampAddress),
creatorAccount,
'redistribution-address',
)
})
}

11668
generator/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
"dependencies": {
"@ethersphere/bee-js": "^3.3.4",
"@openzeppelin/contracts": "^3.1.0",
"truffle": "^5.5.13"
"truffle": "^5.6.3"
},
"keywords": [
"swarm",

View File

@ -1,5 +1,5 @@
BEE_VERSION="1.5.1"
BLOCKCHAIN_VERSION="1.2.0"
BEE_VERSION="1.9.0"
BLOCKCHAIN_VERSION="1.3.0"
BEE_ENV_PREFIX="bee-factory"
BEE_IMAGE_PREFIX="ethersphere"
COMMIT_VERSION_TAG="false"

View File

@ -28,7 +28,7 @@ dockerbuild() {
IMAGE_NAME=$(basename "$1")
IMAGE_NAME="$4/$IMAGE_NAME"
docker build "$1" --no-cache -f "$2" -t "$IMAGE_NAME:$3" $PLATFORM_FLAG --label "org.ethswarm.beefactory.blockchain-version=$BLOCKCHAIN_VERSION"
docker build "$1" --no-cache -f "$2" -t "$IMAGE_NAME:$3" $PLATFORM_FLAG --label "org.ethswarm.beefactory.blockchain-version=$BLOCKCHAIN_VERSION" $5
}
MY_PATH=$(dirname "$0")
@ -71,7 +71,7 @@ echo "Build Dockerfiles"
for BEE_DIR in $BEE_DIRS
do
echo "Build Bee version $BEE_VERSION on $BEE_DIR"
dockerbuild "$BEE_DIR" "$MY_PATH/bee-data-dirs/Dockerfile" "$BEE_VERSION" "$BEE_IMAGE_PREFIX"
dockerbuild "$BEE_DIR" "$MY_PATH/bee-data-dirs/Dockerfile" "$BEE_VERSION" "$BEE_IMAGE_PREFIX" "$1"
done
echo "Docker image builds were successful!"

View File

@ -44,6 +44,16 @@ build_bee() {
"$MY_PATH/utils/build-image-tag.sh" set "$BEE_VERSION"
}
deploy_contracts_and_return_labels() {
LABELS=""
while IFS=$'\n' read -r contract; do
LABELS+=" --label org.ethswarm.beefactory.contracts.$(echo $contract | cut -d ":" -f 4)=$(echo $contract | cut -d ":" -f 5)"
done < <(npm run migrate:contracts | grep ^::CONTRACT)
echo "$LABELS"
}
MY_PATH=$(dirname "$0")
MY_PATH=$( cd "$MY_PATH" && pwd )
COMMIT_HASH=HEAD
@ -106,7 +116,7 @@ if $BUILD_BASE_BEE ; then
fi
"$MY_PATH/network.sh"
"$MY_PATH/blockchain.sh"
npm run migrate:contracts
DOCKER_CONTRACT_LABELS=$(deploy_contracts_and_return_labels)
npm run supply
chmod -R 777 "$MY_PATH/bee-data-dirs/"
@ -138,5 +148,5 @@ if $GEN_TRAFFIC ; then
export BLOCKCHAIN_VERSION+="-for-$BEE_VERSION"
fi
"$MY_PATH/bee-docker-build.sh"
"$MY_PATH/bee-docker-build.sh" "$DOCKER_CONTRACT_LABELS"
"$MY_PATH/blockchain-docker-build.sh"

View File

@ -12,11 +12,7 @@ const NETWORK_NAME_SUFFIX = '-network'
export const WORKER_COUNT = 4
export const BLOCKCHAIN_VERSION_LABEL_KEY = 'org.ethswarm.beefactory.blockchain-version'
// TODO: This should be possible to override with for example ENV variable in case somebody is rocking custom images
const SWAP_FACTORY_ADDRESS = '0x5b1869D9A4C187F2EAa108f3062412ecf0526b24'
const POSTAGE_STAMP_ADDRESS = '0xCfEB869F69431e42cdB54A4F4f105C19C080A601'
const PRICE_ORACLE_ADDRESS = '0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B'
export const CONTRACT_LABEL_KEY_PREFIX = 'org.ethswarm.beefactory.contracts.'
export interface RunOptions {
fresh: boolean
@ -136,6 +132,7 @@ export class Docker {
public async startQueenNode(beeVersion: string, options: RunOptions): Promise<void> {
if (options.fresh) await this.removeContainer(this.queenName)
const contractAddresses = await this.getContractAddresses(this.queenImage(beeVersion))
const container = await this.findOrCreateContainer(this.queenName, {
Image: this.queenImage(beeVersion),
@ -147,7 +144,7 @@ export class Docker {
},
Tty: true,
Cmd: ['start'],
Env: this.createBeeEnvParameters(),
Env: this.createBeeEnvParameters(contractAddresses),
AttachStderr: false,
AttachStdout: false,
HostConfig: {
@ -179,6 +176,7 @@ export class Docker {
options: RunOptions,
): Promise<void> {
if (options.fresh) await this.removeContainer(this.workerName(workerNumber))
const contractAddresses = await this.getContractAddresses(this.workerImage(beeVersion, workerNumber))
const container = await this.findOrCreateContainer(this.workerName(workerNumber), {
Image: this.workerImage(beeVersion, workerNumber),
@ -189,7 +187,7 @@ export class Docker {
'1635/tcp': {},
},
Cmd: ['start'],
Env: this.createBeeEnvParameters(queenAddress),
Env: this.createBeeEnvParameters(contractAddresses, queenAddress),
AttachStderr: false,
AttachStdout: false,
HostConfig: {
@ -266,8 +264,7 @@ export class Docker {
public async getBlockchainVersionFromQueenMetadata(beeVersion: string): Promise<string> {
// Lets pull the Queen's image if it is not present
const pullStream = await this.docker.pull(this.queenImage(beeVersion))
await new Promise(res => this.docker.modem.followProgress(pullStream, res))
await this.pullImageIfNotFound(this.queenImage(beeVersion))
const queenMetadata = await this.docker.getImage(this.queenImage(beeVersion)).inspect()
@ -331,8 +328,7 @@ export class Docker {
}
this.console.info(`Image ${createOptions.Image} not found. Pulling it.`)
const pullStream = await this.docker.pull(createOptions.Image!)
await new Promise(res => this.docker.modem.followProgress(pullStream, res))
await this.pullImageIfNotFound(createOptions.Image!)
return await this.docker.createContainer(createOptions)
}
@ -387,7 +383,7 @@ export class Docker {
}
}
private createBeeEnvParameters(bootnode?: string): string[] {
private createBeeEnvParameters(contractAddresses: Record<string, string>, bootnode?: string): string[] {
const options: Record<string, string> = {
'warmup-time': '0',
'debug-api-enable': 'true',
@ -395,14 +391,12 @@ export class Docker {
'swap-enable': 'true',
mainnet: 'false',
'swap-endpoint': `http://${this.blockchainName}:9545`,
'swap-factory-address': SWAP_FACTORY_ADDRESS,
password: 'password',
'postage-stamp-address': POSTAGE_STAMP_ADDRESS,
'price-oracle-address': PRICE_ORACLE_ADDRESS,
'network-id': '4020',
'full-node': 'true',
'welcome-message': 'You have found the queen of the beehive...',
'cors-allowed-origins': '*',
...contractAddresses,
}
if (bootnode) {
@ -417,4 +411,30 @@ export class Docker {
return previous
}, [])
}
private async pullImageIfNotFound(name: string): Promise<void> {
try {
await this.docker.getImage(name)
} catch (e) {
const pullStream = await this.docker.pull(name)
await new Promise(res => this.docker.modem.followProgress(pullStream, res))
}
}
private async getContractAddresses(imageName: string): Promise<Record<string, string>> {
await this.pullImageIfNotFound(imageName)
const imageMetadata = await this.docker.getImage(imageName).inspect()
const contractAddresses: Record<string, string> = {}
// @ts-ignore: Dockerode typings does not have iterator even though it is a simple object
for (const [labelKey, labelValue] of Object.entries(imageMetadata.Config.Labels)) {
if (labelKey.startsWith(CONTRACT_LABEL_KEY_PREFIX)) {
contractAddresses[labelKey.replace(CONTRACT_LABEL_KEY_PREFIX, '')] = labelValue
}
}
return contractAddresses
}
}